├── .github └── workflows │ └── test.yml ├── .gitignore ├── AUTHORS ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── README.md ├── Vagrantfile ├── compile-bpf.sh ├── doc ├── images │ └── p4xdp-workflow.png ├── lpc18.pdf ├── lpc18 │ ├── .gitignore │ ├── Makefile │ ├── acmart.cls │ ├── architecture.pdf │ ├── background.tex │ ├── compilation.tex │ ├── conclusions.tex │ ├── introduction.tex │ ├── kernel_test.png │ ├── p4cxdp-lpc18.pdf │ ├── results.tex │ ├── stf.pdf │ ├── stf.vsdx │ ├── testing.tex │ ├── testing_workflow.pdf │ ├── testing_workflow.vsdx │ ├── titlesec.sty │ ├── top.bib │ ├── top.tex │ └── user_test.png ├── p4c-xdp-lpc18-presentation.pdf ├── p4xdp-iovisor17.pdf └── perf.txt ├── lib ├── COPYING ├── Makefile ├── bpf_helpers.h ├── bpf_load.c ├── bpf_load.h ├── libbpf.c └── libbpf.h ├── ovs-parse-14.p4 ├── ovs.c ├── ovs.p4 ├── p4c-xdp.cpp ├── p4include └── xdp_model.p4 ├── run-bpf.sh ├── run-p4c-xdp.sh ├── target.cpp ├── target.h ├── test_ebpf_map.c ├── tests ├── Makefile ├── README.md ├── ebpf_headers.p4 ├── ebpf_xdp.h ├── empty.stf ├── load_and_verify.c ├── user_xdp10.c ├── user_xdp5.c ├── xdp.p4 ├── xdp.stf ├── xdp1.p4 ├── xdp1.stf ├── xdp10.p4 ├── xdp11.p4 ├── xdp12.p4 ├── xdp13.p4 ├── xdp14.p4 ├── xdp15.p4 ├── xdp16.p4 ├── xdp17.p4 ├── xdp2.p4 ├── xdp2.stf ├── xdp3.p4 ├── xdp4.p4 ├── xdp5.p4 ├── xdp6.p4 ├── xdp7.p4 ├── xdp8.p4 └── xdp9.p4 ├── tools └── install_dependencies.sh ├── xdpBackend.cpp ├── xdpBackend.h ├── xdpControl.cpp ├── xdpControl.h ├── xdpModel.cpp ├── xdpModel.h ├── xdpProgram.cpp ├── xdpProgram.h └── xdp_target.py /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | schedule: 8 | - cron: "0 13 * * 1" 9 | jobs: 10 | test: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - uses: actions/checkout@v1 14 | - name: ccache 15 | uses: hendrikmuhs/ccache-action@v1 16 | with: 17 | key: ${{ matrix.os }} 18 | max-size: 1000M 19 | - name: Install P4C dependencies 20 | run: | 21 | git clone https://github.com/p4lang/p4c $GITHUB_WORKSPACE/../p4c 22 | cd $GITHUB_WORKSPACE/../p4c 23 | git submodule update --init --recursive 24 | cd $GITHUB_WORKSPACE 25 | ./tools/install_dependencies.sh 26 | - name: Prepare P4C 27 | run: | 28 | mkdir -p $GITHUB_WORKSPACE/../p4c/extensions/ 29 | cp -r $GITHUB_WORKSPACE $GITHUB_WORKSPACE/../p4c/extensions/p4c-xdp 30 | ln -sf $GITHUB_WORKSPACE $GITHUB_WORKSPACE/../p4c/extensions/p4c-xdp 31 | cd $GITHUB_WORKSPACE/../p4c 32 | python3 backends/ebpf/build_libbpf 33 | mkdir $GITHUB_WORKSPACE/../p4c/build 34 | cd $GITHUB_WORKSPACE/../p4c/build 35 | cmake .. -DCMAKE_RUN_CLANG_TIDY=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo 36 | - name: Install P4C with the xdp extension 37 | run: | 38 | cd $GITHUB_WORKSPACE/../p4c/build 39 | make -j2 40 | - name: Test the xdp extension 41 | run: | 42 | cd $GITHUB_WORKSPACE/../p4c/build 43 | sudo -E ctest -R xdp --output-on-failure --schedule-random 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib/*.o 2 | tests/*.c 3 | tests/*.h 4 | tests/*.o 5 | tests/bpfloader 6 | p4c-xdp 7 | 8 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | =============================== =============================================== 2 | Name Email 3 | =============================== =============================================== 4 | Mihai Budiu mbudiu@vmware.com 5 | William Tu u9012063@gmail.com 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakefile for the XDP P4-16 back-end. 2 | 3 | message(STATUS "Start configuring XDP back end") 4 | 5 | set (P4C_XDP_SOURCES 6 | p4c-xdp.cpp 7 | xdpModel.cpp 8 | xdpControl.cpp 9 | xdpProgram.cpp 10 | target.cpp 11 | xdpBackend.cpp 12 | ../../backends/ebpf/ebpfProgram.cpp 13 | ../../backends/ebpf/ebpfOptions.cpp 14 | ../../backends/ebpf/ebpfTable.cpp 15 | ../../backends/ebpf/ebpfControl.cpp 16 | ../../backends/ebpf/ebpfParser.cpp 17 | ../../backends/ebpf/target.cpp 18 | ../../backends/ebpf/ebpfType.cpp 19 | ../../backends/ebpf/codeGen.cpp 20 | ../../backends/ebpf/ebpfModel.cpp 21 | ../../backends/ebpf/midend.cpp 22 | ../../backends/ebpf/lower.cpp 23 | ) 24 | 25 | set (P4C_XDP_HEADERS 26 | xdpModel.h 27 | xdpProgram.h 28 | xdpControl.h 29 | target.h 30 | xdpBackend.h 31 | ) 32 | 33 | set (XDP_DIST_HEADERS p4include/xdp_model.p4) 34 | 35 | add_cpplint_FILES(${CMAKE_CURRENT_SOURCE_DIR} "${P4C_XDP_SOURCES};${P4C_XDP_HEADERS}") 36 | 37 | build_unified(P4C_XDP_SOURCES) 38 | 39 | add_executable(p4c-xdp ${P4C_XDP_SOURCES}) 40 | target_link_libraries(p4c-xdp ${P4C_LIBRARIES} ${P4C_LIB_DEPS}) 41 | 42 | install (TARGETS p4c-xdp 43 | RUNTIME DESTINATION ${P4C_RUNTIME_OUTPUT_DIRECTORY}) 44 | install (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/p4include 45 | DESTINATION ${P4C_ARTIFACTS_OUTPUT_DIRECTORY}) 46 | 47 | add_custom_target(linkp4cxdp 48 | COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_BINARY_DIR}/p4c-xdp ${P4C_BINARY_DIR}/p4c-xdp 49 | COMMAND ${CMAKE_COMMAND} -E make_directory ${P4C_BINARY_DIR}/p4include && 50 | ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${XDP_DIST_HEADERS} ${P4C_BINARY_DIR}/p4include 51 | COMMAND ${CMAKE_COMMAND} -E create_symlink ${P4C_BINARY_DIR}/p4include ${CMAKE_CURRENT_BINARY_DIR}/p4include 52 | ) 53 | 54 | # check for the libbpf library, it is required for p4c-xdp to work 55 | find_library(LIBBPF_XDP NAMES bpf HINTS "${P4C_SOURCE_DIR}/backends/ebpf/runtime/usr/lib64/") 56 | if (LIBBPF_XDP) 57 | message(STATUS "Found libbpf library") 58 | else() 59 | message(FATAL_ERROR "Missing the libbpf dependency, disabling kernel tests." 60 | " You can install libbpf by running './build_libbpf' in the " 61 | "$${P4C_SOURCE_DIR}/backends/ebpf/runtime folder.") 62 | endif() 63 | 64 | # Automatically insert the xdp_target in the targets folder 65 | if(CMAKE_HOST_UNIX) 66 | EXECUTE_PROCESS(COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/xdp_target.py ${P4C_SOURCE_DIR}/backends/ebpf/targets/xdp_target.py 67 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 68 | ) 69 | endif(CMAKE_HOST_UNIX) 70 | 71 | set (XFAIL_TESTS_XDP) 72 | 73 | add_dependencies(p4c_driver linkp4cxdp) 74 | 75 | set(XDP_DRIVER "${P4C_SOURCE_DIR}/backends/ebpf/run-ebpf-test.py -t xdp -c \"${P4C_BINARY_DIR}/p4c-xdp\"") 76 | 77 | # This file will not run the full tests, but it will attempt to compile the p4 files down to C 78 | set (XDP_TEST_SUITES "${CMAKE_CURRENT_SOURCE_DIR}/tests/xdp*.p4") 79 | p4c_add_tests("xdp" ${XDP_DRIVER} ${XDP_TEST_SUITES} "${XFAIL_TESTS_XDP}") 80 | message(STATUS "Done with configuring XDP back end") 81 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:bionic 2 | 3 | WORKDIR /home/ 4 | ENV P4C_DEPS bison \ 5 | build-essential \ 6 | cmake \ 7 | git \ 8 | flex \ 9 | libboost-dev \ 10 | libboost-graph-dev \ 11 | libboost-iostreams-dev \ 12 | libfl-dev \ 13 | libgc-dev \ 14 | libgmp-dev \ 15 | pkg-config \ 16 | python3 \ 17 | python3-pip \ 18 | python3-setuptools 19 | 20 | ENV P4C_EBPF_DEPS libpcap-dev \ 21 | libelf-dev \ 22 | zlib1g-dev \ 23 | llvm \ 24 | clang \ 25 | libprotobuf-dev \ 26 | protobuf-compiler \ 27 | iproute2 \ 28 | tcpdump \ 29 | iptables 30 | 31 | ENV P4C_PIP_PACKAGES pyroute2 \ 32 | ply==3.8 \ 33 | scapy==2.4.0 34 | 35 | RUN apt-get update 36 | RUN apt-get install -y --no-install-recommends $P4C_DEPS 37 | RUN apt-get install -y --no-install-recommends $P4C_EBPF_DEPS 38 | # in some cases wheel is needed to install pip packages 39 | RUN pip3 install wheel 40 | RUN pip3 install $P4C_PIP_PACKAGES 41 | 42 | 43 | # p4c download begin 44 | RUN git clone https://github.com/p4lang/p4c.git && \ 45 | cd p4c && \ 46 | git submodule update --init --recursive && \ 47 | git submodule update --recursive && \ 48 | mkdir extensions 49 | # p4c download end 50 | 51 | 52 | # copy xdp into the extension folder 53 | COPY . /home/p4c/extensions/p4c-xdp 54 | RUN ln -s /home/p4c /home/p4c/extensions/p4c-xdp 55 | 56 | 57 | # build p4c and p4c-xdp 58 | RUN cd /home/p4c/ && \ 59 | python3 backends/ebpf/build_libbpf && \ 60 | mkdir -p build && \ 61 | cd build && \ 62 | cmake .. && \ 63 | make -j `getconf _NPROCESSORS_ONLN` && \ 64 | make install && \ 65 | cd .. 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # p4c-xdp 2 | 3 | This project has been archived. For a more complete ebpf XDP compiler 4 | please see the open-source p4c [repository](https://github.com/p4lang/p4c), in particular the p4c-ebpf 5 | compiler. 6 | 7 | 8 | [![Main Build](https://github.com/vmware/p4c-xdp/actions/workflows/test.yml/badge.svg)](https://github.com/vmware/p4c-xdp/actions/workflows/test.yml) 9 | [![Apache licensed](https://img.shields.io/badge/license-Apache-blue.svg)](https://github.com/vmware/p4c-xdp/blob/master/LICENSE) 10 | [![GPL licensed](https://img.shields.io/badge/license-GPL-blue.svg)](https://github.com/vmware/p4c-xdp/blob/master/lib/COPYING) 11 | 12 | This work presents a P4 compiler backend targeting XDP, the eXpress Data Path. 13 | P4 is a domain-specific language describing how packets are processed by the 14 | data plane of a programmable network elements, including network interface 15 | cards, appliances, and virtual switches. With P4, programmers focus on 16 | defining the protocol parsing, matching, and action executions, instead 17 | of the platform-specific language or implementation details. 18 | 19 | XDP is designed for users who want programmability as well as performance. 20 | XDP allows users to write a C-like packet processing program and loads into 21 | the device driver's receiving queue. When the device observes an incoming 22 | packet, before hanging the packet to the Linux stack, the user-defined XDP 23 | program is triggered to execute against the packet payload, making the 24 | decision as early as possible. 25 | 26 | We bring together the benefits of the two: P4 and XDP. To get started, 27 | first you need to setup the P4-16 compiler, then this project 28 | is an extension to the P4-16. To execute the XDP, you need Linux kernel 29 | version >= 4.10.0-rc7+ due to some BPF verifier limitations 30 | 31 |

32 | 33 |

