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/P4sim/README.md b/doc/P4sim/README.md
new file mode 100644
index 00000000..bc085125
--- /dev/null
+++ b/doc/P4sim/README.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)
+
+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`)
+
+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`)
+
+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
+
+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
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