From 187572ec10f6845ac7bce7f74e72f2bba5707c5c Mon Sep 17 00:00:00 2001 From: Vineet1101 Date: Wed, 10 Dec 2025 05:04:29 -0800 Subject: [PATCH 1/2] added docs for installing p4sim Signed-off-by: Vineet1101 --- doc/Using_p4sim.md | 352 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 doc/Using_p4sim.md diff --git a/doc/Using_p4sim.md b/doc/Using_p4sim.md new file mode 100644 index 00000000..b40e7ca6 --- /dev/null +++ b/doc/Using_p4sim.md @@ -0,0 +1,352 @@ +# P4Sim: NS-3-Based P4 Simulation Environment + +### Index + +- ⭐ [Local Deployment (ns-3.39)](#local-deployment-ns339) +- Virtual Machine as Virtual Env + - [ns-3 Version 3.x – 3.35](#setup-ns335) + - ⭐ [ns-3 Version 3.36 – 3.39](#setup-ns339) +- [Appendix](#appendix) + +## Installation & Usage Guide + +It is recommended to use a **virtual machine** with Vagrant to simplify the installation and ensure compatibility. + +## Local Deployment (ns-3.39) [⤴️](#index) + +This guide walks you through setting up a local environment to run the P4Sim integrated with `ns-3.39` on Ubuntu 24.04. The full setup includes installing the behavioral model (`bmv2`), setting up SSH for remote access, and building the ns-3 project with P4Sim support. This is tested with `Ubuntu 24.04 LTS Desktop`. + +> ⚠️ Note: The bmv2 and P4 software installation step will take **~3 hours** and consume up to **15GB** of disk space. + +--- + +## 📁 1. Initialize the Working Directory + +Create a workspace and install basic development tools. + +```bash +sudo apt update +mkdir ~/workdir +cd ~/workdir +sudo apt install git vim cmake +``` + +--- + +## 🛠️ 2. Install P4 Behavioral Model (bmv2) and Dependencies + +> This installs all necessary libraries and tools for P4 development (via the official `p4lang/tutorials` repo). + +```bash +cd ~ +git clone https://github.com/p4lang/tutorials +mkdir ~/src +cd ~/src +../tutorials/vm-ubuntu-24.04/install.sh |& tee log.txt +``` + +After installation, verify that `simple_switch` is available: + +```bash +simple_switch +``` + +--- + +## 🧪 3. Clone and Build ns-3.39 with P4Simulator + +### Step 3.1: Clone ns-3.39 + +```bash +cd ~/workdir +git clone https://github.com/nsnam/ns-3-dev-git.git ns3.39 +cd ns3.39 +git checkout ns-3.39 +``` + +### Step 3.2: Add P4Sim Module + +```bash +cd contrib +git clone https://github.com/HapCommSys/p4sim.git +cd p4sim +sudo ./set_pkg_config_env.sh +``` + +### Step 3.3: Configure and Build + +```bash +cd ../.. +./ns3 configure --enable-tests --enable-examples +./ns3 build +``` + +--- + +## ▶️ 4. Run an Example + +You can run a built-in example using: + +```bash +./ns3 run "exampleA" # This will run exampleA (name). +``` + +--- + +## 🔧 5. Configure P4 Files in Your Simulation + +You may need to **manually update file paths** for P4 artifacts in your simulation code. + +Example path updates: + +```cpp +// p4 is the username +std::string p4JsonPath = "/home/p4/workdir/ns3.39/contrib/p4sim/test/test_simple/test_simple.json"; +std::string flowTablePath = "/home/p4/workdir/ns3.39/contrib/p4sim/test/test_simple/flowtable_0.txt"; +std::string topoInput = "/home/p4/workdir/ns3.39/contrib/p4sim/test/test_simple/topo.txt"; +``` + +Make sure these paths match your actual working directory and files. + +--- + +## ✅ Done! + +You now have a working ns-3.39 simulator with P4 integration ready for your experiments. + +--- + +## 📬 Feedback or Issues? + +If you encounter problems or have suggestions, feel free to open an issue or contact the maintainer. + +✉️ **Contact:** mingyu.ma@tu-dresden.de + + + +# Virtual Machine as virtual env ## + +`p4sim` integrates an NS-3-based P4 simulation environment with virtual machine configuration files sourced via sparse checkout from the [P4Lang Tutorials repository](https://github.com/p4lang/tutorials/tree/master). + +The `vm` directory contains Vagrant configurations and bootstrap scripts for Ubuntu-based virtual machines (Ubuntu 24.04 recommended). These pre-configured environments streamline the setup process, ensuring compatibility and reducing installation issues. + +Tested with: +- P4Lang Tutorials Commit: `7273da1c2ac2fd05cea0a9dd0504184b8c955eae` +- Date: `2025-01-25` + +Notes: +- Ensure you have `Vagrant` and `VirtualBox` installed before running `vagrant up dev`. +- The setup script (`set_pkg_config_env.sh`) configures the required environment variables for P4Sim. +- `Ubuntu 24.04` is the recommended OS for the virtual machine. + +--- + +## Setup Instructions for ns-3 version 3.x - 3.35 (Build with `waf`) [⤴️](#index) + +This has been tested with ns-3 repo Tag `ns-3.35`. + +### 1. Build the Virtual Machine +```bash +# with vm-ubuntu-24.04/Vagrantfile or vm-ubuntu-20.04/Vagrantfile +vagrant up dev + +sudo apt update +sudo apt install git vim + +cd ~ +git clone https://github.com/p4lang/tutorials +mkdir ~/src +cd ~/src +../tutorials/vm-ubuntu-24.04/install.sh |& tee log.txt +``` + +Please also **check the webpage**: [Introduction build venv of vm-ubuntu-24.04](https://github.com/p4lang/tutorials/tree/7273da1c2ac2fd05cea0a9dd0504184b8c955eae/vm-ubuntu-24.04#introduction), current version you need to install the tools by yourself: [install](https://github.com/p4lang/tutorials/tree/7273da1c2ac2fd05cea0a9dd0504184b8c955eae/vm-ubuntu-24.04#installing-open-source-p4-development-tools-on-the-vm) + +This will create a virtual machine with name "P4 Tutorial Development" with the date. Please **test with `simple_switch` command**, to test if the `bmv2` is correct installed. Later we use the libs. + +### 2. Clone the NS-3 Repository +```bash +cd +mkdir workdir +cd workdir +git clone https://github.com/nsnam/ns-3-dev-git.git ns3.35 +cd ns3.35 +git checkout ns-3.35 +``` + +### 3. Clone & Integrate `p4sim` into NS-3 +```bash +cd /home/p4/workdir/ns3.35/contrib/ +git clone https://github.com/HapCommSys/p4sim.git +``` + +### 4. Set Up the Environment (for external libs) +```bash +cd /home/p4/workdir/ns3.35/contrib/p4sim/ # p4sim root directory +sudo ./set_pkg_config_env.sh +``` + +### 5. Patch for the ns-3 code +```bash +cd ../../ # in ns-3 root directory +git apply ./contrib/p4sim/doc/changes.patch +``` + +### 6. Configure & Build NS-3 +```bash +# in ns-3 root directory +./ns3 configure --enable-examples --enable-tests +./ns3 build +``` + +### 7. Run a Simulation Example +```bash +./ns3 run "exampleA" # This will run exampleA (name). + +# In the p4sim example, you may need to adjust the path of p4 and other files. +# For example: +# std::string p4JsonPath = +# "/home/p4/workdir/ns3.35/contrib/p4sim/test/test_simple/test_simple.json"; +# std::string flowTablePath = +# "/home/p4/workdir/ns3.35/contrib/p4sim/test/test_simple/flowtable_0.txt"; +# std::string topoInput = "/home/p4/workdir/ns3.35/contrib/p4sim/test/test_simple/topo.txt"; +``` + +--- + +## ⭐ Setup Instructions for ns-3 version 3.36 - 3.39 (Build with `Cmake`) [⤴️](#index) + +This has been tested with ns-3 repo Tag `ns-3.39`. Because the virtual machine will build BMv2 and libs with **C++17**, and ns-3 p4simulator using the external inlucde file and libs, therefore the ns-3 also need to build with **C++17**. +The include file is: `/usr/local/include/bm`, the libs is `/usr/local/lib/libbmall.so`. + + +### 1. Build the Virtual Machine +```bash +# with vm-ubuntu-24.04/Vagrantfile or vm-ubuntu-20.04/Vagrantfile +vagrant up dev + +sudo apt update +sudo apt install git vim + +cd ~ +git clone https://github.com/p4lang/tutorials +mkdir ~/src +cd ~/src +../tutorials/vm-ubuntu-24.04/install.sh |& tee log.txt + +``` + +Please also **check the webpage**: [Introduction build venv of vm-ubuntu-24.04](https://github.com/p4lang/tutorials/tree/7273da1c2ac2fd05cea0a9dd0504184b8c955eae/vm-ubuntu-24.04#introduction), current version you need to install the tools by yourself: [install](https://github.com/p4lang/tutorials/tree/7273da1c2ac2fd05cea0a9dd0504184b8c955eae/vm-ubuntu-24.04#installing-open-source-p4-development-tools-on-the-vm) + +This will create a virtual machine with name "P4 Tutorial Development" with the date. Please **test with `simple_switch` command**, to test if the `bmv2` is correct installed. Later we use the libs. + +### 2. install Cmake +```bash +sudo apt update +sudo apt install cmake + +``` + +### 3. Clone the NS-3 Repository +```bash +cd +mkdir workdir +cd workdir +git clone https://github.com/nsnam/ns-3-dev-git.git ns3.39 +cd ns3.39 +git checkout ns-3.39 +``` + +### 4. Clone & Integrate `p4sim` into NS-3 +```bash +cd /home/p4/workdir/ns3.39/contrib/ +git clone https://github.com/HapCommSys/p4sim.git +``` + +### 5. Set Up the Environment (for external libs) +```bash +cd /home/p4/workdir/ns3.39/contrib/p4sim/ # p4sim root directory +sudo ./set_pkg_config_env.sh +``` + +### 6. Configure & Build NS-3 +```bash +# in ns-3 root directory +./ns3 configure --enable-tests --enable-examples +./ns3 build +``` + +### 7. Run a Simulation Example +```bash +./ns3 run "exampleA" # This will run exampleA (name). + +# In the p4sim example, you may need to adjust the path of p4 and other files. +# For example: +# std::string p4JsonPath = +# "/home/p4/workdir/ns3.35/contrib/p4sim/test/test_simple/test_simple.json"; +# std::string flowTablePath = +# "/home/p4/workdir/ns3.35/contrib/p4sim/test/test_simple/flowtable_0.txt"; +# std::string topoInput = "/home/p4/workdir/ns3.35/contrib/p4sim/test/test_simple/topo.txt"; +``` + +--- + +# Install on a local machine + +Not been tested yet. + +# References + +[1] [add_specific_folder_with_submodule_to_a_repository](https://www.reddit.com/r/git/comments/sme7k4/add_specific_folder_with_submodule_to_a_repository/) + +[2] [P4Lang Tutorials repository](https://github.com/p4lang/tutorials/tree/master) + +# Appendix [⤴️](#index) + +After `Install P4 Behavioral Model (bmv2) and Dependencies`, you should have that: + +```bash +# For the libs +(p4dev-python-venv) mm@bb24:~$ ls /usr/local/lib/ | grep bm +libbmall.a +libbmall.la +libbmall.so +libbmall.so.0 +libbmall.so.0.0.0 +libbm_grpc_dataplane.a +libbm_grpc_dataplane.la +libbm_grpc_dataplane.so +libbm_grpc_dataplane.so.0 +libbm_grpc_dataplane.so.0.0.0 +libbmp4apps.a +libbmp4apps.la +libbmp4apps.so +libbmp4apps.so.0 +libbmp4apps.so.0.0.0 +libbmpi.a +libbmpi.la +libbmpi.so +libbmpi.so.0 +libbmpi.so.0.0.0 + +# For the include files +(p4dev-python-venv) mm@bb24:~$ ls /usr/local/include/bm +bm_apps PI PsaSwitch.h SimplePreLAG.h SimpleSwitch.h standard_types.h +bm_grpc pna_nic_constants.h psa_switch_types.h simple_pre_lag_types.h simple_switch_types.h thrift +bm_runtime PnaNic.h simple_pre_constants.h simple_pre_types.h spdlog +bm_sim pna_nic_types.h SimplePre.h simple_switch standard_constants.h +config.h psa_switch_constants.h simple_pre_lag_constants.h simple_switch_constants.h Standard.h + +``` + +After running `sudo ./set_pkg_config_env.sh`, you should have that: (In between, we only use bm.) + +```bash +# set_pkg_config_env.sh, the p4simulator module requires the first (bm) +pkg-config --list-all | grep xxx + bm BMv2 - Behavioral Model + simple_switch simple switch - Behavioral Model Target Simple Switch + boost_system Boost System - Boost System + +# SPD log from BMv2 should be blocked, ns-3 has it's own logging system. +``` \ No newline at end of file From 7d3d62b85d51eeef946ffa124d92f232241d8b14 Mon Sep 17 00:00:00 2001 From: Vineet1101 Date: Thu, 11 Dec 2025 18:02:34 -0800 Subject: [PATCH 2/2] added examples and p4sim section in the top level readme Signed-off-by: Vineet1101 --- README.md | 18 + doc/{Using_p4sim.md => P4sim/README.md} | 30 +- doc/P4sim/examples/P4-basic-controller.cc | 450 ++++++++++++++++++++++ doc/P4sim/examples/P4-basic-example.cc | 434 +++++++++++++++++++++ 4 files changed, 917 insertions(+), 15 deletions(-) rename doc/{Using_p4sim.md => P4sim/README.md} (92%) create mode 100644 doc/P4sim/examples/P4-basic-controller.cc create mode 100644 doc/P4sim/examples/P4-basic-example.cc diff --git a/README.md b/README.md index cedaf68e..a7b3dfed 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ * [Introduction](#introduction) * [Presentation](#presentation) * [P4 Documentation](#p4-documentation) +* [P4Sim] (#p4sim) * [Obtaining required software](#obtaining-required-software) * [To build the virtual machine](#to-build-the-virtual-machine) * [Accessing the VM](#accessing-the-vm) @@ -83,6 +84,23 @@ All exercises in this repository use the v1model architecture, the documentation 1. The BMv2 Simple Switch target document accessible [here](https://github.com/p4lang/behavioral-model/blob/master/docs/simple_switch.md) talks mainly about the v1model architecture. 2. The include file `v1model.p4` has extensive comments and can be accessed [here](https://github.com/p4lang/p4c/blob/master/p4include/v1model.p4). +## P4sim – A Simple P4 Behavioral Simulator + +**P4sim** is a lightweight, dependency-free P4 packet-processing simulator designed for learning and experimentation. +Unlike BMv2 or P4-DPDK, P4sim does **not** emulate a full switch pipeline. Instead, it provides a minimal execution environment that evaluates P4 parser, control, and deparser logic on user-provided packets. + +P4sim is intended for: + +* Running P4 programs without installing large data plane frameworks +* Quickly testing parser and control-flow behavior +* Understanding how P4 programs process packets step-by-step + +P4sim is **not** a performance model and does not aim to replicate full-featured switch behavior (such as multitable pipelines, extern objects, or architecture-specific metadata). +It is mainly a **teaching and debugging tool**. + +To learn more or try P4sim examples, see: +[`P4sim/README.md`](./doc/P4sim/README.md) + ## Obtaining required software If you are starting this tutorial at one of the proctored tutorial events, diff --git a/doc/Using_p4sim.md b/doc/P4sim/README.md similarity index 92% rename from doc/Using_p4sim.md rename to doc/P4sim/README.md index b40e7ca6..bc085125 100644 --- a/doc/Using_p4sim.md +++ b/doc/P4sim/README.md @@ -2,25 +2,25 @@ ### Index -- ⭐ [Local Deployment (ns-3.39)](#local-deployment-ns339) +- [Local Deployment (ns-3.39)](#local-deployment-ns339) - Virtual Machine as Virtual Env - [ns-3 Version 3.x – 3.35](#setup-ns335) - - ⭐ [ns-3 Version 3.36 – 3.39](#setup-ns339) + - [ns-3 Version 3.36 – 3.39](#setup-ns339) - [Appendix](#appendix) ## Installation & Usage Guide It is recommended to use a **virtual machine** with Vagrant to simplify the installation and ensure compatibility. -## Local Deployment (ns-3.39) [⤴️](#index) +## Local Deployment (ns-3.39) This guide walks you through setting up a local environment to run the P4Sim integrated with `ns-3.39` on Ubuntu 24.04. The full setup includes installing the behavioral model (`bmv2`), setting up SSH for remote access, and building the ns-3 project with P4Sim support. This is tested with `Ubuntu 24.04 LTS Desktop`. -> ⚠️ Note: The bmv2 and P4 software installation step will take **~3 hours** and consume up to **15GB** of disk space. +> Note: The bmv2 and P4 software installation step will take **~3 hours** and consume up to **15GB** of disk space. --- -## 📁 1. Initialize the Working Directory +## 1. Initialize the Working Directory Create a workspace and install basic development tools. @@ -33,7 +33,7 @@ sudo apt install git vim cmake --- -## 🛠️ 2. Install P4 Behavioral Model (bmv2) and Dependencies +## 2. Install P4 Behavioral Model (bmv2) and Dependencies > This installs all necessary libraries and tools for P4 development (via the official `p4lang/tutorials` repo). @@ -53,7 +53,7 @@ simple_switch --- -## 🧪 3. Clone and Build ns-3.39 with P4Simulator +## 3. Clone and Build ns-3.39 with P4Simulator ### Step 3.1: Clone ns-3.39 @@ -83,7 +83,7 @@ cd ../.. --- -## ▶️ 4. Run an Example +## 4. Run an Example You can run a built-in example using: @@ -93,7 +93,7 @@ You can run a built-in example using: --- -## 🔧 5. Configure P4 Files in Your Simulation +## 5. Configure P4 Files in Your Simulation You may need to **manually update file paths** for P4 artifacts in your simulation code. @@ -110,17 +110,17 @@ Make sure these paths match your actual working directory and files. --- -## ✅ Done! +## Done! You now have a working ns-3.39 simulator with P4 integration ready for your experiments. --- -## 📬 Feedback or Issues? +## Feedback or Issues? If you encounter problems or have suggestions, feel free to open an issue or contact the maintainer. -✉️ **Contact:** mingyu.ma@tu-dresden.de +**Contact:** mingyu.ma@tu-dresden.de @@ -141,7 +141,7 @@ Notes: --- -## Setup Instructions for ns-3 version 3.x - 3.35 (Build with `waf`) [⤴️](#index) +## Setup Instructions for ns-3 version 3.x - 3.35 (Build with `waf`) This has been tested with ns-3 repo Tag `ns-3.35`. @@ -214,7 +214,7 @@ git apply ./contrib/p4sim/doc/changes.patch --- -## ⭐ Setup Instructions for ns-3 version 3.36 - 3.39 (Build with `Cmake`) [⤴️](#index) +## Setup Instructions for ns-3 version 3.36 - 3.39 (Build with `Cmake`) This has been tested with ns-3 repo Tag `ns-3.39`. Because the virtual machine will build BMv2 and libs with **C++17**, and ns-3 p4simulator using the external inlucde file and libs, therefore the ns-3 also need to build with **C++17**. The include file is: `/usr/local/include/bm`, the libs is `/usr/local/lib/libbmall.so`. @@ -301,7 +301,7 @@ Not been tested yet. [2] [P4Lang Tutorials repository](https://github.com/p4lang/tutorials/tree/master) -# Appendix [⤴️](#index) +# Appendix After `Install P4 Behavioral Model (bmv2) and Dependencies`, you should have that: diff --git a/doc/P4sim/examples/P4-basic-controller.cc b/doc/P4sim/examples/P4-basic-controller.cc new file mode 100644 index 00000000..203eb048 --- /dev/null +++ b/doc/P4sim/examples/P4-basic-controller.cc @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2025 TU Dresden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Mingyu Ma + * + */ + +/** + * This example is same with "basic exerciese" in p4lang/tutorials + * URL: https://github.com/p4lang/tutorials/tree/master/exercises/basic + * The P4 program implements basic ipv4 forwarding, also with ARP. + * Controller + * ┌──────────┐ ┌──────────┐ + * │ Switch 2 \\ /│ Switch 3 │ + * └─────┬────┘ \ // └──────┬───┘ + * │ \ / │ + * │ / │ + * ┌─────┴────┐ / \ ┌──────┴───┐ + * │ Switch 0 // \ \ │ Switch 1 │ + * ┌───┼ │ \\ ┼────┐ + * │ └────────┬─┘ └┬─────────┘ │ + * ┌───┼────┐ ┌─┴──────┐ ┌─────┼──┐ ┌─────┼──┐ + * │ host 4 │ │ host 5 │ │ host 6 │ │ host 7 │ + * └────────┘ └────────┘ └────────┘ └────────┘ + */ + +#include "ns3/applications-module.h" +#include "ns3/bridge-helper.h" +#include "ns3/core-module.h" +#include "ns3/csma-helper.h" +#include "ns3/format-utils.h" +#include "ns3/internet-module.h" +#include "ns3/network-module.h" +#include "ns3/p4-controller.h" +#include "ns3/p4-helper.h" +#include "ns3/p4-topology-reader-helper.h" + +#include +#include + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("P4BasicExample"); + +unsigned long start = getTickCount(); +double global_start_time = 1.0; +double sink_start_time = global_start_time + 1.0; +double client_start_time = sink_start_time + 1.0; +double client_stop_time = client_start_time + 3; +double sink_stop_time = client_stop_time + 5; +double global_stop_time = sink_stop_time + 5; + +bool first_tx = true; +bool first_rx = true; +int counter_sender_10 = 10; +int counter_receiver_10 = 10; +double first_packet_send_time_tx = 0.0; +double last_packet_send_time_tx = 0.0; +double first_packet_received_time_rx = 0.0; +double last_packet_received_time_rx = 0.0; +uint64_t totalTxBytes = 0; +uint64_t totalRxBytes = 0; + +// Convert IP address to hexadecimal format +std::string ConvertIpToHex(Ipv4Address ipAddr) { + std::ostringstream hexStream; + uint32_t ip = ipAddr.Get(); // Get the IP address as a 32-bit integer + hexStream << "0x" << std::hex << std::setfill('0') << std::setw(2) + << ((ip >> 24) & 0xFF) // First byte + << std::setw(2) << ((ip >> 16) & 0xFF) // Second byte + << std::setw(2) << ((ip >> 8) & 0xFF) // Third byte + << std::setw(2) << (ip & 0xFF); // Fourth byte + return hexStream.str(); +} + +// Convert MAC address to hexadecimal format +std::string ConvertMacToHex(Address macAddr) { + std::ostringstream hexStream; + Mac48Address mac = + Mac48Address::ConvertFrom(macAddr); // Convert Address to Mac48Address + uint8_t buffer[6]; + mac.CopyTo(buffer); // Copy MAC address bytes into buffer + + hexStream << "0x"; + for (int i = 0; i < 6; ++i) { + hexStream << std::hex << std::setfill('0') << std::setw(2) + << static_cast(buffer[i]); + } + return hexStream.str(); +} + +void TxCallback(Ptr packet) { + if (first_tx) { + // here we just simple jump the first 10 pkts (include some of ARP packets) + first_packet_send_time_tx = Simulator::Now().GetSeconds(); + counter_sender_10--; + if (counter_sender_10 == 0) { + first_tx = false; + } + } else { + totalTxBytes += packet->GetSize(); + last_packet_send_time_tx = Simulator::Now().GetSeconds(); + } +} + +void RxCallback(Ptr packet, const Address &addr) { + if (first_rx) { + // here we just simple jump the first 10 pkts (include some of ARP packets) + first_packet_received_time_rx = Simulator::Now().GetSeconds(); + counter_receiver_10--; + if (counter_receiver_10 == 0) { + first_rx = false; + } + } else { + totalRxBytes += packet->GetSize(); + last_packet_received_time_rx = Simulator::Now().GetSeconds(); + } +} + +void PrintFinalThroughput() { + double send_time = last_packet_send_time_tx - first_packet_send_time_tx; + double elapsed_time = + last_packet_received_time_rx - first_packet_received_time_rx; + + double finalTxThroughput = (totalTxBytes * 8.0) / (send_time * 1e6); + double finalRxThroughput = (totalRxBytes * 8.0) / (elapsed_time * 1e6); + std::cout << "client_start_time: " << first_packet_send_time_tx + << "client_stop_time: " << last_packet_send_time_tx + << "sink_start_time: " << first_packet_received_time_rx + << "sink_stop_time: " << last_packet_received_time_rx << std::endl; + + std::cout << "======================================" << std::endl; + std::cout << "Final Simulation Results:" << std::endl; + std::cout << "Total Transmitted Bytes: " << totalTxBytes << " bytes in time " + << send_time << std::endl; + std::cout << "Total Received Bytes: " << totalRxBytes << " bytes in time " + << elapsed_time << std::endl; + std::cout << "Final Transmitted Throughput: " << finalTxThroughput << " Mbps" + << std::endl; + std::cout << "Final Received Throughput: " << finalRxThroughput << " Mbps" + << std::endl; + std::cout << "======================================" << std::endl; +} + +// ============================ data struct ============================ +struct SwitchNodeC_t { + NetDeviceContainer switchDevices; + std::vector switchPortInfos; +}; + +struct HostNodeC_t { + NetDeviceContainer hostDevice; + Ipv4InterfaceContainer hostIpv4; + unsigned int linkSwitchIndex; + unsigned int linkSwitchPort; + std::string hostIpv4Str; +}; + +int main(int argc, char *argv[]) { + LogComponentEnable("P4BasicExample", LOG_LEVEL_INFO); + LogComponentEnable("P4Controller", LOG_LEVEL_INFO); + LogComponentEnable("P4CoreV1model", LOG_LEVEL_WARN); + + // ============================ parameters ============================ + int running_number = 0; + uint16_t pktSize = 1000; // in Bytes. 1458 to prevent fragments, default 512 + std::string appDataRate = "3Mbps"; // Default application data rate + std::string ns3_link_rate = "1000Mbps"; + bool enableTracePcap = true; + + std::string p4JsonPath = "/home/p4/workdir/ns3.39/contrib/p4sim/examples/" + "p4src/p4_basic/p4_basic.json"; + std::string flowTableDirPath = + "/home/p4/workdir/ns3.39/contrib/p4sim/examples/p4src/p4_basic/"; + std::string topoInput = + "/home/p4/workdir/ns3.39/contrib/p4sim/examples/p4src/p4_basic/topo.txt"; + std::string topoFormat("CsmaTopo"); + + // ============================ command line ============================ + CommandLine cmd; + cmd.AddValue("runnum", "running number in loops", running_number); + cmd.AddValue("pktSize", "Packet size in bytes (default 1000)", pktSize); + cmd.AddValue("appDataRate", "Application data rate in bps (default 1Mbps)", + appDataRate); + cmd.AddValue("pcap", "Trace packet pacp [true] or not[false]", + enableTracePcap); + cmd.Parse(argc, argv); + + // ============================ topo -> network ============================ + P4TopologyReaderHelper p4TopoHelper; + p4TopoHelper.SetFileName(topoInput); + p4TopoHelper.SetFileType(topoFormat); + NS_LOG_INFO("*** Reading topology from file: " + << topoInput << " with format: " << topoFormat); + + // Get the topology reader, and read the file, load in the m_linksList. + Ptr topoReader = p4TopoHelper.GetTopologyReader(); + + topoReader->PrintTopology(); + + if (topoReader->LinksSize() == 0) { + NS_LOG_ERROR("Problems reading the topology file. Failing."); + return -1; + } + + // get switch and host node + NodeContainer terminals = topoReader->GetHostNodeContainer(); + NodeContainer switchNode = topoReader->GetSwitchNodeContainer(); + + const unsigned int hostNum = terminals.GetN(); + const unsigned int switchNum = switchNode.GetN(); + NS_LOG_INFO("*** Host number: " << hostNum + << ", Switch number: " << switchNum); + + // set default network link parameter + CsmaHelper csma; + csma.SetChannelAttribute("DataRate", StringValue(ns3_link_rate)); + csma.SetChannelAttribute("Delay", TimeValue(MilliSeconds(0.01))); + + // NetDeviceContainer hostDevices; + // NetDeviceContainer switchDevices; + P4TopologyReader::ConstLinksIterator_t iter; + SwitchNodeC_t switchNodes[switchNum]; + HostNodeC_t hostNodes[hostNum]; + unsigned int fromIndex, toIndex; + std::string dataRate, delay; + for (iter = topoReader->LinksBegin(); iter != topoReader->LinksEnd(); + iter++) { + if (iter->GetAttributeFailSafe("DataRate", dataRate)) + csma.SetChannelAttribute("DataRate", StringValue(dataRate)); + if (iter->GetAttributeFailSafe("Delay", delay)) + csma.SetChannelAttribute("Delay", StringValue(delay)); + + fromIndex = iter->GetFromIndex(); + toIndex = iter->GetToIndex(); + NetDeviceContainer link = + csma.Install(NodeContainer(iter->GetFromNode(), iter->GetToNode())); + + if (iter->GetFromType() == 's' && iter->GetToType() == 's') { + NS_LOG_INFO("*** Link from switch " + << fromIndex << " to switch " << toIndex + << " with data rate " << dataRate << " and delay " << delay); + + unsigned int fromSwitchPortNumber = + switchNodes[fromIndex].switchDevices.GetN(); + unsigned int toSwitchPortNumber = + switchNodes[toIndex].switchDevices.GetN(); + switchNodes[fromIndex].switchDevices.Add(link.Get(0)); + switchNodes[fromIndex].switchPortInfos.push_back( + "s" + UintToString(toIndex) + "_" + UintToString(toSwitchPortNumber)); + + switchNodes[toIndex].switchDevices.Add(link.Get(1)); + switchNodes[toIndex].switchPortInfos.push_back( + "s" + UintToString(fromIndex) + "_" + + UintToString(fromSwitchPortNumber)); + } else { + if (iter->GetFromType() == 's' && iter->GetToType() == 'h') { + NS_LOG_INFO("*** Link from switch " + << fromIndex << " to host" << toIndex << " with data rate " + << dataRate << " and delay " << delay); + + unsigned int fromSwitchPortNumber = + switchNodes[fromIndex].switchDevices.GetN(); + switchNodes[fromIndex].switchDevices.Add(link.Get(0)); + switchNodes[fromIndex].switchPortInfos.push_back( + "h" + UintToString(toIndex - switchNum)); + + hostNodes[toIndex - switchNum].hostDevice.Add(link.Get(1)); + hostNodes[toIndex - switchNum].linkSwitchIndex = fromIndex; + hostNodes[toIndex - switchNum].linkSwitchPort = fromSwitchPortNumber; + } else { + if (iter->GetFromType() == 'h' && iter->GetToType() == 's') { + NS_LOG_INFO("*** Link from host " << fromIndex << " to switch" + << toIndex << " with data rate " + << dataRate << " and delay " + << delay); + unsigned int toSwitchPortNumber = + switchNodes[toIndex].switchDevices.GetN(); + switchNodes[toIndex].switchDevices.Add(link.Get(1)); + switchNodes[toIndex].switchPortInfos.push_back( + "h" + UintToString(fromIndex - switchNum)); + + hostNodes[fromIndex - switchNum].hostDevice.Add(link.Get(0)); + hostNodes[fromIndex - switchNum].linkSwitchIndex = toIndex; + hostNodes[fromIndex - switchNum].linkSwitchPort = toSwitchPortNumber; + } else { + NS_LOG_ERROR("link error!"); + abort(); + } + } + } + } + + // ========================Print the Channel Type and NetDevice + // Type======================== + + InternetStackHelper internet; + internet.Install(terminals); + internet.Install(switchNode); + + Ipv4AddressHelper ipv4; + ipv4.SetBase("10.1.1.0", "255.255.255.0"); + std::vector terminalInterfaces(hostNum); + std::vector hostIpv4(hostNum); + + for (unsigned int i = 0; i < hostNum; i++) { + terminalInterfaces[i] = ipv4.Assign(terminals.Get(i)->GetDevice(0)); + hostIpv4[i] = Uint32IpToHex(terminalInterfaces[i].GetAddress(0).Get()); + } + + //=============================== Print IP and MAC + //addresses=============================== + NS_LOG_INFO("Node IP and MAC addresses:"); + for (uint32_t i = 0; i < terminals.GetN(); ++i) { + Ptr node = terminals.Get(i); + Ptr ipv4 = node->GetObject(); + Ptr netDevice = node->GetDevice(0); + + // Get the IP address + Ipv4Address ipAddr = + ipv4->GetAddress(1, 0).GetLocal(); // Interface index 1 corresponds to + // the first assigned IP + + // Get the MAC address + Ptr device = + node->GetDevice(0); // Assuming the first device is the desired one + Mac48Address mac = Mac48Address::ConvertFrom(device->GetAddress()); + + NS_LOG_INFO("Node " << i << ": IP = " << ipAddr << ", MAC = " << mac); + + // Convert to hexadecimal + std::string ipHex = ConvertIpToHex(ipAddr); + std::string macHex = ConvertMacToHex(mac); + NS_LOG_INFO("Node " << i << ": IP = " << ipHex << ", MAC = " << macHex); + } + + // Bridge or P4 switch configuration + P4Helper p4SwitchHelper; + p4SwitchHelper.SetDeviceAttribute("JsonPath", StringValue(p4JsonPath)); + p4SwitchHelper.SetDeviceAttribute("ChannelType", UintegerValue(0)); + p4SwitchHelper.SetDeviceAttribute("P4SwitchArch", UintegerValue(0)); + + P4Controller controller; + + for (unsigned int i = 0; i < switchNum; i++) { + std::string flowTablePath = + flowTableDirPath + "flowtable_" + std::to_string(i) + ".txt"; + p4SwitchHelper.SetDeviceAttribute("FlowTablePath", + StringValue(flowTablePath)); + NS_LOG_INFO("*** P4 switch configuration: " << p4JsonPath << ", \n " + << flowTablePath); + + NetDeviceContainer p4SwitchNetDeviceContainer = + p4SwitchHelper.Install(switchNode.Get(i), switchNodes[i].switchDevices); + + for (uint32_t j = 0; j < p4SwitchNetDeviceContainer.GetN(); j++) { + Ptr p4sw = + DynamicCast(p4SwitchNetDeviceContainer.Get(j)); + if (p4sw) { + controller.RegisterSwitch(p4sw); + controller.ConnectToSwitchEvents(0); // connect early + + Simulator::Schedule(Seconds(2.0), [&controller]() { + controller.PrintTableEntryCount(0, "MyIngress.ipv4_nhop"); + }); + + Simulator::Schedule(Seconds(3.0), [&controller]() { + controller.PrintFlowEntries(0, "MyIngress.ipv4_nhop"); + }); + + Simulator::Schedule(Seconds(4.0), [&, p4sw]() { + p4sw->EmitSwitchEvent(0, "Hello world from test event"); + }); + } else { + NS_LOG_WARN("Failed to cast device at index " + << j << " to P4SwitchNetDevice"); + } + } + } + + // === Configuration for Link: h0 -----> h1 === + unsigned int serverI = 3; + unsigned int clientI = 0; + uint16_t servPort = 9093; // UDP port for the server + + // === Retrieve Server Address === + Ptr node = terminals.Get(serverI); + Ptr ipv4_adder = node->GetObject(); + Ipv4Address serverAddr1 = ipv4_adder->GetAddress(1, 0).GetLocal(); + InetSocketAddress dst1 = InetSocketAddress(serverAddr1, servPort); + + // === Setup Packet Sink on Server === + PacketSinkHelper sink1("ns3::UdpSocketFactory", dst1); + ApplicationContainer sinkApp1 = sink1.Install(terminals.Get(serverI)); + sinkApp1.Start(Seconds(sink_start_time)); + sinkApp1.Stop(Seconds(sink_stop_time)); + + // === Setup OnOff Application on Client === + OnOffHelper onOff1("ns3::UdpSocketFactory", dst1); + onOff1.SetAttribute("PacketSize", UintegerValue(pktSize)); + onOff1.SetAttribute("DataRate", StringValue(appDataRate)); + onOff1.SetAttribute("OnTime", + StringValue("ns3::ConstantRandomVariable[Constant=1]")); + onOff1.SetAttribute("OffTime", + StringValue("ns3::ConstantRandomVariable[Constant=0]")); + + ApplicationContainer app1 = onOff1.Install(terminals.Get(clientI)); + app1.Start(Seconds(client_start_time)); + app1.Stop(Seconds(client_stop_time)); + + // === Setup Tracing === + Ptr ptr_app1 = + DynamicCast(terminals.Get(clientI)->GetApplication(0)); + ptr_app1->TraceConnectWithoutContext("Tx", MakeCallback(&TxCallback)); + sinkApp1.Get(0)->TraceConnectWithoutContext("Rx", MakeCallback(&RxCallback)); + + if (enableTracePcap) { + csma.EnablePcapAll("p4-basic-example"); + } + + // Run simulation + NS_LOG_INFO("Running simulation..."); + unsigned long simulate_start = getTickCount(); + Simulator::Stop(Seconds(global_stop_time)); + Simulator::Run(); + Simulator::Destroy(); + + unsigned long end = getTickCount(); + NS_LOG_INFO("Simulate Running time: " + << end - simulate_start << "ms" << std::endl + << "Total Running time: " << end - start << "ms" << std::endl + << "Run successfully!"); + + PrintFinalThroughput(); + + return 0; +} \ No newline at end of file diff --git a/doc/P4sim/examples/P4-basic-example.cc b/doc/P4sim/examples/P4-basic-example.cc new file mode 100644 index 00000000..41ddf48e --- /dev/null +++ b/doc/P4sim/examples/P4-basic-example.cc @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2025 TU Dresden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Mingyu Ma + * + */ + +/** + * This example is same with "basic exerciese" in p4lang/tutorials + * URL: https://github.com/p4lang/tutorials/tree/master/exercises/basic + * The P4 program implements basic ipv4 forwarding, also with ARP. + * + * ┌──────────┐ ┌──────────┐ + * │ Switch 2 \\ /│ Switch 3 │ + * └─────┬────┘ \ // └──────┬───┘ + * │ \ / │ + * │ / │ + * ┌─────┴────┐ / \ ┌──────┴───┐ + * │ Switch 0 // \ \ │ Switch 1 │ + * ┌───┼ │ \\ ┼────┐ + * │ └────────┬─┘ └┬─────────┘ │ + * ┌───┼────┐ ┌─┴──────┐ ┌─────┼──┐ ┌─────┼──┐ + * │ host 4 │ │ host 5 │ │ host 6 │ │ host 7 │ + * └────────┘ └────────┘ └────────┘ └────────┘ + */ + +#include "ns3/applications-module.h" +#include "ns3/bridge-helper.h" +#include "ns3/core-module.h" +#include "ns3/csma-helper.h" +#include "ns3/format-utils.h" +#include "ns3/internet-module.h" +#include "ns3/network-module.h" +#include "ns3/p4-helper.h" +#include "ns3/p4-topology-reader-helper.h" + +#include +#include + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("P4BasicExample"); + +unsigned long start = getTickCount(); +double global_start_time = 1.0; +double sink_start_time = global_start_time + 1.0; +double client_start_time = sink_start_time + 1.0; +double client_stop_time = client_start_time + 3; +double sink_stop_time = client_stop_time + 5; +double global_stop_time = sink_stop_time + 5; + +bool first_tx = true; +bool first_rx = true; +int counter_sender_10 = 10; +int counter_receiver_10 = 10; +double first_packet_send_time_tx = 0.0; +double last_packet_send_time_tx = 0.0; +double first_packet_received_time_rx = 0.0; +double last_packet_received_time_rx = 0.0; +uint64_t totalTxBytes = 0; +uint64_t totalRxBytes = 0; + +// Convert IP address to hexadecimal format +std::string +ConvertIpToHex(Ipv4Address ipAddr) +{ + std::ostringstream hexStream; + uint32_t ip = ipAddr.Get(); // Get the IP address as a 32-bit integer + hexStream << "0x" << std::hex << std::setfill('0') << std::setw(2) + << ((ip >> 24) & 0xFF) // First byte + << std::setw(2) << ((ip >> 16) & 0xFF) // Second byte + << std::setw(2) << ((ip >> 8) & 0xFF) // Third byte + << std::setw(2) << (ip & 0xFF); // Fourth byte + return hexStream.str(); +} + +// Convert MAC address to hexadecimal format +std::string +ConvertMacToHex(Address macAddr) +{ + std::ostringstream hexStream; + Mac48Address mac = Mac48Address::ConvertFrom(macAddr); // Convert Address to Mac48Address + uint8_t buffer[6]; + mac.CopyTo(buffer); // Copy MAC address bytes into buffer + + hexStream << "0x"; + for (int i = 0; i < 6; ++i) + { + hexStream << std::hex << std::setfill('0') << std::setw(2) << static_cast(buffer[i]); + } + return hexStream.str(); +} + +void +TxCallback(Ptr packet) +{ + if (first_tx) + { + // here we just simple jump the first 10 pkts (include some of ARP packets) + first_packet_send_time_tx = Simulator::Now().GetSeconds(); + counter_sender_10--; + if (counter_sender_10 == 0) + { + first_tx = false; + } + } + else + { + totalTxBytes += packet->GetSize(); + last_packet_send_time_tx = Simulator::Now().GetSeconds(); + } +} + +void +RxCallback(Ptr packet, const Address& addr) +{ + if (first_rx) + { + // here we just simple jump the first 10 pkts (include some of ARP packets) + first_packet_received_time_rx = Simulator::Now().GetSeconds(); + counter_receiver_10--; + if (counter_receiver_10 == 0) + { + first_rx = false; + } + } + else + { + totalRxBytes += packet->GetSize(); + last_packet_received_time_rx = Simulator::Now().GetSeconds(); + } +} + +void +PrintFinalThroughput() +{ + double send_time = last_packet_send_time_tx - first_packet_send_time_tx; + double elapsed_time = last_packet_received_time_rx - first_packet_received_time_rx; + + double finalTxThroughput = (totalTxBytes * 8.0) / (send_time * 1e6); + double finalRxThroughput = (totalRxBytes * 8.0) / (elapsed_time * 1e6); + std::cout << "client_start_time: " << first_packet_send_time_tx + << "client_stop_time: " << last_packet_send_time_tx + << "sink_start_time: " << first_packet_received_time_rx + << "sink_stop_time: " << last_packet_received_time_rx << std::endl; + + std::cout << "======================================" << std::endl; + std::cout << "Final Simulation Results:" << std::endl; + std::cout << "Total Transmitted Bytes: " << totalTxBytes << " bytes in time " << send_time + << std::endl; + std::cout << "Total Received Bytes: " << totalRxBytes << " bytes in time " << elapsed_time + << std::endl; + std::cout << "Final Transmitted Throughput: " << finalTxThroughput << " Mbps" << std::endl; + std::cout << "Final Received Throughput: " << finalRxThroughput << " Mbps" << std::endl; + std::cout << "======================================" << std::endl; +} + +// ============================ data struct ============================ +struct SwitchNodeC_t +{ + NetDeviceContainer switchDevices; + std::vector switchPortInfos; +}; + +struct HostNodeC_t +{ + NetDeviceContainer hostDevice; + Ipv4InterfaceContainer hostIpv4; + unsigned int linkSwitchIndex; + unsigned int linkSwitchPort; + std::string hostIpv4Str; +}; + +int +main(int argc, char* argv[]) +{ + LogComponentEnable("P4BasicExample", LOG_LEVEL_INFO); + + // ============================ parameters ============================ + int running_number = 0; + uint16_t pktSize = 1000; // in Bytes. 1458 to prevent fragments, default 512 + std::string appDataRate = "3Mbps"; // Default application data rate + std::string ns3_link_rate = "1000Mbps"; + bool enableTracePcap = true; + + std::string p4JsonPath = + "/home/p4/workdir/ns-3-dev-git/contrib/p4sim/examples/p4src/p4_basic/p4_basic.json"; + std::string flowTableDirPath = + "/home/p4/workdir/ns-3-dev-git/contrib/p4sim/examples/p4src/p4_basic/"; + std::string topoInput = + "/home/p4/workdir/ns-3-dev-git/contrib/p4sim/examples/p4src/p4_basic/topo.txt"; + std::string topoFormat("CsmaTopo"); + + // ============================ command line ============================ + CommandLine cmd; + cmd.AddValue("runnum", "running number in loops", running_number); + cmd.AddValue("pktSize", "Packet size in bytes (default 1000)", pktSize); + cmd.AddValue("appDataRate", "Application data rate in bps (default 1Mbps)", appDataRate); + cmd.AddValue("pcap", "Trace packet pacp [true] or not[false]", enableTracePcap); + cmd.Parse(argc, argv); + + // ============================ topo -> network ============================ + P4TopologyReaderHelper p4TopoHelper; + p4TopoHelper.SetFileName(topoInput); + p4TopoHelper.SetFileType(topoFormat); + NS_LOG_INFO("*** Reading topology from file: " << topoInput << " with format: " << topoFormat); + + // Get the topology reader, and read the file, load in the m_linksList. + Ptr topoReader = p4TopoHelper.GetTopologyReader(); + + topoReader->PrintTopology(); + + if (topoReader->LinksSize() == 0) + { + NS_LOG_ERROR("Problems reading the topology file. Failing."); + return -1; + } + + // get switch and host node + NodeContainer terminals = topoReader->GetHostNodeContainer(); + NodeContainer switchNode = topoReader->GetSwitchNodeContainer(); + + const unsigned int hostNum = terminals.GetN(); + const unsigned int switchNum = switchNode.GetN(); + NS_LOG_INFO("*** Host number: " << hostNum << ", Switch number: " << switchNum); + + // set default network link parameter + CsmaHelper csma; + csma.SetChannelAttribute("DataRate", StringValue(ns3_link_rate)); + csma.SetChannelAttribute("Delay", TimeValue(MilliSeconds(0.01))); + + // NetDeviceContainer hostDevices; + // NetDeviceContainer switchDevices; + P4TopologyReader::ConstLinksIterator_t iter; + SwitchNodeC_t switchNodes[switchNum]; + HostNodeC_t hostNodes[hostNum]; + unsigned int fromIndex, toIndex; + std::string dataRate, delay; + for (iter = topoReader->LinksBegin(); iter != topoReader->LinksEnd(); iter++) + { + if (iter->GetAttributeFailSafe("DataRate", dataRate)) + csma.SetChannelAttribute("DataRate", StringValue(dataRate)); + if (iter->GetAttributeFailSafe("Delay", delay)) + csma.SetChannelAttribute("Delay", StringValue(delay)); + + fromIndex = iter->GetFromIndex(); + toIndex = iter->GetToIndex(); + NetDeviceContainer link = + csma.Install(NodeContainer(iter->GetFromNode(), iter->GetToNode())); + + if (iter->GetFromType() == 's' && iter->GetToType() == 's') + { + NS_LOG_INFO("*** Link from switch " << fromIndex << " to switch " << toIndex + << " with data rate " << dataRate << " and delay " + << delay); + + unsigned int fromSwitchPortNumber = switchNodes[fromIndex].switchDevices.GetN(); + unsigned int toSwitchPortNumber = switchNodes[toIndex].switchDevices.GetN(); + switchNodes[fromIndex].switchDevices.Add(link.Get(0)); + switchNodes[fromIndex].switchPortInfos.push_back("s" + UintToString(toIndex) + "_" + + UintToString(toSwitchPortNumber)); + + switchNodes[toIndex].switchDevices.Add(link.Get(1)); + switchNodes[toIndex].switchPortInfos.push_back("s" + UintToString(fromIndex) + "_" + + UintToString(fromSwitchPortNumber)); + } + else + { + if (iter->GetFromType() == 's' && iter->GetToType() == 'h') + { + NS_LOG_INFO("*** Link from switch " << fromIndex << " to host" << toIndex + << " with data rate " << dataRate + << " and delay " << delay); + + unsigned int fromSwitchPortNumber = switchNodes[fromIndex].switchDevices.GetN(); + switchNodes[fromIndex].switchDevices.Add(link.Get(0)); + switchNodes[fromIndex].switchPortInfos.push_back("h" + + UintToString(toIndex - switchNum)); + + hostNodes[toIndex - switchNum].hostDevice.Add(link.Get(1)); + hostNodes[toIndex - switchNum].linkSwitchIndex = fromIndex; + hostNodes[toIndex - switchNum].linkSwitchPort = fromSwitchPortNumber; + } + else + { + if (iter->GetFromType() == 'h' && iter->GetToType() == 's') + { + NS_LOG_INFO("*** Link from host " << fromIndex << " to switch" << toIndex + << " with data rate " << dataRate + << " and delay " << delay); + unsigned int toSwitchPortNumber = switchNodes[toIndex].switchDevices.GetN(); + switchNodes[toIndex].switchDevices.Add(link.Get(1)); + switchNodes[toIndex].switchPortInfos.push_back( + "h" + UintToString(fromIndex - switchNum)); + + hostNodes[fromIndex - switchNum].hostDevice.Add(link.Get(0)); + hostNodes[fromIndex - switchNum].linkSwitchIndex = toIndex; + hostNodes[fromIndex - switchNum].linkSwitchPort = toSwitchPortNumber; + } + else + { + NS_LOG_ERROR("link error!"); + abort(); + } + } + } + } + + // ========================Print the Channel Type and NetDevice Type======================== + + InternetStackHelper internet; + internet.Install(terminals); + internet.Install(switchNode); + + Ipv4AddressHelper ipv4; + ipv4.SetBase("10.1.1.0", "255.255.255.0"); + std::vector terminalInterfaces(hostNum); + std::vector hostIpv4(hostNum); + + for (unsigned int i = 0; i < hostNum; i++) + { + terminalInterfaces[i] = ipv4.Assign(terminals.Get(i)->GetDevice(0)); + hostIpv4[i] = Uint32IpToHex(terminalInterfaces[i].GetAddress(0).Get()); + } + + //=============================== Print IP and MAC addresses=============================== + NS_LOG_INFO("Node IP and MAC addresses:"); + for (uint32_t i = 0; i < terminals.GetN(); ++i) + { + Ptr node = terminals.Get(i); + Ptr ipv4 = node->GetObject(); + Ptr netDevice = node->GetDevice(0); + + // Get the IP address + Ipv4Address ipAddr = + ipv4->GetAddress(1, 0) + .GetLocal(); // Interface index 1 corresponds to the first assigned IP + + // Get the MAC address + Ptr device = node->GetDevice(0); // Assuming the first device is the desired one + Mac48Address mac = Mac48Address::ConvertFrom(device->GetAddress()); + + NS_LOG_INFO("Node " << i << ": IP = " << ipAddr << ", MAC = " << mac); + + // Convert to hexadecimal + std::string ipHex = ConvertIpToHex(ipAddr); + std::string macHex = ConvertMacToHex(mac); + NS_LOG_INFO("Node " << i << ": IP = " << ipHex << ", MAC = " << macHex); + } + + // Bridge or P4 switch configuration + P4Helper p4SwitchHelper; + p4SwitchHelper.SetDeviceAttribute("JsonPath", StringValue(p4JsonPath)); + p4SwitchHelper.SetDeviceAttribute("ChannelType", UintegerValue(0)); + p4SwitchHelper.SetDeviceAttribute("P4SwitchArch", UintegerValue(0)); + + for (unsigned int i = 0; i < switchNum; i++) + { + std::string flowTablePath = flowTableDirPath + "flowtable_" + std::to_string(i) + ".txt"; + p4SwitchHelper.SetDeviceAttribute("FlowTablePath", StringValue(flowTablePath)); + NS_LOG_INFO("*** P4 switch configuration: " << p4JsonPath << ", \n " << flowTablePath); + + p4SwitchHelper.Install(switchNode.Get(i), switchNodes[i].switchDevices); + } + + // === Configuration for Link: h0 -----> h1 === + unsigned int serverI = 3; + unsigned int clientI = 0; + uint16_t servPort = 9093; // UDP port for the server + + // === Retrieve Server Address === + Ptr node = terminals.Get(serverI); + Ptr ipv4_adder = node->GetObject(); + Ipv4Address serverAddr1 = ipv4_adder->GetAddress(1, 0).GetLocal(); + InetSocketAddress dst1 = InetSocketAddress(serverAddr1, servPort); + + // === Setup Packet Sink on Server === + PacketSinkHelper sink1("ns3::UdpSocketFactory", dst1); + ApplicationContainer sinkApp1 = sink1.Install(terminals.Get(serverI)); + sinkApp1.Start(Seconds(sink_start_time)); + sinkApp1.Stop(Seconds(sink_stop_time)); + + // === Setup OnOff Application on Client === + OnOffHelper onOff1("ns3::UdpSocketFactory", dst1); + onOff1.SetAttribute("PacketSize", UintegerValue(pktSize)); + onOff1.SetAttribute("DataRate", StringValue(appDataRate)); + onOff1.SetAttribute("OnTime", StringValue("ns3::ConstantRandomVariable[Constant=1]")); + onOff1.SetAttribute("OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]")); + + ApplicationContainer app1 = onOff1.Install(terminals.Get(clientI)); + app1.Start(Seconds(client_start_time)); + app1.Stop(Seconds(client_stop_time)); + + // === Setup Tracing === + Ptr ptr_app1 = + DynamicCast(terminals.Get(clientI)->GetApplication(0)); + ptr_app1->TraceConnectWithoutContext("Tx", MakeCallback(&TxCallback)); + sinkApp1.Get(0)->TraceConnectWithoutContext("Rx", MakeCallback(&RxCallback)); + + if (enableTracePcap) + { + csma.EnablePcapAll("p4-basic-example"); + } + + // Run simulation + NS_LOG_INFO("Running simulation..."); + unsigned long simulate_start = getTickCount(); + Simulator::Stop(Seconds(global_stop_time)); + Simulator::Run(); + Simulator::Destroy(); + + unsigned long end = getTickCount(); + NS_LOG_INFO("Simulate Running time: " << end - simulate_start << "ms" << std::endl + << "Total Running time: " << end - start << "ms" + << std::endl + << "Run successfully!"); + + PrintFinalThroughput(); + + return 0; +} \ No newline at end of file