[{"content":"Executive Summary This project successfully extends the Gigaflow Virtual Switch (GVS) (find out more here) framework to enable hardware acceleration on SmartNICs, specifically targeting NetFPGA platforms. The work bridges the gap between software-defined networking (SDN) and hardware acceleration by implementing a complete pipeline from P4 code compilation to bitstream deployment and runtime rule management.\nEnhanced Gigaflow Virtual Switch (GVS) The core contribution of this project is the extension of the Gigaflow Virtual Switch to support hardware acceleration through SmartNIC offload. The gvs-offload maintains full backward compatibility with the software-only implementation while adding comprehensive hardware acceleration capabilities.\nRepository: gvs-offload\nArchitecture and Design The gvs-offload implements a hybrid software-hardware architecture where the Gigaflow cache can operate in three distinct modes:\nSoftware-only Mode: Traditional CPU-based packet processing with the original Gigaflow cache implementation Full Hardware Mode: Complete pipeline execution on SmartNIC with minimal CPU involvement The following are the additions to the original GVS:\nHardware Integration Layer The hardware integration layer provides a unified abstraction for different SmartNIC platforms. The implementation includes:\nSDNet Driver Integration: The gvs-offload integrates with Xilinx\u0026rsquo;s SDNet IP through custom drivers that handle rule installation, table updates, and statistics collection. The SDNet files included in the project are auto-generated when the bitstream is created through the Vivado software compilation process.\nRule Translation Engine: Converts high-level Gigaflow cache policies into hardware-compatible table entries and match-action rules that can be programmed into the P4 pipeline.\nIntegration with Open vSwitch The gvs-offload maintains full compatibility with OVS through the existing datapath interface while extending it with hardware acceleration flow offload APIs. The integration supports:\nSupporting Components Additional Orchestrators MLX Orchestrator: Initial framework for MLX NIC integration with traffic generation and performance benchmarking utilities Repository: MLX Orchestrator NetFPGA Orchestrator: Kernel-mode integration with comprehensive test suite for NetFPGA platforms, providing rule installation APIs and pipeline validation Repository: NetFPGA Orchestrator P4 Behavioral Simulation: Pre-deployment testing framework using Vivado simulation tools for comprehensive P4 code validation Repository: P4 Behavioral Simulation NetFPGA Hardware Offload P4 Implementation: Complete P4 pipeline for NetFPGA AU250 with Vivado compilation and bitstream generation Repository: P4 Implementation (NetFPGA) Shell Integration: NetFPGA AU250 shell integration with P4SDNet IP configuration and wrapper logic Hardware Optimization: Resource optimization and wire-speed packet processing capabilities Project Workflow This image shows the main workflow of the project. Initially, P4 code was written and tested using the P4 Behavioral Simulation. Then, the P4 code was compiled into a bitstream using Vivado and the P4 Implementation (NetFPGA) repository. This compilation process also developed the SDNet driver integration for the gvs-offload. The bitstream is then loaded onto the Xilinx AU250 card and the SDNet driver is probed to the kernel. Upon this, the test suite inside the P4 Implementation (NetFPGA) repository was run for testing basic functionality of the hardware offload. Following this, GVS was modified to support the hardware offload. While the Gigaflow logic existed in the software-only mode, flow translation was introduced to translate high-level Gigaflow cache policies into hardware-compatible table entries and match-action rules that can be programmed into the P4 pipeline. Finally, the NetFPGA Orchestrator was developed to provide a unified interface for rule installation and pipeline management. This also included the development of a test suite to validate the functionality of the hardware offload. The following is a sample diagram of the project setup: Technical Learning and Expertise Gained Throughout this project, I gained hands-on experience with data center networking and hardware acceleration using P4 and FPGA-based frameworks. I worked with Vivado, P4-SDNet, and the NetFPGA shell to compile P4 programs into SDNet IP, which was then integrated into the NetFPGA platform to build an accelerated switch. On the software side, I extended Open vSwitch (OVS) through the Gigaflow Virtual Switch (GVS), which added a caching layer. This required designing match-action tables that handled wide keys and multiple header fields, making the table structures significantly more complex than simple L2/L3 lookups. I also used DPDK in the software-offload design to enable high-performance packet processing with zero-copy techniques. Finally, I learned how to take P4-to-HDL pipelines through Vivado for synthesis and how to debug and validate the resulting hardware designs.\nI also developed skills in AXI-Stream protocol design for building stream-processing pipelines with backpressure support, and in low-level driver development for PCI Express and DMA engine programming. In learning SDN principles, I worked with the OpenFlow protocol and configured flows directly, installing rules into the Gigaflow pipeline. A key aspect of the project was hardware–software co-design: I partitioned packet-processing algorithms by mapping software pipeline stages into Gigaflow table entries on hardware, optimizing for latency and throughput.\nResults and Performance The hardware-accelerated Gigaflow implementation demonstrates significant performance improvements over software-only solutions. Currently in the process for final steps of benchmarking and testing, but initial results prior to pull request to GVS.\nHelpful Links Gigaflow Project: https://gigaflow-vswitch.github.io/ - Main Gigaflow project page NextGarch Lab: https://nextgarch-lab-ergp6tq.gamma.site/ - Laboratory environment that supported this project Acknowledgments Special thanks to my mentors and the open-source community for their guidance and support throughout this GSoC project. The work builds upon the excellent foundation provided by the original GVS project and the NetFPGA community.\n","permalink":"https://p4lang.github.io/gsoc/blog/posts/2025-09-12-gigaflow/","summary":"\u003ch2 id=\"executive-summary\"\u003eExecutive Summary\u003c/h2\u003e\n\u003cp\u003eThis project successfully extends the Gigaflow Virtual Switch (GVS) (find out more \u003ca href=\"https://gigaflow-vswitch.github.io/\"\u003ehere\u003c/a\u003e) framework to enable hardware acceleration on SmartNICs, specifically targeting NetFPGA platforms. The work bridges the gap between software-defined networking (SDN) and hardware acceleration by implementing a complete pipeline from P4 code compilation to bitstream deployment and runtime rule management.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"enhanced-gigaflow-virtual-switch-gvs\"\u003eEnhanced Gigaflow Virtual Switch (GVS)\u003c/h2\u003e\n\u003cp\u003eThe core contribution of this project is the extension of the Gigaflow Virtual Switch to support hardware acceleration through SmartNIC offload. The gvs-offload maintains full backward compatibility with the software-only implementation while adding comprehensive hardware acceleration capabilities.\u003c/p\u003e","title":"Accelerating OVS with Gigaflow: A Smart Cache for SmartNICs"},{"content":"Abstract This project aims to extend the existing P4sim module integrated within the ns-3 network simulator by implementing control plane functionalities. The P4sim currently supports the simulation of P4-programmable data planes in ns-3, providing a powerful environment for research and development in programmable networking. This project bridges that gap by integrating a control plane to support P4 Runtime, like changing the openconfig-interfaces, the ethernet augments and other runtime configurable features. The enhancements will improve the realism and usability of the simulator for research and experimentation involving P4\nGoals Control Plane Implementation for P4Sim Data Collection and Tracing Mechanism Testing and Example Scenarios Documentation Results The project successfully extends P4Sim with a programmable control-plane abstraction.\nKey contributions include:\nP4Controller class – Manages multiple switches and flow tables. Trace-based integration – Forwarded trace sources from P4SwitchNetDevice to P4Controller, allowing controllers to subscribe to switch events. Wrapper function for bmv2 switch API – Added functions in P4Controller and P4CoreV1model class to wrap functions of bmv2 switch. Examples \u0026amp; testing – Developed ns-3 simulation scripts showing dynamic flow entry queries, event handling, and logging. Documentation – Added usage guidelines, API references, and troubleshooting notes. Links All artifacts developed throughout this GSoC project are available in the following GitHub repository: https://github.com/HapCommSys/p4sim\nArchitecture The architecture of this project extends ns-3 P4Sim by introducing a working control-plane abstraction.\nIt follows a layered design that separates data-plane execution, switch abstraction, and controller logic.\nComponents P4CoreV1model\nImplements the P4 V1Model architecture inside the switch. Provides functions for flow entry management (insert, delete, modify). Exposes an internal API to P4SwitchNetDevice for data-plane execution. P4SwitchNetDevice\nActs as the ns-3 NetDevice abstraction for a P4 switch. Wraps around the P4 core (P4CoreV1model) and exposes it to the simulation. Hosts Trace Sources that allow the switch to emit events to the controller. Bridges between ns-3 simulation environment and the P4 pipeline. P4Controller\nImplements the control-plane logic in simulation. Provides high-level wrapper functions to interact with the switch (e.g., install flow entries, query table state). Subscribes to Trace Sources exposed by switches, enabling event-driven control. Control-Plane Workflow The controller connects to one or more P4 switches. During simulation, the switch emits events (e.g., flow entry installed, packet processed, error occurred) via Trace Sources. The controller’s callback handlers receive these events and take action (e.g., log, update flow table, install new rules). The controller can also proactively configure switches by invoking wrapper APIs (e.g., AddFlowEntry, DeleteFlowEntry). Controller Relevant PRs: https://github.com/HapCommSys/p4sim/pull/4\nAt this point we have P4SwitchNetdevice and P4CoreV1model and the initial implementation of control plane was depreceated.\nIn this p4 wrapper function are added in the P4Controller and P4CoreV1model class\nTracing Mechanism Relevant PR: https://github.com/HapCommSys/p4sim/pull/5\nA key part of this project is enabling runtime communication between the switch and the control plane inside the ns-3 simulation.\nTo achieve this, we have added support for the ns-3 Trace Source mechanism.\nThis means that:\nThe switch (P4SwitchNetDevice) can emit events at runtime when something of interest happens (e.g., a flow entry is added, a packet is processed, or an error occurs). The controller (P4Controller) can subscribe (connect) to these events and react accordingly using its control-plane functions. For example, when a switch emits a SwitchEvent trace source with a message string, the controller can log it, update state, or make flow table modifications.\nCurrent Status Trace Source support is implemented and working for basic switch-to-controller events. The controller can already listen to these events and take actions (via functions like those in the Controller section). This lays the foundation for advanced telemetry and event-driven control logic. How It Works A Trace Source is defined inside P4SwitchNetDevice (e.g., m_switchEvent). The controller registers a callback (e.g., HandleSwitchEvent) to this Trace Source. When the switch triggers the event, the callback in the controller is invoked with the event data. Extending with New Events If you want to extend the control-plane support by introducing new switch events (e.g., statistics updates, error messages, custom notifications), you can follow these steps:\nDeclare the TraceSource in your class\nInside your class (P4SwitchNetDevice), declare a TracedCallback member variable. TracedCallback\u0026lt;uint32_t, const std::string \u0026amp;\u0026gt; m_newEventTrace; This is just for an example usecase. You can add parameters as per your own choice.\nExpose the TraceSource using GetTypeId\nIn your P4SwitchNetDevice class’s GetTypeId function, register the trace source with a descriptive name and description. .AddTraceSource(\u0026#34;NewEvent\u0026#34;, \u0026#34;Fires whenever the new event occurs in the switch\u0026#34;, MakeTraceSourceAccessor(\u0026amp;P4SwitchNetDevice::m_newEventTrace), \u0026#34;ns3::TracedCallback::Uint32String\u0026#34;) Emit the event when appropriate\nCall the trace source whenever the event condition is met. void P4SwitchNetDevice::EmitNewEvent(uint32_t switchId, const std::string \u0026amp;msg) { m_newEventTrace(switchId, msg); } Connect the controller (or any observer) to the new event\nIn your controller (or test script), use the given code to connect and listen to the event std::ostringstream path; path \u0026lt;\u0026lt; \u0026#34;/NodeList/\u0026#34; \u0026lt;\u0026lt; sw-\u0026gt;GetNode()-\u0026gt;GetId() \u0026lt;\u0026lt; \u0026#34;/DeviceList/\u0026#34; \u0026lt;\u0026lt; sw-\u0026gt;GetIfIndex() \u0026lt;\u0026lt; \u0026#34;/$ns3::P4SwitchNetDevice/NewEvents\u0026#34;; Config::ConnectWithoutContext( path.str(), MakeCallback(\u0026amp;P4Controller::HandleSwitchEvent, this)); Implement the callback handler\nIn the controller, define the handler function that processes the event. void P4Controller::HandleNewEvent(uint32_t switchId, const std::string \u0026amp;msg) { NS_LOG_INFO(\u0026#34;[Controller] New event from switch \u0026#34; \u0026lt;\u0026lt; switchId \u0026lt;\u0026lt; \u0026#34;: \u0026#34; \u0026lt;\u0026lt; msg); //Code } Test the new trace source\nUse Simulator::Schedule in a test script to call EmitNewEvent and verify that the controller receives it. Examples and Tests Relevant PR: https://github.com/HapCommSys/p4sim/pull/4\nExtended controller test suite (P4ControllerCheckFlowEntryTestCase). Added examples for flow entry operations and action profile operations. Runtime assertions for flow entry installation and event handling. To run any example\nCopy and paste the example file in scratch/ folder. Run command ./ns3 run scratch/example-file.cc To run tests\n./test.py --suite=p4-controller --text=result.txt Future Work Although the control-plane feature has been added, it is currently implemented only for the V1model architecture. Since P4Sim also supports PSA and PNA, their control-plane implementations are still required. Wrapper functions have been added and tested, but only up to flow-entry operations reason being time constraint and as these are the most commonly used ones. The remaining functions still require proper testing and example implementations. ","permalink":"https://p4lang.github.io/gsoc/blog/posts/2025-09-09-p4sim/","summary":"\u003ch2 id=\"abstract\"\u003eAbstract\u003c/h2\u003e\n\u003cp\u003eThis project aims to extend the existing P4sim module integrated within the ns-3 network simulator by implementing control plane functionalities. The P4sim currently supports the simulation of P4-programmable data planes in ns-3, providing a powerful environment for research and development in programmable networking. This project  bridges that gap by integrating a control plane to support P4 Runtime, like changing the openconfig-interfaces, the ethernet augments and other runtime configurable features. The enhancements will improve the realism and usability of the simulator for research and experimentation involving P4\u003c/p\u003e","title":"P4Sim Control Plane Enhancement"},{"content":"Google Summer of Code 2025 Final Report Project Repository: SpliDT Codebase\nProject Overview SpliDT is a switch-native compiler framework that enables stateful decision tree inference directly in programmable switches, bringing real-time machine learning into the network data plane. SpliDT compiles high-performance decision tree models to enable detection and observability of security-significant flow behaviors across diverse traffic workloads.\nA major challenge in deploying decision trees in this environment is the limited stateful memory of ASIC chips, which makes it impossible to store multiple packet features simultaneously. SpliDT solves the issue with Partitioned Decision Trees (PDTs). Instead of evaluating all features simultaneously, the tree is split into smaller subtrees, each handling only top k-features at a time. Flows are guided across subtrees using Subtree IDs (SIDs), ensuring that all features are eventually considered without exceeding hardware limits. This design reduces memory usage, removes latency overheads, and maintains classification accuracy, while making the system scalable and efficient.\nBy combining P4-based dataplane logic with a lightweight control plane, SpliDT provides a practical and extensible framework for developers and researchers working on in-network ML, traffic classification, and real-time security detection.\nProject Goals Core Framework Stateful P4 Implementation: Built complete decision tree classifier with SID-based traversal, recirculation logic, and multi-stage processing Dynamic Controller System: Developed P4Runtime and Barefoot Runtime integration supporting installation of control plane rules for partitioned models, graceful error handling Automated Code Generation: Created Jinja2-based template system generating complete P4 programs from user-given partitioned DT models Hardware Validation: Successfully deployed and tested on Intel Tofino Model and BMv2 software targets Production-Ready Components Component Status Functionality P4 Data Plane Completed Stateful classification, SID management, packet recirculation P4Runtime/BftRuntime Controller Completed Custom-DT Model loading, rule installation P4 Code Generation Framework Completed Automated P4 generation from ML models Testing Infrastructure Completed Mininet simulation, packet verification Deployment Automation Completed Makefile workflow for reproducible deployments Implementation Details Project Architecture The SpliDT framework implements a complete model-to-deployment pipeline that transforms network datasets into hardware-optimized decision tree inference running on programmable switches. The architecture bridges machine learning model training with P4-based data plane deployment through automated code generation and runtime management.\n1. Model Compilation (SpliDT Compiler) Repository Location: dt-framework/ + custom_dts/\nThe SpliDT Compiler processes raw network datasets and produces optimal partitioned decision tree models:\nInput: Dataset, target objectives, performance constraints\nComponents:\nCICFlowMeter: Extracts bidirectional flow features from PCAP files HyperMapper: Automated hyperparameter optimization for tree partitioning Grafana + Postgres: Performance monitoring and dataset analysis Training Process: Uses design search exploration and feasibility testing to determine optimal subtree partitions Output: Partitioned decision tree model as JSON/DOT files + corresponding pickle files\nKey Innovation: The compiler automatically determines how to split decision trees into SID-based subtrees that fit within ASIC memory constraints while maintaining classification accuracy.\n2. Code Generation and Standardization (SpliDT Generator) Repository Location: utility/\nThe SpliDT Generator transforms trained models into deployable P4 programs:\nInput Processing:\nutility/filter/: Processes decision tree models to generate files that map the required stateful features in the P4 program to their corresponding operations (sum, min, max) utility/netbeacon/: Converts decision tree models into TCAM rules (inspired by NetBeacon [1] ) Code Generation:\nutility/p4codegen/: Jinja2-based P4 generator that creates complete P4 programs from model inputs Outputs:\nP4 Program: Complete data plane implementation with SID-based stateful processing Controller Code: P4Runtime client for dynamic rule installation Configuration Files: Mapping between model features and P4 metadata fields 3. Runtime Deployment (Control + Data Plane) Repository Location: dataplane_driver/\nThe Runtime Deployment stage compiles and deploys the generated code:\nCompilation Pipeline:\nP4 Compiler: Processes generated P4 program → produces target binary .p4info Generation: Creates P4Runtime interface definitions Target Driver: Intel Tofino(switch) or BMv2(mininet) software switch initialization Control Plane Operation:\nP4/Bft Runtime Client: Installs the TCAM Rules of each subtree by matching on the Subtree ID(SID) gRPC Communication: Installs match-action table entries via P4Runtime protocol Data Plane Execution:\nStateful Processing: Packets processed through SID-based subtree traversal Feature Extraction: Headers parsed into metadata fields f1, f2, f3, sid Classification: Partitioned decision-tree inference with recirculation Result Output: Classification results via digest emission Repository Structure Architecture Component Repository Location Function SpliDT Compiler dt-framework/ Dataset processing, model training, hyperparameter optimization Sample Models custom_dts/ Custom decision trees with visualizations Model Processing utility/filter/ DOT file parsing and data mapping Format Conversion utility/netbeacon/ DOT to PKL conversion pipeline Code Generation utility/p4codegen/ P4 program and controller generation Runtime Deployment dataplane_driver/ P4 compilation, switch deployment, testing Workflow Automation Makefile End-to-end pipeline orchestration Future Scope Ansible-based Deployment: Ansible playbooks for automating environment setup, model deployment, controller startup. MoonGen Traffic Generation Integration: Enable 100 Gbps stress testing with realistic traffic patterns for comprehensive performance validation Decision Tree Pipeline Integration: As future work, the optimal partitioned models generated using HyperMapper to maximize both accuracy and supported flows will be translated into P4. In the current prototype, we assume the model is provided by the user. Conclusion This GSoC journey gave me valuable research experience in the field of computer science and networking within an open-source community. More specifically, the SpliDT project allowed me to improve my understanding of P4, while also exploring network-programmable devices and addressing real research challenges, such as implementing decision tree inference directly in programmable switches.\nBeyond the technical work, I also had the opportunity to engage with the P4 Language Consortium Community by participating in community activities and contributing to my project. This experience inspired me to write a blog to share what I learned during Google Summer of Code.\nThe friendly tech savvy, tech-savvy mentors really helped me nurture a lot along with the project, and I would like to thank them again.\nWith such a colorful time over the past few months, I am truly grateful to everyone for believing in me and making things very real and possible!\nThanks a lot with deepest regards!\nReferences [1] NetBeacon Project: IDP-code/NetBeacon\nGuangmeng Zhou, Zhuotao Liu, Chuanpu Fu, Qi Li, and Ke Xu. An Efficient Design of Intelligent Network Data Plane.\nIn 32nd USENIX Security Symposium (USENIX Security 23), pages 6203–6220, Anaheim, CA, August 2023. USENIX Association.\n","permalink":"https://p4lang.github.io/gsoc/blog/posts/2025-09-09-splidt/","summary":"\u003ch2 id=\"google-summer-of-code-2025-final-report\"\u003eGoogle Summer of Code 2025 Final Report\u003c/h2\u003e\n\u003cimg width=\"1400\" height=\"604\" alt=\"image\" src=\"https://github.com/user-attachments/assets/99b70979-e9e0-46c9-8ae0-e1386950bbf5\" /\u003e\n\u003cp\u003e\u003cstrong\u003eProject Repository:\u003c/strong\u003e \u003ca href=\"https://github.com/blackdragoon26/splidt.git\"\u003eSpliDT Codebase\u003c/a\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"project-overview\"\u003eProject Overview\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eSpliDT\u003c/strong\u003e is a switch-native compiler framework that enables stateful decision tree inference directly in programmable switches, bringing real-time machine learning into the network data plane.\nSpliDT compiles high-performance decision tree models to enable detection and observability of security-significant flow behaviors across diverse traffic workloads.\u003c/p\u003e\n\u003cp\u003eA major challenge in deploying decision trees in this environment is the limited stateful memory of ASIC chips, which makes it impossible to store multiple packet features simultaneously.\nSpliDT solves the issue with \u003cstrong\u003ePartitioned Decision Trees (PDTs)\u003c/strong\u003e. Instead of evaluating all features simultaneously, the tree is split into smaller subtrees, each handling only top k-features at a time. Flows are guided across subtrees using Subtree IDs (SIDs), ensuring that all features are eventually considered without exceeding hardware limits. This design reduces memory usage, removes latency overheads, and maintains classification accuracy, while making the system scalable and efficient.\u003c/p\u003e","title":"SpliDT: Scaling Stateful Decision Tree Algorithms in P4"},{"content":"Abstract There are many situations where it is more useful to have all possible outputs from a P4 simulation rather than only a single one. One such instance is diff testing. Diff testing compares the outputs of packets sent through the switch and BMv2. By providing multiple outputs to the packets that get sent to BMv2, it provides more accurate portrayal of the the correctness of the switch\u0026rsquo;s behavior.\nMultiple allowed behaviors usually arise from various multi-path constructs (e.g. ECMP, WCMP, or perhaps LAGs) usually modeled as action profiles/selectors in P4. BMv2 currently allows users to set a mode determining action selector behavior, like round robin which means that every time you send in the same packet, it should result in the next possible outcome (eventually wrapping around).\nGoals Provide a new fanout mode for BMv2 to instead output ALL possible outputs from action selectors; Add corresponding tests for correctness check; Support better traces that can help distinguish usual packets (e.g. the ones replicated for multicast) from fanout packets (e.g. the ones fanout for ActionSelector-based WCMP); Document all changes for better maintainability. Results A complete implementation of an extended optional mode for V1Model called selector_fanout. A set of test cases for different dataplane setups; A new trace instance in the event logger when fanout is triggered; Updated documentation on simple_switch\u0026rsquo;s pipeline. Pipeline Overview Implementation details Project PR: https://github.com/p4lang/behavioral-model/pull/1316\nFanout Manager It is a singleton instantiated with the switch. It handles the following:\nThread registration: because in BMv2, each -gress would be an individual thread, like in simple_switch there is a single thread for ingress and N egress threads where N is the number of egress ports, we need to maintain info per -gress. For instance, the most important info we need to carry would be what packets got replicated from the fanout.\nContext capture: since we want to replicate for all possible members in a selector, we need to have the following critical information:\nthe current selector; the table the selector is applied to; the group of members to replicate for. We use the instance to capture such information. This is a bit of hacky because it requires aggregating pointers of aforementioned objects, which adds extra coupling. Yet as of now it is a less intrusive approach.\nPacket replication: with all the necessary information mentioned above, packets can be replicated. The replicated packets will be stored in the manager until the current pipeline is done.\nFanout Pkt Selection Call stack of components It is a class inherits SelectorIface, which is an abstract class used for customizing selector behaviors. It behaves as such:\nThere are several pure virtual functions used by the core BMv2 like add_member_to_group and remove_member_from_group. FanoutPktSelection leaves them blank as they are simply used as customized callbacks after the actual operation is done in the selector. The fanout manager is invoked to packet replication upon get_from_hash is called in an action selector. So this class works like a bridge that decouples and also connects the core BMv2 logic and this fanout feature, making the overall approach less intrusive. Tracing Currently, trace in BMv2 is using packed trivial struct for storing trace information and transmitting it via NanoMsg as raw binaries. The receiving side of the trace (nanomsg_client.py) is connected with the switch instance. Upon receiving a trace, it checks the raw binary for hardcoded position of event id and parse it accordingly. Although this approach could be less flexible, it is efficient and requires only a few changes, so we adopted it.\nTraces in BMv2 is handled by an event logger, where trace is produced upon an event is triggered. For instance, BMv2 supports ActionExecute which produces a trace whenever an action is executed. Similarly, a new event called FanoutGen is produced each time a packet is replicated for fanout.\nThe FanoutGen trace includes:\nPacket ID: the ID of the very original packet, does not change under replication/multicast Copy ID: current replicated packet\u0026rsquo;s copy ID, represents the seq num. Parent copy ID: the copy id of its parent There are 2 reasons to include such information:\nDistinguish selector fanout and other replication behaviors like multicast, who also updates the copy ID. Trace the history of a packet, track its complete path after fanout. Test cases We use the same testing framework used by simple_switch_grpc, and the tests are integrated as target tests in simple_switch_grpc.\nTesting procedure for each test case:\nThe testing framework swaps to the new testing P4 program; Configure the dataplane via gRPC; Send some hardcoded packets to specified ingress port on the switch; Wait and receive an expected number of packets, fail if timeout; Compare against the set of expected packets, fail if different. List of test cases for fanout:\ningress_single_selector_test: most basic test that involves a single table with selector in only the ingress block; Expects N output packets where N is the number of members in the matched selector group; ingress_two_selectors_test: same as above but with 2 tables with selectors; Expects N*M output packets where N and M are the numbers of members in the matched selector groups in each selector; inress_single_selector_mc_test: add another table to set the multicast group for multicasting, which happens after the action selector; Expects N*M output packets where N is the number of members in the matched selector group and M is the number of members in the specified muticast group; egress_single_selector_test: multicast group is specified in the ingress, selector is in the egress. Expects N*M output packets where N is the number of members in the specified muticast group and M is the number of members in the matched selector group; Note: there is no good way for now to have it integrated in the GitHub CI, as it requires PI.\nHow it works Here we briefly introduce the lifetime of a packet when fanout is on.\nPacket Fanout Mode This fanout extension exists in the form of a new HashAlgorithm defined in v1model.p4:\nenum HashAlgorithm { selector_fanout, crc32, crc32_custom, random, identity, ... } By specifying the mode of a selector to selector_fanout, BMv2 marks the selector as fanout-enabled during pipeline initialization.\nWe have not really merged the change to the v1model.p4 in p4lang/p4c. Similar to modes like round_robin, they are implemented in BMv2, but not added to the enum. The user of such mode should add the mode to the enum.\nFanout Packet Replication As outlined in the pipeline, when a input packet arrives to a fanout-enabled selector, it will be passed to fanout packet manager. It is processed as following:\nIts header is matched against the table for entry lookups; The entry of a table with action selector is either a group of member actions or a single member action; If it is single action, then no fanout behavior. If it is a group of N members, it will: Take out the first member, apply it to the input packet, make it flow down the pipeline, as if it actually selected one member; Replicate one packet for each of the remaining N-1 member actions, and the next_table will also be extracted as part of the action information. Fanout Packet Continuation Upon starting handling a new input packet, simple_switch will first check if the packet has the optional field next_node specified right after it gets popped from the buffer. If no, it will be processed as a normal input packet. If yes, it will bypass the parsing, as it is already \u0026ldquo;parsed\u0026rdquo; by inheriting the layout and information from the packet it replicates from.\nFor normal packets, mau-\u0026gt;apply is invoked, which iteratively applies tables to the packet from the initial table. Yet for fanout packets, mau-\u0026gt;apply_from_next_node is invoked, which takes out the next_node field in the packet, and continue the execution from that point. Conceptually, it \u0026ldquo;inserts\u0026rdquo; the packet back to some spot in the pipeline.\nExample Setup Here we use the aforementioned test case egress_single_selector_test as an example. The dataplane is configured as such:\negress_single_selector_test Overview We have a multicast after the ingress, which pushes to replicas to 3 egress ports. They will go through the selector in the egress block, which has 3 members in this configured group (foo1~3). So in total, it will have 3*3=9 packets output. Be aware that only 2 of the 3 output packets (colored yellow with dashed borderline) are \u0026ldquo;generated\u0026rdquo; from the fanout (see the demo below for corresponding event logs). They green one is generated from applying the first member to the input packet. Note that after multicast, the original packet will be dropped.\nTrace Output FANOUT_GEN event:\ntype: FANOUT_GEN, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 4, table_id: 1 (selector_tbl), parent_packet_copy_id: 1 type: FANOUT_GEN, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 5, table_id: 1 (selector_tbl), parent_packet_copy_id: 2 type: FANOUT_GEN, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 6, table_id: 1 (selector_tbl), parent_packet_copy_id: 1 type: FANOUT_GEN, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 7, table_id: 1 (selector_tbl), parent_packet_copy_id: 2 type: FANOUT_GEN, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 8, table_id: 1 (selector_tbl), parent_packet_copy_id: 3 type: FANOUT_GEN, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 9, table_id: 1 (selector_tbl), parent_packet_copy_id: 3 We observe 6 FANOUT_GEN events because 3 of the 9 packets are the original packets arrive at egress due to multicast and flowed downward instead of getting held by the fanout packet manager.\nSince all the packets are derived from the same packet with ID 0, their \u0026ldquo;packet ID\u0026rdquo; are also 0. The copy_id ranges from 4 to 9 because, again, 1~3 are flowed downward the pipeline directly. As suggested by parent_packet_copy_id, we can track which packet a fanout packet was replicated from.\nPACKET_OUT event:\ntype: PACKET_OUT, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 1, port_out: 0 type: PACKET_OUT, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 3, port_out: 2 type: PACKET_OUT, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 8, port_out: 2 type: PACKET_OUT, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 2, port_out: 1 type: PACKET_OUT, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 9, port_out: 2 type: PACKET_OUT, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 5, port_out: 1 type: PACKET_OUT, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 4, port_out: 0 type: PACKET_OUT, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 7, port_out: 1 type: PACKET_OUT, switch_id: 0, cxt_id: 0, sig: 7317885152576657337, id: 0, copy_id: 6, port_out: 0 Here we can observe all the output packets, including the ones with copy_id from 1 to 3, which were replicated originally for the multicast.\n","permalink":"https://p4lang.github.io/gsoc/blog/posts/2025-09-02-bmv2-pkt-out/","summary":"\u003ch2 id=\"abstract\"\u003eAbstract\u003c/h2\u003e\n\u003cp\u003eThere are many situations where it is more useful to have all possible outputs from a P4 simulation rather than only a single one. One such instance is diff testing. Diff testing compares the outputs of packets sent through the switch and BMv2. By providing multiple outputs to the packets that get sent to BMv2, it provides more accurate portrayal of the the correctness of the switch\u0026rsquo;s behavior.\u003c/p\u003e\n\u003cp\u003eMultiple allowed behaviors usually arise from various multi-path constructs (e.g. ECMP, WCMP, or perhaps LAGs) usually modeled as action profiles/selectors in P4. BMv2 currently allows users to set a mode determining action selector behavior, like \u003ccode\u003eround robin\u003c/code\u003e which means that every time you send in the same packet, it should result in the next possible outcome (eventually wrapping around).\u003c/p\u003e","title":"BMv2 With All Possible Output Packets"},{"content":"Abstract This project aims to develop a code formatter for P4. While the reference P4 compiler (P4C) provides foundational components, such as an AST/IR parser and a pretty printer, key gaps remain. The current AST/IR does not retain comments from the original source code, and the pretty printer lacks the flexibility required for common formatting options. This project addresses these issues by enhancing the AST/IR to preserve comments and adapting the pretty printer to support customizable formatting rules, ultimately creating a functional P4 code formatter for the first time.\nGoals Modify the AST to attach comments from the original source code. Update the pretty printer to print the newly attached comments. Implement common formatting options to enhance the code formatting capabilities. Results The project has successfully integrated comment preservation and this has been leveraged to improve the pretty printer, enabling it to print the newly attached comments.\nImplementation details New p4fmt backend Relevant PRs:\nhttps://github.com/p4lang/p4c/pull/4710 https://github.com/p4lang/p4c/pull/4845 https://github.com/p4lang/p4c/pull/4795 https://github.com/p4lang/p4c/pull/4718 Build \u0026amp; Usage Instructions: https://github.com/p4lang/p4c/tree/main/backends/p4fmt\nThe primary task in creating the P4Fmt backend was the attachment of comments to IR nodes, ensuring the preservation of comments during code formatting. We drew inspiration from how Bazel tools preserve comments, and adapted the strategy to fit the P4 language\u0026rsquo;s specific requirements.\nThe lexer collects all comments along with their position information into a global list managed by the InputSources class. Each AST node embeds a Comments struct to store relevant comments, which are categorized into two types:\nPrefix Comments: Comments that appear before a node. Suffix Comments: Inline comments that trail after a node. Attachment is done in two AST passes:\nPre-order Pass: During this traversal, we attach comments preceding a node (i.e., prefix comments) to the AST node immediately after the comment. Post-order Pass: For inline trailing comments (suffix comments), we attach them to the AST node right before the comment. Optimizing Comment Management:\nInitially, we embedded comments directly within each AST node, using two vectors (prefix and suffix) to store them. However, this approach could have introduced overhead for every node, potentially slowing down compilation and increasing memory consumption. To address this, we moved the comments into a local side map (\u0026lt;node-id, comments\u0026gt;), associating unique node IDs (clone_id) with their comments. This map allowed us to efficiently attach comments during the second traversal, reducing both memory usage and performance impact.\nPretty-Printer for p4fmt Relevant PRs:\nhttps://github.com/p4lang/p4c/pull/4862 https://github.com/p4lang/p4c/pull/4795 https://github.com/p4lang/p4c/pull/4887 We modified the existing pretty-printer, top4 , to handle the newly attached comments in the P4Fmt backend. This update ensures that both prefix and suffix comments, now stored and attached to IR nodes via the side map, are correctly formatted and printed alongside the relevant P4 constructs.\nReference checker for p4fmt Relevant PR: https://github.com/p4lang/p4c/pull/4778\nBuild \u0026amp; Usage Instructions: https://github.com/p4lang/p4c/tree/main/backends/p4fmt\nWe also implemented a reference checker for the P4Fmt formatter, using golden tests to validate the formatted output against expected results. It processes an input P4 file, formats it with P4Fmt, and then compares the result to a reference file using diff.\nKey Features:\nFormats the input P4 file and compares it to the provided reference file. The --overwrite option or the P4FMT_REPLACE environment variable allows updating the reference file with the new formatted output instead of comparing. Limitations Free floating comments are not handled currently and remain unassociated with any nodes. // struct comment struct headers { ... } // free floating comments // control block comments control MyVerifyChecksum(inout headers hdr, inout metadata meta) { ... } Code constructs with multiple potential points for comment attachment are not supported. const /* (1) */ bit\u0026lt;16\u0026gt; /* (2) */ TYPE_IPV4 /* (3) */ = /* (4) */ 0x800 /* (5) */; The formatter currently does not preserve comments through IR transformations that occur during compilation. For now, it is designed to handle comment printing immediately after parsing and before any transformations are applied. Future Work Better algorithm for comment attachment. We need a robust set of heuristics for this, as comment association is the most challenging aspect to get right. For more discussion on the topic, see: - https://github.com/golang/go/issues/20744. - https://github.com/dave/dst. - https://go-review.googlesource.com/c/go/+/429639.\nSupport for formatting options(line wrapping, indentation etc.). Customization of formatting styles via configuration files \u0026amp; cmd-line options. ","permalink":"https://p4lang.github.io/gsoc/blog/posts/2024-09-09-formatter/","summary":"\u003ch2 id=\"abstract\"\u003eAbstract\u003c/h2\u003e\n\u003cp\u003eThis project aims to develop a code formatter for P4. While the reference P4 compiler (P4C) provides foundational components, such as an AST/IR parser and a pretty printer, key gaps remain.\nThe current AST/IR does not retain comments from the original source code, and the pretty printer lacks the flexibility required for common formatting options.\nThis project addresses these issues by enhancing the AST/IR to preserve comments and adapting the pretty printer to support customizable formatting rules, ultimately creating a functional P4 code formatter for the first time.\u003c/p\u003e","title":"P4 Formatter"},{"content":"Abstract As the P4 use cases on the NIC side increase, so does the need for a P4 simulator that supports Portable NIC Architecture (PNA). Currently, the BMv2 simulator only supports the v1model and Portable Switch Architecture (PSA) specifications. This project aims to integrate a minimal core subset of the PNA into the BMv2 and the P4C. The PNA Architecture Block Diagram is shown in below:\nGoals Create a new target in BMv2 supporting the core subset of PNA Create a new P4C backend for the new BMv2\u0026rsquo;s PNA NIC target Design test cases to ensure the PNA NIC target is working Results The project successfully creates a new target in BMv2 to support PNA and a corresponding backend in P4C. A test case is implemented to ensure the new PNA NIC target can forward the packets between interfaces.\nImplementation details New PNA NIC target in BMv2 Relevant PRs:\nhttps://github.com/p4lang/behavioral-model/pull/1255 https://github.com/p4lang/behavioral-model/pull/1262 https://github.com/p4lang/behavioral-model/pull/1263 https://github.com/p4lang/behavioral-model/pull/1265 Created a basic PNA NIC target with basic externs (Meter, Counter, Register, etc). A test case was added to check whether the new PNA NIC could load the P4C-generated PNA JSON file. Makefiles are adjusted to build the new PNA NIC target while building the bmv2 repository. Just follow the build commands mentioned in the repository.\nResultant Binary: pna_nic\nP4C backend for the new BMv2 PNA NIC target Relevant PR: https://github.com/p4lang/p4c/pull/4729\nCreated a new p4c backend for the PNA NIC target.\nSince both PSA_SWITCH and PNA_NIC compiler backends have a lot of common code, I created a common code base and called it \u0026ldquo;portable_common.\u0026rdquo; The PSA and PNA components will now inherit from the \u0026ldquo;portable\u0026rdquo; components.\nMerged the ProgramStrucutre and CodeGenerator classes into one class.\nIncluded the PNA specification file from p4lang/pna/pna.p4.\nMakefiles are adjusted to build the new P4C backend for the PNA NIC target while building the P4C repository. Just follow the build commands mentioned in the repository.\nResultant Binary: p4c-bm2-pna\nDemo Created a similar virtual interface setup as mentioned in this tutorial.\nThe pna-demo-L2-one-table.p4 program from p4lang/pna repository is used to test the new PNA NIC target.\nCompile pna-demo-L2-one-table.p4 using p4c-bm2-pna binary.\np4c-bm2-pna pna-demo-L2-one-table.p4 -o pna-demo-L2-one-table.json To start the pna_nic target:\nstart the pna_nic_CLI and populate the L2_fwd table mentioned in the P4 program using the below example (or your own) data: table_add L2_fwd L2_send_to_port ba:dd:b1:f5:03:3c =\u0026gt; 0 table_add L2_fwd L2_send_to_port be:05:e2:d2:4a:bc =\u0026gt; 1 table_add L2_fwd L2_send_to_port 52:04:da:9b:15:59 =\u0026gt; 2 Send the Packets from one interface and listen on another interface. Example: Future Work This project integrates a minimal core subset of the PNA into BMv2 and P4C. Many PNA use cases have yet to be implemented in the BMv2.\nStandard PNA Features (Implemented) Basic Forwarding Counters Meters Hash Internet Checksum Random Standard PNA Features (Yet to be Implemented) Recirculation Dropping Differentiate between Host and Network Interfaces Experimental PNA Features (Yet to be Implemented) IPSec Encrypt/Decrypt Large Receive Offload (LRO) Receive Side Scaling (RSS) TCP segmentation offloading (TSO) ","permalink":"https://p4lang.github.io/gsoc/blog/posts/2024-08-31-bmv2-pna/","summary":"\u003ch2 id=\"abstract\"\u003eAbstract\u003c/h2\u003e\n\u003cp\u003eAs the P4 use cases on the NIC side increase, so does the need for a P4 simulator that supports \u003ca href=\"https://p4.org/p4-spec/docs/PNA-v0.7.html\"\u003ePortable NIC Architecture (PNA)\u003c/a\u003e. Currently, the BMv2 simulator only supports the v1model and \u003ca href=\"https://p4.org/p4-spec/docs/PSA-v1.2.html\"\u003ePortable Switch Architecture (PSA)\u003c/a\u003e specifications. This project aims to integrate a minimal core subset of the PNA into the BMv2 and the P4C. The PNA Architecture Block Diagram is shown in below:\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"PNA Block Diagram\" src=\"assets/pna-block-diagram.png\" width=\"700\"\u003e\n\u003c/p\u003e\n\u003ch2 id=\"goals\"\u003eGoals\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eCreate a new target in BMv2 supporting the core subset of PNA\u003c/li\u003e\n\u003cli\u003eCreate a new P4C backend for the new BMv2\u0026rsquo;s PNA NIC target\u003c/li\u003e\n\u003cli\u003eDesign test cases to ensure the PNA NIC target is working\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"results\"\u003eResults\u003c/h2\u003e\n\u003cp\u003eThe project successfully creates a new target in BMv2 to support PNA and a corresponding backend in P4C. A test case is implemented to ensure the new PNA NIC target can forward the packets between interfaces.\u003c/p\u003e","title":"BMv2 PNA Support"},{"content":"\nProject Overview The primary objective of this project was to improve the documentation infrastructure of the P4 Compiler (p4c). This involved upgrading tools, automating deployment processes, enhancing the user interface, and ensuring that the documentation is both comprehensive and accessible. The project was aimed at making the documentation easier to maintain and more useful for both developers and users of the P4 language.\nTechnical Considerations Note: Previews in this report are mockups and do not reflect the current state of the documentation. They were used for iterative development during the project.\nDoxygen vs. Sphinx: Documentation Tools Comparison Initially, I considered using Sphinx with Doxygen and Breathe for documentation [1][2]. However, after discussing with mentors and evaluating the tools, we decided to use Doxygen with Doxygen Awesome CSS for the following reasons:\nDoxygen: Provides straightforward and easy-to-generate documentation but lacks mobile responsiveness. Examples of Doxygen-generated docs can be found here:\nEigen Documentation ALIB Documentation Doxygen + Awesome CSS: Addresses the mobile responsiveness issue and improves visual appeal with enhanced CSS styling. It allows for customization without modifying the HTML structure and includes dark mode support. You can view an example here:\nP4C Prototype Documentation Doxygen + Breathe / Exhale + Sphinx: Sphinx offers greater flexibility and control over documentation structure compared to Doxygen’s markup. It\u0026rsquo;s particularly useful for including tutorials. However, we decided against it for our project to avoid added complexity, especially since tutorials would be maintained separately.\nAfter discussions with mentors, it became clear that Doxygen with Doxygen Awesome CSS met all our needs while avoiding the additional complexity of incorporating Sphinx and Exhale. This choice allowed us to utilize a tool familiar to maintainers and contributors without introducing a new layer of complexity.\nDocumentation Development and Build Process During the early stages of development, I utilized a personal repository with Netlify to create and share website mockups. This approach allowed for iterative testing and refinement before deploying to the official GitHub Pages site. The following mockups were created during this phase:\nDoxygen output with obsolete documentation This early mockup used Doxygen to generate basic documentation, which was hosted on Netlify. Sphinx version with “pydata” theme An alternative mockup using Sphinx with the pydata theme was explored but later discarded in favor of Doxygen. Doxygen + Doxygen Awesome CSS With Nav Bar The mockup which was used as a base for the official documentation. Deployment Options : Transition to GitHub Pages After finalizing the mockups and build process, the documentation was deployed on GitHub Pages. This transition from Netlify to GitHub Pages streamlined hosting, ensured consistent integration with the main repository, and eliminated additional costs and administrative overhead. The choice was made after evaluating platforms like Netlify and Vercel and consulting with mentors.\nWorkflow Automation Doxygen Automated Builds Workflow PR - Configure: DOXYGEN for documentation generation PR - Docs : Configure Doxygen GitHub pages deploy Action An automated workflow was set up to handle Doxygen documentation builds. This process ensures that the documentation is updated consistently with the latest changes, providing a reliable and up-to-date reference for all contributors.\nAutomated PR Preview Workflow PR - Feat: PR Preview Workflow via GitHub Pages To make reviewing documentation changes smoother and more transparent, I introduced a new PR preview workflow. This update enhances how we assess documentation updates before they merge into the main branch by using GitHub Pages for live previews. This means we can see exactly how changes will look in real-time, making it easier to review and refine documentation. Key Updates PR Preview: Automatically builds and deploys each documentation PR to a unique GitHub Pages URL. Comment Updates: Adds/Updates comments with preview links and build status. Broken Builds: Detects and reports any build issues. Benefits Immediate Feedback: Real-time previews for faster review and adjustments. Automatic Notifications: Alerts for build statuses and failures. Usage When a PR is tagged with documentation, the workflow deploys a preview to GitHub Pages and posts a comment with the preview link and build status.\nBackground : Challenges with PRs from External Forks Handling pull requests (PRs) from external forks presents unique challenges in GitHub Actions, primarily due to security restrictions imposed by GitHub. When a PR is triggered from a fork, the GITHUB_TOKEN generated for that workflow run has limited permissions, specifically lacking write access to the repository. This limitation is designed to prevent unauthorized actions that could potentially compromise the security of the repository. As a result, actions such as commenting on a PR, which require write access, fails with an error message like Resource not accessible by integration.\nWorkaround Using pull_request_target event instead of pull_request: This event runs in the context of the base repository, where the workflow file is stored, and grants the GITHUB_TOKEN the necessary write permissions. However, this approach comes with its own set of risks, as it executes code from the forked repository with elevated privileges, which could potentially be malicious.\nBackground : Label Check Implementation for pull_request_target Trigger In my GitHub Actions workflow, I encountered a limitation with the pull_request_target trigger. Specifically, the conditional check using contains on PR labels wasn\u0026rsquo;t functioning as expected.\n# This condition didn\u0026#39;t work with the pull_request_target trigger if: ${{ contains(github.event.pull_request.labels.*.name, \u0026#39;documentation\u0026#39;) }} Workaround Custom Label Check To ensure that only pull requests with the \u0026lsquo;documentation\u0026rsquo; label trigger the workflow, I implemented a custom label check using actions/github-script@v7(GitHub’s REST API).\nThis script iterates over the labels attached to the PR and returns true if the documentation label is present. This condition is then used to control subsequent steps in the workflow.\n- name: Check PR Label id: check-label if: ${{ github.event_name == \u0026#39;pull_request_target\u0026#39; }} uses: actions/github-script@v7 with: script: | const { data: labels } = await github.rest.issues.listLabelsOnIssue({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number }); const hasDocumentationLabel = labels.some(label =\u0026gt; label.name === \u0026#39;documentation\u0026#39;); return hasDocumentationLabel; Doxygen v1.12.0 Update: Enhancements and Open Source Impact PR - Bump Documentation build workflow action from Doxygen v1.11.0 to v1.12.0 Background on Doxygen v1.12.0 Feature Doxygen v1.12.0 introduced a critical feature enabling the use of GitHub-flavored comments, allowing Doxygen commands to be hidden in GitHub previews using the \u0026lt;!--! ... --\u0026gt; syntax. This feature was the direct result of a feature request [3] I submitted to address the issue of visible Doxygen commands cluttering the rendered views of Markdown documents(README Files) on GitHub.\nImpact of the Update Enhanced Documentation: By adopting this new comment style, all Doxygen commands are now hidden in GitHub previews, making the documentation cleaner and more professional.\nPR - Docs : Content Organization and Documentation Updates Updated Workflow: I updated the Doxygen workflow action from v1.11.0 to v1.12.0, ensuring that all documentation now takes advantage of this new feature. This update mandates the strict use of Doxygen v1.12.0 for consistent output across the project.\nDependency Management: Dependencies were updated accordingly to support this new Doxygen version. This change requires all contributors to upgrade to v1.12.0 to avoid confusion during documentation merges, particularly when using commands like \u0026lt;!--!\\include{doc} \u0026quot;../lib/README.md\u0026quot;--\u0026gt; as they will not be processed by previous versions of doxygen.\nKey Improvements and Achievements Enhanced Documentation Quality Organized Documentation: Configured Doxygen and Doxygen Awesome CSS for better organization and clarity. PR - Docs : Configuring Doxygen Awesome CSS Interactive SVGs: Replaced PNGs with interactive SVGs for high-quality, scalable diagrams. Click to View Previous Diagrams | | Click to view Updated Diagrams Dynamic Architecture Diagram: Added a homepage diagram illustrating the P4 Compiler architecture with interactive links. Utilized Graphviz\u0026rsquo;s diagram generation to visualize class and function relationships. Improved Documentation Accessibility Documentation Sync and Cleanup: Updated and fixed broken links to align with the latest codebase. Centralized Hosting: Deployed documentation to GitHub Pages for real-time updates and accessibility. Permanent Links: Added links to specific sections for easier navigation and sharing. Enhanced Visual and Functional Features Dark Mode and Formatting: Introduced dark mode and improved navigation bar formatting for a better reading experience. Customized Theme: Adopted a P4 color scheme for a consistent visual experience. Efficient Onboarding and Project Management Contribution Guidelines: Created detailed guidelines to assist new contributors with best practices and setup instructions.\nCreate CONTRIBUTING.md PR - [docs] Add Comment Style Guide Changelog Integration: Added changelogs to track changes and help contributors stay informed about project updates.\nPR - [Docs] Add initial CHANGELOG.md with changelogs from previous release PR - Docs : Changelogs release update for v1.2.4.13 PR - [Docs] : Changelogs release update for v1.2.4.14 Automated and Streamlined Processes GitHub Actions Integration: Developed actions to automate documentation builds and previews. PR Review Enhancements: Automated preview creation for pull requests, with status updates and links for efficient review. Supporting Pull Requests Contributions to P4 Tutorials Contributions to P4 Compiler Docs:Content Organization \u0026amp; Add \u0026lsquo;Frontend\u0026rsquo; and \u0026lsquo;Midend\u0026rsquo; Sections Docs: Update compiler file structure Docs: Initial sequence of pages in Documentation Docs: Fixing diagram paths in generated docs Minor Fix: Graphs Backend Example Images Docs: Add theme dependency and instructions Fix : Compatibility issues with Doxygen version in runner and configuration [Docs] Non-Breaking change of \u0026ldquo;p4c\u0026rdquo; to \u0026ldquo;P4C\u0026rdquo; Fix: Outdated links [Docs] Add Table of Content Improve \u0026amp; Move \u0026ldquo;How to contribute\u0026rdquo; section to Top level readme [DOCS][Refactor] Replace direct links with word links Fix Resource links in Doxygen documentation Closing Note A Touch of Fun in the P4 Compiler Architecture Diagram PR - Docs : Add Home page for documentation In the spirit of keeping things lively, we came up with the idea of sneaking a little Easter egg into the P4 Compiler’s documentation during one of our team meetings. If you’re feeling curious (or just bored), try clicking on the P4 Compiler logo in the top left corner of the homepage. What happens next? Well, the architecture diagram decides to throw a tiny celebration by animating its lines, as if to say, “Surprise! You found us!”\nThis quirky touch isn’t just for giggles — it’s a subtle reminder that even in the serious world of compiler documentation, a bit of fun can go a long way. Plus, it might just keep you on your toes, wondering what other surprises might be hiding in the corners of the site. Spoiler: there aren’t any… yet.\nReflections and Learnings During this project, I often had to change my approach and come up with new ideas to solve problems. I learned how a bit of help from others can quickly solve issues that might take me hours to figure out on my own.\nI learned the importance of learning in public. Initially, I felt skeptical about sharing my progress and failures openly, but the overwhelmingly positive response and genuine support from the community and mentors reassured me. This encouragement was instrumental in helping me work confidently towards the project.\nReflections on Open Source Collaboration This experience highlighted the immense power of open-source tools and communities. The rapid incorporation of the feature request into Doxygen\u0026rsquo;s latest release underscored the collaborative spirit and responsiveness that are the hallmarks of open source. It also reinforced the importance of contributing to open-source projects, as even small changes can have a significant impact on a wide range of users.\nIn this project, I saw firsthand how contributions from the community can lead to enhancements that benefit everyone. Working within an open-source ecosystem allowed me to both give back and learn from others, reinforcing the idea that open-source software isn\u0026rsquo;t just about code — it\u0026rsquo;s about community.\nWhat\u0026rsquo;s Left to Do (Future Steps) Documentation is an ever-evolving aspect of the project. Future work includes regularly updating the documentation to reflect new features, changes, and improvements.\nThe PR - Docs: Update Contributor Documentation Guidelines provides detailed instructions for future contributors on how to use Doxygen and maintain the documentation effectively. These guidelines will play a crucial role in ensuring that contributors can consistently update and expand the documentation. Continued attention to these instructions, along with regular updates and automated changelog integration, will help keep the documentation accurate and comprehensive.\nFinal Thoughts This was one of the best experiences I’ve ever had. I sincerely thank Google and my project mentors for selecting me for the GSoC program and giving me the opportunity to discover how amazing coding can be.\nReferences [1]: Medium article on using Sphinx, Doxygen, and Breathe: C++ Documentation with Doxygen, CMake, Sphinx \u0026amp; Breathe [2] Microsoft Dev Blog on functional C++ documentation: Clear Functional C++ Documentation with Sphinx, Breathe, and Doxygen [3]: Feature Request : Capability to render GitHub flavor Markdown comments ","permalink":"https://p4lang.github.io/gsoc/blog/posts/2024-08-26-documentation/","summary":"\u003cp\u003e\u003cimg alt=\"Gsoc2024 - The P4 Language Consortium\" loading=\"lazy\" src=\"https://github.com/user-attachments/assets/9a9ba7f0-772e-464f-a1e9-81d42286bc6e\"\u003e\u003c/p\u003e\n\u003ch2 id=\"project-overview\"\u003eProject Overview\u003c/h2\u003e\n\u003cp\u003eThe primary objective of this project was to improve the documentation infrastructure of the P4 Compiler (p4c). This involved upgrading tools, automating deployment processes, enhancing the user interface, and ensuring that the documentation is both comprehensive and accessible. The project was aimed at making the documentation easier to maintain and more useful for both developers and users of the P4 language.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"https://github.com/user-attachments/assets/97be1b94-fcac-422f-b9e8-5da007948c0d\"\u003e\u003c/p\u003e","title":"P4 Compiler Documentation"},{"content":"Abstract Kubernetes has recently introduced a container checkpoint/restore (C/R) feature to enable the creation of stateful snapshots for running containers. This feature allows containers to be restarted from checkpoints in different pods, enabling use cases such as live migration of microservices. However, preserving established TCP connections during migration is challenging because Kubernetes dynamically allocates IP addresses to Pods in the cluster. As a result, the restored container will run with a different IP address after migration, while clients will continue to send packets to the old IP address. This project aims to address this problem by developing a P4-based load balancer that keeps track of established TCP connections and routes application traffic to the correct IP address after container migration.\nGoals Write a load balancer program in the P4 language that forwards TCP packets to hosts while preserving established TCP connections during container migration Develop a P4Runtime controller that automatically updates the packet forwarding rules during container migration Explore the integration of a P4 software switch with Kubernetes clusters Design evaluation experiments that demonstrate the preservation of established TCP connections during container migration Results The project successfully demonstrates the application of the P4 language to address the challenge of preserving established TCP connections during container migration, even when IP addresses change. Several migration scenarios were implemented using the BMv2 switch to illustrate the effectiveness of this approach.\nKey contributions include:\nProcess migration: Demonstrating how TCP connections can be maintained during migration of Linux processes from one network namespace to another. Container migration with a BMv2 switch: Demonstrating migration of containers between Pods while preserving TCP connections. Container migration with a fully containerized setup: The scenario above has been extended to include the BMv2 switch running in a container. Container migration of an in-memory database: This example demonstrates the migration of a container running an in-memory database (Redis) while preserving established TCP connections. Kubernetes integration using kube-router: A use case was explored involving kube-router, with plans to replace it with BMv2 in future work. Links All artifacts developed throughout this GSoC project are available in the following GitHub repository: https://github.com/stano45/p4containerflow\nImplementation details This project was implemented in small, incremental steps, from simple process migration using CRIU to an integration with Kubernetes. The following sections describe each step of the project, outline some of the key challenges faced during development, and discuss their solutions. The relevant code and resources for each step are linked in the sections below.\nInitial prototype Relevant PR: https://github.com/stano45/p4containerflow/pull/1\nThe tutorials repository provides a set of scripts for building a virtual machine (VM) which includes all relevant P4 packages. This VM runs Ubuntu 24.04 and includes packages such as:\nThe BMv2 software switch, The p4 compiler (p4c), mininet, p4runtime (python library for interacting with switches). Our initial prototype was inspired by the load_balance exercise available in the p4lang/tutorials repository. This is a simple example of a P4-based load balancer that hashes connections based on the five-tuple (source IP, source port, destination IP, destination port, and protocol) into 2 buckets representing hosts. The output of this hash is 0 or 1, which is then used to index the target host (h2 or h3) using the ecmp_nhop table.\nThe following diagram shows the initial network topology, with three hosts (h1, h2, h3) and three switches (s1, s2, s3), where s1 is the load balancer:\nController Relevant PRs:\nhttps://github.com/stano45/p4containerflow/pull/3 https://github.com/stano45/p4containerflow/pull/5 https://github.com/stano45/p4containerflow/pull/16 https://github.com/stano45/p4containerflow/pull/17 Next, we want to be able to change the hosts during runtime. We built a Python-based controller, utilizing the p4runtime library, and further Python utils from the p4lang/tutorials repository. Furthermore, we set up a simple flask HTTP server with three endpoints: /insert_node, /update_node, and /delete_node. These endpoints insert, update, and delete entries in the ecmp_nhop table.\nThe controller also keeps track of the match-action table state, by a target IP -\u0026gt; index mapping. The index mapping corresponds to the value in the ecmp_nhop table. This makes it more efficient to look up the index of a specific IP in the table, without the need to fetch table state from the switch on each update request.\nDuring this stage, we faced minor issues:\nThere is a distinction between INSERT and MODIFY operations when updating table entries. The INSERT operation will not work when trying to modify an existing table entry. Therefore, we modified the utils from p4lang/tutorials to correctly set the operation type in the request (see code). Initially, the controller initiates a connection to each switch in order, e.g. s1, then s2, etc. To be able to perform write operations, the switch needs to be set as master, by performing the master arbitration update. Therefore, when we wrote the initial match-action tables from the .json runtime files in order of switch id and then attempted to update s1 during runtime, we encountered an error, that this controller is not set as the master. A simple fix is to perform a master arbitration update again, or simply program s1 as the last switch on initialization. Later in the project, we modified the controller to accept a .json configuration for switches. This configuration allows for an arbitrary number of switches and flexible programming of each switch (see PR). The BMv2 switches take time to initialize and start up the gRPC server. During this period, the controller cannot initiate a connection with the switches. We solved this by simply waiting 2 seconds before starting the controller (see code). A more practical solution would be to implement a retry mechanism in the controller code when initiating a switch connection. TCP Checksum Relevant PR: https://github.com/stano45/p4containerflow/pull/6\nAt this point, we have a working load balancer that forwards traffic to one of two hosts. We are also able to update the load balancer node IPs using the controller. However, the load balancer in this state only supports traffic in the direction of the target hosts, while traffic from the hosts back to the client is not properly handled.\nTo support TCP connections, the following needs to be adjusted:\nRewrite the IP address of response packets. This is necessary, since the load balancer also has an IP address, and packets returning from target hosts would not be recognized as being a part of the same TCP connection. Recompute the checksum for TCP packets, in addition to the IPv4 checksum. Rewriting the source was relatively straightforward. Using p4’s match-action tables, we inserted an entry to the ecmp_group table, matching on the client’s IP (fixed at 10.0.1.1 in this example, see code) and wrote an action to rewrite the source address to the switches’ IP (see code).\nCalculating the TCP checksum correctly was a bit trickier. The key issues to look out for are properly parsing TCP headers (see code) and passing headers to the hash function in the right order (see code and p4 forum thread).\nDuring this stage, I found Wireshark to be a very useful tool, since it verifies the checksum of each TCP packet. If the checksum is calculated incorrectly, this will be indicated in the packet trace. This can help test any changes to the checksum quickly, without being left to wonder why packets are being dropped. A similar functionality is provided by the tcpdump CLI.\nLinux Networking Relevant PR: https://github.com/stano45/p4containerflow/pull/8/\nThe initial prototype relies on mininet to build a network using virtual interfaces and network namespaces. Mininet is a useful tool for rapidly building and evaluating network topologies. However, since the goal is to integrate this system into Kubernetes, mininet is a limitation. We decided to rebuild the same topology using the Linux networking stack. The main building blocks of this step are:\nNetwork namespaces (netns), Virtual ethernet pairs (veth pairs). A network namespace is a fundamental building block of Linux networking that allows for the creation of isolated network environments. Each namespace has its own set of network interfaces, routing tables, and iptables rules, making it possible to simulate multiple network environments on a single host.\nVirtual ethernet pairs can be used to create virtual interfaces, and automatically link them together. It is possible to “place” one half of the pair inside a network namespace, to simulate a link to a separate network. The other half of the veth pair acts as a gateway for the network living in the namespace.\nUsing netns, veth pairs, and the iproute2 suite to set MAC and IP addresses of virtual interfaces, we rebuilt the mininet topology on the Linux Networking Stack. This is done in the form of bash scripts (see code).\nThe following diagram shows the created topology, including the virtual interfaces:\nSetting up a complex virtual network topology is not straightforward. We outlined key challenges we faced during implementation:\nIt is important to set the gateway address of each of the network namespaces, followed by static ARP entries. This ensures the routing works properly. The number of virtual interfaces can grow quite large when building a complex network topology like ours. This is just a tip for anyone attempting the same: Whenever you write a script to build a network element (veth pair, netns), make sure to immediately write a script to tear it down the same way. Always a new interface/namespace, and then tear it down. It is easy to forget this part, and then have to manually tear down the network with additional effort. Containerizing hosts Relevant PR: https://github.com/stano45/p4containerflow/pull/9\nAfter everything was running properly on a virtualized network, we immediately recognized that the next step would be to start containerizing components. Container management tools (such as Podman) create network namespaces and wire them up using veth pairs in a very similar way as we did in the previous step.\nSpecifically, Podman uses the netavark backend for virtual networking. Each network is a separate network namespace, and each container in the network is connected to a network bridge. The network has a reserved IP address range, and the network bridge acts as the gateway. Each container assigned to a network gets its own IP.\nThe following diagram shows the network topology of a single host and the connection of the network to a switch port:\nWe wrote a script to create a network and a pod for each container. A pod is a group of containers with a shared IP address. This is also the case within the Kubernetes network model. We built a container image using the TCP server executable from the previous steps. Analogously, we built a client image.\nThere is no explicit link from the network bridge to the switch port. We ensured correct routing by setting the corresponding switch port as the default gateway for each host and inserted a static ARP entry. This way, the container immediately sent all traffic (outside its network) to the switch port’s MAC address, and the switch handled all further routing. This was repeated for all hosts to mimic the previous topology\nHowever, this approach is not ideal, since the IP route and the static MAC address have to be inserted inside the container. This is not possible with most containers, since most only include the required binaries and no networking packages, such as iproute2. Therefore, a further configuration of each container would be necessary, which might not be scalable in real-world scenarios. We did not address this problem, as this is only an intermediate step in the project, and the topology will change in the next steps.\nFurthermore, when setting up the routing, we encountered a strange issue: each established TCP connection would receive an RST packet from some interface and terminate. If you ever encounter such an issue in your experiments, it might be easier to check the following:\nAre there IP table rules that drop TCP packets anywhere? This would mean the kernel sends the TCP RST from that interface. Is the switch interface in promiscuous mode? Sometimes, when a packet is addressed at an interface, the kernel sends a RST packet, since there is no process listening on that interface. This issue can be solved by blocking TCP reset packets generated by the interface.\nContainerizing BMv2 switches Relevant PR: https://github.com/stano45/p4containerflow/pull/10\nAfter containerizing the hosts, we also decided to containerize the BMv2 switches. We simplified the topology to only include one switch, which uses the host network bridges as ports. These network bridges act as gateways for the Podman networks created in the previous step.\nFor simplicity, the switch container runs on the host network. The container image used is docker.io/p4lang/p4c image, which includes the BMv2 binary.\nThe following diagram shows the network topology, which consists of 4 Podman networks interfaced by bridges, connected to a single BMv2 switch running in a container, but on the host network:\nRedis container migration Relevant PR: https://github.com/stano45/p4containerflow/pull/18\nTo demonstrate container migration on the network topology we created, we set up a system with multiple components: An in-memory database (Redis) instance running on host 2 (h2), A backend application (App) running on host 1 (h1), exposing an endpoint to fetch data from the Redis DB, additionally containing the uptime of the Redis instance and number of connected clients, A client, also running on h1, that periodically fetches data from the BE and prints them in the console (simulating an oversimplified frontend application), A producer, also running on h1, periodically fetches a numerical ‘counter’ entry from the Redis DB, increments it by one, and updates the entry in the database.\nAfter deploying this system, we can observe the counter being periodically incremented by the producer. We perform a migration of the Redis DB from host 2 to host 3, the same way as in the previous examples, and observe little to no downtime from the client’s perspective.\nWe confirm that the migration has been successfully performed, since: traffic from the producer and the app is correctly routed to h3 instead of h2, the counter does not reset, confirming data consistency, the uptime of the Redis instance does not reset, confirming the instance previously running on h2 is now running on h3, the connected clients count stays at 2, confirming both the app’s and producer’s TCP connections to the Redis DB have been preserved.\nThe Kubernetes network model Before we dive into the Kubernetes integration, it is important to understand the Kubernetes network model, and how load balancing is handled in practice. This is how Kubernetes networking is described in the official documentation:\nEvery Pod in a cluster gets its own unique cluster-wide IP address (one address per IP address family). This means you do not need to explicitly create links between Pods and you almost never need to deal with mapping container ports to host ports. This creates a clean, backwards-compatible model where Pods can be treated much like VMs or physical hosts from the perspectives of port allocation, naming, service discovery, load balancing, application configuration, and migration.\nThis model is similar to what we have deployed in previous examples, where each pod was assigned a unique IP address, while the pod network interface is often a bridge. However, the pod IP is allocated by Kubernetes. We will discuss the implications of this in the next section.\nFurthermore, it is important to understand how load balancing is handled in Kubernetes. As always, there are multiple options. Firstly, it is possible to create a Service of type LoadBalancer. The load balancer is implemented by the cloud provider and is usually a proprietary component, which cannot be deployed locally without extra effort.\nTherefore, we evaluated various open source solutions to Kubernetes to assess how we can approach the integration of the BMv2 switch into the Kubernetes ecosystem.\nFirstly, we looked at MetalLB, which provides a load balancer implementation for bare metal clusters. Using MetalLB in layer 2 mode, all service traffic is routed to a single node, where kube-proxy spreads the traffic to pods. This model could be potentially extended by integrating a BMv2 switch running our P4 load load balancer program.\nSecondly, Kubernetes provides a Gateway API implementation that includes protocol-specific Route resources such as TCPRoute allowing to forward a TCP stream to a specified backend. Since this is similar to the goal of this project, a potential approach could be to modify this functionality to route traffic to a BMv2 switch.\nLastly, it is possible to build a solution similar to kube-router. The kube-router solution is deployed on the cluster as a DaemonSet, which means that all nodes run a copy of the pod (see the Kubernetes docs for more information). Kube-router uses the standard Linux networking stack (as discussed in a previous section), which makes it more straightforward to use than solutions using SDN or overlays.\nBecause of the simplicity of the kube-router solution, and the potential ease of replacing it with a controller using the BMv2 software switch, we decided to first deploy kube-router, and then look at possibilites of replacing it with a custom solution.\nInitial Kubernetes integration Relevant PRs:\nhttps://github.com/stano45/p4containerflow/pull/21 https://github.com/stano45/p4containerflow/pull/22 As a first step to start with the Kubernetes integration, we created a single-node cluster using kubeadm. To handle networking, we deployed a DaemonSet on the node running kube-router to load-balance traffic between pods. Furthermore, we deployed a simple http-server service, running in 2 pods. There is one container running in each pod, which accepts requests, holds them for a couple of seconds, and then sends a response.\nMigration within of a container running inside a Kubernetes pod requires multiple steps:\nCalling the kubelet checkpoint API to create a container checkpoint, Building a container image from the checkpoint, Pushing the container image to a container registry (local or hosted), Editing manifests to deploy this container image, Applying the manifest to restore the container. However, there are multiple issues with this approach, which will need to be addressed in future development of the project:\nRestoring a container with a new IP address in Kubernetes presents a \u0026ldquo;chicken or the egg\u0026rdquo; problem. In our approach, the checkpoint is modified with the new IP address (similar to scripts/edit_files_img.py), but Kubernetes allocates the IP address during restore. This could potentially be handled by running an init container in the target pod to make Kubernetes assing an IP address before container restore. The \u0026ldquo;Connection reset by peer\u0026rdquo; error always occurs after restore because checkpointing is performed for a container in a network namespace allocated to a Pod. There is no mechanism currently to prevent the Linux kernel from closing the TCP connection when a new packet is received from the client while the checkpointed container is not running. This problem could be solved by attaching CRIU action scripts to Kubernetes lifecycle events to lock the network and prevent the Kernel from sending a RST packet to the client. The current checkpoint/restore mechanism in Kubernetes was primarily designed for forensic analysis and unsuitable for live migration. In particular, it takes significant time to create a checkpoint archive, upload it to a container registry, and restore the container in a new Pod. This results in significant downtime. Within future work, there is room to optimize this process in multiple places. Future Work While this project presents multiple experiments showcasing container migration with established TCP connections, there is an additional effort required to utilize the P4 load balancer within Kubernetes. Specifically, the above-described example with kube-router should be modified to utilize a BMv2 switch for load balancing. This approach requires writing a Kubernetes controller that interacts with the BMv2 switch.\nFurthermore, it would be particularly useful to deploy a multi-node Kubernetes cluster and run the load balancer on a P4-programmable switch, such as Intel Tofino to assess the feasibility of this approach in a realistic scenario. Similarly, such an evaluation could be performed using SmartNICs such as the NVIDIA BlueField DPU.\n","permalink":"https://p4lang.github.io/gsoc/blog/posts/2024-08-25-container-migration/","summary":"\u003ch2 id=\"abstract\"\u003eAbstract\u003c/h2\u003e\n\u003cp\u003eKubernetes has recently introduced a container checkpoint/restore (C/R) feature to enable the creation of stateful snapshots for running containers. This feature allows containers to be restarted from checkpoints in different pods, enabling use cases such as live migration of microservices. However, preserving established TCP connections during migration is challenging because Kubernetes dynamically allocates IP addresses to Pods in the cluster. As a result, the restored container will run with a different IP address after migration, while clients will continue to send packets to the old IP address. This project aims to address this problem by developing a P4-based load balancer that keeps track of established TCP connections and routes application traffic to the correct IP address after container migration.\u003c/p\u003e","title":"P4-Enabled Container Migration in Kubernetes"}]