34 | 35 | ## Presentations 36 | - IOVisor Summit 2017 37 | [slides](https://github.com/vmware/p4c-xdp/blob/master/doc/p4xdp-iovisor17.pdf), 38 | [demo1](https://youtu.be/On7hEJ6bPVU), [demo2](https://youtu.be/vlp1MzWVOc8), [demo3](https://youtu.be/TibGxCXPNVc) 39 | - Linux Plumbers' Conference 2018 40 | [slides](https://github.com/vmware/p4c-xdp/blob/master/doc/p4c-xdp-lpc18-presentation.pdf), 41 | [paper](https://github.com/vmware/p4c-xdp/blob/master/doc/lpc18.pdf) 42 | 43 | ## Installation 44 | ### Docker/Vagrant 45 | Please see Dockerfile. There is also a public docker image available as u9012063/p4xdp 46 | ```bash 47 | $ docker pull u9012063/p4xdp 48 | ``` 49 | will pull the latest image. However, the XDP BPF code has dependency on your kernel version. 50 | Currently for some complicated cases we require kernel >= 4.10.0-rc7. So a vagrant box is 51 | also provided with kernel 4.10.0-rc8. 52 | ```bash 53 | $ vagrant init u9012063/p4xdp 54 | $ vagrant up 55 | $ vagrant ssh 56 | ubuntu@ubuntu-xenial:~$ sudo su 57 | root@ubuntu-xenial:/home/ubuntu# docker images 58 | REPOSITORY TAG IMAGE ID CREATED SIZE 59 | u9012063/p4xdp latest 3c77fbbd84e5 41 hours ago 2.469 GB 60 | root@ubuntu-xenial:/home/ubuntu# docker run -it -u root --privileged 61 | ``` 62 | Will boot this VM, pull the docker image, and you can try p4c-xdp. 63 | 64 | ### P4-16 Compiler 65 | First you need to follow the installation guide of [P4-16](https://github.com/p4lang/p4c/) 66 | When you have P4-16 compiler, then add this project as an extension. 67 | Assuming you have P4-16 at your dir ~/p4c/, to setup P4C-XDP: 68 | ```bash 69 | cd ~/p4c/ 70 | mkdir extensions 71 | cd extensions 72 | git clone https://github.com/vmware/p4c-xdp.git 73 | ln -s ~/p4c p4c-xdp/p4c 74 | ``` 75 | Now that you have cloned p4c-xdp at ~/p4c/extensions/p4c-xdp, the next step is to 76 | recompile p4c: 77 | ```bash 78 | cd ~/p4c/ 79 | mkdir -p build 80 | cd build/ 81 | cmake .. 82 | make 83 | ``` 84 | This generates a p4c-xdp binary in ~/p4c/build. And install the xdp test target 85 | in `backends/ebpf/targets`. 86 | Next create a soft link to the binary: 87 | ```bash 88 | cd ~/p4c/extensions/p4c-xdp 89 | ln -s ~/p4c/build/p4c-xdp p4c-xdp 90 | ``` 91 | And a soft link to the test runtime: 92 | ```bash 93 | cd ~/p4c/extensions/p4c-xdp 94 | ln -s ~/p4c/backends/ebpf/run-ebpf-test.py run-ebpf-test.py 95 | 96 | ``` 97 | Now you can run the p4c-xdp tests: 98 | ``` 99 | cd ~/p4c/build/ 100 | make check-xdp 101 | ``` 102 | This will check your llvm and clang version, 103 | compile all .p4 files, generate .c files, and load them into the kernel 104 | to be checked by the BPF verifier. 105 | 106 | ## XDP: eXpress Data Path 107 | XDP is a packet processing mechanism implemented within the device driver with eBPF. 108 | Currently to compile a P4 to C program, use 109 | ```bash 110 | # ./p4c-xdp --target xdp -o 111 | ./p4c-xdp --target xdp -o /tmp/xdp1.c xdp1.p4 112 | ``` 113 | then you need to compile the xdp1.c to eBPF bytecode, xdp1.o, then load it 114 | into your driver. To compile a single .c file 115 | ```bash 116 | clang -Wno-unused-value -Wno-pointer-sign \ 117 | -Wno-compare-distinct-pointer-types \ 118 | -Wno-gnu-variable-sized-type-not-at-end \ 119 | -Wno-tautological-compare \ 120 | -O2 -emit-llvm -g -c /tmp/xdp1.c -o -| llc -march=bpf -filetype=obj -o /tmp/xdp1.o 121 | ``` 122 | Then load it into the driver with XDP support 123 | ```bash 124 | ip link set dev $DEV xdp obj xdp1.o verb 125 | ``` 126 | to unload the XDP object 127 | ```bash 128 | ip link set dev $DEV xdp off 129 | ``` 130 | ## Sample Code 131 | Please see the [tests folder](https://github.com/vmware/p4c-xdp/tree/master/tests) 132 | Simply run 'make' will start the build 133 | 134 | ## Related BPF/XDP work 135 | * Dive into BPF: a list of reading material, Quentin Monnet [link](https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf/) 136 | * BPF: Next Generation of Programmable Datapath by Thomas Graf, OVS Conf 2016 [video](https://www.youtube.com/watch?v=QJfmmoH2nSQ&t=1046s) 137 | * Fast Programmable Networks & Encapsulated Protocols, David S. Miller, netdev 1.2 [video](https://www.youtube.com/watch?v=NlMQ0i09HMU) 138 | 139 | ## License 140 | The p4c-xdp/lib/\* contains BPF loader licensed under the [General Public License, Version 2.0](lib/COPYING). The rest of p4c-xdp components are licensed under the [Apache License, Version 2.0](LICENSE). 141 | 142 | ## TODO 143 | * Remove the private kernel patch requirement when latest kernel with BPF fixed is ready 144 | * Apply the workaround of BPF\_MAX\_STACK 145 | * Control plane example using perf\_event\_output 146 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure(2) do |config| 5 | # The most common configuration options are documented and commented below. 6 | # For a complete reference, please see the online documentation at 7 | # https://docs.vagrantup.com. 8 | 9 | config.vm.box = "ubuntu/xenial64" 10 | 11 | # Disable automatic box update checking. If you disable this, then 12 | # boxes will only be checked for updates when the user runs 13 | # `vagrant box outdated`. This is not recommended. 14 | config.vm.box_check_update = false 15 | config.vm.provision "shell", inline: <<-SHELL 16 | sudo apt-get update 17 | sudo apt-get install -y build-essential dpkg 18 | 19 | # Kernel v4.11-rc1 20 | # This has our latest changes to BPF verifier 21 | # However the MAX_BPF_STACK issue hasn't been resolved. 22 | wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.11-rc1/linux-image-4.11.0-041100rc1-generic_4.11.0-041100rc1.201703051731_amd64.deb 23 | dpkg -i linux-image-4.11.0-041100rc1-generic_4.11.0-041100rc1.201703051731_amd64.deb 24 | wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.11-rc1/linux-headers-4.11.0-041100rc1-generic_4.11.0-041100rc1.201703051731_amd64.deb 25 | dpkg -i linux-headers-4.11.0-041100rc1-generic_4.11.0-041100rc1.201703051731_amd64.deb 26 | wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.11-rc1/linux-headers-4.11.0-041100rc1_4.11.0-041100rc1.201703051731_all.deb 27 | dpkg -i linux-headers-4.11.0-041100rc1_4.11.0-041100rc1.201703051731_all.deb 28 | 29 | # Fetch the p4xdp docker image 30 | apt-get install -y docker.io 31 | docker pull u9012063/p4xdp 32 | SHELL 33 | # reboot to the newer kernel 34 | config.vm.provision :reload 35 | end 36 | 37 | -------------------------------------------------------------------------------- /compile-bpf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | KERNEL=/root/net-next/ 3 | 4 | echo "ebpf filename:" $1 5 | 6 | clang -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/5.4.0/include/ \ 7 | -I../ -I$KERNEL/arch/x86/include -I$KERNEL/arch/x86/include/generated/uapi \ 8 | -I$KERNEL/arch/x86/include/generated -I$KERNEL/include \ 9 | -I$KERNEL/arch/x86/include/uapi \ 10 | -I$KERNEL/include/uapi -I$KERNEL/include/generated/uapi \ 11 | -include $KERNEL/include/linux/kconfig.h \ 12 | -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \ 13 | -Wno-compare-distinct-pointer-types -emit-llvm -O2 -c $1 -o -| \ 14 | llc -march=bpf -filetype=obj -o tmp.o 15 | 16 | echo "output to tmp.o" 17 | 18 | exit 0 19 | ./p4test --p4-14 ../backends/p4c-ovs-ebpf/ovs-parse.p4 20 | ./p4test --pp x.p4 --p4-14 ../backends/p4c-ovs-ebpf/ovs-parse.p4 21 | 22 | -------------------------------------------------------------------------------- /doc/images/p4xdp-workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/images/p4xdp-workflow.png -------------------------------------------------------------------------------- /doc/lpc18.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/lpc18.pdf -------------------------------------------------------------------------------- /doc/lpc18/.gitignore: -------------------------------------------------------------------------------- 1 | *.aux 2 | *.log 3 | top.pdf 4 | top.out 5 | *.bbl 6 | *.blg 7 | *.synctex.gz 8 | -------------------------------------------------------------------------------- /doc/lpc18/Makefile: -------------------------------------------------------------------------------- 1 | TEXFILES = background.tex compilation.tex conclusions.tex introduction.tex results.tex testing.tex top.tex 2 | FIGURES = 3 | GRAPHS = 4 | 5 | top.pdf: $(FIGURES) $(TEXFILES) $(GRAPHS) 6 | texi2pdf top.tex -o p4cxdp-lpc18.pdf 7 | xdg-open p4cxdp-lpc18.pdf 8 | 9 | clean: clean-graphs clean-figures 10 | rm -f top.bbl top.blg top.log top.aux 11 | rm -f p4cxdp-lpc18.pdf 12 | 13 | clean-graphs: 14 | rm -f $(GRAPHS) 15 | 16 | clean-figures: 17 | rm -f $(FIGURES) 18 | 19 | %.pdf: %.eps 20 | epstopdf $< > $@ 21 | -------------------------------------------------------------------------------- /doc/lpc18/architecture.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/lpc18/architecture.pdf -------------------------------------------------------------------------------- /doc/lpc18/compilation.tex: -------------------------------------------------------------------------------- 1 | \section{Compiling P4 to eBPF}\label{sec:compilation} 2 | 3 | In this section we describe two open-source compilers that translate 4 | P4 programs in stylized C programs, that can in turn be compiled into 5 | eBPF programs using the LLVM eBPF back-end. 6 | 7 | \subsection{Packet filters with eBPF}\label{sec:ebpf} 8 | 9 | The eBPF back-end is part of the P4 reference compiler 10 | implementation~\cite{p4-ebpf-backend}. This back-end targets a 11 | relatively simple packet filter architecture. 12 | Figure~\ref{fig:ebpf-model} shows the architectural model of an eBPF 13 | packet filter expressed in P4. This architecture comprises a parser 14 | and a control block; the control block must produce a Boolean value 15 | which indicates whether the packet is accepted or not. 16 | 17 | \begin{figure}[h] 18 | \begin{lstlisting} 19 | #include 20 | 21 | extern CounterArray { 22 | CounterArray(bit<32> max_index, bool sparse); 23 | void increment(in bit<32> index); 24 | } 25 | 26 | extern array_table { 27 | array_table(bit<32> size); 28 | } 29 | 30 | extern hash_table { 31 | hash_table(bit<32> size); 32 | } 33 | 34 | parser parse(packet_in packet, out H headers); 35 | control filter(in H headers, out bool accept); 36 | 37 | package ebpfFilter(parse prs, 38 | filter filt); 39 | \end{lstlisting} 40 | \caption{Packet filter P4 architectural model for an eBPF 41 | target.}\label{fig:ebpf-model} 42 | \end{figure} 43 | 44 | Figure~\ref{fig:count} shows a P4 program written for this 45 | architecture that counts the number of IPv4 packets that are 46 | encountered. 47 | 48 | \begin{figure} 49 | \begin{lstlisting} 50 | #include 51 | #include 52 | 53 | typedef bit<48> EthernetAddress; 54 | typedef bit<32> IPv4Address; 55 | 56 | header Ethernet { 57 | EthernetAddress dstAddr; 58 | EthernetAddress srcAddr; 59 | bit<16> etherType; 60 | } 61 | 62 | // IPv4 header without options 63 | header IPv4 { 64 | bit<4> version; 65 | bit<4> ihl; 66 | bit<8> diffserv; 67 | bit<16> totalLen; 68 | bit<16> identification; 69 | bit<3> flags; 70 | bit<13> fragOffset; 71 | bit<8> ttl; 72 | bit<8> protocol; 73 | bit<16> hdrChecksum; 74 | IPv4Address srcAddr; 75 | IPv4Address dstAddr; 76 | } 77 | 78 | struct Headers { 79 | Ethernet eth; 80 | IPv4 ipv4; 81 | } 82 | 83 | parser prs(packet_in p, out Headers headers) { 84 | state start { 85 | p.extract(headers.eth); 86 | transition select(headers.eth.etherType) { 87 | 0x800 : ip; 88 | default : reject; 89 | } 90 | } 91 | 92 | state ip { 93 | p.extract(headers.ipv4); 94 | transition accept; 95 | } 96 | } 97 | 98 | control pipe(in Headers headers, out bool pass){ 99 | CounterArray(32w10, true) ctr; 100 | 101 | apply { 102 | if (headers.ipv4.isValid()) { 103 | ctr.increment(headers.ipv4.dstAddr); 104 | pass = true; 105 | } else 106 | pass = false; 107 | } 108 | } 109 | 110 | // Instantiate main package 111 | ebpfFilter(prs(), pipe()) main; 112 | \end{lstlisting} 113 | \caption{A P4 program that counts the number of IPv4 packets 114 | encountered.}\label{fig:count} 115 | \end{figure} 116 | 117 | Compilation to C is fairly straightforward; the generated C program is 118 | always memory-safe, using bounds-checks for all packet accesses. For 119 | the entire P4 program a single C function is generated which returns a 120 | Boolean value. Table~\ref{table:translation} shows how each P4 121 | construct is converted to a C construct. Currently programs with 122 | parser loops are rejected, but a parser loop unrolling pass (under 123 | development) will allow such programs to be compiled. 124 | 125 | \begin{table}[h] 126 | \footnotesize 127 | \begin{tabular}{|l|p{4.8cm}|} \hline 128 | \textbf{P4 construct} & \textbf{C Translation} \\ \hline \hline 129 | \texttt{header} & \texttt{struct} with an additional \texttt{valid} bit \\ \hline 130 | \texttt{struct} & \texttt{struct} \\ \hline 131 | parser state & block statement \\ \hline 132 | state transition & \texttt{goto} statement \\ \hline 133 | \texttt{extract} call & load/shift/mask data from packet \\ \hline 134 | table & 2 eBPF maps --- one for actions, one for the default action \\ \hline 135 | table key type & \texttt{struct} type \\ \hline 136 | \texttt{action} & tagged \texttt{union} with action parameters \\ \hline 137 | \texttt{action} parameters & \texttt{struct} \\ \hline 138 | \texttt{action} body & block statement \\ \hline 139 | table \texttt{apply} & lookup in eBFP map + \texttt{switch} statement with all actions \\ \hline 140 | counters & eBPF map \\ \hline 141 | \end{tabular} 142 | \caption{Compiling P4 constructs to C.}\label{table:translation} 143 | \end{table} 144 | 145 | \subsection{Packet forwarding with XDP}\label{sec:xdp} 146 | 147 | 148 | A second P4 to C compiler, P4C-XDP, is available as an open-source 149 | project hosted at~\cite{p4-xdp-backend}. P4C-XDP is licensed under the GNU 150 | GPL and Apache License. 151 | This compiler extends the eBPF compiler from Section~\ref{sec:ebpf}. It can 152 | target either a packet filter, or a packet switch. The following listing shows 153 | the XDP architectural model targeted by this compiler. You can see that a 154 | P4 XDP program can (1) return to the kernel one of the four outcomes 155 | described in Section~\ref{sec:xdp-background}, and (2) it can also 156 | modify the packet itself, by inserting, modifying or deleting headers. 157 | 158 | \begin{figure} 159 | \begin{lstlisting} 160 | #include 161 | enum xdp_action { 162 | XDP_ABORTED, 163 | XDP_DROP, 164 | XDP_PASS, 165 | XDP_TX 166 | } 167 | struct xdp_input { bit<32> input_port } 168 | 169 | struct xdp_output { 170 | xdp_action output_action; 171 | bit<32> output_port; 172 | } 173 | 174 | parser xdp_parse(packet_in packet, 175 | out H headers); 176 | control xdp_switch(inout H hdrs, 177 | in xdp_input i, 178 | out xdp_output o); 179 | control xdp_deparse(in H headers, 180 | packet_out packet); 181 | 182 | package xdp(xdp_parse p, 183 | xdp_switch s, 184 | xdp_deparse d); 185 | \end{lstlisting} 186 | \caption{XDP packet switching architectural 187 | model.}\label{fig:xdp-model} 188 | \end{figure} 189 | -------------------------------------------------------------------------------- /doc/lpc18/conclusions.tex: -------------------------------------------------------------------------------- 1 | \section{Lessons learned}\label{sec:conclusions} 2 | 3 | In general, our development experience is mirrored by the lessons described 4 | in~\cite{minao-hspr18} and ~\cite{bertin-netdev17}. 5 | 6 | \paragraph{No multi-/broadcast support} 7 | While XDP is able to redirect single frames it does not have the ability to 8 | clone and redirect multiple packets similar to \texttt{bpf\_clone\_redirect}. 9 | This makes development of more sophisticated P4 forwarding programs problematic. 10 | 11 | \paragraph{The stack size is too small} 12 | More complex generated XDP programs are rejected by the verifier despite their 13 | safeness. 14 | This is a particular challenge when attempting to implement network function 15 | chaining or advanced pipelined packet processing in a single XDP program. 16 | 17 | \paragraph{Generic XDP and TCP} 18 | Our testing framework uses virtual Linux interfaces and generic 19 | XDP~\cite{genericxdp} to verify XDP programs. 20 | Unfortunately, we are unable to test TCP streams as the protocol is not 21 | supported by this driver~\cite{xdptcp}. 22 | Any program loaded by generic XDP operates after the creation of 23 | the \texttt{skb} and requires the original packet data. Since TCP clones every 24 | packet and passes the unmodifiable \texttt{skb} clone, generic XDP is 25 | bypassed and never receives the datagram. 26 | 27 | \paragraph{Using the libbpf userspace library} 28 | Compilation of eBPF programs in user-space requires substantial 29 | effort. Many function calls and variables available in sample programs are not 30 | available as general C library. Any user trying to interact with the 31 | generated C code has to provide their own sources. Currently, P4C-XDP maintains 32 | copies of utilities from kernel code or various online sources. This is not a 33 | sustainable approach. We plan to integrate \texttt{libbpf} with our repository 34 | to control and manage the eBPF programs and maps. 35 | 36 | \paragraph{Pinned eBPF maps in network namespaces} 37 | When using eBPF programs in namespaces, maps that were pinned via \texttt{tc} 38 | do not persist across \texttt{ip netns exec} calls. As consequence, any 39 | program has to be run in a single continuous shell command. Example: 40 | {\scriptsize 41 | \begin{verbatim} 42 | bash -c "tc filter add ...; ls /sys/fs/bpf/tc/globals" 43 | \end{verbatim} 44 | } 45 | Once \texttt{ip netns exec} has finished, the reference to the eBPF 46 | map and all its associated state disappear. The eBPF program, however, remains 47 | attached to the virtual interface, leading to inconsistent packet processing 48 | behavior. 49 | 50 | -------------------------------------------------------------------------------- /doc/lpc18/introduction.tex: -------------------------------------------------------------------------------- 1 | \section{Introduction}\label{sec:introduction} 2 | 3 | The introduction of Software Defined Networking (SDN)~\cite{rfc7426} 4 | has decoupled the network control-plane from the data-plane. The Open 5 | Flow Protocol~\cite{mckeown-ccr08} is a typical incarnation of SDN. 6 | Even though SDN makes the control-plane programmable, it still assumes 7 | that the {data-plane} is fixed. The inability to program data-planes 8 | is a significant impediment to innovation: for example, the deployment 9 | of the VxLAN protocol~\cite{rfc7348} took 4 years between the initial 10 | proposal and its commercial availability in high-speed devices. 11 | 12 | To address this state of affairs, \cite{bosshart-ccr14} introduced the 13 | P4 language: Programming Protocol-independent Packet Processors, which 14 | is designed to make the behavior of \emph{data-planes} expressible as 15 | software. P4 has gained rapid adoption. The p4.org 16 | consortium~\cite{p4org} was created to steward the language evolution; 17 | p4.org currently includes more than 100 organizations in the areas of 18 | networking, cloud systems, network chip design, and academic 19 | institutions. The P4 specification is open and 20 | public~\cite{p416-spec17}. Reference implementations for compilers, 21 | simulation and debugging tools are available with a permissive license 22 | at the GitHub P4 repository~\cite{p4lang}. While initially P4 was 23 | designed for programming network switches, its scope has been 24 | broadened to cover a large variety of packet-processing systems (e.g., 25 | network cards, FPGAs, etc.). 26 | -------------------------------------------------------------------------------- /doc/lpc18/kernel_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/lpc18/kernel_test.png -------------------------------------------------------------------------------- /doc/lpc18/p4cxdp-lpc18.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/lpc18/p4cxdp-lpc18.pdf -------------------------------------------------------------------------------- /doc/lpc18/results.tex: -------------------------------------------------------------------------------- 1 | \section{Experimental results}\label{sec:results} 2 | \subsection{Testbed} 3 | All of our performance results use a hardware testbed that consists of 4 | two Intel Xeon E5 2440 v2 1.9GHz servers, each with 1 CPU socket and 5 | 8 physical cores with hyperthreading enabled. 6 | Each target server has an Intel 10GbE X540-AT2 dual 7 | port NIC, with the two ports of the Intel NIC on one server connected 8 | to the two ports on the identical NIC on the other server. 9 | We installed p4c-xdp on one server, the {\em target server}, and 10 | attached the XDP program to the port that receives the packets 11 | The other server, the {\em source server}, generates packets 12 | at the maximum 10~Gbps packet rate of 14.88~Mpps using the DPDK-based 13 | TRex~\cite{trex} traffic generator. The source server sends minimum 14 | length 64-byte packets in a {\em single} UDP flow to one port of the 15 | target server, and receives the forwarded packets on the same port. 16 | At the target server, we use only one core to process all packets. 17 | Every packet received goes through the pipeline specified in P4. 18 | 19 | We use the sample p4 programs in the tests directory and the following 20 | metrics to understand the performance impact of the P4-generated XDP 21 | program: 22 | \begin{itemize} 23 | \item Packet Processing Rate (Mpps): Once the XDP program finishes 24 | processing the packet, it returns one of the actions mentioned in 25 | section~\ref{sec:background}. When we want to count the number of 26 | packets that can be dropped per second, we modify each p4 program to 27 | always return XDP\_DROP. 28 | \item CPU Utilization: Every packet processed by the XDP program is run 29 | under the per-core software IRQ daemon, named 30 | \texttt{ksoftirqd/\textit{core}}. All packets are processed by only 31 | one core with one kthread, the ksoftirqd, and we measure the CPU 32 | utilization of the ksoftirqd on the core. 33 | \item Number of BPF instructions verified: For each program, we list 34 | the complexity as the number of BPF instructions the eBPF 35 | verifier scans. 36 | \end{itemize} 37 | 38 | The target server is running Linux kernel 4.19-rc5 and for all our 39 | tests, the BPF JIT (Just-In-Time) compiler is enabled and JIT hardening 40 | is disabled. All programs are compiled with clang 3.8 with llvm 5.0. 41 | For each test program, we use the following 42 | command from iproute2 to load it into kernel: 43 | \begin{lstlisting}[frame=none] 44 | ip link set dev eth0 xdp obj xdp1.o verb 45 | \end{lstlisting} 46 | 47 | The Intel 10GbE X540 NIC is running the \texttt{ixgbe} driver with 16 RX queues 48 | set-up. Since the source server is sending single UDP flow, packets 49 | always arrive at a single queue ID. As a result, we collect the number 50 | of packets being dropped at this queue. 51 | 52 | \subsection{Results} 53 | 54 | To compute the baseline performance we wrote two small XDP programs by 55 | hand: \texttt{SimpleDrop}, drops all packets by returning 56 | \texttt{XDP\_DROP} immediately. \texttt{SimpleTX} forwards the packet 57 | to the receiving port returning \texttt{XDP\_TX}. Each of these 58 | programs consists of only two BPF instructions. 59 | 60 | \begin{lstlisting}[frame=none] 61 | /* SimpleDrop */ 62 | 0: (b7) r0 = 1 // XDP_DROP 63 | 1: (95) exit 64 | 65 | /* SimpleTX */ 66 | 0: (b7) r0 = 3 // XDP_TX 67 | 1: (95) exit 68 | \end{lstlisting} 69 | 70 | After, we attached the following P4 programs to the receiving device: 71 | \begin{itemize} 72 | \item xdp1.p4: Parse Ethernet/IPv4 header, deparse it, and drop. 73 | \item xdp3.p4: Parse Ethernet/IPv4 header, lookup a MAC address 74 | in a map, deparse it, and drop. 75 | \item xdp6.p4: Parse Ethernet/IPv4 header, lookup and get a new TTL value 76 | from eBPF map, set to IPv4 header, deparse it, and drop. 77 | \item xdp7.p4: Parse Ethernet/IPv4/UDP header, write a pre-defined source port 78 | and source IP, recalculate checksum, deparse, and drop. 79 | \item xdp11.p4: Parse Ethernet/IPv4 header, swap src/dst MAC address, 80 | deparse it, and send back to the same port (XDP\_TX). 81 | \item xdp15.p4: Parse Ethernet header, insert a customized 8-byte header, 82 | deparse it, and send back to the same port (XDP\_TX). 83 | \end{itemize} 84 | 85 | \begin{table} 86 | \centering 87 | \small 88 | \begin{tabular}{llll} 89 | \underline{P4 program} & \underline{CPU Util.} & \underline{Mpps} & \underline{Insns./Stack}\\ 90 | SimpleDrop & 75\% & 14.4 & 2/0 \\ 91 | SimpleTX & 100\% & 7.2 & 2/0 \\ 92 | xdp1.p4 & 100\% & 8.1 & 277/256 \\ 93 | xdp3.p4 & 100\% & 7.1 & 326/256 \\ 94 | xdp6.p4 & 100\% & 2.5 & 335/272 \\ 95 | xdp7.p4 & 100\% & 5.7 & 5821/336 \\ 96 | xdp11.p4 & 100\% & 4.7 & 335/216 \\ 97 | xdp15.p4 & 100\% & 5.5 & 96/56\\ 98 | \end{tabular} 99 | \caption{\footnotesize Performance of XDP program generated by 100 | p4c-xdp compiler using single core.} 101 | \label{tab:perf} 102 | \end{table} 103 | 104 | As shown in Table~\ref{tab:perf}, xdp1.p4 allows us to measure the 105 | overhead introduced by parsing and deparsing: a drop from 14.4~Mpps to 106 | 8.1~Mpps. xdp3.p4 reduces the rate by another million PPS due to the 107 | eBPF map lookup (this operation always returns NULL, no value from the 108 | map is accessed). xdp6.p4 has significant overhead because it 109 | accesses a map, finds a new TTL value, and writes to the IPv4 header. 110 | Interestingly, although xdp7.p4 does extra parsing to the UDP header 111 | and checksum recalculation, it has only a moderate overhead because of 112 | the lack of map accesses. 113 | 114 | Finally, xdp11.p4 and xdp15.p4 show the transmit (XDP\_TX) 115 | performance. Compared with xdp11, xdp15.p4 invokes the 116 | \texttt{bpf\_adjust\_head} helper function to reset the pointer for extra 117 | bytes. It does not incur much overhead because there 118 | is already a reserved space in front of every XDP packet frame. 119 | 120 | \subsection{Performance Analysis} 121 | 122 | To further understand the performance overhead of programs generated 123 | by p4c-xdp, we started broke down the CPU utilization. We used the Linux 124 | perf tool on the process ID of the ksoftirqd that shows 100\%: 125 | \begin{lstlisting}[frame=none] 126 | perf record -p sleep 10 127 | \end{lstlisting} 128 | 129 | 130 | \noindent The following output shows the profile of xdp1.p4: 131 | {\scriptsize 132 | \begin{verbatim} 133 | 83.19% [kernel.kallsyms] [k] ___bpf_prog_run 134 | 8.14% [ixgbe] [k] ixgbe_clean_rx_irq 135 | 4.82% [kernel.kallsyms] [k] nmi 136 | 1.48% [kernel.kallsyms] [k] bpf_xdp_adjust_head 137 | 1.07% [kernel.kallsyms] [k] __rcu_read_unlock 138 | 0.40% [ixgbe] [k] ixgbe_alloc_rx_buffers 139 | \end{verbatim} 140 | } 141 | 142 | This confirms that most of the CPU cycles are spent on executing the 143 | XDP program, \texttt{\_\_\_bpf\_prog\_run}, which caused us to investigate the 144 | eBPF C code of xdp1.p4. 145 | 146 | \begin{table} 147 | \centering 148 | \small 149 | \begin{tabular}{llll} 150 | \underline{P4 program} & \underline{CPU Util.} & \underline{Mpps} & \underline{Insns./Stack}\\ 151 | xdp1.p4 & 77\% & 14.8 & 26/0 \\ 152 | xdp3.p4 & 100\% & 13 & 100/16 \\ 153 | xdp6.p4 & 100\% & 12 & 98/40 \\ 154 | \end{tabular} 155 | \caption{\footnotesize Performance of XDP program without deparser.} 156 | \label{tab:perf2} 157 | \end{table} 158 | 159 | After commenting out the deparser C code, performance increases 160 | significantly (see Table~\ref{tab:perf2}). In the generated 161 | code, the p4c-xdp compiler always writes back the entire packet 162 | content, even when the P4 program does not modify any fields. In 163 | addition, the parser/deparser incur byte-order translation, e.g., 164 | htonl, ntohl. This could be avoided by always using network 165 | byte-order in P4 and XDP. We plan to implement optimizations to 166 | reduce this overhead. 167 | -------------------------------------------------------------------------------- /doc/lpc18/stf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/lpc18/stf.pdf -------------------------------------------------------------------------------- /doc/lpc18/stf.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/lpc18/stf.vsdx -------------------------------------------------------------------------------- /doc/lpc18/testing.tex: -------------------------------------------------------------------------------- 1 | \section{Testing eBPF programs}\label{sec:testing} 2 | 3 | To test the P4 to C compilers we have adapted and extended the 4 | existing P4 testing infrastructure. The infrastructure can perform 5 | both functional correctness testing at the user level, and complete 6 | end-to-end testing running programs in the kernel. 7 | 8 | \subsection{User-Space Testing} 9 | 10 | User-space testing validates the correctness of the code generated by 11 | compiler and can be performed even on systems that lack eBPF support 12 | in the kernel. The user space testing framework does not depend on 13 | the LLVM~\cite{llvm} or any particular kernel version. It also does 14 | not require usage of \texttt{iproute2}~\cite{iproute} tooling such as 15 | \texttt{tc} or \texttt{ip}. It is also easier to debug failing tests 16 | in user-space, by using tools such as GDB~\cite{gdb}, 17 | Valgrind~\cite{valgrind}, or Wireshark~\cite{wireshark}. 18 | 19 | \begin{table}[h] 20 | \footnotesize 21 | \begin{center} 22 | \begin{tabular}{|p{2.8cm}|p{4.3cm}|} \hline 23 | \textbf{Command} & \textbf{Description} \\ \hline \hline 24 | \textbf{packet} port data & Insert a frame of bytes 25 | \textit{data} into port \textit{port}. \\ \hline 26 | \textbf{expect} port data & Expect a frame of bytes 27 | \textit{data} on port \textit{port}. \\ \hline 28 | \textbf{add} tbl priority match action & Insert a 29 | match-action entry with key \textit{match} and action 30 | \textit{action} into table \textit{tbl}. \\ \hline 31 | \textbf{setdefault} tbl action & Set the default action for table 32 | \textit{tbl}. \\ 33 | \hline 34 | \textbf{check\_counter} tbl key==n & Check if the value on 35 | the entry \textit{key} in counter table \textit{tbl} matches 36 | \textit{n}. \\ 37 | \hline 38 | \textbf{wait} & Pause for a second. \\ \hline 39 | \end{tabular} 40 | \caption{The STF command palette.}\label{table:stf} 41 | \end{center} 42 | \end{table} 43 | 44 | \subsection{The Simple Test Framework} 45 | \begin{figure} 46 | \centering 47 | \includegraphics[width=\linewidth]{stf} 48 | \caption{Annotated example of a Simple Testing Framework (STF) 49 | program for testing the P4 compiler.} 50 | \label{fig:stf} 51 | \end{figure} 52 | 53 | The P4 compiler includes a simple language (STF = Simple Testing 54 | Framework) to describe input/output packets and to populate P4 tables. 55 | Initially STF was used with software simulators to validate P4 56 | programs, but we have adapted it for testing the eBPF and XDP 57 | back-ends. The STF framework is written in Python. 58 | Figure~\ref{fig:stf} shows a small program written in the STF 59 | language. Table \ref{table:stf} describes the list of currently 60 | supported STF operations in the eBPF testing backend. 61 | 62 | The STF \texttt{packet} statement describes an input port and the 63 | contents of a inbound packet. The \texttt{expect} statement describes 64 | an output port and the contents of an outbound packet. 65 | 66 | Tables can be populated using the \texttt{add} statement, which 67 | indicates a P4 table and an action to insert in the table, including 68 | values for the action parameters. Currently we assume that all 69 | \texttt{add} statements are executed prior to all the packet 70 | manipulation statements. 71 | 72 | Our testing framework converts \texttt{packet} statements into PCAP 73 | (Packet CAPture) files, one for each input port tested. \texttt{add} 74 | statements are converted into C programs that populate eBPF maps. 75 | 76 | Although STF supports testing counters as well, our eBPF testing 77 | framework does not yet support this feature. 78 | 79 | \begin{figure*} 80 | \centering 81 | \includegraphics[width=\linewidth]{testing_workflow} 82 | \caption{Testing workflow for a P4-eBPF program. Environment and 83 | target are provided by the user.} 84 | \label{fig:p4_testflow} 85 | \end{figure*} 86 | 87 | \subsection{The Test Runtime} 88 | 89 | Executing a P4 eBPF test is done in five stages (Figure 90 | \ref{fig:p4_testflow}): 91 | 92 | \begin{enumerate} 93 | \item\textbf{compile-p4:} Compile the P4 file to C program. 94 | \item \textbf{parse-stf:} Convert the STF file to a C program and into 95 | input PCAP files. 96 | \item \textbf{compile-data-plane:} Compile and load the C programs 97 | into an executable. 98 | \item \textbf{run:} Wire up the executable to read from the input PCAP 99 | files; run the executable -- first populate tables then execute the 100 | program over the input packets. Capture the produced output packets 101 | into output files. 102 | \item \textbf{check-results:} Compare the output packets with the 103 | expected results. 104 | \end{enumerate} 105 | 106 | \noindent These five stages look slightly differently when testing in 107 | user-space and in kernel-space. 108 | 109 | In user space we use a hash-table library to emulate eBPF maps. 110 | 111 | When testing in kernel-space we compile the eBPF/XDP programs to eBPF 112 | object files using LLVM. Before the eBPF/XDP program is loaded, the 113 | framework creates a bridge running in a network namespace. 114 | Namespace-based isolation allows us to run multiple tests in parallel. 115 | Virtual interfaces are attached to the bridge. The testing runtime 116 | injects packets into the associated ports using raw sockets. The 117 | output results are recorded by attaching Tcpdump~\cite{tcpdump} to 118 | each output virtual interface. 119 | -------------------------------------------------------------------------------- /doc/lpc18/testing_workflow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/lpc18/testing_workflow.pdf -------------------------------------------------------------------------------- /doc/lpc18/testing_workflow.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/lpc18/testing_workflow.vsdx -------------------------------------------------------------------------------- /doc/lpc18/top.tex: -------------------------------------------------------------------------------- 1 | \documentclass[9pt,twocolumn,times]{article} 2 | 3 | \usepackage{color} 4 | \usepackage{caption} 5 | \usepackage{float} 6 | \usepackage{courier} 7 | \usepackage{xspace} 8 | \usepackage{url} 9 | \usepackage{listings} 10 | \usepackage{graphicx} 11 | \usepackage{mdframed} 12 | \usepackage[letterpaper, margin=1in]{geometry} 13 | \usepackage{enumitem} 14 | \usepackage[compact]{titlesec} 15 | 16 | \setitemize{noitemsep,topsep=0pt,parsep=0pt,partopsep=0pt,leftmargin=.5cm} 17 | \setlist[itemize]{noitemsep,topsep=0pt,parsep=0pt,partopsep=0pt,leftmargin=.5cm} 18 | \setlist[enumerate]{noitemsep,topsep=0pt,parsep=0pt,partopsep=0pt,leftmargin=.5cm} 19 | \setlist[description]{noitemsep,topsep=0pt,parsep=0pt,partopsep=0pt,leftmargin=.5cm} 20 | 21 | \lstset{ 22 | language=C, 23 | basicstyle=\ttfamily\footnotesize, 24 | frame=lrbt, 25 | morekeywords={action,apply,bit,bool,% 26 | const,control,default,else,% 27 | enum,error,extern,exit,% 28 | false,header,header_union,if,% 29 | in,inout,int,match_kind,% 30 | package,parser,out,return,% 31 | select,state,struct,switch,% 32 | table,transition,true,tuple% 33 | typedef,varbit,verify,void,% 34 | % 35 | abstract,interface,class,virtual% used for IR 36 | } 37 | } 38 | 39 | \newcommand{\code}[1]{\texttt{#1}} 40 | \newcommand{\keyword}[1]{{\bf \texttt{#1}}} 41 | \newcommand{\vonemodel}{\code{v1model}\xspace} 42 | \newcommand{\mycomment}[1]{} 43 | 44 | \title{Linux Network Programming with P4} 45 | \author{William Tu\\ 46 | VMware Inc.\\ 47 | \texttt{tuc@vmware.com} 48 | \and 49 | Fabian Ruffy\\ 50 | University of British Columbia\\ 51 | \texttt{fruffy@cs.ubc.ca} 52 | \and 53 | Mihai Budiu\\ 54 | VMware Research\\ 55 | \texttt{mbudiu@vmware.com} 56 | } 57 | \date{} 58 | 59 | \begin{document} 60 | \maketitle 61 | 62 | \begin{abstract} 63 | P4 is a domain-specific language for implementing network data-planes. 64 | The P4 abstraction allows programmers to write network protocols in a 65 | generalized fashion, without needing to know the configuration specifics 66 | of the targeted data-plane. 67 | 68 | The extended Berkely Packet Filter (eBPF) is a safe virtual machine for 69 | executing sand-boxed programs in the Linux kernel. eBPF, and its extension 70 | the eXpress Data Path (XDP), effectively serve as programmable data-planes of 71 | the kernel. 72 | 73 | P4C-XDP is a project combining the performance of XDP with the generality and 74 | usability of P4. In this document, we describe how P4 can be 75 | translated into eBPF/XDP. We review the fundamental limitations of both 76 | technologies, analyze the performance of several generated XDP programs, and 77 | discuss problems we have faced while working on this new technology. 78 | 79 | \end{abstract} 80 | 81 | 82 | \input{introduction} 83 | \input{background} 84 | \input{compilation} 85 | \input{testing} 86 | \input{results} 87 | \input{conclusions} 88 | 89 | \bibliography{top} 90 | \bibliographystyle{acm} 91 | 92 | \end{document} 93 | -------------------------------------------------------------------------------- /doc/lpc18/user_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/lpc18/user_test.png -------------------------------------------------------------------------------- /doc/p4c-xdp-lpc18-presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/p4c-xdp-lpc18-presentation.pdf -------------------------------------------------------------------------------- /doc/p4xdp-iovisor17.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmware-archive/p4c-xdp/00dabaefc384668f4939ad9a1621ecf099ff88fe/doc/p4xdp-iovisor17.pdf -------------------------------------------------------------------------------- /doc/perf.txt: -------------------------------------------------------------------------------- 1 | 03/24/2017 2 | Performance test using ixgbe XDP dirver 3 | sender is using DPDK pktgen, receiving side is running XDP 4 | 5 | === sender at server prmh-138 === 6 | root@prmh-nsx-perf-server138:~/dpdk/pktgen-dpdk-new# ./pktgendpdk.sh pcap/test1.pcap 7 | 8 | === XDP receiver === 9 | root@prmh-nsx-perf-server139:# echo 1 > /proc/sys/net/core/bpf_jit_enable 10 | root@prmh-nsx-perf-server139:# ip link set dev enp5s0f1 xdp obj xdp11.o 11 | 12 | === xdp2_kern.c === 13 | # at receiving side 14 | $ sar -n DEV 1 15 | 03:33:16 PM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil 16 | 3:33:17 PM enp5s0f1 1139681.00 1139681.00 231286.55 231286.55 0.00 0.00 0.00 18.95 17 | 18 | $ perf top 19 | 53.42% [kernel] [k] __bpf_prog_run 20 | 2.57% [kernel] [k] menu_select 21 | 2.22% [kernel] [k] cpuidle_enter_state 22 | 2.19% [kernel] [k] irq_entries_start 23 | 1.66% [kernel] [k] __tick_nohz_idle_enter 24 | 1.35% [kernel] [k] copy_page 25 | 1.25% [kernel] [k] napi_complete_done 26 | 1.17% [kernel] [k] swiotlb_sync_single_for_device 27 | 0.98% [kernel] [k] percpu_array_map_lookup_elem 28 | 29 | === xdp11.o === 30 | 03:33:16 PM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil 31 | 03:47:36 PM enp5s0f1 1139756.00 1139735.00 231301.22 231295.65 0.00 0.00 0.00 18.95 32 | 33 | 71.04% [kernel] [k] __bpf_prog_run 34 | 2.56% [kernel] [k] page_frag_free 35 | 0.87% [kernel] [k] cpuidle_enter_state 36 | 0.73% [kernel] [k] irq_entries_start 37 | 0.64% [kernel] [k] memcpy_erms 38 | 0.63% [kernel] [k] menu_select 39 | 40 | 88.43% [kernel] [k] __bpf_prog_run 41 | 0.98% [kernel] [k] memcpy_erms 42 | 0.57% [kernel] [k] irq_entries_start 43 | 0.49% [kernel] [k] swiotlb_map_page 44 | 0.46% [kernel] [k] __tick_nohz_idle_enter 45 | 0.43% [kernel] [k] array_map_update_elem 46 | 0.32% [kernel] [k] cpuidle_enter_state 47 | 48 | === xdp9 (force to do XDP_TX) === 49 | 03:33:16 PM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil 50 | 03:52:59 PM enp5s0f1 1139754.00 1139722.00 231299.64 222388.89 0.00 0.00 0.00 18.95 51 | 87.06% [kernel] [k] __bpf_prog_run 52 | 0.59% [kernel] [k] memcpy_erms 53 | 0.55% [kernel] [k] htab_map_lookup_elem 54 | 0.55% [kernel] [k] menu_select 55 | 0.53% [kernel] [k] __htab_map_lookup_elem 56 | 0.48% [kernel] [k] array_map_lookup_elem 57 | 0.47% [kernel] [k] __tick_nohz_idle_enter 58 | 59 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | 3 | all: libbpf.o bpf_load.o 4 | 5 | libbpf.o: libbpf.c libbpf.h 6 | $(CC) -g -c libbpf.c 7 | 8 | bpf_load.o: bpf_load.c bpf_load.h 9 | $(CC) -g -c bpf_load.c 10 | 11 | clean: 12 | @rm -f *.o 13 | -------------------------------------------------------------------------------- /lib/bpf_helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef __BPF_HELPERS_H 2 | #define __BPF_HELPERS_H 3 | 4 | /* helper macro to place programs, maps, license in 5 | * different sections in elf_bpf file. Section names 6 | * are interpreted by elf_bpf loader 7 | */ 8 | #define SEC(NAME) __attribute__((section(NAME), used)) 9 | 10 | /* helper functions called from eBPF programs written in C */ 11 | static void *(*bpf_map_lookup_elem)(void *map, void *key) = 12 | (void *) BPF_FUNC_map_lookup_elem; 13 | static int (*bpf_map_update_elem)(void *map, void *key, void *value, 14 | unsigned long long flags) = 15 | (void *) BPF_FUNC_map_update_elem; 16 | static int (*bpf_map_delete_elem)(void *map, void *key) = 17 | (void *) BPF_FUNC_map_delete_elem; 18 | static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = 19 | (void *) BPF_FUNC_probe_read; 20 | static unsigned long long (*bpf_ktime_get_ns)(void) = 21 | (void *) BPF_FUNC_ktime_get_ns; 22 | static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = 23 | (void *) BPF_FUNC_trace_printk; 24 | static void (*bpf_tail_call)(void *ctx, void *map, int index) = 25 | (void *) BPF_FUNC_tail_call; 26 | static unsigned long long (*bpf_get_smp_processor_id)(void) = 27 | (void *) BPF_FUNC_get_smp_processor_id; 28 | static unsigned long long (*bpf_get_current_pid_tgid)(void) = 29 | (void *) BPF_FUNC_get_current_pid_tgid; 30 | static unsigned long long (*bpf_get_current_uid_gid)(void) = 31 | (void *) BPF_FUNC_get_current_uid_gid; 32 | static int (*bpf_get_current_comm)(void *buf, int buf_size) = 33 | (void *) BPF_FUNC_get_current_comm; 34 | static int (*bpf_perf_event_read)(void *map, int index) = 35 | (void *) BPF_FUNC_perf_event_read; 36 | static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) = 37 | (void *) BPF_FUNC_clone_redirect; 38 | static int (*bpf_redirect)(int ifindex, int flags) = 39 | (void *) BPF_FUNC_redirect; 40 | static int (*bpf_perf_event_output)(void *ctx, void *map, 41 | unsigned long long flags, void *data, 42 | int size) = 43 | (void *) BPF_FUNC_perf_event_output; 44 | static int (*bpf_get_stackid)(void *ctx, void *map, int flags) = 45 | (void *) BPF_FUNC_get_stackid; 46 | static int (*bpf_probe_write_user)(void *dst, void *src, int size) = 47 | (void *) BPF_FUNC_probe_write_user; 48 | static int (*bpf_current_task_under_cgroup)(void *map, int index) = 49 | (void *) BPF_FUNC_current_task_under_cgroup; 50 | static int (*bpf_skb_get_tunnel_key)(void *ctx, void *key, int size, int flags) = 51 | (void *) BPF_FUNC_skb_get_tunnel_key; 52 | static int (*bpf_skb_set_tunnel_key)(void *ctx, void *key, int size, int flags) = 53 | (void *) BPF_FUNC_skb_set_tunnel_key; 54 | static int (*bpf_skb_get_tunnel_opt)(void *ctx, void *md, int size) = 55 | (void *) BPF_FUNC_skb_get_tunnel_opt; 56 | static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = 57 | (void *) BPF_FUNC_skb_set_tunnel_opt; 58 | static unsigned long long (*bpf_get_prandom_u32)(void) = 59 | (void *) BPF_FUNC_get_prandom_u32; 60 | static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = 61 | (void *) BPF_FUNC_xdp_adjust_head; 62 | 63 | 64 | /* llvm builtin functions that eBPF C program may use to 65 | * emit BPF_LD_ABS and BPF_LD_IND instructions 66 | */ 67 | struct sk_buff; 68 | unsigned long long load_byte(void *skb, 69 | unsigned long long off) asm("llvm.bpf.load.byte"); 70 | unsigned long long load_half(void *skb, 71 | unsigned long long off) asm("llvm.bpf.load.half"); 72 | unsigned long long load_word(void *skb, 73 | unsigned long long off) asm("llvm.bpf.load.word"); 74 | 75 | /* a helper structure used by eBPF C program 76 | * to describe map attributes to elf_bpf loader 77 | */ 78 | struct bpf_map_def { 79 | unsigned int type; 80 | unsigned int key_size; 81 | unsigned int value_size; 82 | unsigned int max_entries; 83 | unsigned int map_flags; 84 | /* we need this for tc, not for bpf syscall */ 85 | unsigned int id; 86 | unsigned int pinning; 87 | }; 88 | 89 | static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = 90 | (void *) BPF_FUNC_skb_load_bytes; 91 | static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = 92 | (void *) BPF_FUNC_skb_store_bytes; 93 | static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = 94 | (void *) BPF_FUNC_l3_csum_replace; 95 | static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) = 96 | (void *) BPF_FUNC_l4_csum_replace; 97 | static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = 98 | (void *) BPF_FUNC_skb_under_cgroup; 99 | static int (*bpf_skb_change_head)(void *, int len, int flags) = 100 | (void *) BPF_FUNC_skb_change_head; 101 | 102 | #if defined(__x86_64__) 103 | 104 | #define PT_REGS_PARM1(x) ((x)->di) 105 | #define PT_REGS_PARM2(x) ((x)->si) 106 | #define PT_REGS_PARM3(x) ((x)->dx) 107 | #define PT_REGS_PARM4(x) ((x)->cx) 108 | #define PT_REGS_PARM5(x) ((x)->r8) 109 | #define PT_REGS_RET(x) ((x)->sp) 110 | #define PT_REGS_FP(x) ((x)->bp) 111 | #define PT_REGS_RC(x) ((x)->ax) 112 | #define PT_REGS_SP(x) ((x)->sp) 113 | #define PT_REGS_IP(x) ((x)->ip) 114 | 115 | #elif defined(__s390x__) 116 | 117 | #define PT_REGS_PARM1(x) ((x)->gprs[2]) 118 | #define PT_REGS_PARM2(x) ((x)->gprs[3]) 119 | #define PT_REGS_PARM3(x) ((x)->gprs[4]) 120 | #define PT_REGS_PARM4(x) ((x)->gprs[5]) 121 | #define PT_REGS_PARM5(x) ((x)->gprs[6]) 122 | #define PT_REGS_RET(x) ((x)->gprs[14]) 123 | #define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */ 124 | #define PT_REGS_RC(x) ((x)->gprs[2]) 125 | #define PT_REGS_SP(x) ((x)->gprs[15]) 126 | #define PT_REGS_IP(x) ((x)->psw.addr) 127 | 128 | #elif defined(__aarch64__) 129 | 130 | #define PT_REGS_PARM1(x) ((x)->regs[0]) 131 | #define PT_REGS_PARM2(x) ((x)->regs[1]) 132 | #define PT_REGS_PARM3(x) ((x)->regs[2]) 133 | #define PT_REGS_PARM4(x) ((x)->regs[3]) 134 | #define PT_REGS_PARM5(x) ((x)->regs[4]) 135 | #define PT_REGS_RET(x) ((x)->regs[30]) 136 | #define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */ 137 | #define PT_REGS_RC(x) ((x)->regs[0]) 138 | #define PT_REGS_SP(x) ((x)->sp) 139 | #define PT_REGS_IP(x) ((x)->pc) 140 | 141 | #elif defined(__powerpc__) 142 | 143 | #define PT_REGS_PARM1(x) ((x)->gpr[3]) 144 | #define PT_REGS_PARM2(x) ((x)->gpr[4]) 145 | #define PT_REGS_PARM3(x) ((x)->gpr[5]) 146 | #define PT_REGS_PARM4(x) ((x)->gpr[6]) 147 | #define PT_REGS_PARM5(x) ((x)->gpr[7]) 148 | #define PT_REGS_RC(x) ((x)->gpr[3]) 149 | #define PT_REGS_SP(x) ((x)->sp) 150 | #define PT_REGS_IP(x) ((x)->nip) 151 | 152 | #endif 153 | 154 | #ifdef __powerpc__ 155 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) 156 | #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP 157 | #else 158 | #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ \ 159 | bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) 160 | #define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ \ 161 | bpf_probe_read(&(ip), sizeof(ip), \ 162 | (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) 163 | #endif 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /lib/bpf_load.h: -------------------------------------------------------------------------------- 1 | #ifndef __BPF_LOAD_H 2 | #define __BPF_LOAD_H 3 | 4 | #define MAX_MAPS 32 5 | #define MAX_PROGS 32 6 | 7 | extern int map_fd[MAX_MAPS]; 8 | extern int prog_fd[MAX_PROGS]; 9 | extern int event_fd[MAX_PROGS]; 10 | extern int prog_cnt; 11 | 12 | /* parses elf file compiled by llvm .c->.o 13 | * . parses 'maps' section and creates maps via BPF syscall 14 | * . parses 'license' section and passes it to syscall 15 | * . parses elf relocations for BPF maps and adjusts BPF_LD_IMM64 insns by 16 | * storing map_fd into insn->imm and marking such insns as BPF_PSEUDO_MAP_FD 17 | * . loads eBPF programs via BPF syscall 18 | * 19 | * One ELF file can contain multiple BPF programs which will be loaded 20 | * and their FDs stored stored in prog_fd array 21 | * 22 | * returns zero on success 23 | */ 24 | int load_bpf_file(char *path); 25 | 26 | void read_trace_pipe(void); 27 | struct ksym { 28 | long addr; 29 | char *name; 30 | }; 31 | 32 | int load_kallsyms(void); 33 | struct ksym *ksym_search(long key); 34 | int set_link_xdp_fd(int ifindex, int fd); 35 | #endif 36 | -------------------------------------------------------------------------------- /lib/libbpf.c: -------------------------------------------------------------------------------- 1 | /* eBPF mini library */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "libbpf.h" 15 | 16 | static __u64 ptr_to_u64(void *ptr) 17 | { 18 | return (__u64) (unsigned long) ptr; 19 | } 20 | 21 | int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, 22 | int max_entries) 23 | { 24 | union bpf_attr attr = { 25 | .map_type = map_type, 26 | .key_size = key_size, 27 | .value_size = value_size, 28 | .max_entries = max_entries, 29 | }; 30 | 31 | return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr)); 32 | } 33 | 34 | int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags) 35 | { 36 | union bpf_attr attr = { 37 | .map_fd = fd, 38 | .key = ptr_to_u64(key), 39 | .value = ptr_to_u64(value), 40 | .flags = flags, 41 | }; 42 | 43 | return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); 44 | } 45 | 46 | int bpf_lookup_elem(int fd, void *key, void *value) 47 | { 48 | union bpf_attr attr = { 49 | .map_fd = fd, 50 | .key = ptr_to_u64(key), 51 | .value = ptr_to_u64(value), 52 | }; 53 | 54 | return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); 55 | } 56 | 57 | int bpf_delete_elem(int fd, void *key) 58 | { 59 | union bpf_attr attr = { 60 | .map_fd = fd, 61 | .key = ptr_to_u64(key), 62 | }; 63 | 64 | return syscall(__NR_bpf, BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)); 65 | } 66 | 67 | int bpf_get_next_key(int fd, void *key, void *next_key) 68 | { 69 | union bpf_attr attr = { 70 | .map_fd = fd, 71 | .key = ptr_to_u64(key), 72 | .next_key = ptr_to_u64(next_key), 73 | }; 74 | 75 | return syscall(__NR_bpf, BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr)); 76 | } 77 | 78 | #define ROUND_UP(x, n) (((x) + (n) - 1u) & ~((n) - 1u)) 79 | 80 | char bpf_log_buf[LOG_BUF_SIZE]; 81 | 82 | int bpf_prog_load(enum bpf_prog_type prog_type, 83 | const struct bpf_insn *insns, int prog_len, 84 | const char *license, int kern_version) 85 | { 86 | union bpf_attr attr = { 87 | .prog_type = prog_type, 88 | .insns = ptr_to_u64((void *) insns), 89 | .insn_cnt = prog_len / sizeof(struct bpf_insn), 90 | .license = ptr_to_u64((void *) license), 91 | .log_buf = ptr_to_u64(bpf_log_buf), 92 | .log_size = LOG_BUF_SIZE, 93 | .log_level = 1, 94 | }; 95 | 96 | /* assign one field outside of struct init to make sure any 97 | * padding is zero initialized 98 | */ 99 | attr.kern_version = kern_version; 100 | 101 | bpf_log_buf[0] = 0; 102 | 103 | return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); 104 | } 105 | 106 | int bpf_obj_pin(int fd, const char *pathname) 107 | { 108 | union bpf_attr attr = { 109 | .pathname = ptr_to_u64((void *)pathname), 110 | .bpf_fd = fd, 111 | }; 112 | 113 | return syscall(__NR_bpf, BPF_OBJ_PIN, &attr, sizeof(attr)); 114 | } 115 | 116 | int bpf_obj_get(const char *pathname) 117 | { 118 | union bpf_attr attr = { 119 | .pathname = ptr_to_u64((void *)pathname), 120 | }; 121 | 122 | return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr)); 123 | } 124 | 125 | int open_raw_sock(const char *name) 126 | { 127 | struct sockaddr_ll sll; 128 | int sock; 129 | 130 | sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL)); 131 | if (sock < 0) { 132 | printf("cannot create raw socket\n"); 133 | return -1; 134 | } 135 | 136 | memset(&sll, 0, sizeof(sll)); 137 | sll.sll_family = AF_PACKET; 138 | sll.sll_ifindex = if_nametoindex(name); 139 | sll.sll_protocol = htons(ETH_P_ALL); 140 | if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { 141 | printf("bind to %s\n", name); 142 | close(sock); 143 | return -1; 144 | } 145 | 146 | return sock; 147 | } 148 | 149 | int perf_event_open(struct perf_event_attr *attr, int pid, int cpu, 150 | int group_fd, unsigned long flags) 151 | { 152 | return syscall(__NR_perf_event_open, attr, pid, cpu, 153 | group_fd, flags); 154 | } 155 | -------------------------------------------------------------------------------- /lib/libbpf.h: -------------------------------------------------------------------------------- 1 | /* eBPF mini library */ 2 | #ifndef __LIBBPF_H 3 | #define __LIBBPF_H 4 | 5 | struct bpf_insn; 6 | 7 | int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, 8 | int max_entries); 9 | int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags); 10 | int bpf_lookup_elem(int fd, void *key, void *value); 11 | int bpf_delete_elem(int fd, void *key); 12 | int bpf_get_next_key(int fd, void *key, void *next_key); 13 | 14 | int bpf_prog_load(enum bpf_prog_type prog_type, 15 | const struct bpf_insn *insns, int insn_len, 16 | const char *license, int kern_version); 17 | 18 | int bpf_obj_pin(int fd, const char *pathname); 19 | int bpf_obj_get(const char *pathname); 20 | 21 | #define LOG_BUF_SIZE 262144 22 | extern char bpf_log_buf[LOG_BUF_SIZE]; 23 | 24 | /* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ 25 | 26 | #define BPF_ALU64_REG(OP, DST, SRC) \ 27 | ((struct bpf_insn) { \ 28 | .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ 29 | .dst_reg = DST, \ 30 | .src_reg = SRC, \ 31 | .off = 0, \ 32 | .imm = 0 }) 33 | 34 | #define BPF_ALU32_REG(OP, DST, SRC) \ 35 | ((struct bpf_insn) { \ 36 | .code = BPF_ALU | BPF_OP(OP) | BPF_X, \ 37 | .dst_reg = DST, \ 38 | .src_reg = SRC, \ 39 | .off = 0, \ 40 | .imm = 0 }) 41 | 42 | /* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */ 43 | 44 | #define BPF_ALU64_IMM(OP, DST, IMM) \ 45 | ((struct bpf_insn) { \ 46 | .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ 47 | .dst_reg = DST, \ 48 | .src_reg = 0, \ 49 | .off = 0, \ 50 | .imm = IMM }) 51 | 52 | #define BPF_ALU32_IMM(OP, DST, IMM) \ 53 | ((struct bpf_insn) { \ 54 | .code = BPF_ALU | BPF_OP(OP) | BPF_K, \ 55 | .dst_reg = DST, \ 56 | .src_reg = 0, \ 57 | .off = 0, \ 58 | .imm = IMM }) 59 | 60 | /* Short form of mov, dst_reg = src_reg */ 61 | 62 | #define BPF_MOV64_REG(DST, SRC) \ 63 | ((struct bpf_insn) { \ 64 | .code = BPF_ALU64 | BPF_MOV | BPF_X, \ 65 | .dst_reg = DST, \ 66 | .src_reg = SRC, \ 67 | .off = 0, \ 68 | .imm = 0 }) 69 | 70 | #define BPF_MOV32_REG(DST, SRC) \ 71 | ((struct bpf_insn) { \ 72 | .code = BPF_ALU | BPF_MOV | BPF_X, \ 73 | .dst_reg = DST, \ 74 | .src_reg = SRC, \ 75 | .off = 0, \ 76 | .imm = 0 }) 77 | 78 | /* Short form of mov, dst_reg = imm32 */ 79 | 80 | #define BPF_MOV64_IMM(DST, IMM) \ 81 | ((struct bpf_insn) { \ 82 | .code = BPF_ALU64 | BPF_MOV | BPF_K, \ 83 | .dst_reg = DST, \ 84 | .src_reg = 0, \ 85 | .off = 0, \ 86 | .imm = IMM }) 87 | 88 | /* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */ 89 | #define BPF_LD_IMM64(DST, IMM) \ 90 | BPF_LD_IMM64_RAW(DST, 0, IMM) 91 | 92 | #define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ 93 | ((struct bpf_insn) { \ 94 | .code = BPF_LD | BPF_DW | BPF_IMM, \ 95 | .dst_reg = DST, \ 96 | .src_reg = SRC, \ 97 | .off = 0, \ 98 | .imm = (__u32) (IMM) }), \ 99 | ((struct bpf_insn) { \ 100 | .code = 0, /* zero is reserved opcode */ \ 101 | .dst_reg = 0, \ 102 | .src_reg = 0, \ 103 | .off = 0, \ 104 | .imm = ((__u64) (IMM)) >> 32 }) 105 | 106 | #ifndef BPF_PSEUDO_MAP_FD 107 | # define BPF_PSEUDO_MAP_FD 1 108 | #endif 109 | 110 | /* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ 111 | #define BPF_LD_MAP_FD(DST, MAP_FD) \ 112 | BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) 113 | 114 | 115 | /* Direct packet access, R0 = *(uint *) (skb->data + imm32) */ 116 | 117 | #define BPF_LD_ABS(SIZE, IMM) \ 118 | ((struct bpf_insn) { \ 119 | .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ 120 | .dst_reg = 0, \ 121 | .src_reg = 0, \ 122 | .off = 0, \ 123 | .imm = IMM }) 124 | 125 | /* Memory load, dst_reg = *(uint *) (src_reg + off16) */ 126 | 127 | #define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ 128 | ((struct bpf_insn) { \ 129 | .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ 130 | .dst_reg = DST, \ 131 | .src_reg = SRC, \ 132 | .off = OFF, \ 133 | .imm = 0 }) 134 | 135 | /* Memory store, *(uint *) (dst_reg + off16) = src_reg */ 136 | 137 | #define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ 138 | ((struct bpf_insn) { \ 139 | .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ 140 | .dst_reg = DST, \ 141 | .src_reg = SRC, \ 142 | .off = OFF, \ 143 | .imm = 0 }) 144 | 145 | /* Memory store, *(uint *) (dst_reg + off16) = imm32 */ 146 | 147 | #define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ 148 | ((struct bpf_insn) { \ 149 | .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ 150 | .dst_reg = DST, \ 151 | .src_reg = 0, \ 152 | .off = OFF, \ 153 | .imm = IMM }) 154 | 155 | /* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */ 156 | 157 | #define BPF_JMP_REG(OP, DST, SRC, OFF) \ 158 | ((struct bpf_insn) { \ 159 | .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ 160 | .dst_reg = DST, \ 161 | .src_reg = SRC, \ 162 | .off = OFF, \ 163 | .imm = 0 }) 164 | 165 | /* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */ 166 | 167 | #define BPF_JMP_IMM(OP, DST, IMM, OFF) \ 168 | ((struct bpf_insn) { \ 169 | .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ 170 | .dst_reg = DST, \ 171 | .src_reg = 0, \ 172 | .off = OFF, \ 173 | .imm = IMM }) 174 | 175 | /* Raw code statement block */ 176 | 177 | #define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ 178 | ((struct bpf_insn) { \ 179 | .code = CODE, \ 180 | .dst_reg = DST, \ 181 | .src_reg = SRC, \ 182 | .off = OFF, \ 183 | .imm = IMM }) 184 | 185 | /* Program exit */ 186 | 187 | #define BPF_EXIT_INSN() \ 188 | ((struct bpf_insn) { \ 189 | .code = BPF_JMP | BPF_EXIT, \ 190 | .dst_reg = 0, \ 191 | .src_reg = 0, \ 192 | .off = 0, \ 193 | .imm = 0 }) 194 | 195 | /* create RAW socket and bind to interface 'name' */ 196 | int open_raw_sock(const char *name); 197 | 198 | struct perf_event_attr; 199 | int perf_event_open(struct perf_event_attr *attr, int pid, int cpu, 200 | int group_fd, unsigned long flags); 201 | #endif 202 | -------------------------------------------------------------------------------- /ovs-parse-14.p4: -------------------------------------------------------------------------------- 1 | /* OVS P4 header file 2 | * - Copy header from include/headers.p4, parser.p4 at github.com/p4lang/switch.git 3 | * - Copy struct definition from OVS 4 | */ 5 | 6 | #define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ 7 | #define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ 8 | #define ETH_P_ARP 0x0806 9 | #define ETH_P_IPV4 0x0800 10 | #define ETH_P_IPV6 0x86DD 11 | 12 | #define IPPROTO_ICMP 1 13 | #define IPPROTO_IGMP 2 14 | #define IPPROTO_TCP 6 15 | #define IPPROTO_UDP 17 16 | #define IPPROTO_GRE 47 17 | #define IPPROTO_SCTP 132 18 | 19 | header_type ethernet_t { 20 | fields { 21 | dstAddr : 48; 22 | srcAddr : 48; 23 | etherType : 16; 24 | } 25 | } 26 | 27 | header_type vlan_tag_t { 28 | fields { 29 | pcp : 3; 30 | cfi : 1; 31 | vid : 12; 32 | etherType : 16; 33 | } 34 | } 35 | 36 | header_type mpls_t { 37 | fields { 38 | label : 20; 39 | exp : 3; 40 | bos : 1; 41 | ttl : 8; 42 | } 43 | } 44 | 45 | header_type arp_rarp_t { 46 | fields { 47 | hwType : 16; 48 | protoType : 16; 49 | hwAddrLen : 8; 50 | protoAddrLen : 8; 51 | opcode : 16; 52 | } 53 | } 54 | 55 | header_type arp_rarp_ipv4_t { 56 | fields { 57 | srcHwAddr : 48; 58 | srcProtoAddr : 32; 59 | dstHwAddr : 48; 60 | dstProtoAddr : 32; 61 | } 62 | } 63 | 64 | header_type ipv4_t { 65 | fields { 66 | version : 4; 67 | ihl : 4; 68 | diffserv : 8; 69 | totalLen : 16; 70 | identification : 16; 71 | flags : 3; 72 | fragOffset : 13; 73 | ttl : 8; 74 | protocol : 8; 75 | hdrChecksum : 16; 76 | srcAddr : 32; 77 | dstAddr: 32; 78 | } 79 | } 80 | 81 | header_type ipv6_t { 82 | fields { 83 | version : 4; 84 | trafficClass : 8; 85 | flowLabel : 20; 86 | payloadLen : 16; 87 | nextHdr : 8; 88 | hopLimit : 8; 89 | srcAddr : 128; 90 | dstAddr : 128; 91 | } 92 | } 93 | 94 | header_type icmp_t { 95 | fields { 96 | typeCode : 16; 97 | hdrChecksum : 16; 98 | } 99 | } 100 | 101 | header_type tcp_t { 102 | fields { 103 | srcPort : 16; 104 | dstPort : 16; 105 | seqNo : 32; 106 | ackNo : 32; 107 | dataOffset : 4; 108 | res : 4; 109 | flags : 8; 110 | window : 16; 111 | checksum : 16; 112 | urgentPtr : 16; 113 | } 114 | } 115 | 116 | header_type udp_t { 117 | fields { 118 | srcPort : 16; 119 | dstPort : 16; 120 | length_ : 16; 121 | checksum : 16; 122 | } 123 | } 124 | 125 | header_type sctp_t { 126 | fields { 127 | srcPort : 16; 128 | dstPort : 16; 129 | verifTag : 32; 130 | checksum : 32; 131 | } 132 | } 133 | 134 | header_type gre_t { 135 | fields { 136 | C : 1; 137 | R : 1; 138 | K : 1; 139 | S : 1; 140 | s : 1; 141 | recurse : 3; 142 | flags : 5; 143 | ver : 3; 144 | proto : 16; 145 | } 146 | } 147 | /* ----------------- metadata ---------------- */ 148 | header_type pkt_metadata_t { 149 | fields { 150 | recirc_id : 32; /* Recirculation id carried with the 151 | recirculating packets. 0 for packets 152 | received from the wire. */ 153 | dp_hash : 32; /* hash value computed by the recirculation 154 | action. */ 155 | skb_priority : 32; /* Packet priority for QoS. */ 156 | pkt_mark : 32; /* Packet mark. */ 157 | ct_state : 16; /* Connection state. */ 158 | ct_zone : 16; /* Connection zone. */ 159 | ct_mark : 32; /* Connection mark. */ 160 | ct_label : 128; /* Connection label. */ 161 | in_port : 32; /* Input port. */ 162 | } 163 | } 164 | 165 | header_type flow_tnl_t { 166 | fields { 167 | /* struct flow_tnl: 168 | * Tunnel information used in flow key and metadata. 169 | */ 170 | ip_dst : 32; 171 | ipv6_dst : 64; 172 | ip_src: 32; 173 | ipv6_src : 64; 174 | tun_id : 64; 175 | flags : 16; 176 | ip_tos : 8; 177 | ip_ttl : 8; 178 | tp_src : 16; 179 | tp_dst : 16; 180 | gbp_id : 16; 181 | gbp_flags : 8; 182 | pad1: 40; /* Pad to 64 bits. */ 183 | /* struct tun_metadata metadata; */ 184 | } 185 | } 186 | 187 | header ethernet_t ethernet; 188 | header ipv4_t ipv4; 189 | header ipv6_t ipv6; 190 | header arp_rarp_t arp; 191 | header tcp_t tcp; 192 | header udp_t udp; 193 | header icmp_t icmp; 194 | header vlan_tag_t vlan; 195 | metadata pkt_metadata_t md; 196 | metadata flow_tnl_t tnl_md; 197 | 198 | /* ------------------ A tree, no loop is allowed -------------------------------- */ 199 | 200 | parser start { 201 | return parse_ethernet; 202 | } 203 | 204 | parser parse_ethernet{ 205 | extract(ethernet); 206 | return select(latest.etherType) { 207 | ETH_P_8021Q: parse_vlan; 208 | ETH_P_8021AD: parse_vlan; 209 | ETH_P_ARP: parse_arp; 210 | ETH_P_IPV4: parse_ipv4; 211 | ETH_P_IPV6: parse_ipv6; 212 | default: ingress; 213 | } 214 | } 215 | 216 | parser parse_vlan { 217 | extract(vlan); 218 | return select(latest.etherType) { 219 | ETH_P_ARP: parse_arp; 220 | ETH_P_IPV4: parse_ipv4; 221 | ETH_P_IPV6: parse_ipv6; 222 | default: ingress; 223 | } 224 | } 225 | 226 | parser parse_arp { 227 | extract(arp); 228 | return ingress; 229 | } 230 | 231 | parser parse_ipv4 { 232 | extract(ipv4); 233 | return select(latest.protocol) { 234 | IPPROTO_TCP: parse_tcp; 235 | IPPROTO_UDP: parse_udp; 236 | IPPROTO_ICMP: parse_icmp; 237 | default: ingress; 238 | } 239 | } 240 | 241 | parser parse_ipv6 { 242 | extract(ipv6); 243 | return select(latest.nextHdr) { 244 | IPPROTO_TCP: parse_tcp; 245 | IPPROTO_UDP: parse_udp; 246 | IPPROTO_ICMP: parse_icmp; 247 | default: ingress; 248 | } 249 | } 250 | 251 | parser parse_tcp { 252 | extract(tcp); 253 | return ingress; 254 | } 255 | 256 | parser parse_udp { 257 | extract(udp); 258 | return ingress; 259 | } 260 | 261 | parser parse_icmp { 262 | extract(icmp); 263 | return ingress; 264 | } 265 | 266 | /* ------------------------------------------------------------------------- */ 267 | 268 | action nop() {} 269 | 270 | table ovs_tbl { 271 | reads { 272 | /* we are not using it. */ 273 | ipv4.dstAddr: exact; 274 | md.in_port: exact; 275 | tnl_md.tun_id: exact; 276 | } 277 | actions { 278 | nop; 279 | } 280 | } 281 | 282 | control ingress 283 | { 284 | apply(ovs_tbl); 285 | } 286 | 287 | -------------------------------------------------------------------------------- /ovs.p4: -------------------------------------------------------------------------------- 1 | /* 2 | OVS.p4 using tc-ebpf P4 architecture 3 | Requirements: 4 | - This should be an example which uses tc-ebpf P4 model to implement 5 | all the protocols supported by OVS. 6 | 7 | Protocol supports: 8 | - L2: VLAN,... 9 | - L3: ... 10 | Tunneling: 11 | - Support Linux's bpf tunnel protocol (ipip, vxlan, gre, geneve, etc) 12 | */ 13 | 14 | // TODO: define a new model ovs_ebpf_model.p4 15 | #include 16 | 17 | #define OUTPUT_OFS (1<<0) 18 | #define PUSHVLAN_OFS (1<<1) 19 | #define POPVLAN_OFS (1<<2) 20 | #define SETMASKED_OFS (1<<3) 21 | #define SETTUNEL_OFS (1<<4) 22 | #define TRUNC_OFS (1<<5) 23 | 24 | extern void bpf_skb_clone_redirect(in bit<16> port); 25 | extern void bpf_skb_vlan_push(in bit<16> proto, in bit<16> tci); 26 | extern void bpf_skb_vlan_pop(); 27 | 28 | struct pkt_metadata_t { 29 | bit<32> recirc_id; 30 | bit<32> dp_hash; 31 | bit<32> skb_priority; 32 | bit<32> pkt_mark; 33 | bit<16> ct_state; 34 | bit<16> ct_zone; 35 | bit<32> ct_mark; 36 | bit<128> ct_label; 37 | bit<32> in_port; 38 | } 39 | 40 | struct flow_tnl_t { 41 | bit<32> ip_dst; 42 | bit<64> ipv6_dst; 43 | bit<32> ip_src; 44 | bit<64> ipv6_src; 45 | bit<64> tun_id; 46 | bit<16> flags; 47 | bit<8> ip_tos; 48 | bit<8> ip_ttl; 49 | bit<16> tp_src; 50 | bit<16> tp_dst; 51 | bit<16> gbp_id; 52 | bit<8> gbp_flags; 53 | bit<40> pad1; 54 | } 55 | 56 | header arp_rarp_t { 57 | bit<16> hwType; 58 | bit<16> protoType; 59 | bit<8> hwAddrLen; 60 | bit<8> protoAddrLen; 61 | bit<16> opcode; 62 | } 63 | 64 | header ethernet_t { 65 | bit<48> dstAddr; 66 | bit<48> srcAddr; 67 | bit<16> etherType; 68 | } 69 | 70 | header icmp_t { 71 | bit<16> typeCode; 72 | bit<16> hdrChecksum; 73 | } 74 | 75 | header ipv4_t { 76 | bit<4> version; 77 | bit<4> ihl; 78 | bit<8> diffserv; 79 | bit<16> totalLen; 80 | bit<16> identification; 81 | bit<3> flags; 82 | bit<13> fragOffset; 83 | bit<8> ttl; 84 | bit<8> protocol; 85 | bit<16> hdrChecksum; 86 | bit<32> srcAddr; 87 | bit<32> dstAddr; 88 | } 89 | 90 | header ipv6_t { 91 | bit<4> version; 92 | bit<8> trafficClass; 93 | bit<20> flowLabel; 94 | bit<16> payloadLen; 95 | bit<8> nextHdr; 96 | bit<8> hopLimit; 97 | bit<128> srcAddr; 98 | bit<128> dstAddr; 99 | } 100 | 101 | header tcp_t { 102 | bit<16> srcPort; 103 | bit<16> dstPort; 104 | bit<32> seqNo; 105 | bit<32> ackNo; 106 | bit<4> dataOffset; 107 | bit<4> res; 108 | bit<8> flags; 109 | bit<16> window; 110 | bit<16> checksum; 111 | bit<16> urgentPtr; 112 | } 113 | 114 | header udp_t { 115 | bit<16> srcPort; 116 | bit<16> dstPort; 117 | bit<16> length_; 118 | bit<16> checksum; 119 | } 120 | 121 | header vlan_tag_t { 122 | bit<3> pcp; 123 | bit<1> cfi; 124 | bit<12> vid; 125 | bit<16> etherType; 126 | } 127 | 128 | struct metadata { 129 | pkt_metadata_t md; 130 | flow_tnl_t tnl; 131 | } 132 | 133 | struct ovs_packet { 134 | arp_rarp_t arp; 135 | ethernet_t ethernet; 136 | icmp_t icmp; 137 | ipv4_t ipv4; 138 | ipv6_t ipv6; 139 | tcp_t tcp; 140 | udp_t udp; 141 | vlan_tag_t vlan; 142 | } 143 | 144 | // metadata to execute on actions 145 | struct output_md_t { 146 | bit<16> port; //ifindex 147 | } 148 | 149 | struct pushvlan_md_t { 150 | bit<16> tci; 151 | bit<16> proto; 152 | } 153 | 154 | struct settunnel_md_t { 155 | bit<32> ip_dst; 156 | bit<32> ip_src; 157 | bit<64> tun_id; 158 | bit<16> flags; 159 | } 160 | 161 | // it's better if we have union 162 | struct action_md_t { 163 | output_md_t output; 164 | pushvlan_md_t pushvlan; 165 | settunnel_md_t settunnel; 166 | } 167 | 168 | /* implement OVS's key_extract() in net/openvswitch/flow.c */ 169 | parser TopParser(packet_in packet, /*inout bpf_sk_buff skbU, */ out ovs_packet hdr) 170 | { 171 | state parse_arp { 172 | packet.extract(hdr.arp); 173 | transition accept; 174 | } 175 | state parse_ethernet { 176 | packet.extract(hdr.ethernet); 177 | transition select(hdr.ethernet.etherType) { 178 | 16w0x8100: parse_vlan; 179 | 16w0x88a8: parse_vlan; 180 | 16w0x806: parse_arp; 181 | 16w0x800: parse_ipv4; 182 | 16w0x86dd: parse_ipv6; 183 | default: accept; 184 | } 185 | } 186 | state parse_icmp { 187 | packet.extract(hdr.icmp); 188 | transition accept; 189 | } 190 | state parse_ipv4 { 191 | packet.extract(hdr.ipv4); 192 | transition select(hdr.ipv4.protocol) { 193 | 8w6: parse_tcp; 194 | 8w17: parse_udp; 195 | 8w1: parse_icmp; 196 | default: accept; 197 | } 198 | } 199 | state parse_ipv6 { 200 | packet.extract(hdr.ipv6); 201 | transition select(hdr.ipv6.nextHdr) { 202 | 8w6: parse_tcp; 203 | 8w17: parse_udp; 204 | 8w1: parse_icmp; 205 | default: accept; 206 | } 207 | } 208 | state parse_tcp { 209 | packet.extract(hdr.tcp); 210 | transition accept; 211 | } 212 | state parse_udp { 213 | packet.extract(hdr.udp); 214 | transition accept; 215 | } 216 | state parse_vlan { 217 | packet.extract(hdr.vlan); 218 | transition select(hdr.vlan.etherType) { 219 | 16w0x806: parse_arp; 220 | 16w0x800: parse_ipv4; 221 | 16w0x86dd: parse_ipv6; 222 | default: accept; 223 | } 224 | } 225 | state start { 226 | transition parse_ethernet; 227 | } 228 | } 229 | 230 | 231 | control Ingress(inout ovs_packet hdr, 232 | out bool pass) 233 | { 234 | // TODO: This should become an in parameter of Ingress 235 | metadata md; 236 | // TODO: this should become an out parameter of Ingress 237 | bit<16> outputPort; 238 | bit<32> toRun; 239 | action_md_t action_md; 240 | 241 | action Output() 242 | { 243 | outputPort = action_md.output.port; 244 | //bpf_skb_clone_redirect(outputPort); 245 | } 246 | 247 | action PushVlan() 248 | { 249 | bit<16> tci = action_md.pushvlan.tci; 250 | bit<16> proto = action_md.pushvlan.proto; 251 | //bpf_skb_vlan_push(proto, tci); 252 | } 253 | 254 | action PopVlan() 255 | { 256 | //bpf_skb_vlan_pop(); 257 | } 258 | 259 | action SetMasked() 260 | { 261 | //bpf_skb_store_byte 262 | } 263 | 264 | action SetTunnel() 265 | { 266 | // bpf_set_tunnel_key 267 | } 268 | 269 | action Trunc() 270 | { 271 | // bpf_skb_change_tail 272 | } 273 | 274 | // a flow miss upcall sends the packet with its md 275 | // to the userspace (ovs-vswitchd), which will look 276 | // up OF tables and install flow entry into map 277 | action Upcall() 278 | { 279 | // bpf_perf_event_output(skb, &perf_events, len, msg, sizeof(msg)); 280 | } 281 | 282 | // OVS datapath action dispatcher 283 | // Execute a fixed sequence of actions 284 | action OVS(bit<32> bitmap, action_md_t md) { 285 | // the bitmap is set by the control plane and indicates 286 | // which actions should be executed 287 | toRun = bitmap; 288 | // the action_md is the metadata for action to execute 289 | action_md = md; 290 | } 291 | 292 | table ovsTable() { 293 | key = { hdr.ipv4.srcAddr : exact; } 294 | actions = { 295 | OVS; 296 | Upcall; // Send to userspce ovs-vswitchd 297 | } 298 | default_action = Upcall; //NoAction; 299 | implementation = hash_table(1024); 300 | } 301 | 302 | apply { 303 | pass = true; 304 | toRun = 0; 305 | 306 | ovsTable.apply(); 307 | 308 | // the sequece matters 309 | if ((toRun & OUTPUT_OFS) != 0) { 310 | Output(); 311 | } 312 | if ((toRun & PUSHVLAN_OFS) != 0) { 313 | PushVlan(); 314 | } 315 | if ((toRun & POPVLAN_OFS) != 0) { 316 | PopVlan(); 317 | } 318 | if ((toRun & SETMASKED_OFS) != 0) { 319 | SetMasked(); 320 | } 321 | if ((toRun & SETTUNEL_OFS) != 0) { 322 | SetTunnel(); 323 | } 324 | if ((toRun & TRUNC_OFS) != 0) { 325 | Trunc(); 326 | } 327 | } 328 | } 329 | 330 | // TODO: this should be argument to the new model ovs_ebpf_model() 331 | control Deparser(packet_out packet, in ovs_packet hdr) { 332 | apply { 333 | packet.emit(hdr.ethernet); 334 | packet.emit(hdr.vlan); 335 | packet.emit(hdr.ipv6); 336 | packet.emit(hdr.ipv4); 337 | packet.emit(hdr.icmp); 338 | packet.emit(hdr.udp); 339 | packet.emit(hdr.tcp); 340 | packet.emit(hdr.arp); 341 | } 342 | } 343 | 344 | // TODO: replace this with ovs_ebpf_model 345 | ebpfFilter(TopParser(), Ingress()) main; 346 | -------------------------------------------------------------------------------- /p4c-xdp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "ir/ir.h" 22 | #include "lib/log.h" 23 | #include "lib/crash.h" 24 | #include "lib/exceptions.h" 25 | #include "lib/gc.h" 26 | #include "lib/nullstream.h" 27 | 28 | #include "backends/ebpf/midend.h" 29 | #include "backends/ebpf/ebpfOptions.h" 30 | #include "xdpBackend.h" 31 | #include "frontends/common/parseInput.h" 32 | #include "frontends/p4/frontend.h" 33 | 34 | void compile(EbpfOptions& options) { 35 | auto hook = options.getDebugHook(); 36 | bool isv1 = options.langVersion == CompilerOptions::FrontendVersion::P4_14; 37 | if (isv1) { 38 | ::error(ErrorType::ERR_UNSUPPORTED, "This compiler only handles P4-16"); 39 | return; 40 | } 41 | auto program = P4::parseP4File(options); 42 | if (::errorCount() > 0) 43 | return; 44 | P4::FrontEnd frontend; 45 | frontend.addDebugHook(hook); 46 | program = frontend.run(options, program); 47 | if (::errorCount() > 0) 48 | return; 49 | 50 | EBPF::MidEnd midend; 51 | midend.addDebugHook(hook); 52 | auto toplevel = midend.run(options, program); 53 | if (options.dumpJsonFile) 54 | JSONGenerator(*openFile(options.dumpJsonFile, true)) << program << std::endl; 55 | if (::errorCount() > 0) 56 | return; 57 | 58 | XDP::run_xdp_backend(options, toplevel, &midend.refMap, &midend.typeMap); 59 | } 60 | 61 | int main(int argc, char *const argv[]) { 62 | setup_gc_logging(); 63 | setup_signals(); 64 | 65 | AutoCompileContext autoEbpfContext(new EbpfContext); 66 | auto& options = EbpfContext::get().options(); 67 | options.compilerVersion = "0.0.1"; 68 | 69 | if (options.process(argc, argv) != nullptr) 70 | options.setInputFile(); 71 | if (::errorCount() > 0) 72 | exit(1); 73 | 74 | compile(options); 75 | 76 | if (Log::verbose()) 77 | std::cerr << "Done." << std::endl; 78 | return ::errorCount() > 0; 79 | } 80 | -------------------------------------------------------------------------------- /p4include/xdp_model.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | 19 | This file describes a P4 architectural model called XDP. This model 20 | generates EBPF code that is run under the XDP (eXpress Data Path): 21 | https://www.iovisor.org/technology/xdp. We support two different architectures: 22 | 23 | - a packet filter, which is identical to the ebpfFilter architecture 24 | - a switch, which has a parser, control pipline and deparser 25 | 26 | */ 27 | 28 | #ifndef _XDP_MODEL_P4 29 | #define _XDP_MODEL_P4 30 | 31 | #include 32 | #include // we continue to support the EBPF packet filter model 33 | 34 | enum xdp_action { 35 | XDP_ABORTED, // some fatal error occurred during processing; 36 | XDP_DROP, // packet should be dropped 37 | XDP_PASS, // packet should be passed to the Linux kernel 38 | XDP_TX, // packet should be resent out on the same interface 39 | XDP_REDIRECT // packet should be sent to a different interface 40 | } 41 | 42 | /* architectural model for a packet switch architecture */ 43 | struct xdp_input { 44 | bit<32> input_port; 45 | } 46 | 47 | struct xdp_output { 48 | xdp_action output_action; 49 | bit<32> output_port; // output port for packet 50 | } 51 | 52 | // Rather ugly to have this very specific function here. 53 | extern bit<16> ebpf_ipv4_checksum(in bit<4> version, in bit<4> ihl, in bit<8> diffserv, 54 | in bit<16> totalLen, in bit<16> identification, in bit<3> flags, 55 | in bit<13> fragOffset, in bit<8> ttl, in bit<8> protocol, 56 | in bit<32> srcAddr, in bit<32> dstAddr); 57 | 58 | //Implements RFC 1624 (Incremental Internet Checksum) 59 | extern bit<16> csum_replace2(in bit<16> csum, // current csum 60 | in bit<16> old, // old value of the field 61 | in bit<16> new); 62 | 63 | extern bit<16> csum_replace4(in bit<16> csum, 64 | in bit<32> old, 65 | in bit<32> new); 66 | 67 | extern bit<32> BPF_PERF_EVENT_OUTPUT(); 68 | // FIXME: use 64 bit 69 | extern bit<32> BPF_KTIME_GET_NS(); 70 | 71 | parser xdp_parse(packet_in packet, out H headers); 72 | control xdp_switch(inout H headers, in xdp_input imd, out xdp_output omd); 73 | control xdp_deparse(in H headers, packet_out packet); 74 | 75 | package xdp(xdp_parse p, xdp_switch s, xdp_deparse d); 76 | 77 | #endif /* _XDP_MODEL_P4 */ 78 | -------------------------------------------------------------------------------- /run-bpf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | set -e 4 | # OVS BPF script for attaching to tc 5 | # filename: tmp.o 6 | # section name: _ebpf_filter 7 | 8 | DEV=enp0s16 9 | 10 | tc qdisc add dev $DEV clsact 11 | tc filter add dev $DEV ingress bpf da obj tmp.o sec _ebpf_filter verb 12 | #tc qdisc delete dev $DEV clsact 13 | #tc filter add dev $DEV egress bpf da obj tcbpf_ovs.o sec ovs_egress 14 | 15 | exit 16 | #tc qdisc delete dev $DEV clsact 17 | #tc filter add dev $DEV egress bpf da obj tcbpf_ovs.o exp /tmp/bpf-uds sec ovs_ingress 18 | -------------------------------------------------------------------------------- /run-p4c-xdp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This program is invoked by make check-xdp to run 4 | # some xdp tests. 5 | 6 | folder="$1" 7 | verbose=0 8 | shift 9 | 10 | while getopts "bvf" opt; do 11 | case $opt in 12 | v) 13 | verbose=1 14 | ;; 15 | esac 16 | shift 17 | done 18 | 19 | file="$1" 20 | 21 | if [ $verbose -eq "1" ]; then 22 | echo $folder/build/p4c-xdp $file 23 | fi 24 | $folder/build/p4c-xdp -o tmp.c $file 25 | -------------------------------------------------------------------------------- /target.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "target.h" 18 | 19 | namespace XDP { 20 | 21 | void XdpTarget::emitIncludes(Util::SourceCodeBuilder* builder) const { 22 | builder->append( 23 | "#define KBUILD_MODNAME \"xdptest\"\n" 24 | "#include \"ebpf_xdp.h\"\n" 25 | "\n"); 26 | } 27 | } // namespace XDP 28 | -------------------------------------------------------------------------------- /target.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef _EXTENSIONS_P4C_XDP_TARGET_H_ 18 | #define _EXTENSIONS_P4C_XDP_TARGET_H_ 19 | 20 | #include "backends/ebpf/target.h" 21 | 22 | namespace XDP { 23 | 24 | // Target XDP 25 | class XdpTarget : public EBPF::KernelSamplesTarget { 26 | public: 27 | XdpTarget() : KernelSamplesTarget(false, "XDP") {} 28 | void emitIncludes(Util::SourceCodeBuilder* builder) const override; 29 | cstring forwardReturnCode() const override { return "XDP_PASS"; } 30 | cstring dropReturnCode() const override { return "XDP_DROP"; } 31 | cstring abortReturnCode() const override { return "XDP_ABORTED"; } 32 | cstring sysMapPath() const override { return "/sys/fs/bpf/xdp/globals"; } 33 | }; 34 | 35 | } // namespace XDP 36 | 37 | #endif /* _EXTENSIONS_P4C_XDP_TARGET_H_ */ 38 | -------------------------------------------------------------------------------- /test_ebpf_map.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Tool for accessing eBPF maps generated by P4 3 | * $gcc test_bpf_map.c libbpf.o -o test_bpf_map 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "libbpf.h" 17 | 18 | #define MAP_PATH "/sys/fs/bpf/tc/globals/match_action" 19 | typedef uint32_t u32; 20 | 21 | // FIXME: This is copied from ovs.c 22 | struct match_action_key { 23 | u32 field0; 24 | }; 25 | enum match_action_actions { 26 | Reject, 27 | NoAction_1, 28 | }; 29 | struct match_action_value { 30 | enum match_action_actions action; 31 | union { 32 | struct { 33 | u32 addr; 34 | } Reject; 35 | struct { 36 | } NoAction_1; 37 | } u; 38 | }; 39 | 40 | int main(void) 41 | { 42 | int ret; 43 | int fd; 44 | struct match_action_key key; 45 | struct match_action_value value; 46 | struct in_addr inp; 47 | 48 | memset(&value, 0, sizeof(value)); 49 | memset(&key, 0, sizeof(key)); 50 | value.action = NoAction_1; 51 | 52 | ret = inet_aton("192.168.218.1", &inp); 53 | if (ret != 1) 54 | return 1; 55 | key.field0 = (u32)htonl(inp.s_addr); 56 | 57 | printf("=== Open BPF map: %s ===\n", MAP_PATH); 58 | fd = bpf_obj_get(MAP_PATH); 59 | if (fd < 0) { 60 | printf("BPF match_action map not loaded\n"); 61 | return 1; 62 | } 63 | 64 | printf("=== Write to eBPF map ===\n"); 65 | printf("key = %x value = %x\n", key.field0, value.action); 66 | ret = bpf_update_elem(fd, &key, &value, BPF_ANY); 67 | if (ret) { 68 | perror("error updating map element\n"); 69 | return 1; 70 | } 71 | 72 | return 0; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | LLC ?= llc 2 | CLANG ?= clang 3 | CC= gcc 4 | CFLAGS=-g 5 | BPFOBJ= xdp1.o xdp2.o xdp3.o xdp4.o xdp5.o xdp6.o xdp7.o xdp8.o xdp9.o xdp10.o \ 6 | xdp11.o xdp12.o xdp13.o xdp14.o xdp15.o xdp16.o 7 | BPFLOADER=bpfloader 8 | BPFLIB=../lib/ 9 | #IFACE=enp0s16 10 | SRC=/root/p4c 11 | INCLUDE=-I ../p4include -I../../../backends/ebpf/runtime/ 12 | P4C=p4c-xdp 13 | 14 | all: bpfloader $(BPFOBJ) verify_target_bpf 15 | 16 | bpfloader: load_and_verify.c 17 | make -C ../lib/ 18 | $(CC) -I$(BPFLIB) $(BPFLIB)/libbpf.o $(BPFLIB)/bpf_load.o load_and_verify.c -lelf -o bpfloader 19 | 20 | # Verify LLVM compiler tools are available and bpf target is supported by llc 21 | .PHONY: verify_cmds verify_target_bpf $(CLANG) $(LLC) 22 | 23 | verify_cmds: $(CLANG) $(LLC) 24 | @for TOOL in $^ ; do \ 25 | if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \ 26 | echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\ 27 | exit 1; \ 28 | else \ 29 | echo "pass verify_cmds:" \ 30 | true; fi; \ 31 | done 32 | 33 | verify_target_bpf: verify_cmds 34 | @if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \ 35 | echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\ 36 | echo " NOTICE: LLVM version >= 3.7.1 required" ;\ 37 | exit 2; \ 38 | else \ 39 | echo "pass verify_target_bpf:" \ 40 | true; fi 41 | 42 | # P4 generate .c 43 | # keep the intermediate .c file 44 | .PRECIOUS: %.c 45 | %.c: %.p4 46 | @if ! ($(P4C) --help > /dev/null 2>&1); then \ 47 | echo "*** ERROR: Cannot find p4c-xdp"; \ 48 | exit 1;\ 49 | fi; 50 | $(P4C) --Werror $(INCLUDE) --target xdp -o $@ $<; 51 | 52 | # BPF program 53 | # do extra CLANG so first pass can return non-zero to shell and stop make 54 | %.o: %.c 55 | @$(CLANG) $(INCLUDE) \ 56 | -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \ 57 | -Wno-compare-distinct-pointer-types \ 58 | -Wno-gnu-variable-sized-type-not-at-end \ 59 | -Wno-tautological-compare \ 60 | -O2 -emit-llvm -g -c $< -o - > /dev/null 61 | $(CLANG) $(INCLUDE)\ 62 | -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \ 63 | -Wno-compare-distinct-pointer-types \ 64 | -Wno-gnu-variable-sized-type-not-at-end \ 65 | -Wno-tautological-compare \ 66 | -O2 -emit-llvm -g -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@ 67 | sudo ./$(BPFLOADER) $@ || true 68 | 69 | clean: clean_loader 70 | rm -f *.o *_ebpf.c xdp[1-9]*.[ch] 71 | 72 | clean_loader: 73 | rm -f bpfloader 74 | 75 | # For actually attaching BPF 76 | attach: 77 | ip link show $(IFACE); \ 78 | for TOOL in $(BPFOBJ); do \ 79 | ip link set dev $(IFACE) xdp obj $${TOOL} verb; \ 80 | ip link set dev $(IFACE) xdp off; \ 81 | done 82 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # p4c-xdp test cases 2 | To get started, simply run 'make' 3 | 4 | ## [xdp1.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp1.p4) (basic parser) 5 | - Parse: 6 | L2(Ethernet) and L3 (IPv4) 7 | - Action: 8 | Drop if not IP packet (the ARP will be dropped) 9 | - Deparse: 10 | None 11 | 12 | ## [xdp2.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp2.p4) (parser + deparser) 13 | - Parse: 14 | L2, L3 (IPv4), L4 (icmp) 15 | - Action: 16 | Drop if not IP packet (the ARP will be dropped) 17 | - Deparse: 18 | L2, L3 (IPv4), L4 (icmp) 19 | 20 | ## [xdp3.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp3.p4) (parser + lookup + deparser) 21 | - Parse: 22 | L2, L3 (IPv4) 23 | - Table: 24 | Ethernet.destination as key, action could be drop or pass 25 | ```C 26 | table dstmactable() { 27 | key = { hdr.ethernet.destination : exact; } 28 | actions = { 29 | Fallback_action; 30 | Drop_action; 31 | } 32 | ``` 33 | - Deparse: 34 | L2, L3 (IPv4) 35 | 36 | ## [xdp4.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp4.p4) (parser + lookup with multiple fields as key + deparser) 37 | - Parse: 38 | L2, L3 (IPv4) 39 | - Table: 40 | Ethernet.{destination, protocl} as key, action could be drop or pass 41 | ```C 42 | table dstmactable() { 43 | key = { hdr.ethernet.destination : exact; 44 | hdr.ethernet.protocol: exact;} 45 | ``` 46 | - Deparse: 47 | L2, L3 (IPv4) 48 | 49 | ## [xdp5.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp5.p4) (Test control plane) 50 | Test using user\_xdp5.c 51 | ```bash 52 | # gcc -I ../lib/ ../lib/libbpf.o user_xdp5.c -o xdp5 53 | # ./xdp5 54 | ``` 55 | The function "initialize\_tables()" will set default to drop the packet. 56 | Then we populate the table by allowing the IP packet to execute Fallback\_action 57 | 58 | ## [xdp6.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp6.p4) (Add action metadata to set IP ttl) 59 | - Parse: 60 | L2, L3 (IPv4) 61 | - Table: 62 | ```C 63 | action SetTTL_action(action_md_t md) 64 | { 65 | hd.ipv4.ttl = md.ttl; 66 | xout.output_action = xdp_action.XDP_PASS; 67 | } 68 | ``` 69 | - Deparse: 70 | L2, L3 (IPv4) 71 | 72 | ## [xdp7.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp7.p4) (SNAT and checksum update) 73 | - Parse L2, L3, and L4 to TCP 74 | - Modify source port and source IP 75 | - Return to Linux network stack (XDP\_PASS) 76 | - Exercise the csum\_replace{2,4} feature 77 | 78 | ## xdp8.p4 79 | Internal use for debugging 80 | 81 | ## [xdp9.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp9.p4) (ipv4 checksum recalc) 82 | - Parse: 83 | L2, L3 (IPv4), L4 (TCP, UDP, ICMP) 84 | - Table: 85 | 86 | ```C 87 | action Fallback_action() 88 | { 89 | hd.ipv4.ttl = 4; 90 | hd.ipv4.hdrChecksum = ebpf_ipv4_checksum( 91 | hd.ipv4.version, hd.ipv4.ihl, hd.ipv4.diffserv, 92 | ``` 93 | - Deparse: 94 | L2, L3 95 | 96 | ## [xdp10.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp10.p4) (Counter) 97 | Count the number of received ipv4 packets for a particular 98 | IPv4 destination address. A BPF hashmap is created for key 99 | equals ipv4.dstAddr, value equals u32 counter. 100 | ```C 101 | apply { 102 | if (hd.ipv4.isValid()) 103 | { 104 | counters.increment((bit<32>)hd.ipv4.dstAddr); 105 | } 106 | ``` 107 | Compile the user\_xdp10.c and it will dump the counter 108 | 109 | ## [xdp11.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp11.p4) (Swap ethernet src and dst, then XDP\_TX) 110 | - Try to do similar feature as kernel's samples/bpf/xdp2\_kern.c 111 | - [demo video](https://youtu.be/On7hEJ6bPVU) 112 | ```C 113 | bit<48> tmp; 114 | apply { 115 | if (hd.ipv4.isValid()) 116 | { 117 | tmp = hd.ethernet.destination; 118 | hd.ethernet.destination = hd.ethernet.source; 119 | hd.ethernet.source = tmp; 120 | } 121 | ``` 122 | ## [xdp12.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp12.p4) 123 | - Parse IPv4/IPv6 ping 124 | - Update ipv4 statistics, and return XDP\_PASS 125 | - Drop ipv6 ping, and return XDP\_DROP 126 | - [demo video](https://youtu.be/vlp1MzWVOc8) 127 | 128 | ## [xdp13.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp13.p4) (Multiple Tables, Single Action) 129 | - Parse L2, L3, L4 (icmp) 130 | - Create and lookup L2, L3, L4 tables 131 | - Default will drop ipv4 ICMP 132 | - XDP\_PASS for the rest of the traffic 133 | 134 | ## [xdp14.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp14.p4) (Multiple Actions, Single Table) 135 | - Parse L2, L3, L4 (icmp) 136 | - Create 1 table with value = bitmap of actions to execute 137 | 138 | ## [xdp15.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp15.p4) (Encapsulation) 139 | - unconditionally append a fixed customized header in front of Ethernet header 140 | ```C 141 | /* encap my own header */ 142 | header myhdr_t { 143 | bit<32> id; 144 | bit<32> timestamp; 145 | } 146 | ``` 147 | then at deparser, emit it before ethernet header 148 | ```C 149 | control Deparser(in Headers hdrs, packet_out packet) { 150 | apply { 151 | packet.emit(hdrs.myhdr); 152 | packet.emit(hdrs.ethernet); 153 | } 154 | } 155 | ``` 156 | ## [xdp16.p4](https://github.com/williamtu/p4c-xdp/blob/master/tests/xdp16.p4) (Encap and BPF XDP helpers) 157 | - [demo video](https://youtu.be/TibGxCXPNVc) 158 | ## TODO 159 | 160 | -------------------------------------------------------------------------------- /tests/ebpf_headers.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013-present Barefoot Networks, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef _EBPF_HEADERS_ 18 | #define _EBPF_HEADERS_ 19 | 20 | @ethernetaddress typedef bit<48> EthernetAddress; 21 | @ipv4address typedef bit<32> IPv4Address; 22 | 23 | // standard Ethernet header 24 | header Ethernet_h 25 | { 26 | EthernetAddress dstAddr; 27 | EthernetAddress srcAddr; 28 | bit<16> etherType; 29 | } 30 | 31 | // IPv4 header without options 32 | header IPv4_h { 33 | bit<4> version; 34 | bit<4> ihl; 35 | bit<8> diffserv; 36 | bit<16> totalLen; 37 | bit<16> identification; 38 | bit<3> flags; 39 | bit<13> fragOffset; 40 | bit<8> ttl; 41 | bit<8> protocol; 42 | bit<16> hdrChecksum; 43 | IPv4Address srcAddr; 44 | IPv4Address dstAddr; 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /tests/ebpf_xdp.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /* 18 | * This file contains all functions and definitions necessary for the xdp target C code to compile. It must be included with any file generated by the p4c-xdp compiler. 19 | */ 20 | 21 | #ifndef __BPF_HELPERS_H 22 | #define __BPF_HELPERS_H 23 | 24 | #include "ebpf_kernel.h" 25 | 26 | /* xdp descriptor similar to sk_buff */ 27 | #ifdef SK_BUFF 28 | #undef SK_BUFF 29 | #endif 30 | #define SK_BUFF struct xdp_md 31 | 32 | #ifdef load_dword 33 | #undef load_dword 34 | #endif 35 | 36 | #define load_dword(data, b) (*(u64 *)((u8*)(data) + (b))) << 16 37 | 38 | 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /tests/empty.stf: -------------------------------------------------------------------------------- 1 | # empty stf file, used to just validate the code produced by p4c-xdp 2 | -------------------------------------------------------------------------------- /tests/load_and_verify.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "bpf_load.h" 28 | #include "libbpf.h" 29 | 30 | int main(int ac, char **argv) 31 | { 32 | char filename[256]; 33 | struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 34 | 35 | snprintf(filename, sizeof(filename), "%s", argv[1]); 36 | 37 | if (setrlimit(RLIMIT_MEMLOCK, &r)) { 38 | perror("setrlimit(RLIMIT_MEMLOCK, RLIM_INFINITY)"); 39 | return 1; 40 | } 41 | 42 | if (ac != 2) { 43 | printf("usage: %s BPF.o\n", argv[0]); 44 | return 1; 45 | } 46 | 47 | if (load_bpf_file(filename)) { 48 | printf("FAILED: %s", bpf_log_buf); 49 | return 1; 50 | } 51 | printf("PASS\n"); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /tests/user_xdp10.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Tool for accessing eBPF maps generated by P4, at tests/ 3 | * gcc -I ../lib/ ../lib/libbpf.o user_xdp10.c -o xdp10 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "libbpf.h" 18 | 19 | #include "xdp10.h" 20 | 21 | #define TABLE "/dstmactable" 22 | #define STATS "/counters" 23 | 24 | int main(void) 25 | { 26 | int ret; 27 | int fd, i; 28 | struct in_addr addr; 29 | counters_key key; 30 | counters_value value; 31 | 32 | inet_aton("10.2.2.9", &addr); 33 | memcpy(&key, &addr, 4); 34 | key = ntohl(key); 35 | 36 | printf("=== Open BPF map: %s ===\n", MAP_PATH STATS); 37 | fd = bpf_obj_get(MAP_PATH STATS); 38 | if (fd < 0) { 39 | printf("BPF map %s not loaded\n", MAP_PATH STATS); 40 | exit(1); 41 | } 42 | 43 | /* populate the match-action table */ 44 | for (i = 0; i < 10; i++) { 45 | printf("=== Lookup to eBPF map ===\n"); 46 | ret = bpf_lookup_elem(fd, &key, &value); 47 | if (!ret) 48 | printf("counter = %d\n", value); 49 | sleep(1); 50 | } 51 | close(fd); 52 | return 0; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /tests/user_xdp5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Tool for accessing eBPF maps generated by P4, at tests/ 3 | * gcc -I ../lib/ ../lib/libbpf.o user_xdp5.c 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "libbpf.h" 18 | 19 | #define CONTROL_PLANE 1 20 | #include "xdp5.h" 21 | 22 | // we might want to put this into header 23 | #define TABLE "/dstmactable" 24 | 25 | int main(void) 26 | { 27 | int ret; 28 | int fd; 29 | struct dstmactable_key key; 30 | struct dstmactable_value value; 31 | 32 | /* populate the default table */ 33 | initialize_tables(); 34 | 35 | value.action = Fallback_action; 36 | key.field0 = 0x800; // IP packet 37 | 38 | printf("=== Open BPF map: %s ===\n", MAP_PATH TABLE); 39 | fd = bpf_obj_get(MAP_PATH TABLE); 40 | if (fd < 0) { 41 | printf("BPF map %s not loaded\n", MAP_PATH TABLE); 42 | exit(1); 43 | } 44 | 45 | /* populate the match-action table */ 46 | printf("=== Write to eBPF map ===\n"); 47 | printf("key = %x value = %x\n", key.field0, value.action); 48 | ret = bpf_update_elem(fd, &key, &value, BPF_ANY); 49 | if (ret) { 50 | perror("error updating map element\n"); 51 | exit(1); 52 | } 53 | 54 | close(fd); 55 | return 0; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /tests/xdp.p4: -------------------------------------------------------------------------------- 1 | #include "xdp_model.p4" 2 | 3 | struct ovs_packet {} 4 | 5 | parser Parser(packet_in packet, out ovs_packet hdr) { 6 | state start { 7 | hdr = {}; 8 | transition accept; 9 | } 10 | } 11 | 12 | control Ingress(inout ovs_packet hdr, in xdp_input xin, out xdp_output xout) { 13 | apply { 14 | xout.output_port = 0; 15 | xout.output_action = xdp_action.XDP_DROP; 16 | } 17 | } 18 | 19 | control Deparser(in ovs_packet hdrs, packet_out packet) { 20 | apply { 21 | ; 22 | } 23 | } 24 | 25 | xdp(Parser(), Ingress(), Deparser()) main; 26 | -------------------------------------------------------------------------------- /tests/xdp.stf: -------------------------------------------------------------------------------- 1 | # bit<32> A bit<32> B 2 | # In the output B = (A + 10) 3 | 4 | 5 | packet 0 00000000 00000000 00000000 00000000 00000000 ABCDEF01 6 | 7 | packet 0 001b1700 0130b881 98b7aeb7 08004500 00344a6f 40004006 53920a01 98453212 c86acf2c 01bbd0fa 585c4ccc b2ac8010 0353c314 00000101 080a0192 463911a0 c06f 8 | 9 | 10 | packet 0 00000000 00000000 00000000 00000000 00000000 00010000 11 | packet 0 00000000 00000000 00000000 00000000 00000000 00011000 12 | 13 | packet 1 001b1700 0130b881 98b7aeb7 08004500 00344a6f 40004006 53920a01 98453212 d86acf2c 01bbd0fa 585c4ccc b2ac8010 0353c314 00000101 080a0192 463911a0 c06f 14 | -------------------------------------------------------------------------------- /tests/xdp1.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | struct Headers { 41 | Ethernet ethernet; 42 | IPv4 ipv4; 43 | } 44 | 45 | parser Parser(packet_in packet, out Headers hd) { 46 | state start { 47 | packet.extract(hd.ethernet); 48 | transition select(hd.ethernet.protocol) { 49 | 16w0x800: parse_ipv4; 50 | default: accept; 51 | } 52 | } 53 | 54 | state parse_ipv4 { 55 | packet.extract(hd.ipv4); 56 | transition accept; 57 | } 58 | } 59 | 60 | control Ingress(inout Headers hdr, in xdp_input xin, out xdp_output xout) { 61 | apply { 62 | xout.output_port = 0; 63 | xout.output_action = (hdr.ethernet.protocol != 0x800) ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 64 | } 65 | } 66 | 67 | control Deparser(in Headers hdrs, packet_out packet) { 68 | apply { 69 | packet.emit(hdrs.ethernet); 70 | packet.emit(hdrs.ipv4); 71 | } 72 | } 73 | 74 | xdp(Parser(), Ingress(), Deparser()) main; 75 | -------------------------------------------------------------------------------- /tests/xdp1.stf: -------------------------------------------------------------------------------- 1 | # bit<32> A bit<32> B 2 | # In the output B = (A + 10) 3 | 4 | 5 | packet 0 00000000 00000000 00000000 00000000 00000000 ABCDEF01 6 | 7 | 8 | # | ethernet header | IPv4 header | TCP header 9 | # dstAddr srcAddr type| VL len id ttl csum src dst | sp dp len csum 10 | packet 0 001b17000130 b88198b7aeb7 0800 45 00 0034 4a6f 4000 40 06 5392 0a019845 3212c86a cf2c 01bb d0fa 585c4ccc b2ac8010 0353 c314 00000101 080a0192 463911a0 c06f 11 | expect 0 001b17000130 b88198b7aeb7 0800 45 00 0034 4a6f 4000 40 06 5392 0a019845 3212c86a cf2c 01bbd0fa 585c4ccc b2ac8010 0353c314 00000101 080a0192 463911a0 c06f 12 | 13 | packet 0 00000000 00000000 00000000 00000000 00000000 00010000 14 | 15 | packet 0 00000000 00000000 00000000 00000000 00000000 00011000 -------------------------------------------------------------------------------- /tests/xdp10.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> source; 21 | bit<48> destination; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | struct Headers { 41 | Ethernet ethernet; 42 | IPv4 ipv4; 43 | } 44 | 45 | parser Parser(packet_in packet, out Headers hd) { 46 | state start { 47 | packet.extract(hd.ethernet); 48 | transition select(hd.ethernet.protocol) { 49 | 16w0x800: parse_ipv4; 50 | default: accept; 51 | } 52 | } 53 | state parse_ipv4 { 54 | packet.extract(hd.ipv4); 55 | transition select(hd.ipv4.protocol) { 56 | default: accept; 57 | } 58 | } 59 | } 60 | 61 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 62 | bool xoutdrop = false; 63 | CounterArray(32w10, true) counters; 64 | 65 | action SetTTL_action() 66 | { 67 | hd.ipv4.ttl = 4; 68 | xoutdrop = false; 69 | } 70 | 71 | action Fallback_action() 72 | { 73 | xoutdrop = false; 74 | } 75 | 76 | action Drop_action() 77 | { 78 | xoutdrop = true; 79 | } 80 | 81 | table dstmactable { 82 | key = { hd.ipv4.dstAddr : exact; } 83 | actions = { 84 | SetTTL_action; 85 | Fallback_action; 86 | Drop_action; 87 | } 88 | default_action = SetTTL_action; 89 | implementation = hash_table(64); 90 | } 91 | 92 | apply { 93 | if (hd.ipv4.isValid()) 94 | { 95 | counters.increment((bit<32>)hd.ipv4.dstAddr); 96 | } 97 | dstmactable.apply(); 98 | xout.output_port = 0; 99 | xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 100 | } 101 | } 102 | 103 | control Deparser(in Headers hdrs, packet_out packet) { 104 | apply { 105 | packet.emit(hdrs.ethernet); 106 | packet.emit(hdrs.ipv4); 107 | } 108 | } 109 | 110 | xdp(Parser(), Ingress(), Deparser()) main; 111 | -------------------------------------------------------------------------------- /tests/xdp11.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> source; 21 | bit<48> destination; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | struct Headers { 41 | Ethernet ethernet; 42 | IPv4 ipv4; 43 | } 44 | 45 | parser Parser(packet_in packet, out Headers hd) { 46 | state start { 47 | packet.extract(hd.ethernet); 48 | transition select(hd.ethernet.protocol) { 49 | 16w0x800: parse_ipv4; 50 | default: accept; 51 | } 52 | } 53 | state parse_ipv4 { 54 | packet.extract(hd.ipv4); 55 | transition select(hd.ipv4.protocol) { 56 | default: accept; 57 | } 58 | } 59 | } 60 | 61 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 62 | 63 | bit<48> tmp; 64 | xdp_action xact = xdp_action.XDP_PASS; 65 | 66 | apply { 67 | if (hd.ipv4.isValid()) 68 | { 69 | tmp = hd.ethernet.destination; 70 | hd.ethernet.destination = hd.ethernet.source; 71 | hd.ethernet.source = tmp; 72 | xact = xdp_action.XDP_TX; 73 | } 74 | xout.output_port = 0; 75 | xout.output_action = xact; 76 | } 77 | } 78 | 79 | control Deparser(in Headers hdrs, packet_out packet) { 80 | apply { 81 | packet.emit(hdrs.ethernet); 82 | packet.emit(hdrs.ipv4); 83 | } 84 | } 85 | 86 | xdp(Parser(), Ingress(), Deparser()) main; 87 | -------------------------------------------------------------------------------- /tests/xdp12.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | header IPv6 { 41 | bit<4> version; 42 | bit<8> trafficClass; 43 | bit<20> flowLabel; 44 | bit<16> payloadLen; 45 | bit<8> nextHdr; 46 | bit<8> hopLimit; 47 | bit<128> srcAddr; 48 | bit<128> dstAddr; 49 | } 50 | 51 | struct Headers { 52 | Ethernet ethernet; 53 | IPv4 ipv4; 54 | IPv6 ipv6; 55 | } 56 | 57 | parser Parser(packet_in packet, out Headers hd) { 58 | state start { 59 | packet.extract(hd.ethernet); 60 | transition select(hd.ethernet.protocol) { 61 | 16w0x0800: parse_ipv4; 62 | 16w0x86dd: parse_ipv6; 63 | default: accept; 64 | } 65 | } 66 | 67 | state parse_ipv4 { 68 | packet.extract(hd.ipv4); 69 | transition accept; 70 | } 71 | state parse_ipv6 { 72 | packet.extract(hd.ipv6); 73 | transition accept; 74 | } 75 | } 76 | 77 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 78 | 79 | bool xoutdrop = false; 80 | CounterArray(32w10, true) counters; 81 | 82 | apply { 83 | if (hd.ipv4.isValid()) 84 | { 85 | counters.increment((bit<32>)hd.ipv4.dstAddr); 86 | xoutdrop = false; 87 | } 88 | if (hd.ipv6.isValid()) 89 | { 90 | xoutdrop = true; 91 | } 92 | xout.output_port = 0; 93 | xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 94 | } 95 | } 96 | 97 | control Deparser(in Headers hdrs, packet_out packet) { 98 | apply { 99 | packet.emit(hdrs.ethernet); 100 | packet.emit(hdrs.ipv4); 101 | packet.emit(hdrs.ipv6); 102 | } 103 | } 104 | 105 | xdp(Parser(), Ingress(), Deparser()) main; 106 | -------------------------------------------------------------------------------- /tests/xdp13.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | header icmp_t { 41 | bit<16> typeCode; 42 | bit<16> hdrChecksum; 43 | } 44 | 45 | struct Headers { 46 | Ethernet ethernet; 47 | IPv4 ipv4; 48 | icmp_t icmp; 49 | } 50 | 51 | parser Parser(packet_in packet, out Headers hd) { 52 | state start { 53 | packet.extract(hd.ethernet); 54 | transition select(hd.ethernet.protocol) { 55 | 16w0x800: parse_ipv4; 56 | default: accept; 57 | } 58 | } 59 | state parse_ipv4 { 60 | packet.extract(hd.ipv4); 61 | transition select(hd.ipv4.protocol) { 62 | 8w1: parse_icmp; 63 | default: accept; 64 | } 65 | } 66 | state parse_icmp { 67 | packet.extract(hd.icmp); 68 | transition accept; 69 | } 70 | } 71 | 72 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 73 | 74 | xdp_action xact = xdp_action.XDP_PASS; 75 | 76 | action l2_Fallback_action() { xact = xdp_action.XDP_PASS; } 77 | action l2_Drop_action() { xact = xdp_action.XDP_DROP; } 78 | 79 | table l2table { 80 | key = { hd.ethernet.protocol : exact; } 81 | actions = { 82 | l2_Fallback_action; 83 | l2_Drop_action; 84 | } 85 | default_action = l2_Fallback_action; 86 | implementation = hash_table(64); 87 | } 88 | 89 | action l3_Fallback_action() { xact = xdp_action.XDP_PASS; } 90 | action l3_Drop_action() { xact = xdp_action.XDP_DROP; } 91 | table l3table { 92 | key = { hd.ipv4.dstAddr : exact; } 93 | actions = { 94 | l3_Fallback_action; 95 | l3_Drop_action; 96 | } 97 | default_action = l3_Fallback_action; 98 | implementation = hash_table(64); 99 | } 100 | 101 | action l4_Fallback_action() { xact = xdp_action.XDP_PASS; } 102 | action l4_Drop_action() { xact = xdp_action.XDP_DROP; } 103 | table l4table { 104 | key = {hd.icmp.typeCode : exact; } 105 | actions = { 106 | l4_Drop_action; // Default Drop 107 | l4_Fallback_action; 108 | } 109 | default_action = l4_Drop_action; 110 | implementation = hash_table(64); 111 | } 112 | 113 | apply { 114 | l2table.apply(); 115 | l3table.apply(); 116 | if (hd.icmp.isValid()) { 117 | l4table.apply(); 118 | } 119 | xout.output_port = 0; 120 | xout.output_action = xact; 121 | } 122 | } 123 | 124 | control Deparser(in Headers hdrs, packet_out packet) { 125 | apply { 126 | ; 127 | } 128 | } 129 | 130 | xdp(Parser(), Ingress(), Deparser()) main; 131 | -------------------------------------------------------------------------------- /tests/xdp14.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | #define OUTPUT_OFS (1<<0) 20 | #define PUSHVLAN_OFS (1<<1) 21 | #define SETTUNNEL_OFS (1<<2) 22 | 23 | header Ethernet { 24 | bit<48> destination; 25 | bit<48> source; 26 | bit<16> protocol; 27 | } 28 | 29 | header IPv4 { 30 | bit<4> version; 31 | bit<4> ihl; 32 | bit<8> diffserv; 33 | bit<16> totalLen; 34 | bit<16> identification; 35 | bit<3> flags; 36 | bit<13> fragOffset; 37 | bit<8> ttl; 38 | bit<8> protocol; 39 | bit<16> hdrChecksum; 40 | bit<32> srcAddr; 41 | bit<32> dstAddr; 42 | } 43 | 44 | header icmp_t { 45 | bit<16> typeCode; 46 | bit<16> hdrChecksum; 47 | } 48 | 49 | struct Headers { 50 | Ethernet ethernet; 51 | IPv4 ipv4; 52 | icmp_t icmp; 53 | } 54 | 55 | struct output_md_t { 56 | bit<32> port; /* ifindex */ 57 | } 58 | 59 | struct pushvlan_md_t { 60 | bit<16> tci; 61 | bit<16> proto; 62 | } 63 | 64 | struct settunnel_md_t { 65 | bit<32> ip_dst; 66 | bit<32> ip_src; 67 | bit<64> tun_id; 68 | bit<16> flags; 69 | } 70 | 71 | struct action_md_t { 72 | output_md_t output; 73 | pushvlan_md_t pushvlan; 74 | settunnel_md_t settunnel; 75 | } 76 | 77 | parser Parser(packet_in packet, out Headers hd) { 78 | state start { 79 | packet.extract(hd.ethernet); 80 | transition select(hd.ethernet.protocol) { 81 | 16w0x800: parse_ipv4; 82 | default: accept; 83 | } 84 | } 85 | state parse_ipv4 { 86 | packet.extract(hd.ipv4); 87 | transition select(hd.ipv4.protocol) { 88 | 8w1: parse_icmp; 89 | default: accept; 90 | } 91 | } 92 | state parse_icmp { 93 | packet.extract(hd.icmp); 94 | transition accept; 95 | } 96 | } 97 | 98 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 99 | 100 | bit<32> outport = 0; 101 | bit<32> bitmap = 0; 102 | action_md_t action_md; 103 | xdp_action xact = xdp_action.XDP_PASS; 104 | 105 | action output() { 106 | outport = action_md.output.port; 107 | xact = xdp_action.XDP_DROP; 108 | } 109 | action push_vlan() { 110 | bit<16> tci = action_md.pushvlan.tci; 111 | bit<16> proto = action_md.pushvlan.proto; 112 | // bpf_skb_vlan_push not support in XDP 113 | // how to inform deparser? 114 | } 115 | action set_tunnel() { 116 | bit<32> dst = action_md.settunnel.ip_dst; 117 | bit<32> src = action_md.settunnel.ip_src; 118 | bit<64> tun_id = action_md.settunnel.tun_id; 119 | bit<16> flags = action_md.settunnel.flags; 120 | // bpf_skb_set_tunnel_key not support in XDP 121 | // how to pass tunnel key to deparser? 122 | } 123 | action fallback() { 124 | xact = xdp_action.XDP_PASS; 125 | } 126 | 127 | action exec_action(bit<32> __bitmap, action_md_t md) { 128 | bitmap = __bitmap; 129 | action_md = md; 130 | } 131 | 132 | table action_bitmap { 133 | key = { hd.ethernet.protocol : exact; } 134 | actions = { 135 | fallback; 136 | exec_action; 137 | } 138 | default_action = fallback; 139 | implementation = hash_table(64); 140 | } 141 | 142 | apply { 143 | action_md.output.port = 0; 144 | action_bitmap.apply(); 145 | 146 | /* Execute 3 actions in order, if its bit is set */ 147 | if ((bitmap & PUSHVLAN_OFS) != 0) { 148 | push_vlan(); 149 | } 150 | if ((bitmap & SETTUNNEL_OFS) != 0) { 151 | set_tunnel(); 152 | } 153 | if ((bitmap & OUTPUT_OFS) != 0) { 154 | output(); 155 | } 156 | xout.output_port = outport; 157 | xout.output_action = xact; 158 | } 159 | } 160 | 161 | control Deparser(in Headers hdrs, packet_out packet) { 162 | apply { 163 | ; // We do not change packet content 164 | } 165 | } 166 | 167 | xdp(Parser(), Ingress(), Deparser()) main; 168 | -------------------------------------------------------------------------------- /tests/xdp15.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | /* encap my own header */ 26 | header myhdr_t { 27 | bit<32> id; 28 | bit<32> timestamp; 29 | } 30 | 31 | struct Headers { 32 | Ethernet ethernet; 33 | myhdr_t myhdr; 34 | } 35 | 36 | parser Parser(packet_in packet, out Headers hd) { 37 | state start { 38 | packet.extract(hd.ethernet); 39 | transition select(hd.ethernet.protocol) { 40 | default: accept; 41 | } 42 | } 43 | } 44 | 45 | control Ingress(inout Headers hdr, in xdp_input xin, out xdp_output xout) { 46 | 47 | apply { 48 | hdr.myhdr.id = 0xfefefefe; // get ID from map or else 49 | hdr.myhdr.timestamp = 0x12345678; 50 | hdr.myhdr.setValid(); 51 | xout.output_port = 0; 52 | xout.output_action = xdp_action.XDP_PASS; 53 | } 54 | } 55 | 56 | control Deparser(in Headers hdrs, packet_out packet) { 57 | apply { 58 | packet.emit(hdrs.myhdr); 59 | packet.emit(hdrs.ethernet); 60 | } 61 | } 62 | 63 | xdp(Parser(), Ingress(), Deparser()) main; 64 | -------------------------------------------------------------------------------- /tests/xdp16.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | // your customized header 26 | header myhdr_t { 27 | bit<32> id; 28 | bit<32> ts; // timestamp 29 | } 30 | 31 | struct Headers { 32 | myhdr_t myhdr; 33 | Ethernet ethernet; 34 | } 35 | 36 | parser Parser(packet_in packet, out Headers hd) { 37 | state start { 38 | packet.extract(hd.ethernet); 39 | transition select(hd.ethernet.protocol) { 40 | default: accept; 41 | } 42 | } 43 | } 44 | 45 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 46 | 47 | bool xoutdrop = false; 48 | action TS_action() 49 | { 50 | // Get the timestamp by calling BPF helper 51 | hd.myhdr.ts = BPF_KTIME_GET_NS(); 52 | hd.myhdr.id = 0xfefefefe; 53 | xoutdrop = false; 54 | } 55 | 56 | action Drop_action() 57 | { 58 | // Send the packet to userspace before drop 59 | BPF_PERF_EVENT_OUTPUT(); 60 | xoutdrop = true; 61 | } 62 | 63 | table dstmactable { 64 | key = { hd.ethernet.protocol : exact; } 65 | actions = { 66 | TS_action; 67 | Drop_action; 68 | } 69 | default_action = TS_action; 70 | implementation = hash_table(64); 71 | } 72 | 73 | apply { 74 | dstmactable.apply(); 75 | // set valid of myhdr so deparser will emit 76 | hd.myhdr.setValid(); 77 | xout.output_port = 0; 78 | xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 79 | } 80 | } 81 | 82 | control Deparser(in Headers hdrs, packet_out packet) { 83 | apply { 84 | // prepend the customized header in the front 85 | packet.emit(hdrs.myhdr); 86 | packet.emit(hdrs.ethernet); 87 | } 88 | } 89 | 90 | xdp(Parser(), Ingress(), Deparser()) main; 91 | -------------------------------------------------------------------------------- /tests/xdp17.p4: -------------------------------------------------------------------------------- 1 | #include "xdp_model.p4" 2 | 3 | header some { 4 | bit<32> b; 5 | } 6 | 7 | struct ovs_packet { 8 | some some; 9 | } 10 | 11 | parser Parser(packet_in packet, out ovs_packet hdr) { 12 | state start { 13 | hdr = { { 0 } }; 14 | transition accept; 15 | } 16 | } 17 | 18 | control Ingress(inout ovs_packet hdr, in xdp_input xin, out xdp_output xout) { 19 | apply { 20 | xout.output_port = 0; 21 | xout.output_action = xdp_action.XDP_DROP; 22 | } 23 | } 24 | 25 | control Deparser(in ovs_packet hdrs, packet_out packet) { 26 | apply { 27 | if (hdrs.some.isValid() && hdrs.some.b != 0) 28 | packet.emit(hdrs.some); 29 | } 30 | } 31 | 32 | xdp(Parser(), Ingress(), Deparser()) main; 33 | -------------------------------------------------------------------------------- /tests/xdp2.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | header icmp_h { 41 | bit<8> type_; 42 | bit<8> code; 43 | bit<16> hdrChecksum; 44 | } 45 | 46 | struct Headers { 47 | Ethernet ethernet; 48 | IPv4 ipv4; 49 | icmp_h icmp; 50 | } 51 | 52 | parser Parser(packet_in packet, out Headers hd) { 53 | state start { 54 | packet.extract(hd.ethernet); 55 | transition select(hd.ethernet.protocol) { 56 | 16w0x800: parse_ipv4; 57 | default: accept; 58 | } 59 | } 60 | 61 | state parse_ipv4 { 62 | packet.extract(hd.ipv4); 63 | transition select(hd.ipv4.protocol) { 64 | 8w0x1: parse_icmp; 65 | default: reject; 66 | } 67 | } 68 | 69 | state parse_icmp { 70 | packet.extract(hd.icmp); 71 | transition accept; 72 | } 73 | } 74 | 75 | control Ingress(inout Headers hdr, in xdp_input xin, out xdp_output xout) { 76 | apply { 77 | xout.output_port = 0; 78 | xout.output_action = hdr.ethernet.protocol != 0x800 ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 79 | } 80 | } 81 | 82 | control Deparser(in Headers hdrs, packet_out packet) { 83 | apply { 84 | packet.emit(hdrs.ethernet); 85 | packet.emit(hdrs.ipv4); 86 | packet.emit(hdrs.icmp); 87 | } 88 | } 89 | 90 | xdp(Parser(), Ingress(), Deparser()) main; 91 | -------------------------------------------------------------------------------- /tests/xdp2.stf: -------------------------------------------------------------------------------- 1 | # bit<32> A bit<32> B 2 | # In the output B = (A + 10) 3 | 4 | 5 | packet 0 00000000 00000000 00000000 00000000 00000000 ABCDEF01 6 | 7 | 8 | # | ethernet header | IPv4 header | TCP header 9 | # dstAddr srcAddr type| VL len id ttl csum src dst | sp dp len csum 10 | packet 0 001b17000130 b88198b7aeb7 0800 45 00 0034 4a6f 4000 40 06 5392 0a019845 3212c86a cf2c 01bb d0fa 585c4ccc b2ac8010 0353 c314 00000101 080a0192 463911a0 c06f 11 | 12 | packet 0 00000000 00000000 00000000 00000000 00000000 00010000 13 | 14 | packet 0 00000000 00000000 00000000 00000000 00000000 00011000 15 | 16 | # | ethernet header | IPv4 header | ICMP header 17 | # dstAddr srcAddr type| VL len id ttl csum src dst |type 18 | packet 0 001b17000130 b88198b7aeb7 0800 45 00 0054 6dde 4000 40 01 fcf0 0a01b5c9 08080808 0800 384c 34fd 0001 2bc96d5b 0000 0000 2ebe 0400 0000 0000 10111213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 19 | expect 0 001b17000130 b88198b7aeb7 0800 45 00 0054 6dde 4000 40 01 fcf0 0a01b5c9 08080808 0800 384c 34fd 0001 2bc96d5b 0000 0000 2ebe 0400 0000 0000 10111213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 20 | 21 | packet 1 b88198b7aeb7 001b17000130 0800 45 00 0054 c67e 0000 79 01 ab50 08080808 0a01b5c9 0000 404c 34fd0001 2bc9 6d5b 0000 0000 2ebe 0400 00000000 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 22 | expect 1 b88198b7aeb7 001b17000130 0800 45 00 0054 c67e 0000 79 01 ab50 08080808 0a01b5c9 0000 404c 34fd0001 2bc9 6d5b 0000 0000 2ebe 0400 00000000 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 23 | 24 | packet 0 001b17000130 b88198b7aeb7 0800 45 00 0054 6dde 4000 40 01 fcf0 0a01b5c9 08080808 0800 4b46 34fd 0002 2cc9 6d5b 00000000 1ac3 0400 0000 0000 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 25 | expect 0 001b17000130 b88198b7aeb7 0800 45 00 0054 6dde 4000 40 01 fcf0 0a01b5c9 08080808 0800 4b46 34fd 0002 2cc9 6d5b 00000000 1ac3 0400 0000 0000 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 26 | 27 | packet 2 b88198b7aeb7 001b17000130 0800 45 00 0054 c67e 0000 79 01 ab50 08080808 0a01b5c9 0000 5346 34fd 0002 2cc9 6d5b 00000000 1ac3 0400 0000 0000 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 28 | expect 2 b88198b7aeb7 001b17000130 0800 45 00 0054 c67e 0000 79 01 ab50 08080808 0a01b5c9 0000 5346 34fd 0002 2cc9 6d5b 00000000 1ac3 0400 0000 0000 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 29 | 30 | packet 0 001b17000130 b88198b7aeb7 0800 45 00 0054 6dde 4000 40 01 fcf0 0a01b5c9 08080808 0800 8940 34fd 0003 2dc96d5b 0000 0000 dbc7 0400 0000 0000 1011 1213 14151617 1819 1a1b 1c1d 1e1f 20212223 2425 2627 2829 2a2b 2c2d 2e2f 30313233 3435 3637 31 | expect 0 001b17000130 b88198b7aeb7 0800 45 00 0054 6dde 4000 40 01 fcf0 0a01b5c9 08080808 0800 8940 34fd 0003 2dc96d5b 0000 0000 dbc7 0400 0000 0000 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 32 | 33 | packet 3 b88198b7aeb7 001b17000130 0800 45 00 0054 c67e 0000 79 01 ab50 08080808 0a01b5c9 0000 9140 34fd 0003 2dc9 6d5b 0000 0000 dbc7 0400 0000 0000 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 34 | expect 3 b88198b7aeb7 001b17000130 0800 45 00 0054 c67e 0000 79 01 ab50 08080808 0a01b5c9 0000 9140 34fd 0003 2dc9 6d5b 0000 0000 dbc7 0400 0000 0000 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 -------------------------------------------------------------------------------- /tests/xdp3.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | struct Headers { 41 | Ethernet ethernet; 42 | IPv4 ipv4; 43 | } 44 | 45 | parser Parser(packet_in packet, out Headers hd) { 46 | state start { 47 | packet.extract(hd.ethernet); 48 | transition select(hd.ethernet.protocol) { 49 | 16w0x800: parse_ipv4; 50 | default: accept; 51 | } 52 | } 53 | 54 | state parse_ipv4 { 55 | packet.extract(hd.ipv4); 56 | transition accept; 57 | } 58 | } 59 | 60 | control Ingress(inout Headers hdr, in xdp_input xin, out xdp_output xout) { 61 | 62 | bool xoutdrop = false; 63 | 64 | action Fallback_action() 65 | { 66 | xoutdrop = false; 67 | } 68 | 69 | action Drop_action() 70 | { 71 | xoutdrop = true; 72 | } 73 | 74 | table dstmactable { 75 | key = { hdr.ethernet.destination : exact; } 76 | actions = { 77 | Fallback_action; 78 | Drop_action; 79 | } 80 | default_action = Drop_action; 81 | implementation = hash_table(64); 82 | } 83 | 84 | apply { 85 | dstmactable.apply(); 86 | xout.output_port = 0; 87 | xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 88 | } 89 | } 90 | 91 | control Deparser(in Headers hdrs, packet_out packet) { 92 | apply { 93 | packet.emit(hdrs.ethernet); 94 | packet.emit(hdrs.ipv4); 95 | } 96 | } 97 | 98 | xdp(Parser(), Ingress(), Deparser()) main; 99 | -------------------------------------------------------------------------------- /tests/xdp4.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | struct Headers { 41 | Ethernet ethernet; 42 | IPv4 ipv4; 43 | } 44 | 45 | parser Parser(packet_in packet, out Headers hd) { 46 | state start { 47 | packet.extract(hd.ethernet); 48 | transition select(hd.ethernet.protocol) { 49 | 16w0x800: parse_ipv4; 50 | default: accept; 51 | } 52 | } 53 | 54 | state parse_ipv4 { 55 | packet.extract(hd.ipv4); 56 | transition accept; 57 | } 58 | } 59 | 60 | control Ingress(inout Headers hdr, in xdp_input xin, out xdp_output xout) { 61 | 62 | bool xoutdrop = false; 63 | 64 | action Fallback_action() 65 | { 66 | xoutdrop = false; 67 | } 68 | 69 | action Drop_action() 70 | { 71 | xoutdrop = true; 72 | } 73 | 74 | table dstmactable { 75 | key = { hdr.ethernet.destination : exact; 76 | hdr.ethernet.protocol: exact;} 77 | actions = { 78 | Fallback_action; 79 | Drop_action; 80 | } 81 | default_action = Drop_action; 82 | implementation = hash_table(64); 83 | } 84 | 85 | apply { 86 | dstmactable.apply(); 87 | xout.output_port = 0; 88 | xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 89 | } 90 | } 91 | 92 | control Deparser(in Headers hdrs, packet_out packet) { 93 | apply { 94 | packet.emit(hdrs.ethernet); 95 | packet.emit(hdrs.ipv4); 96 | } 97 | } 98 | 99 | xdp(Parser(), Ingress(), Deparser()) main; 100 | -------------------------------------------------------------------------------- /tests/xdp5.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | struct Headers { 41 | Ethernet ethernet; 42 | IPv4 ipv4; 43 | } 44 | 45 | parser Parser(packet_in packet, out Headers hd) { 46 | state start { 47 | packet.extract(hd.ethernet); 48 | transition select(hd.ethernet.protocol) { 49 | 16w0x800: parse_ipv4; 50 | default: accept; 51 | } 52 | } 53 | 54 | state parse_ipv4 { 55 | packet.extract(hd.ipv4); 56 | transition accept; 57 | } 58 | } 59 | 60 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 61 | 62 | bool xoutdrop = false; 63 | 64 | action Fallback_action() 65 | { 66 | xoutdrop = false; 67 | } 68 | 69 | action Drop_action() 70 | { 71 | xoutdrop = true; 72 | } 73 | 74 | table dstmactable { 75 | key = { hd.ethernet.protocol : exact; } 76 | actions = { 77 | Fallback_action; 78 | Drop_action; 79 | } 80 | default_action = Drop_action; 81 | implementation = hash_table(64); 82 | } 83 | 84 | apply { 85 | dstmactable.apply(); 86 | xout.output_port = 0; 87 | xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 88 | } 89 | } 90 | 91 | control Deparser(in Headers hdrs, packet_out packet) { 92 | apply { 93 | packet.emit(hdrs.ethernet); 94 | packet.emit(hdrs.ipv4); 95 | } 96 | } 97 | 98 | xdp(Parser(), Ingress(), Deparser()) main; 99 | -------------------------------------------------------------------------------- /tests/xdp6.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | struct Headers { 41 | Ethernet ethernet; 42 | IPv4 ipv4; 43 | } 44 | 45 | struct action_md_t { 46 | bit<8> ttl; 47 | } 48 | 49 | parser Parser(packet_in packet, out Headers hd) { 50 | state start { 51 | packet.extract(hd.ethernet); 52 | transition select(hd.ethernet.protocol) { 53 | 16w0x800: parse_ipv4; 54 | default: accept; 55 | } 56 | } 57 | 58 | state parse_ipv4 { 59 | packet.extract(hd.ipv4); 60 | transition accept; 61 | } 62 | } 63 | 64 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 65 | 66 | action SetTTL_action(action_md_t md) 67 | { 68 | hd.ipv4.ttl = md.ttl; 69 | xout.output_action = xdp_action.XDP_PASS; 70 | } 71 | 72 | action Fallback_action() 73 | { 74 | xout.output_action = xdp_action.XDP_PASS; 75 | } 76 | 77 | action Drop_action() 78 | { 79 | xout.output_action = xdp_action.XDP_DROP; 80 | } 81 | 82 | table dstmactable { 83 | key = { 84 | hd.ethernet.protocol : exact; 85 | hd.ipv4.dstAddr : exact; 86 | } 87 | actions = { 88 | Fallback_action; 89 | Drop_action; 90 | SetTTL_action; 91 | } 92 | default_action = Drop_action; 93 | implementation = hash_table(64); 94 | } 95 | 96 | apply { 97 | dstmactable.apply(); 98 | xout.output_port = 0; 99 | } 100 | } 101 | 102 | control Deparser(in Headers hdrs, packet_out packet) { 103 | apply { 104 | packet.emit(hdrs.ethernet); 105 | packet.emit(hdrs.ipv4); 106 | } 107 | } 108 | 109 | xdp(Parser(), Ingress(), Deparser()) main; 110 | -------------------------------------------------------------------------------- /tests/xdp7.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> checksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | header icmp_t { 41 | bit<16> typeCode; 42 | bit<16> checksum; 43 | } 44 | 45 | header tcp_t { 46 | bit<16> srcPort; 47 | bit<16> dstPort; 48 | bit<32> seqNo; 49 | bit<32> ackNo; 50 | bit<4> dataOffset; 51 | bit<4> res; 52 | bit<8> flags; 53 | bit<16> window; 54 | bit<16> checksum; 55 | bit<16> urgentPtr; 56 | } 57 | 58 | header udp_t { 59 | bit<16> srcPort; 60 | bit<16> dstPort; 61 | bit<16> length_; 62 | bit<16> checksum; 63 | } 64 | 65 | 66 | struct Headers { 67 | Ethernet ethernet; 68 | IPv4 ipv4; 69 | tcp_t tcp; 70 | udp_t udp; 71 | icmp_t icmp; 72 | } 73 | 74 | parser Parser(packet_in packet, out Headers hd) { 75 | state start { 76 | packet.extract(hd.ethernet); 77 | transition select(hd.ethernet.protocol) { 78 | 16w0x800: parse_ipv4; 79 | default: accept; 80 | } 81 | } 82 | state parse_ipv4 { 83 | packet.extract(hd.ipv4); 84 | transition select(hd.ipv4.protocol) { 85 | 8w6: parse_tcp; 86 | 8w17: parse_udp; 87 | 8w1: parse_icmp; 88 | default: accept; 89 | } 90 | } 91 | state parse_icmp { 92 | packet.extract(hd.icmp); 93 | transition accept; 94 | } 95 | state parse_tcp { 96 | packet.extract(hd.tcp); 97 | transition accept; 98 | } 99 | state parse_udp { 100 | packet.extract(hd.udp); 101 | transition accept; 102 | } 103 | } 104 | 105 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 106 | 107 | bool xoutdrop = false; 108 | // from, to are host byte order 109 | bit<16> from; 110 | bit<16> to; 111 | bit<32> from_addr; 112 | bit<32> to_addr; 113 | 114 | action Fallback_action() 115 | { 116 | #if 0 117 | // UDP port 118 | from = hd.udp.dstPort; 119 | to = 16w0x400; 120 | hd.udp.dstPort = to; 121 | hd.udp.checksum = csum_replace2(hd.udp.checksum, from, to); 122 | 123 | // UDP IP addr 124 | from_addr = hd.ipv4.dstAddr; 125 | to_addr = 32w0x01020304; 126 | hd.ipv4.dstAddr = to_addr; 127 | hd.ipv4.checksum = csum_replace4(hd.ipv4.checksum, from_addr, to_addr); 128 | hd.udp.checksum = csum_replace4(hd.udp.checksum, from_addr, to_addr); 129 | #endif 130 | // TCP 131 | from = hd.tcp.srcPort; 132 | to = 16w0x841; 133 | hd.tcp.srcPort = to; 134 | hd.tcp.checksum = csum_replace2(hd.tcp.checksum, from, to); 135 | 136 | // TCP IP addr 137 | from_addr = hd.ipv4.srcAddr; 138 | to_addr = 32w0x05060708; 139 | hd.ipv4.srcAddr = to_addr; 140 | hd.ipv4.checksum = csum_replace4(hd.ipv4.checksum, from_addr, to_addr); 141 | hd.tcp.checksum = csum_replace4(hd.tcp.checksum, from_addr, to_addr); 142 | 143 | xoutdrop = false; 144 | } 145 | 146 | action Drop_action() 147 | { 148 | xoutdrop = true; 149 | } 150 | 151 | table dstmactable { 152 | key = { hd.ethernet.protocol : exact; } 153 | actions = { 154 | Fallback_action; 155 | Drop_action; 156 | } 157 | default_action = Fallback_action; 158 | implementation = hash_table(64); 159 | } 160 | 161 | apply { 162 | dstmactable.apply(); 163 | xout.output_port = 0; 164 | xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 165 | } 166 | } 167 | 168 | control Deparser(in Headers hdrs, packet_out packet) { 169 | apply { 170 | packet.emit(hdrs.ethernet); 171 | packet.emit(hdrs.ipv4); 172 | 173 | // hit Verifier MAX_BPF_STACK issue 174 | // packet.emit(hdrs.tcp); 175 | // packet.emit(hdrs.udp); 176 | 177 | packet.emit(hdrs.icmp); 178 | packet.emit(hdrs.udp); 179 | packet.emit(hdrs.tcp); 180 | } 181 | } 182 | 183 | xdp(Parser(), Ingress(), Deparser()) main; 184 | -------------------------------------------------------------------------------- /tests/xdp8.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | /* change ipv4.ttl to 4 20 | * update iph->csum 21 | */ 22 | 23 | header Ethernet { 24 | bit<48> source; 25 | bit<48> destination; 26 | bit<16> protocol; 27 | } 28 | 29 | header IPv4 { 30 | bit<4> version; 31 | bit<4> ihl; 32 | bit<8> diffserv; 33 | bit<16> totalLen; 34 | bit<16> identification; 35 | bit<3> flags; 36 | bit<13> fragOffset; 37 | bit<8> ttl; 38 | bit<8> protocol; 39 | bit<16> hdrChecksum; 40 | bit<32> srcAddr; 41 | bit<32> dstAddr; 42 | } 43 | 44 | struct Headers { 45 | Ethernet ethernet; 46 | IPv4 ipv4; 47 | } 48 | 49 | parser Parser(packet_in packet, out Headers hd) { 50 | state start { 51 | packet.extract(hd.ethernet); 52 | transition select(hd.ethernet.protocol) { 53 | 16w0x800: parse_ipv4; 54 | default: accept; 55 | } 56 | } 57 | state parse_ipv4 { 58 | packet.extract(hd.ipv4); 59 | transition select(hd.ipv4.protocol) { 60 | default: accept; 61 | } 62 | } 63 | } 64 | 65 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 66 | 67 | bool xoutdrop = false; 68 | 69 | action SetTTL_action() 70 | { 71 | hd.ipv4.ttl = 4; 72 | xoutdrop = false; 73 | } 74 | 75 | action Fallback_action() 76 | { 77 | xoutdrop = false; 78 | } 79 | 80 | action Drop_action() 81 | { 82 | xoutdrop = true; 83 | } 84 | 85 | table dstmactable { 86 | key = { hd.ipv4.dstAddr : exact; } 87 | actions = { 88 | SetTTL_action; 89 | Fallback_action; 90 | Drop_action; 91 | } 92 | default_action = SetTTL_action; 93 | implementation = hash_table(64); 94 | } 95 | 96 | apply { 97 | dstmactable.apply(); 98 | xout.output_port = 0; 99 | xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 100 | } 101 | } 102 | 103 | control Deparser(in Headers hdrs, packet_out packet) { 104 | apply { 105 | packet.emit(hdrs.ethernet); 106 | packet.emit(hdrs.ipv4); 107 | } 108 | } 109 | 110 | xdp(Parser(), Ingress(), Deparser()) main; 111 | -------------------------------------------------------------------------------- /tests/xdp9.p4: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMWare, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdp_model.p4" 18 | 19 | header Ethernet { 20 | bit<48> destination; 21 | bit<48> source; 22 | bit<16> protocol; 23 | } 24 | 25 | header IPv4 { 26 | bit<4> version; 27 | bit<4> ihl; 28 | bit<8> diffserv; 29 | bit<16> totalLen; 30 | bit<16> identification; 31 | bit<3> flags; 32 | bit<13> fragOffset; 33 | bit<8> ttl; 34 | bit<8> protocol; 35 | bit<16> hdrChecksum; 36 | bit<32> srcAddr; 37 | bit<32> dstAddr; 38 | } 39 | 40 | header icmp_t { 41 | bit<16> typeCode; 42 | bit<16> hdrChecksum; 43 | } 44 | 45 | header tcp_t { 46 | bit<16> srcPort; 47 | bit<16> dstPort; 48 | bit<32> seqNo; 49 | bit<32> ackNo; 50 | bit<4> dataOffset; 51 | bit<4> res; 52 | bit<8> flags; 53 | bit<16> window; 54 | bit<16> checksum; 55 | bit<16> urgentPtr; 56 | } 57 | 58 | header udp_t { 59 | bit<16> srcPort; 60 | bit<16> dstPort; 61 | bit<16> length_; 62 | bit<16> checksum; 63 | } 64 | 65 | 66 | struct Headers { 67 | Ethernet ethernet; 68 | IPv4 ipv4; 69 | tcp_t tcp; 70 | udp_t udp; 71 | icmp_t icmp; 72 | } 73 | 74 | parser Parser(packet_in packet, out Headers hd) { 75 | state start { 76 | packet.extract(hd.ethernet); 77 | transition select(hd.ethernet.protocol) { 78 | 16w0x800: parse_ipv4; 79 | default: accept; 80 | } 81 | } 82 | state parse_ipv4 { 83 | packet.extract(hd.ipv4); 84 | transition select(hd.ipv4.protocol) { 85 | 8w6: parse_tcp; 86 | 8w17: parse_udp; 87 | 8w1: parse_icmp; 88 | default: accept; 89 | } 90 | } 91 | state parse_icmp { 92 | packet.extract(hd.icmp); 93 | transition accept; 94 | } 95 | state parse_tcp { 96 | packet.extract(hd.tcp); 97 | transition accept; 98 | } 99 | state parse_udp { 100 | packet.extract(hd.udp); 101 | transition accept; 102 | } 103 | } 104 | 105 | control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) { 106 | 107 | bool xoutdrop = false; 108 | 109 | action Fallback_action() 110 | { 111 | hd.ipv4.ttl = 4; 112 | hd.ipv4.hdrChecksum = ebpf_ipv4_checksum( 113 | hd.ipv4.version, hd.ipv4.ihl, hd.ipv4.diffserv, 114 | hd.ipv4.totalLen, hd.ipv4.identification, hd.ipv4.flags, 115 | hd.ipv4.fragOffset, hd.ipv4.ttl, hd.ipv4.protocol, 116 | hd.ipv4.srcAddr, hd.ipv4.dstAddr); 117 | xoutdrop = false; 118 | } 119 | 120 | action Drop_action() 121 | { 122 | xoutdrop = true; 123 | } 124 | 125 | table dstmactable { 126 | key = { hd.ethernet.protocol : exact; } 127 | actions = { 128 | Fallback_action; 129 | Drop_action; 130 | } 131 | default_action = Fallback_action; 132 | implementation = hash_table(64); 133 | } 134 | 135 | apply { 136 | dstmactable.apply(); 137 | xout.output_port = 0; 138 | xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_PASS; 139 | } 140 | } 141 | 142 | control Deparser(in Headers hdrs, packet_out packet) { 143 | apply { 144 | packet.emit(hdrs.ethernet); 145 | packet.emit(hdrs.ipv4); 146 | 147 | /* hit BPF_MAX_STACK size */ 148 | // packet.emit(hdrs.tcp); 149 | // packet.emit(hdrs.udp); 150 | packet.emit(hdrs.icmp); 151 | } 152 | } 153 | 154 | xdp(Parser(), Ingress(), Deparser()) main; 155 | -------------------------------------------------------------------------------- /tools/install_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # exit when any command fails 4 | set -e 5 | 6 | # fetch submodules and update apt 7 | echo "Initializing submodules..." 8 | git submodule update --init --recursive 9 | sudo apt update 10 | 11 | SRC_DIR="$(pwd)" 12 | 13 | echo "Installing P4C dependencies..." 14 | 15 | # Install pip and python 16 | sudo apt install -y python3 17 | sudo apt install -y python3-pip 18 | sudo apt install -y python3-setuptools 19 | 20 | # Install the p4 compiler dependencies 21 | sudo apt install -y bison \ 22 | build-essential \ 23 | cmake \ 24 | git \ 25 | flex \ 26 | libboost-dev \ 27 | libboost-graph-dev \ 28 | libboost-iostreams-dev \ 29 | libfl-dev \ 30 | libgc-dev \ 31 | libgmp-dev \ 32 | pkg-config 33 | 34 | # Install the eBPF dependencies 35 | sudo apt install -y libpcap-dev \ 36 | libelf-dev \ 37 | zlib1g-dev \ 38 | llvm \ 39 | clang \ 40 | libprotobuf-dev \ 41 | protobuf-compiler \ 42 | iproute2 \ 43 | tcpdump \ 44 | iptables 45 | 46 | # This only works on Ubuntu 18+ 47 | sudo apt install -y libprotoc-dev protobuf-compiler 48 | 49 | # install python packages using pip 50 | pip3 install --user wheel 51 | pip3 install --user pyroute2 ipaddr ply==3.8 scapy==2.4.0 52 | 53 | echo "Successfully installed P4C dependencies." 54 | -------------------------------------------------------------------------------- /xdpBackend.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "lib/error.h" 18 | #include "lib/nullstream.h" 19 | #include "frontends/p4/evaluator/evaluator.h" 20 | 21 | #include "xdpBackend.h" 22 | #include "xdpProgram.h" 23 | #include "target.h" 24 | #include "backends/ebpf/ebpfType.h" 25 | 26 | namespace XDP { 27 | 28 | void run_xdp_backend(const EbpfOptions& options, const IR::ToplevelBlock* toplevel, 29 | P4::ReferenceMap* refMap, P4::TypeMap* typeMap) { 30 | if (toplevel == nullptr) 31 | return; 32 | 33 | auto main = toplevel->getMain(); 34 | if (main == nullptr) { 35 | ::warning(ErrorType::WARN_MISSING, 36 | "Could not locate top-level block; is there a %1% module?", IR::P4Program::main); 37 | return; 38 | } 39 | 40 | EBPF::Target* target; 41 | if (options.target == "bcc") { 42 | target = new EBPF::BccTarget(); 43 | } else if (options.target == "kernel") { 44 | target = new EBPF::KernelSamplesTarget(); 45 | } else if (options.target.isNullOrEmpty() || options.target == "xdp") { 46 | target = new XdpTarget(); 47 | } else { 48 | ::error(ErrorType::ERR_UNSUPPORTED, 49 | "Unknown target %s; legal choices are 'bcc', 'xdp', and 'kernel'", options.target); 50 | return; 51 | } 52 | 53 | EBPF::EBPFTypeFactory::createFactory(typeMap); 54 | auto prog = new XDPProgram(options, toplevel->getProgram(), refMap, typeMap, toplevel); 55 | if (!prog->build()) 56 | return; 57 | 58 | if (options.outputFile.isNullOrEmpty()) 59 | return; 60 | cstring cfile = options.outputFile; 61 | auto cstream = openFile(cfile, false); 62 | if (cstream == nullptr) 63 | return; 64 | 65 | cstring hfile; 66 | const char* dot = cfile.findlast('.'); 67 | if (dot == nullptr) 68 | hfile = cfile + ".h"; 69 | else 70 | hfile = cfile.before(dot) + ".h"; 71 | auto hstream = openFile(hfile, false); 72 | if (hstream == nullptr) 73 | return; 74 | 75 | EBPF::CodeBuilder c(target); 76 | EBPF::CodeBuilder h(target); 77 | // The order is important 78 | prog->emitH(&h, hfile); 79 | prog->emitC(&c, hfile); 80 | 81 | *cstream << c.toString(); 82 | *hstream << h.toString(); 83 | cstream->flush(); 84 | hstream->flush(); 85 | } 86 | 87 | } // namespace XDP 88 | -------------------------------------------------------------------------------- /xdpBackend.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef _EXTENSIONS_P4C_XDP_XDPBACKEND_H_ 18 | #define _EXTENSIONS_P4C_XDP_XDPBACKEND_H_ 19 | 20 | #include "backends/ebpf/ebpfOptions.h" 21 | #include "ir/ir.h" 22 | #include "frontends/p4/evaluator/evaluator.h" 23 | 24 | namespace XDP { 25 | 26 | void run_xdp_backend(const EbpfOptions& options, const IR::ToplevelBlock* toplevel, 27 | P4::ReferenceMap* refMap, P4::TypeMap* typeMap); 28 | 29 | } // namespace XDP 30 | 31 | #endif /* _EXTENSIONS_P4C_XDP_XDPBACKEND_H_ */ 32 | -------------------------------------------------------------------------------- /xdpControl.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdpControl.h" 18 | #include "lib/error.h" 19 | #include "backends/ebpf/ebpfControl.h" 20 | 21 | namespace XDP { 22 | 23 | XDPSwitch::XDPSwitch(const XDPProgram* program, 24 | const IR::ControlBlock* block, 25 | const IR::Parameter* parserHeaders) : 26 | EBPF::EBPFControl(program, block, parserHeaders), 27 | inputMeta(nullptr), outputMeta(nullptr) {} 28 | 29 | bool XDPSwitch::build() { 30 | hitVariable = program->refMap->newName("hit"); 31 | auto pl = controlBlock->container->type->applyParams; 32 | if (pl->size() != 3) { 33 | ::error(ErrorType::ERR_EXPECTED, 34 | "Expected switch block to have exactly 3 parameters"); 35 | return false; 36 | } 37 | 38 | auto it = pl->parameters.begin(); 39 | headers = *it; 40 | ++it; 41 | inputMeta = *it; 42 | ++it; 43 | outputMeta = *it; 44 | 45 | codeGen = new EBPF::ControlBodyTranslator(this); 46 | codeGen->substitute(headers, parserHeaders); 47 | 48 | scanConstants(); 49 | return ::errorCount() == 0; 50 | } 51 | 52 | ////////////////////////////////////////////////////////////////////////// 53 | 54 | namespace { 55 | 56 | class OutHeaderSize final : public EBPF::CodeGenInspector { 57 | P4::ReferenceMap* refMap; 58 | P4::TypeMap* typeMap; 59 | const XDPProgram* program; 60 | 61 | std::map substitution; 62 | 63 | bool illegal(const IR::Statement* statement) 64 | { ::error(ErrorType::ERR_UNSUPPORTED, 65 | "%1%: not supported in deparser", statement); return false; } 66 | 67 | public: 68 | OutHeaderSize(P4::ReferenceMap* refMap, P4::TypeMap* typeMap, 69 | const XDPProgram* program): 70 | EBPF::CodeGenInspector(refMap, typeMap), refMap(refMap), typeMap(typeMap), 71 | program(program) { 72 | CHECK_NULL(refMap); CHECK_NULL(typeMap); CHECK_NULL(program); 73 | setName("OutHeaderSize"); } 74 | bool preorder(const IR::PathExpression* expression) override { 75 | auto decl = refMap->getDeclaration(expression->path, true); 76 | auto param = decl->getNode()->to(); 77 | if (param != nullptr) { 78 | auto subst = ::get(substitution, param); 79 | if (subst != nullptr) { 80 | builder->append(subst->name); 81 | return false; 82 | } 83 | } 84 | builder->append(expression->path->name); 85 | return false; 86 | } 87 | bool preorder(const IR::SwitchStatement* statement) override 88 | { return illegal(statement); } 89 | bool preorder(const IR::AssignmentStatement* statement) override 90 | { return illegal(statement); } 91 | bool preorder(const IR::ReturnStatement* statement) override 92 | { return illegal(statement); } 93 | bool preorder(const IR::ExitStatement* statement) override 94 | { return illegal(statement); } 95 | bool preorder(const IR::MethodCallStatement* statement) override { 96 | auto &p4lib = P4::P4CoreLibrary::instance; 97 | 98 | auto mi = P4::MethodInstance::resolve(statement->methodCall, refMap, typeMap); 99 | auto method = mi->to(); 100 | if (method == nullptr) 101 | return illegal(statement); 102 | 103 | auto declType = method->originalExternType; 104 | if (declType->name.name != p4lib.packetOut.name || 105 | method->method->name.name != p4lib.packetOut.emit.name || 106 | method->expr->arguments->size() != 1) { 107 | return illegal(statement); 108 | } 109 | 110 | auto h = method->expr->arguments->at(0); 111 | auto type = typeMap->getType(h); 112 | auto ht = type->to(); 113 | if (ht == nullptr) { 114 | ::error(ErrorType::ERR_INVALID, "Cannot emit a non-header type %1%", h); 115 | return false; 116 | } 117 | unsigned width = ht->width_bits(); 118 | 119 | builder->append("if ("); 120 | visit(h); 121 | builder->append(".ebpf_valid) "); 122 | builder->appendFormat("%s += %d;", program->outHeaderLengthVar.c_str(), width); 123 | return false; 124 | } 125 | 126 | void substitute(const IR::Parameter* p, const IR::Parameter* with) 127 | { substitution.emplace(p, with); } 128 | }; 129 | 130 | } // namespace 131 | 132 | XDPDeparser::XDPDeparser(const XDPProgram* program, const IR::ControlBlock* block, 133 | const IR::Parameter* parserHeaders) : 134 | EBPF::EBPFControl(program, block, parserHeaders), packet(nullptr) {} 135 | 136 | bool XDPDeparser::build() { 137 | hitVariable = program->refMap->newName("hit"); 138 | auto pl = controlBlock->container->type->applyParams; 139 | if (pl->size() != 2) { 140 | ::error(ErrorType::ERR_EXPECTED, "Expected switch block to have exactly 3 parameters"); 141 | return false; 142 | } 143 | 144 | auto it = pl->parameters.begin(); 145 | headers = *it; 146 | ++it; 147 | packet = *it; 148 | 149 | codeGen = new EBPF::ControlBodyTranslator(this); 150 | codeGen->substitute(headers, parserHeaders); 151 | 152 | return true; 153 | } 154 | 155 | void XDPDeparser::emit(EBPF::CodeBuilder* builder) { 156 | OutHeaderSize ohs(program->refMap, program->typeMap, 157 | static_cast(program)); 158 | ohs.substitute(headers, parserHeaders); 159 | ohs.setBuilder(builder); 160 | 161 | builder->emitIndent(); 162 | (void)controlBlock->container->body->apply(ohs); 163 | builder->newline(); 164 | 165 | builder->emitIndent(); 166 | builder->appendFormat("bpf_xdp_adjust_head(%s, BYTES(%s) - BYTES(%s));", 167 | program->model.CPacketName.str(), 168 | program->offsetVar.c_str(), 169 | getProgram()->outHeaderLengthVar.c_str()); 170 | builder->newline(); 171 | 172 | builder->emitIndent(); 173 | builder->appendFormat("%s = %s;", 174 | program->packetStartVar, 175 | builder->target->dataOffset(program->model.CPacketName.str())); 176 | builder->newline(); 177 | builder->emitIndent(); 178 | builder->appendFormat("%s = %s;", 179 | program->packetEndVar, 180 | builder->target->dataEnd(program->model.CPacketName.str())); 181 | builder->newline(); 182 | 183 | builder->emitIndent(); 184 | builder->appendFormat("%s = 0;", program->offsetVar.c_str()); 185 | builder->newline(); 186 | 187 | EBPF::EBPFControl::emit(builder); 188 | } 189 | 190 | } // namespace XDP 191 | -------------------------------------------------------------------------------- /xdpControl.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013-present Barefoot Networks, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef _EXTENSIONS_P4C_XDP_XDPCONTROL_H_ 18 | #define _EXTENSIONS_P4C_XDP_XDPCONTROL_H_ 19 | 20 | #include "backends/ebpf/ebpfControl.h" 21 | #include "xdpProgram.h" 22 | 23 | namespace XDP { 24 | 25 | class XDPSwitch : public EBPF::EBPFControl { 26 | public: 27 | const IR::Parameter* inputMeta; 28 | const IR::Parameter* outputMeta; 29 | 30 | XDPSwitch(const XDPProgram* program, const IR::ControlBlock* block, 31 | const IR::Parameter* parserHeaders); 32 | bool build() override; 33 | }; 34 | 35 | class XDPDeparser : public EBPF::EBPFControl { 36 | public: 37 | const IR::Parameter* packet; 38 | 39 | XDPDeparser(const XDPProgram* program, const IR::ControlBlock* block, 40 | const IR::Parameter* parserHeaders); 41 | bool build() override; 42 | void emit(EBPF::CodeBuilder* builder) override; 43 | const XDPProgram* getProgram() const 44 | { return dynamic_cast(program); } 45 | }; 46 | 47 | } // namespace XDP 48 | 49 | #endif /* _EXTENSIONS_P4C_XDP_XDPCONTROL_H_ */ 50 | -------------------------------------------------------------------------------- /xdpModel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "xdpModel.h" 18 | 19 | namespace XDP { 20 | 21 | XDPModel XDPModel::instance; 22 | 23 | } // namespace XDP 24 | -------------------------------------------------------------------------------- /xdpModel.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 VMware, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef _XDPMODEL_H_ 18 | #define _XDPMODEL_H_ 19 | 20 | #include "backends/ebpf/ebpfModel.h" 21 | 22 | namespace XDP { 23 | 24 | struct XDP_Action_Model : public ::Model::Enum_Model { 25 | XDP_Action_Model() : Enum_Model("xdp_action"), 26 | aborted("XDP_ABORTED"), drop("XDP_DROP"), 27 | pass("XDP_PASS"), tx("XDP_TX") {} 28 | ::Model::Elem aborted; 29 | ::Model::Elem drop; 30 | ::Model::Elem pass; 31 | ::Model::Elem tx; 32 | }; 33 | 34 | struct XDP_Switch_Model : public ::Model::Elem { 35 | XDP_Switch_Model() : Elem("xdp"), 36 | parser("p"), swtch("s"), deparser("d") {} 37 | ::Model::Elem parser; 38 | ::Model::Elem swtch; 39 | ::Model::Elem deparser; 40 | }; 41 | 42 | struct InputMetadataModel : public ::Model::Type_Model { 43 | InputMetadataModel() : ::Model::Type_Model("xdp_input"), 44 | inputPort("input_port"), inputPortType(IR::Type_Bits::get(32)) 45 | {} 46 | 47 | ::Model::Elem inputPort; 48 | const IR::Type* inputPortType; 49 | }; 50 | 51 | struct OutputMetadataModel : public ::Model::Type_Model { 52 | OutputMetadataModel() : ::Model::Type_Model("xdp_output"), 53 | outputPort("output_port"), outputPortType(IR::Type_Bits::get(32)), 54 | output_action("output_action") 55 | {} 56 | 57 | ::Model::Elem outputPort; 58 | const IR::Type* outputPortType; 59 | ::Model::Elem output_action; 60 | }; 61 | 62 | // Keep this in sync with xdp_model.p4 63 | class XDPModel : public EBPF::EBPFModel { 64 | protected: 65 | XDPModel() : EBPF::EBPFModel(), xdp(), inputMetadataModel(), outputMetadataModel(), 66 | ipv4_checksum("ebpf_ipv4_checksum"), 67 | csum_replace2("csum_replace2"), 68 | csum_replace4("csum_replace4"), 69 | bpf_event_output("bpf_event_output"), 70 | bpf_ktime_get_ns("bpf_ktime_get_ns"), 71 | action_enum() 72 | {} 73 | 74 | public: 75 | static XDPModel instance; 76 | XDP_Switch_Model xdp; 77 | InputMetadataModel inputMetadataModel; 78 | OutputMetadataModel outputMetadataModel; 79 | ::Model::Extern_Model ipv4_checksum; 80 | ::Model::Extern_Model csum_replace2; 81 | ::Model::Extern_Model csum_replace4; 82 | ::Model::Extern_Model bpf_event_output; 83 | ::Model::Extern_Model bpf_ktime_get_ns; 84 | XDP_Action_Model action_enum; 85 | }; 86 | 87 | } // namespace XDP 88 | 89 | #endif /* _XDPMODEL_H_ */ 90 | -------------------------------------------------------------------------------- /xdpProgram.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013-present Barefoot Networks, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "backends/ebpf/ebpfType.h" 18 | #include "backends/ebpf/ebpfControl.h" 19 | #include "backends/ebpf/ebpfParser.h" 20 | #include "backends/ebpf/ebpfTable.h" 21 | #include "frontends/p4/coreLibrary.h" 22 | #include "xdpProgram.h" 23 | #include "xdpControl.h" 24 | 25 | namespace XDP { 26 | 27 | bool XDPProgram::build() { 28 | auto pack = toplevel->getMain(); 29 | unsigned paramCount = pack->getConstructorParameters()->size(); 30 | 31 | cstring parserParamName; 32 | if (paramCount == 2) { 33 | parserParamName = model.filter.parser.name; 34 | } else if (paramCount == 3) { 35 | parserParamName = xdp_model.xdp.parser.name; 36 | } else { 37 | ::error(ErrorType::ERR_EXPECTED, 38 | "%1%: Expected 2 or 3 package parameters", pack); 39 | } 40 | 41 | auto pb = pack->getParameterValue(parserParamName) 42 | ->to(); 43 | BUG_CHECK(pb != nullptr, "No parser block found"); 44 | parser = new EBPF::EBPFParser(this, pb, typeMap); 45 | bool success = parser->build(); 46 | if (!success) 47 | return success; 48 | 49 | if (paramCount == 2) { 50 | cstring controlParamName = model.filter.filter.name; 51 | auto cb = pack->getParameterValue(controlParamName) 52 | ->to(); 53 | BUG_CHECK(cb != nullptr, "No control block found"); 54 | control = new EBPF::EBPFControl(this, cb, parser->headers); 55 | success = control->build(); 56 | if (!success) 57 | return success; 58 | } else { 59 | cstring controlParamName = xdp_model.xdp.swtch.name; 60 | auto cb = pack->getParameterValue(controlParamName) 61 | ->to(); 62 | BUG_CHECK(cb != nullptr, "No control block found"); 63 | control = new XDPSwitch(this, cb, parser->headers); 64 | success = control->build(); 65 | if (!success) 66 | return success; 67 | } 68 | 69 | if (paramCount == 3) { 70 | auto db = pack->getParameterValue(xdp_model.xdp.deparser.name) 71 | ->to(); 72 | BUG_CHECK(db != nullptr, "No deparser block found"); 73 | deparser = new XDPDeparser(this, db, parser->headers); 74 | success = deparser->build(); 75 | if (!success) 76 | return success; 77 | } 78 | 79 | return true; 80 | } 81 | 82 | void XDPProgram::emitTypes(EBPF::CodeBuilder* builder) { 83 | for (auto d : program->objects) { 84 | if (!d->is()) continue; 85 | 86 | if (d->is() || d->is() || 87 | d->is() || d->is() || 88 | d->is() || d->is()) 89 | continue; 90 | 91 | if (d->is()) { 92 | if (d->to()->name == XDPModel::instance.action_enum.name) 93 | continue; 94 | } 95 | 96 | auto type = EBPF::EBPFTypeFactory::instance->create(d->to()); 97 | if (type == nullptr) 98 | continue; 99 | type->emit(builder); 100 | builder->newline(); 101 | } 102 | } 103 | 104 | void XDPProgram::emitC(EBPF::CodeBuilder* builder, cstring headerFile) { 105 | emitGeneratedComment(builder); 106 | 107 | if (!switchTarget()) { 108 | EBPF::EBPFProgram::emitC(builder, headerFile); 109 | return; 110 | } 111 | 112 | if (builder->target->name != "XDP") { 113 | ::error(ErrorType::ERR_EXPECTED, 114 | "This program must be compiled with --target xdp"); 115 | return; 116 | } 117 | 118 | builder->appendFormat("#include \"%s\"", headerFile); 119 | builder->newline(); 120 | builder->target->emitIncludes(builder); 121 | emitPreamble(builder); 122 | control->emitTableInstances(builder); 123 | 124 | builder->appendLine( 125 | "inline u16 ebpf_ipv4_checksum(u8 version, u8 ihl, u8 diffserv,\n" 126 | " u16 totalLen, u16 identification, u8 flags,\n" 127 | " u16 fragOffset, u8 ttl, u8 protocol,\n" 128 | " u32 srcAddr, u32 dstAddr) {\n" 129 | " u32 checksum = __bpf_htons(((u16)version << 12) | ((u16)ihl << 8) | (u16)diffserv);\n" 130 | " checksum += __bpf_htons(totalLen);\n" 131 | " checksum += __bpf_htons(identification);\n" 132 | " checksum += __bpf_htons(((u16)flags << 13) | fragOffset);\n" 133 | " checksum += __bpf_htons(((u16)ttl << 8) | (u16)protocol);\n" 134 | " srcAddr = __bpf_ntohl(srcAddr);\n" 135 | " dstAddr = __bpf_ntohl(dstAddr);\n" 136 | " checksum += (srcAddr >> 16) + (u16)srcAddr;\n" 137 | " checksum += (dstAddr >> 16) + (u16)dstAddr;\n" 138 | " // Fields in 'struct Headers' are host byte order.\n" 139 | " // Deparser converts to network byte-order\n" 140 | " return bpf_ntohs(~((checksum & 0xFFFF) + (checksum >> 16)));\n" 141 | "}"); 142 | 143 | builder->appendLine( 144 | "inline u16 csum16_add(u16 csum, u16 addend) {\n" 145 | " u16 res = csum;\n" 146 | " res += addend;\n" 147 | " return (res + (res < addend));\n" 148 | "}\n" 149 | "inline u16 csum16_sub(u16 csum, u16 addend) {\n" 150 | " return csum16_add(csum, ~addend);\n" 151 | "}\n" 152 | "inline u16 csum_replace2(u16 csum, u16 old, u16 new) {\n" 153 | " return (~csum16_add(csum16_sub(~csum, old), new));\n" 154 | "}\n"); 155 | 156 | builder->appendLine( 157 | "inline u16 csum_fold(u32 csum) {\n" 158 | " u32 r = csum << 16 | csum >> 16;\n" 159 | " csum = ~csum;\n" 160 | " csum -= r;\n" 161 | " return (u16)(csum >> 16);\n" 162 | "}\n" 163 | "inline u32 csum_unfold(u16 csum) {\n" 164 | " return (u32)csum;\n" 165 | "}\n" 166 | "inline u32 csum32_add(u32 csum, u32 addend) {\n" 167 | " u32 res = csum;\n" 168 | " res += addend;\n" 169 | " return (res + (res < addend));\n" 170 | "}\n" 171 | "inline u32 csum32_sub(u32 csum, u32 addend) {\n" 172 | " return csum32_add(csum, ~addend);\n" 173 | "}\n" 174 | "inline u16 csum_replace4(u16 csum, u32 from, u32 to) {\n" 175 | " u32 tmp = csum32_sub(~csum_unfold(csum), from);\n" 176 | " return csum_fold(csum32_add(tmp, to));\n" 177 | "}\n"); 178 | 179 | builder->appendLine( 180 | "struct bpf_elf_map SEC(\"maps\") perf_event = {\n" 181 | " .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,\n" 182 | " .size_key = sizeof(u32),\n" 183 | " .size_value = sizeof(u32),\n" 184 | " .pinning = 1,\n" 185 | " .max_elem = 2,\n" 186 | "};\n" 187 | "#define BPF_PERF_EVENT_OUTPUT() do {\\\n" 188 | " int pktsize = (int)(skb->data_end - skb->data);\\\n" 189 | " bpf_perf_event_output(skb, &perf_event, ((u64)pktsize << 32), &pktsize, 4);\\\n" 190 | "} while(0);\n"); 191 | 192 | builder->appendLine( 193 | "#define BPF_KTIME_GET_NS() ({\\\n" 194 | " u32 ___ts = (u32)bpf_ktime_get_ns(); ___ts; })\\\n"); 195 | 196 | // The table used for forwarding: we write the output in it 197 | // TODO: this should use target->emitTableDecl(). 198 | // We can't do it today because it has a different map type PERCPU_ARRAY 199 | builder->emitIndent(); 200 | builder->appendFormat("struct bpf_elf_map SEC(\"maps\") %s = ", outTableName.c_str()); 201 | builder->blockStart(); 202 | builder->emitIndent(); 203 | builder->append(".type = "); 204 | builder->appendLine("BPF_MAP_TYPE_PERCPU_ARRAY,"); 205 | 206 | builder->emitIndent(); 207 | builder->append(".size_key = sizeof(u32),"); 208 | builder->newline(); 209 | 210 | builder->emitIndent(); 211 | builder->appendFormat(".size_value = sizeof(u32),"); 212 | builder->newline(); 213 | 214 | builder->emitIndent(); 215 | builder->appendFormat(".pinning = 2, /* PIN_OBJECT_NS */"); 216 | builder->newline(); 217 | 218 | builder->emitIndent(); 219 | builder->appendFormat(".max_elem = 1 /* No multicast support */"); 220 | builder->newline(); 221 | 222 | builder->blockEnd(false); 223 | builder->endOfStatement(true); 224 | 225 | builder->newline(); 226 | builder->emitIndent(); 227 | builder->target->emitCodeSection(builder, "prog"); 228 | builder->emitIndent(); 229 | builder->target->emitMain(builder, functionName, model.CPacketName.str()); 230 | builder->blockStart(); 231 | 232 | emitHeaderInstances(builder); 233 | builder->append(" = "); 234 | parser->headerType->emitInitializer(builder); 235 | builder->endOfStatement(true); 236 | 237 | emitLocalVariables(builder); 238 | builder->newline(); 239 | builder->emitIndent(); 240 | builder->appendFormat("goto %s;", IR::ParserState::start.c_str()); 241 | builder->newline(); 242 | 243 | parser->emit(builder); 244 | emitPipeline(builder); 245 | 246 | builder->emitIndent(); 247 | builder->append(endLabel); 248 | builder->appendLine(":"); 249 | 250 | // write output port to a table 251 | builder->emitIndent(); 252 | builder->appendFormat("bpf_map_update_elem(&%s, &%s, &%s.%s, BPF_ANY)", 253 | outTableName.c_str(), zeroKey.c_str(), 254 | getSwitch()->outputMeta->name.name, 255 | XDPModel::instance.outputMetadataModel.outputPort.str()); 256 | builder->endOfStatement(true); 257 | 258 | builder->emitIndent(); 259 | builder->appendFormat("return %s.%s", 260 | getSwitch()->outputMeta->name.name, 261 | XDPModel::instance.outputMetadataModel.output_action.str()); 262 | builder->endOfStatement(true); 263 | builder->blockEnd(true); // end of function 264 | 265 | builder->target->emitLicense(builder, license); 266 | } 267 | 268 | void XDPProgram::emitPipeline(EBPF::CodeBuilder* builder) { 269 | builder->emitIndent(); 270 | builder->append(IR::ParserState::accept); 271 | builder->append(":"); 272 | builder->newline(); 273 | 274 | builder->emitIndent(); 275 | builder->blockStart(); 276 | control->emit(builder); 277 | builder->blockEnd(true); 278 | 279 | if (switchTarget()) { 280 | builder->emitIndent(); 281 | builder->append("/* deparser */"); 282 | builder->newline(); 283 | builder->emitIndent(); 284 | builder->blockStart(); 285 | deparser->emit(builder); 286 | builder->blockEnd(true); 287 | } 288 | } 289 | 290 | void XDPProgram::emitLocalVariables(EBPF::CodeBuilder* builder) { 291 | if (!switchTarget()) { 292 | EBPF::EBPFProgram::emitLocalVariables(builder); 293 | return; 294 | } 295 | 296 | builder->emitIndent(); 297 | builder->appendFormat("unsigned %s = 0;", offsetVar); 298 | builder->newline(); 299 | 300 | builder->emitIndent(); 301 | builder->appendFormat("enum %s %s = %s;", errorEnum, errorVar, 302 | P4::P4CoreLibrary::instance.noError.str()); 303 | builder->newline(); 304 | 305 | builder->emitIndent(); 306 | builder->appendFormat("void* %s = %s;", 307 | packetStartVar, builder->target->dataOffset(model.CPacketName.str())); 308 | builder->newline(); 309 | builder->emitIndent(); 310 | builder->appendFormat("void* %s = %s;", 311 | packetEndVar, builder->target->dataEnd(model.CPacketName.str())); 312 | builder->newline(); 313 | 314 | builder->emitIndent(); 315 | builder->appendFormat("u32 %s = 0;", zeroKey); 316 | builder->newline(); 317 | 318 | builder->emitIndent(); 319 | builder->appendFormat("u8 %s = 0;", byteVar); 320 | builder->newline(); 321 | 322 | builder->emitIndent(); 323 | builder->appendFormat("u32 %s = 0;", outHeaderLengthVar); 324 | builder->newline(); 325 | 326 | builder->emitIndent(); 327 | builder->appendFormat("struct %s %s;", xdp_model.outputMetadataModel.name, 328 | getSwitch()->outputMeta->name.name); 329 | builder->newline(); 330 | 331 | builder->emitIndent(); 332 | builder->appendFormat("/* TODO: this should be initialized by the environment. HOW? */"); 333 | builder->newline(); 334 | 335 | builder->emitIndent(); 336 | builder->appendFormat("struct %s %s;", xdp_model.inputMetadataModel.name, 337 | getSwitch()->inputMeta->name.name); 338 | builder->newline(); 339 | } 340 | 341 | XDPSwitch* XDPProgram::getSwitch() const { 342 | return dynamic_cast(control); 343 | } 344 | 345 | } // namespace XDP 346 | -------------------------------------------------------------------------------- /xdpProgram.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013-present Barefoot Networks, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef _EXTENSIONS_P4C_XDP_XDPPROGRAM_H_ 18 | #define _EXTENSIONS_P4C_XDP_XDPPROGRAM_H_ 19 | 20 | #include "target.h" 21 | #include "xdpModel.h" 22 | #include "ir/ir.h" 23 | #include "frontends/p4/typeMap.h" 24 | #include "frontends/p4/evaluator/evaluator.h" 25 | #include "backends/ebpf/ebpfObject.h" 26 | #include "backends/ebpf/ebpfOptions.h" 27 | #include "backends/ebpf/ebpfProgram.h" 28 | 29 | namespace XDP { 30 | 31 | class XDPDeparser; 32 | class XDPSwitch; 33 | 34 | class XDPProgram : public EBPF::EBPFProgram { 35 | public: 36 | // If the deparser is missing we are still 37 | // compiling for the old EBPF model. 38 | XDPDeparser* deparser; 39 | XDPModel& xdp_model; 40 | cstring outHeaderLengthVar; 41 | cstring outTableName; 42 | 43 | XDPProgram(const EbpfOptions& options, const IR::P4Program* program, 44 | P4::ReferenceMap* refMap, P4::TypeMap* typeMap, 45 | const IR::ToplevelBlock* toplevel) : 46 | EBPF::EBPFProgram(options, program, refMap, typeMap, toplevel), 47 | deparser(nullptr), xdp_model(XDPModel::instance) { 48 | outHeaderLengthVar = EBPF::EBPFModel::reserved("outHeaderLength"); 49 | outTableName = EBPF::EBPFModel::reserved("outTable"); 50 | } 51 | 52 | // If the deparser is null we are compiling for the old EBPF model 53 | bool switchTarget() const { return deparser != nullptr; } 54 | 55 | void emitC(EBPF::CodeBuilder* builder, cstring headerFile) override; 56 | bool build() override; // return 'true' on success 57 | void emitLocalVariables(EBPF::CodeBuilder* builder) override; 58 | void emitPipeline(EBPF::CodeBuilder* builder) override; 59 | XDPSwitch* getSwitch() const; 60 | void emitTypes(EBPF::CodeBuilder* builder) override; 61 | }; 62 | 63 | } // namespace XDP 64 | 65 | #endif /* _EXTENSIONS_P4C_XDP_XDPPROGRAM_H_ */ 66 | -------------------------------------------------------------------------------- /xdp_target.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright 2018 VMware, Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import sys 17 | # path to the tools folder of the compiler 18 | sys.path.insert(0, 'p4c/tools') 19 | # path to the framework repository of the compiler 20 | sys.path.insert(0, 'p4c/backends/ebpf/targets') 21 | from .kernel_target import Target as EBPFKernelTarget 22 | from testutils import * 23 | 24 | 25 | class Target(EBPFKernelTarget): 26 | EBPF_MAP_PATH = "/sys/fs/bpf/xdp/globals" 27 | 28 | def __init__(self, tmpdir, options, template, outputs): 29 | EBPFKernelTarget.__init__(self, tmpdir, options, template, outputs) 30 | # We use a different compiler, override the inherited default 31 | components = options.compiler.split("/")[0:-1] 32 | self.compiler = "/".join(components) + "/p4c-xdp" 33 | print("Compiler is", self.compiler) 34 | 35 | def compile_dataplane(self): 36 | # Just call into the parent with the target set to kernel 37 | old_target = self.options.target 38 | self.options.target = "kernel" 39 | result = super(Target, self).compile_dataplane() 40 | self.options.target = old_target 41 | return result 42 | 43 | def _create_runtime(self): 44 | # Just call into the parent with the target set to kernel 45 | old_target = self.options.target 46 | self.options.target = "kernel" 47 | result = super(Target, self)._create_runtime() 48 | self.options.target = old_target 49 | return result 50 | 51 | def _load_filter(self, bridge, proc, port_name): 52 | # Load the specified eBPF object to "port_name" ingress and egress 53 | # As a side-effect, this may create maps in /sys/fs/bpf/ 54 | cmd = ("ip link set dev %s xdp obj %s verb" % 55 | (port_name, self.template + ".o")) 56 | return bridge.ns_proc_write(proc, cmd) 57 | 58 | def _attach_filters(self, bridge, proc): 59 | # Get the command to load XDP code to all the attached ports 60 | # We load XDP directly to the bridge ports instead of the edges as with tc 61 | if len(bridge.br_ports) > 0: 62 | for port in bridge.br_ports: 63 | result = self._load_filter(bridge, proc, port) 64 | bridge.ns_proc_append(proc, "") 65 | else: 66 | # No ports attached (no pcap files), load to bridge instead 67 | result = self._load_filter(bridge, proc, bridge.br_name) 68 | bridge.ns_proc_append(proc, "") 69 | if result != SUCCESS: 70 | return result 71 | return SUCCESS 72 | --------------------------------------------------------------------------------