From 7227d3758f177e1d021700de4dcac4133bc9583c Mon Sep 17 00:00:00 2001 From: fruffy Date: Tue, 3 Dec 2024 10:38:53 -0500 Subject: [PATCH 1/2] CI installation refactoring. Signed-off-by: fruffy --- .github/workflows/ci-lint.yaml | 12 +- .github/workflows/ci-p4tools.yml | 2 +- .github/workflows/ci-ptf-kernels-weekly.yml | 2 +- .github/workflows/ci-ptf.yml | 2 +- .github/workflows/ci-static-build-test.yml | 2 +- .github/workflows/ci-test-debian.yml | 36 ++- .github/workflows/ci-test-fedora.yml | 8 +- .github/workflows/ci-test-mac.yml | 6 +- .github/workflows/ci-ubuntu-18-nightly.yml | 2 +- .../ci-ubuntu-20-sanitizer-nightly.yml | 2 +- .github/workflows/ci-validation-nightly.yml | 2 +- .gitignore | 3 + Dockerfile | 1 + backends/bmv2/run-bmv2-ptf-test.py | 10 +- backends/ebpf/CMakeLists.txt | 2 +- pyproject.toml | 46 ++++ requirements.txt | 18 -- tools/ci-build.sh | 50 ++-- tools/install.sh | 214 ++++++++++++++++++ tools/install_fedora_deps.sh | 7 +- tools/install_mac_deps.sh | 9 +- 21 files changed, 367 insertions(+), 69 deletions(-) delete mode 100644 requirements.txt create mode 100755 tools/install.sh diff --git a/.github/workflows/ci-lint.yaml b/.github/workflows/ci-lint.yaml index c938f577185..7791c7a064f 100644 --- a/.github/workflows/ci-lint.yaml +++ b/.github/workflows/ci-lint.yaml @@ -35,12 +35,16 @@ jobs: tools/ci-build.sh - name: Run cpplint on C/C++ files. - run: cmake --build build --target cpplint + run: poetry run cmake --build build --target cpplint - name: Run clang-format on C/C++ files. - run: cmake --build build --target clang-format + run: | + poetry run clang-format --version + poetry run cmake --build build --target clang-format - name: Run black and isort on Python files. run: | - cmake --build build --target black - cmake --build build --target isort + poetry run black --version + poetry run cmake --build build --target black + poetry run isort --version + poetry run cmake --build build --target isort diff --git a/.github/workflows/ci-p4tools.yml b/.github/workflows/ci-p4tools.yml index de2fe523c5d..57277b7834f 100644 --- a/.github/workflows/ci-p4tools.yml +++ b/.github/workflows/ci-p4tools.yml @@ -40,5 +40,5 @@ jobs: - name: Run tests (Ubuntu 22.04) # Need to use sudo for the eBPF kernel tests. - run: sudo -E ctest -R "testgen|smith" --output-on-failure --schedule-random + run: sudo -E env PATH="$PATH" poetry run ctest -R "testgen|smith" --output-on-failure --schedule-random working-directory: ./build diff --git a/.github/workflows/ci-ptf-kernels-weekly.yml b/.github/workflows/ci-ptf-kernels-weekly.yml index 9073f755a7c..85dab9d387a 100644 --- a/.github/workflows/ci-ptf-kernels-weekly.yml +++ b/.github/workflows/ci-ptf-kernels-weekly.yml @@ -119,4 +119,4 @@ jobs: - name: Install VM and run PTF tests for eBPF backend run: | - sudo -E ./tools/ci-ptf/run_test.sh sudo docker run --privileged -v /sys/fs/bpf:/sys/fs/bpf -w /p4c/backends/ebpf/tests p4c ./test.sh + sudo -E ./tools/ci-ptf/run_test.sh sudo docker run --privileged -v /sys/fs/bpf:/sys/fs/bpf -w /p4c/backends/ebpf/tests p4c poetry run ./test.sh diff --git a/.github/workflows/ci-ptf.yml b/.github/workflows/ci-ptf.yml index 3304f705ae4..b057c899a1b 100644 --- a/.github/workflows/ci-ptf.yml +++ b/.github/workflows/ci-ptf.yml @@ -53,5 +53,5 @@ jobs: tools/ci-build.sh - name: Run PTF tests for eBPF backend (Ubuntu 20.04) - run: sudo -E ./test.sh + run: sudo -E env PATH="$PATH" poetry run ./test.sh working-directory: ./backends/ebpf/tests diff --git a/.github/workflows/ci-static-build-test.yml b/.github/workflows/ci-static-build-test.yml index 230a11d9cbf..a0c63d31a0b 100644 --- a/.github/workflows/ci-static-build-test.yml +++ b/.github/workflows/ci-static-build-test.yml @@ -48,6 +48,6 @@ jobs: - shell: bash name: Build (Ubuntu 20.04) run: | - sudo -E tools/ci-build.sh + tools/ci-build.sh ./tools/ci-check-static.sh ./build/p4c-bm2-ss ./build/p4c-dpdk ./build/p4c-ebpf \ ./build/p4c-pna-p4tc ./build/p4c-ubpf ./build/p4test ./build/p4testgen diff --git a/.github/workflows/ci-test-debian.yml b/.github/workflows/ci-test-debian.yml index ac2b2d5e4be..aa212045358 100644 --- a/.github/workflows/ci-test-debian.yml +++ b/.github/workflows/ci-test-debian.yml @@ -14,6 +14,36 @@ concurrency: cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: + + # Build with GCC and test P4C on Ubuntu 24.04. + test-ubuntu24: + runs-on: ubuntu-24.04 + env: + CTEST_PARALLEL_LEVEL: 4 + IMAGE_TYPE: test + BUILD_GENERATOR: Ninja + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1 + with: + key: test-${{ runner.os }}-gcc + max-size: 1000M + + - name: Build (Ubuntu 24.04, GCC) + run: | + tools/ci-build.sh + + - name: Run tests (Ubuntu 24.04) + # Need to use sudo for the eBPF kernel tests. + run: | + sudo -E env PATH="$PATH" poetry run ctest --output-on-failure --schedule-random + working-directory: ./build + # Build with GCC and test P4C on Ubuntu 22.04. test-ubuntu22: runs-on: ubuntu-22.04 @@ -39,7 +69,8 @@ jobs: - name: Run tests (Ubuntu 22.04) # Need to use sudo for the eBPF kernel tests. - run: sudo -E ctest --output-on-failure --schedule-random + run: | + sudo -E env PATH="$PATH" poetry run ctest --output-on-failure --schedule-random working-directory: ./build # Build with GCC and test Tofino backend on Ubuntu 22.04. @@ -112,6 +143,7 @@ jobs: - name: Run tests (Ubuntu 20.04) # Need to use sudo for the eBPF kernel tests. - run: sudo -E ctest --output-on-failure --schedule-random + run: | + sudo -E env PATH="$PATH" poetry run ctest --output-on-failure --schedule-random working-directory: ./build if: matrix.unity == 'ON' && matrix.gtest == 'ON' diff --git a/.github/workflows/ci-test-fedora.yml b/.github/workflows/ci-test-fedora.yml index 2b4a4d95287..d818acaa6f1 100644 --- a/.github/workflows/ci-test-fedora.yml +++ b/.github/workflows/ci-test-fedora.yml @@ -35,7 +35,9 @@ jobs: submodules: recursive - name: Install dependencies (Fedora Linux) - run: tools/install_fedora_deps.sh + run: | + export PATH="$HOME/.local/bin:$PATH" + tools/install_fedora_deps.sh - name: ccache uses: hendrikmuhs/ccache-action@v1 @@ -45,10 +47,12 @@ jobs: - name: Build p4c (Fedora Linux) run: | + export PATH="$HOME/.local/bin:$PATH" ./bootstrap.sh -DCMAKE_BUILD_TYPE=Release -DCMAKE_UNITY_BUILD=ON --build-generator "Ninja" cmake --build build -- -j $(nproc) - name: Run p4c tests (Fedora Linux) run: | - export PATH="$HOME/.local/bin:$PATH"; ctest --output-on-failure --schedule-random + export PATH="$HOME/.local/bin:$PATH" + poetry run ctest --output-on-failure --schedule-random working-directory: ./build diff --git a/.github/workflows/ci-test-mac.yml b/.github/workflows/ci-test-mac.yml index 4c8845dbe07..fb2d693c064 100644 --- a/.github/workflows/ci-test-mac.yml +++ b/.github/workflows/ci-test-mac.yml @@ -46,6 +46,7 @@ jobs: - name: Install dependencies (MacOS) run: | + source ~/.bash_profile tools/install_mac_deps.sh - name: Build (MacOS) @@ -58,7 +59,7 @@ jobs: - name: Run tests (MacOS) run: | source ~/.bash_profile - ctest --output-on-failure --schedule-random -E "bpf|ubpf|testgen|smith" + poetry run ctest --output-on-failure --schedule-random -E "bpf|ubpf|testgen|smith" working-directory: ./build # Build and test p4c on MacOS 13 on x86. @@ -93,6 +94,7 @@ jobs: - name: Install dependencies (MacOS) run: | + source ~/.bash_profile tools/install_mac_deps.sh - name: Build (MacOS) @@ -105,5 +107,5 @@ jobs: - name: Run tests (MacOS) run: | source ~/.bash_profile - ctest --output-on-failure --schedule-random -E "bpf|ubpf|testgen|smith" + poetry run ctest --output-on-failure --schedule-random -E "bpf|ubpf|testgen|smith" working-directory: ./build diff --git a/.github/workflows/ci-ubuntu-18-nightly.yml b/.github/workflows/ci-ubuntu-18-nightly.yml index 68ada935503..b4fe85f17cd 100644 --- a/.github/workflows/ci-ubuntu-18-nightly.yml +++ b/.github/workflows/ci-ubuntu-18-nightly.yml @@ -39,4 +39,4 @@ jobs: # this is needed to create network namespaces for the ebpf tests. - name: Run tests (Ubuntu 18.04) run: | - sudo -E docker run --privileged -w /p4c/build -e $CTEST_PARALLEL_LEVEL p4c ctest --output-on-failure --schedule-random + sudo -E docker run --privileged -w /p4c/build -e $CTEST_PARALLEL_LEVEL p4c poetry run --output-on-failure --schedule-random diff --git a/.github/workflows/ci-ubuntu-20-sanitizer-nightly.yml b/.github/workflows/ci-ubuntu-20-sanitizer-nightly.yml index 830ddade2cd..665764af7c1 100644 --- a/.github/workflows/ci-ubuntu-20-sanitizer-nightly.yml +++ b/.github/workflows/ci-ubuntu-20-sanitizer-nightly.yml @@ -40,5 +40,5 @@ jobs: - name: Run tests (Ubuntu 20.04) # Need to use sudo for the eBPF kernel tests. - run: sudo -E ctest --output-on-failure --schedule-random + run: sudo -E env PATH="$PATH" poetry run ctest --output-on-failure --schedule-random working-directory: ./build diff --git a/.github/workflows/ci-validation-nightly.yml b/.github/workflows/ci-validation-nightly.yml index b947f603d63..47d985f1a67 100644 --- a/.github/workflows/ci-validation-nightly.yml +++ b/.github/workflows/ci-validation-nightly.yml @@ -38,5 +38,5 @@ jobs: - name: Validate run: | - ctest -R toz3-validate-p4c --output-on-failure --schedule-random + poetry run ctest -R toz3-validate-p4c --output-on-failure --schedule-random working-directory: ./build diff --git a/.gitignore b/.gitignore index 7772b8a5d40..e9bf002d74f 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,6 @@ docs/doxygen/awesome_css/ # thirdparty backends/tofino/third_party/ + +# Poetry +poetry.lock diff --git a/Dockerfile b/Dockerfile index 50a34e25158..92215d07ad2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,6 +46,7 @@ ARG BUILD_AUTO_VAR_INIT_PATTERN=OFF # - Disable leaks detector as p4c uses GC. ENV UBSAN_OPTIONS=print_stacktrace=1 ENV ASAN_OPTIONS=print_stacktrace=1:detect_leaks=0 +ENV PATH="/root/.local/bin:$PATH" # Delegate the build to tools/ci-build. COPY . /p4c/ diff --git a/backends/bmv2/run-bmv2-ptf-test.py b/backends/bmv2/run-bmv2-ptf-test.py index 8843827ee02..76d62d11710 100755 --- a/backends/bmv2/run-bmv2-ptf-test.py +++ b/backends/bmv2/run-bmv2-ptf-test.py @@ -42,7 +42,7 @@ PARSER.add_argument( "-n", "--num-ifaces", - default=8, + default=10, dest="num_ifaces", help="How many virtual interfaces to create.", ) @@ -91,7 +91,7 @@ class Options: # The base directory where tests are executed. rootdir: Path = Path(".") # The number of interfaces to create for this particular test. - num_ifaces: int = 8 + num_ifaces: int = 10 # Whether to use nanomsg for packet delivery as opposed to Linux veth interfaces. use_nn: bool = False @@ -201,7 +201,7 @@ def run_ptf(self, grpc_port: int, json_name: Path, info_name: Path) -> int: ) # TODO: There is currently a bug where we can not support more than 344 ports at once. # The nanomsg test back end simply hangs, the reason is unclear. - port_range = "0-8" + port_range = f"0-{self.options.num_ifaces - 1}" run_ptf_cmd = ( f"ptf --platform nn --device-socket 0-{{{port_range}}}@ipc://{self.options.testdir}/" f"bmv2_packets_1.ipc --pypath {pypath} " @@ -244,7 +244,7 @@ def run_simple_switch_grpc(self, switchlog: Path, grpc_port: int) -> Optional[su thrift_port = testutils.pick_tcp_port(GRPC_ADDRESS, THRIFT_PORT) simple_switch_grpc = ( f"simple_switch_grpc --thrift-port {thrift_port} --device-id 0 --log-file {switchlog} " - f"{ifaces} --log-flush -i 0@0 --no-p4 " + f"{ifaces} --log-flush --no-p4 " f"-- --grpc-server-addr {GRPC_ADDRESS}:{grpc_port}" ) bridge_cmd = self.bridge.get_ns_prefix() + " " + simple_switch_grpc @@ -271,7 +271,7 @@ def run_ptf(self, grpc_port: int, json_name: Path, info_name: Path) -> int: ifaces = self.get_iface_str(num_ifaces=self.options.num_ifaces, prefix="br_") test_params = ( f"grpcaddr='{GRPC_ADDRESS}:{grpc_port}';p4info='{info_name}';config='{json_name}';" - f"packet_wait_time='0.1';" + f"packet_wait_time='0.5';" ) run_ptf_cmd = ( f"ptf --pypath {pypath} {ifaces} --log-file {self.options.testdir.joinpath('ptf.log')} " diff --git a/backends/ebpf/CMakeLists.txt b/backends/ebpf/CMakeLists.txt index a2d126fc7f0..3d26c338707 100644 --- a/backends/ebpf/CMakeLists.txt +++ b/backends/ebpf/CMakeLists.txt @@ -223,7 +223,7 @@ else() endif() # check for the libbpf library -find_library(LIBBPF NAMES bpf HINTS "${CMAKE_CURRENT_SOURCE_DIR}/runtime/usr/lib64/") +find_library(LIBBPF NAMES bpf HINTS "${CMAKE_CURRENT_SOURCE_DIR}/runtime/install/libbpf/") if (LIBBPF) message(STATUS "Found libbpf library at ${LIBBPF}") else() diff --git a/pyproject.toml b/pyproject.toml index 368e195d59e..d90c5f02bb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,3 +51,49 @@ disable = [ "missing-function-docstring", "too-few-public-methods", ] + +[tool.poetry] +name = "p4c" +version = "1.2.5.1" +description = "" +authors = ["Your Name "] +license = "Apache" +readme = "README.md" +package-mode = false + +[tool.poetry.dependencies] +# Fixme: This restriction is because of pyinstaller +python = "<3.14,>=3.8" +# General testing utilties. +pyroute2 = "0.7.3" +ply = "3.11" +scapy = "2.5.0" +# Utilities for BMv2 and PTF tests. +ptf = "0.10.0" +googleapis-common-protos = "1.53.0" +grpcio = [ + {version = "1.67.0", python = ">3.6"}, + {version = "1.48.2", python = "<=3.6"} +] +thrift = "0.21.0 " +protobuf = [ + {version = "3.20.2", python = ">3.6"}, + {version = "3.19.2", python = "<=3.6"} +] +# Linters and formatters. +clang-format = "18.1.0" +black = [ + {version = "24.3.0", python = ">3.6"}, + {version = "22.8.0", python = "<=3.6"} +] +isort = [ + {version = "5.13.2", python = ">3.6"}, + {version = "5.10.1", python = "<=3.6"} +] +# FIXME: We should figure out a way to synchronize P4Runtime versions across CMake and Poetry. This is the same commit hash as defined in the top-level CMakelists.txt +p4runtime = { git = "https://github.com/p4lang/p4runtime.git", rev = "ec4eb5ef70dbcbcbf2f8357a4b2b8c2f218845a5", subdirectory = "py"} + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7df5e9fb622..00000000000 --- a/requirements.txt +++ /dev/null @@ -1,18 +0,0 @@ -###### Python3 dependencies with version specifiers ###### -# Install with pip3 install --user -r requirements.txt -pyroute2==0.7.3 -ply==3.11 -ptf==0.10.0 -scapy==2.5.0 -clang-format==18.1.0 -isort==5.13.2; python_version > '3.6' -black==24.3.0; python_version > '3.6' -protobuf==3.20.2; python_version > '3.6' -grpcio==1.67.0; python_version > '3.6' -googleapis-common-protos==1.53.0; python_version > '3.6' -# Ubuntu 18.04 uses Python 3.6, which is not supported by recent versions of Protobuf and Black. -protobuf==3.19.2; python_version <= '3.6' -googleapis-common-protos==1.53.0; python_version <= '3.6' -grpcio==1.48.2; python_version <= '3.6' -isort==5.10.1; python_version <= '3.6' -black==22.8.0; python_version <= '3.6' diff --git a/tools/ci-build.sh b/tools/ci-build.sh index 40549580f2d..9b0dd0ad133 100755 --- a/tools/ci-build.sh +++ b/tools/ci-build.sh @@ -68,13 +68,17 @@ function build_cmake_enabled_backend_string() { done } +pushd ${P4C_DIR} . /etc/lsb-release # In Docker builds, sudo is not available. So make it a noop. -if [ "$IN_DOCKER" == "TRUE" ]; then +if [ "$IN_DOCKER" = "TRUE" ]; then echo "Executing within docker container." - function sudo() { command "$@"; } + sudo() { "$@"; } # No-op function; just execute the command +else + # Preserve PATH and environment variables when using sudo + sudo() { command sudo -E env PATH="$PATH" "$@"; } fi @@ -104,8 +108,10 @@ fi sudo apt-get update sudo apt-get install -y --no-install-recommends ${P4C_DEPS} -sudo pip3 install --upgrade pip -sudo pip3 install -r ${P4C_DIR}/requirements.txt +# Set up poetry. +sudo apt-get install -y python3-venv curl +curl -sSL https://install.python-poetry.org | python3 - +poetry install if [ "${BUILD_GENERATOR,,}" == "ninja" ] && [ ! $(command -v ninja) ] then @@ -119,25 +125,25 @@ fi if [[ "${DISTRIB_RELEASE}" == "18.04" ]] ; then ccache --set-config cache_dir=.ccache # For Ubuntu 18.04 install the pypi-supplied version of cmake instead. - sudo pip3 install cmake==3.16.3 + poetry update cmake=3.16.3 fi ccache --set-config max_size=1G # ! ------ BEGIN BMV2 ----------------------------------------------- function build_bmv2() { - # TODO: Remove this check once 18.04 is deprecated. - if [[ "${DISTRIB_RELEASE}" == "18.04" ]] ; then - P4C_RUNTIME_DEPS_BOOST="libboost-graph1.65.1 libboost-iostreams1.65.1" - else - P4C_RUNTIME_DEPS_BOOST="libboost-graph1.7* libboost-iostreams1.7*" - fi P4C_RUNTIME_DEPS="cpp \ ${P4C_RUNTIME_DEPS_BOOST} \ libgc1* \ libgmp-dev \ + python3-dev \ libnanomsg-dev" + # TODO: Remove this check once 18.04 is deprecated. + if [[ "${DISTRIB_RELEASE}" == "18.04" ]] ; then + P4C_RUNTIME_DEPS+=" libboost-graph1.65.1 libboost-iostreams1.65.1 " + fi + # TODO: Remove this check once 18.04 is deprecated. if [[ "${DISTRIB_RELEASE}" == "18.04" ]] || [[ "$(which simple_switch 2> /dev/null)" != "" ]] ; then # Use GCC 9 from https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test @@ -146,7 +152,7 @@ function build_bmv2() { P4C_RUNTIME_DEPS+=" gcc-9 g++-9" export CC=gcc-9 export CXX=g++-9 - else + elif [[ "${DISTRIB_RELEASE}" != "24.04" ]] ; then sudo apt-get install -y wget ca-certificates # Add the p4lang opensuse repository. echo "deb http://download.opensuse.org/repositories/home:/p4lang/xUbuntu_${DISTRIB_RELEASE}/ /" | sudo tee /etc/apt/sources.list.d/home:p4lang.list @@ -158,7 +164,7 @@ function build_bmv2() { if [[ "${DISTRIB_RELEASE}" != "18.04" ]] ; then # To run PTF nanomsg tests. Not available on 18.04. - sudo pip3 install nnpy + poetry run pip install nnpy==1.4.2 fi } @@ -172,12 +178,10 @@ function build_ebpf() { P4C_EBPF_DEPS="libpcap-dev \ libelf-dev \ zlib1g-dev \ - llvm \ - clang \ iproute2 \ iptables \ net-tools" - + P4C_EBPF_DEPS+=" llvm clang " sudo apt-get install -y --no-install-recommends ${P4C_EBPF_DEPS} } @@ -217,8 +221,7 @@ fi function build_dpdk() { # Replace existing Protobuf with one that works. # TODO: Debug protobuf mismatch. - sudo -E pip3 uninstall -y protobuf - sudo pip3 install protobuf==3.20.3 netaddr==0.9.0 + poetry run pip install protobuf==3.20.3 netaddr==0.9.0 } if [[ "${ENABLE_DPDK}" == "ON" ]]; then @@ -231,7 +234,7 @@ fi function build_tofino() { P4C_TOFINO_PACKAGES="rapidjson-dev" sudo apt-get install -y --no-install-recommends ${P4C_TOFINO_PACKAGES} - sudo pip3 install jsl==0.2.4 pyinstaller==6.11.0 + poetry run pip install jsl=="0.2.4" pyinstaller=="6.11.0" jsonschema=="4.23.0" } if [[ "${ENABLE_TOFINO}" == "ON" ]]; then @@ -288,16 +291,17 @@ if [ "$ENABLE_SANITIZERS" == "ON" ]; then echo "Warning: building with ASAN and UBSAN sanitizers, GC must be disabled." fi + # Run CMake in the build folder. if [ -e build ]; then /bin/rm -rf build; fi mkdir -p ${P4C_DIR}/build cd ${P4C_DIR}/build -cmake ${CMAKE_FLAGS} -G "${BUILD_GENERATOR}" .. +poetry run cmake ${CMAKE_FLAGS} -G "${BUILD_GENERATOR}" .. # If CMAKE_ONLY is active, only run CMake. Do not build. if [ "$CMAKE_ONLY" == "OFF" ]; then - cmake --build . -- -j $(nproc) - sudo cmake --install . + poetry run cmake --build . -- -j $(nproc) + sudo poetry run cmake --install . # Print ccache statistics after building ccache -p -s fi @@ -313,3 +317,5 @@ elif [[ "${IMAGE_TYPE}" == "test" ]] ; then echo 'Test image ready' fi + +popd diff --git a/tools/install.sh b/tools/install.sh new file mode 100755 index 00000000000..fbb656c9443 --- /dev/null +++ b/tools/install.sh @@ -0,0 +1,214 @@ +#!/bin/bash + +THIS_DIR=$( cd -- "$( dirname -- "${0}" )" &> /dev/null && pwd ) +P4C_DIR=$(readlink -f ${THIS_DIR}/..) + +# Get distribution info +DISTRO=$(lsb_release -i | awk -F: '{print $2}' | xargs) +VERSION=$(lsb_release -r | awk -F: '{print $2}' | xargs) + +# Define supported distributions and versions +SUPPORTED_DISTROS=("Ubuntu" "Pop") +SUPPORTED_UBUNTU_VERSIONS=("20.04" "22.04" "24.04") + +# Function to check if an element exists in an array +contains() { + local element + for element in "${@:2}"; do + if [[ "$element" == "$1" ]]; then + return 0 + fi + done + return 1 +} + +# Check distribution and version +if contains "$DISTRO" "${SUPPORTED_DISTROS[@]}"; then + if [[ "$DISTRO" == "Ubuntu" ]]; then + if contains "$VERSION" "${SUPPORTED_UBUNTU_VERSIONS[@]}"; then + echo "Supported distribution: $DISTRO $VERSION" + exit 0 + else + echo "Error: Unsupported Ubuntu version: $VERSION" + exit 1 + fi + else + # Assume all versions of Pop!_OS and Linux Mint are supported + echo "Supported distribution: $DISTRO $VERSION" + fi +else + echo "Error: Unsupported distribution: $DISTRO" + exit 1 +fi + +# Whether to install dependencies required to run PTF-ebpf tests +: "${INSTALL_PTF_EBPF_DEPENDENCIES:=OFF}" +# The build generator to use. Defaults to Make. +: "${BUILD_GENERATOR:="ninja"}" +# BMv2 is enable by default. +: "${ENABLE_BMV2:=ON}" +# eBPF is enabled by default. +: "${ENABLE_EBPF:=ON}" +# This is the list of back ends that can be enabled. +# Back ends can be enabled from the command line with "ENABLE_[backend]=TRUE/FALSE" +ENABLE_BACKENDS=("TOFINO" "BMV2" "EBPF" "UBPF" "DPDK" + "P4TC" "P4FMT" "P4TEST" "P4C_GRAPHS" + "TEST_TOOLS" +) +function build_cmake_enabled_backend_string() { + CMAKE_ENABLE_BACKENDS="" + for backend in "${ENABLE_BACKENDS[@]}"; + do + enable_var=ENABLE_${backend} + if [ -n "${!enable_var}" ]; then + echo "${enable_var}=${!enable_var} is set." + CMAKE_ENABLE_BACKENDS+="-D${enable_var}=${!enable_var} " + fi + done +} + + +. /etc/lsb-release + +# In Docker builds, sudo is not available. So make it a noop. +if [ "$IN_DOCKER" == "TRUE" ]; then + echo "Executing within docker container." + function sudo() { command "$@"; } +fi + + +# ! ------ BEGIN CORE ----------------------------------------------- +P4C_DEPS="bison \ + flex \ + cmake \ + build-essential \ + ccache \ + ninja-build \ + g++ \ + git \ + lld \ + libboost-graph-dev \ + libboost-iostreams-dev \ + libfl-dev \ + python3 \ + python3-pip \ + python3-setuptools" + +sudo apt-get update +sudo apt-get install -y --no-install-recommends ${P4C_DEPS} + +sudo apt-get install -y python3-poetry python3-venv +poetry install +poetry shell + +if [ "${BUILD_GENERATOR,,}" == "ninja" ] && [ ! $(command -v ninja) ] +then + echo "Selected ninja as build generator, but ninja could not be found." + exit 1 +fi + +# ! ------ END CORE ----------------------------------------------- + +ccache --set-config max_size=1G + +# ! ------ BEGIN BMV2 ----------------------------------------------- +function build_bmv2() { + P4C_RUNTIME_DEPS_BOOST="libboost-graph1.7* libboost-iostreams1.7*" + + P4C_RUNTIME_DEPS="cpp \ + ${P4C_RUNTIME_DEPS_BOOST} \ + libgc1* \ + libgmp-dev \ + libnanomsg-dev" + + sudo apt-get install -y wget ca-certificates + # Add the p4lang opensuse repository. + echo "deb http://download.opensuse.org/repositories/home:/p4lang/xUbuntu_${DISTRIB_RELEASE}/ /" | sudo tee /etc/apt/sources.list.d/home:p4lang.list + curl -fsSL https://download.opensuse.org/repositories/home:p4lang/xUbuntu_${DISTRIB_RELEASE}/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/home_p4lang.gpg > /dev/null + P4C_RUNTIME_DEPS+=" p4lang-bmv2" + + sudo apt-get update && sudo apt-get install -y --no-install-recommends ${P4C_RUNTIME_DEPS} + +} + +if [[ "${ENABLE_BMV2}" == "ON" ]] ; then + build_bmv2 +fi +# ! ------ END BMV2 ----------------------------------------------- + +# ! ------ BEGIN EBPF ----------------------------------------------- +function build_ebpf() { + P4C_EBPF_DEPS="libpcap-dev \ + libelf-dev \ + zlib1g-dev \ + llvm \ + clang \ + tcpdump \ + iproute2 \ + iptables \ + net-tools" + + sudo apt-get install -y --no-install-recommends ${P4C_EBPF_DEPS} +} + +function install_ptf_ebpf_test_deps() ( + P4C_PTF_PACKAGES="gcc-multilib \ + python3-six \ + libgmp-dev \ + libjansson-dev" + sudo apt-get install -y --no-install-recommends ${P4C_PTF_PACKAGES} + + git clone --depth 1 --recursive --branch v0.3.1 https://github.com/NIKSS-vSwitch/nikss /tmp/nikss + pushd /tmp/nikss + ./build_libbpf.sh + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Release -G "${BUILD_GENERATOR}" .. + cmake --build . -- -j $(nproc) + sudo cmake --install . + + # install bpftool + git clone --recurse-submodules --branch v7.3.0 https://github.com/libbpf/bpftool.git /tmp/bpftool + cd /tmp/bpftool/src + make "-j$(nproc)" + sudo make install + popd +) + +if [[ "${ENABLE_EBPF}" == "ON" ]] ; then + build_ebpf + if [[ "${INSTALL_PTF_EBPF_DEPENDENCIES}" == "ON" ]] ; then + install_ptf_ebpf_test_deps + fi +fi +# ! ------ END EBPF ----------------------------------------------- + +# ! ------ BEGIN DPDK ----------------------------------------------- +function build_dpdk() { + # Replace existing Protobuf with one that works. + # TODO: Debug protobuf mismatch. + sudo -E pip3 uninstall -y protobuf + sudo pip3 install protobuf==3.20.3 netaddr==0.9.0 +} + +if [[ "${ENABLE_DPDK}" == "ON" ]]; then + build_dpdk +fi +# ! ------ END DPDK ----------------------------------------------- + +# ! ------ BEGIN TOFINO -------------------------------------------- + +function build_tofino() { + P4C_TOFINO_PACKAGES="rapidjson-dev" + sudo apt-get install -y --no-install-recommends ${P4C_TOFINO_PACKAGES} + sudo pip3 install jsl==0.2.4 pyinstaller==6.11.0 +} + +if [[ "${ENABLE_TOFINO}" == "ON" ]]; then + echo "Installing Tofino dependencies" + build_tofino +fi +# ! ------ END TOFINO ---------------------------------------------- + +echo "Test image ready" + diff --git a/tools/install_fedora_deps.sh b/tools/install_fedora_deps.sh index 0788268af09..f079456c933 100755 --- a/tools/install_fedora_deps.sh +++ b/tools/install_fedora_deps.sh @@ -51,6 +51,8 @@ sudo dnf install -y -q \ procps-ng \ python3 \ python3-pip \ + python3-virtualenv \ + curl \ python3-thrift \ readline-devel \ tcpdump \ @@ -59,8 +61,9 @@ sudo dnf install -y -q \ zlib-devel \ ninja-build -pip3 install --upgrade pip -pip3 install -r ${P4C_DIR}/requirements.txt +# Set up poetry. +curl -sSL https://install.python-poetry.org | python3 - +poetry install MAKEFLAGS="-j$(nproc)" export MAKEFLAGS diff --git a/tools/install_mac_deps.sh b/tools/install_mac_deps.sh index 09a93ff31e5..87d55817a9d 100755 --- a/tools/install_mac_deps.sh +++ b/tools/install_mac_deps.sh @@ -41,7 +41,7 @@ brew update BOOST_LIB="boost@1.85" REQUIRED_PACKAGES=( autoconf automake ccache cmake libtool - openssl coreutils bison grep ninja + openssl coreutils bison grep ninja virtualenv curl ${BOOST_LIB} ) for package in "${REQUIRED_PACKAGES[@]}"; do @@ -60,8 +60,9 @@ fi if ! grep -q "$HOMEBREW_PREFIX/opt/grep/libexec/gnubin" ~/.bash_profile; then echo 'export PATH="$HOMEBREW_PREFIX/opt/grep/libexec/gnubin:$PATH"' >> ~/.bash_profile fi +echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bash_profile source ~/.bash_profile -# Install required pip packages -# TODO: Should we use --break-system-packages or should we set up a venv? -pip3 install --user --break-system-packages -r requirements.txt +# Set up poetry. +curl -sSL https://install.python-poetry.org | python3 - +poetry install From 1a6a9b61390ce05c72afa19ba8ffda9d8752bfb6 Mon Sep 17 00:00:00 2001 From: fruffy Date: Thu, 19 Dec 2024 16:10:52 +0100 Subject: [PATCH 2/2] Use bpftool instead of iproute2 to load eBPF and XDP maps. Signed-off-by: fruffy --- backends/ebpf/CMakeLists.txt | 8 +- backends/ebpf/build_libbpf | 27 ++++--- backends/ebpf/ebpfProgram.cpp | 2 +- backends/ebpf/run-ebpf-test.py | 4 +- backends/ebpf/runtime/.gitignore | 1 + backends/ebpf/runtime/contrib/.gitignore | 1 + backends/ebpf/runtime/ebpf_kernel.h | 55 ++++++------- backends/ebpf/runtime/kernel.mk | 2 +- backends/ebpf/targets/ebpfenv.py | 6 +- backends/ebpf/targets/kernel_target.py | 77 +++++++++++++++---- cmake/Linters.cmake | 2 +- .../extern_modules/extern-conntrack-ebpf.c | 2 +- 12 files changed, 119 insertions(+), 68 deletions(-) create mode 100644 backends/ebpf/runtime/.gitignore diff --git a/backends/ebpf/CMakeLists.txt b/backends/ebpf/CMakeLists.txt index 3d26c338707..4d48a3c4c6e 100644 --- a/backends/ebpf/CMakeLists.txt +++ b/backends/ebpf/CMakeLists.txt @@ -21,9 +21,9 @@ if(NOT APPLE) set(FETCHCONTENT_QUIET OFF) fetchcontent_declare( bpfrepo - URL https://github.com/libbpf/libbpf/archive/refs/tags/v1.4.1.tar.gz - URL_HASH SHA256=cc01a3a05d25e5978c20be7656f14eb8b6fcb120bb1c7e8041e497814fc273cb - SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/runtime/contrib/libbpf + URL https://github.com/libbpf/bpftool/releases/download/v7.5.0/bpftool-libbpf-v7.5.0-sources.tar.gz + # URL_HASH SHA256=cc01a3a05d25e5978c20be7656f14eb8b6fcb120bb1c7e8041e497814fc273cb + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/runtime/contrib/bpftool USES_TERMINAL_DOWNLOAD TRUE GIT_PROGRESS TRUE DOWNLOAD_EXTRACT_TIMESTAMP TRUE @@ -31,7 +31,7 @@ if(NOT APPLE) fetchcontent_makeavailable(bpfrepo) set(FETCHCONTENT_QUIET ${FETCHCONTENT_QUIET_PREV}) # Check if we have already built the libbpf library. - find_library(LIBBPF NAMES bpf HINTS "${CMAKE_CURRENT_SOURCE_DIR}/runtime/usr/lib64/") + find_library(LIBBPF NAMES bpf HINTS "${CMAKE_CURRENT_SOURCE_DIR}/runtime/install/libbpf/") if (NOT LIBBPF) message("Building libbpf...") execute_process( diff --git a/backends/ebpf/build_libbpf b/backends/ebpf/build_libbpf index 8ff46ad5987..f3541770338 100755 --- a/backends/ebpf/build_libbpf +++ b/backends/ebpf/build_libbpf @@ -10,7 +10,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -""" This programs builds a libbpf static library and places it in the runtime +""" This programs builds a bpftool binary and the libbpf static library and places it in the runtime folder. The library and its headers are required by the kernel target. """ @@ -31,21 +31,24 @@ def main() -> int: level=logging.WARN, filemode="w", ) - libbpf_dir = FILE_DIR.joinpath("runtime/contrib/libbpf") - libbpf_src_dir = libbpf_dir.joinpath("src") - libbpf_build_dir = libbpf_src_dir.joinpath("build") - libbpf_target_dir = FILE_DIR.joinpath("runtime") - # Create the libbpf build directory - testutils.check_and_create_dir(libbpf_build_dir) - # Build libbpf - mk_cmd = f"make -C {libbpf_src_dir} install " + bpftool_dir = FILE_DIR.joinpath("runtime/contrib/bpftool") + bpftool_src_dir = bpftool_dir.joinpath("src") + bpftool_build_dir = bpftool_src_dir.joinpath("build") + bpftool_target_dir = FILE_DIR.joinpath("runtime/install/") + bpftool_target_dir.mkdir(parents=True, exist_ok=True) + # Create the bpftool build directory + testutils.check_and_create_dir(bpftool_build_dir) + # Build bpftool + mk_cmd = f"make -C {bpftool_src_dir} install " + mk_cmd += f"OUTPUT={bpftool_target_dir}/ " mk_cmd += "BUILD_STATIC_ONLY=y " - mk_cmd += f"OBJDIR={libbpf_build_dir} " - mk_cmd += f"DESTDIR={libbpf_target_dir} " + mk_cmd += f"OBJDIR={bpftool_build_dir} " + mk_cmd += f"DESTDIR={bpftool_target_dir} " mk_cmd += "EXTRA_CFLAGS=-fPIE" + print(mk_cmd) result = testutils.exec_process(args=mk_cmd) if result.returncode != testutils.SUCCESS: - testutils.log.error("Could not build libbpf") + testutils.log.error("Could not build bpftool") return result.returncode diff --git a/backends/ebpf/ebpfProgram.cpp b/backends/ebpf/ebpfProgram.cpp index e57a6c55a14..c8381e3d2d9 100644 --- a/backends/ebpf/ebpfProgram.cpp +++ b/backends/ebpf/ebpfProgram.cpp @@ -102,7 +102,7 @@ void EBPFProgram::emitC(CodeBuilder *builder, const std::filesystem::path &heade if (model.arch == ModelArchitecture::XdpSwitch) builder->target->emitCodeSection(builder, "xdp"_cs); else - builder->target->emitCodeSection(builder, "prog"_cs); + builder->target->emitCodeSection(builder, "tc"_cs); builder->emitIndent(); builder->target->emitMain(builder, functionName, model.CPacketName.toString()); builder->blockStart(); diff --git a/backends/ebpf/run-ebpf-test.py b/backends/ebpf/run-ebpf-test.py index 6757042a4bd..f69b9aaf851 100755 --- a/backends/ebpf/run-ebpf-test.py +++ b/backends/ebpf/run-ebpf-test.py @@ -123,9 +123,9 @@ def __init__(self): self.replace = False # Replace previous outputs. self.target = "test" # The name of the target compiler. # Actual location of the test framework. - self.testdir = str(FILE_DIR) + self.testdir = FILE_DIR # The location of the eBPF runtime, some targets may overwrite this. - self.runtimedir = str(FILE_DIR.joinpath("runtime")) + self.runtimedir = FILE_DIR.joinpath("runtime") self.extern = "" # Path to C file with extern definition. diff --git a/backends/ebpf/runtime/.gitignore b/backends/ebpf/runtime/.gitignore new file mode 100644 index 00000000000..7c32f559819 --- /dev/null +++ b/backends/ebpf/runtime/.gitignore @@ -0,0 +1 @@ +install diff --git a/backends/ebpf/runtime/contrib/.gitignore b/backends/ebpf/runtime/contrib/.gitignore index cc3ea8dd440..d30b12ae8bf 100644 --- a/backends/ebpf/runtime/contrib/.gitignore +++ b/backends/ebpf/runtime/contrib/.gitignore @@ -1 +1,2 @@ libbpf +bpftool diff --git a/backends/ebpf/runtime/ebpf_kernel.h b/backends/ebpf/runtime/ebpf_kernel.h index 6813c0affca..4012795483a 100644 --- a/backends/ebpf/runtime/ebpf_kernel.h +++ b/backends/ebpf/runtime/ebpf_kernel.h @@ -23,7 +23,7 @@ limitations under the License. #include "ebpf_common.h" -#include // definitions for bpf_ntohs etc... +#include "install/libbpf/include/bpf/bpf_endian.h" // definitions for bpf_ntohs etc... #undef htonl #undef htons @@ -47,7 +47,7 @@ limitations under the License. #ifdef CONTROL_PLANE // BEGIN EBPF USER SPACE DEFINITIONS -#include // bpf_obj_get/pin, bpf_map_update_elem +#include "install/libbpf/include/bpf/bpf.h" // bpf_obj_get/pin, bpf_map_update_elem #define BPF_USER_MAP_UPDATE_ELEM(index, key, value, flags)\ bpf_map_update_elem(index, key, value, flags) @@ -56,30 +56,18 @@ limitations under the License. #else // BEGIN EBPF KERNEL DEFINITIONS -#include // TC_ACT_OK, TC_ACT_SHOT -#include "linux/bpf.h" // types, and general bpf definitions +// These files are provided by the system, not libbpf. +#include "contrib/bpftool/include/uapi/linux/bpf.h" // BPF_ANY, +#include "contrib/bpftool/include/uapi/linux/pkt_cls.h" // TC_ACT_OK, TC_ACT_SHOT // This file contains the definitions of all the kernel bpf essentials -#include - -/// A helper structure used by an eBPF C program -/// to describe map attributes for the elf_bpf loader -/// FIXME: We only need this because we are loading with iproute2 -struct bpf_elf_map { - __u32 type; - __u32 size_key; - __u32 size_value; - __u32 max_elem; - __u32 flags; - __u32 id; - __u32 pinning; - __u32 inner_id; - __u32 inner_idx; -}; +#include "install/libbpf/include/bpf/bpf_helpers.h" +#include "install/libbpf/include/bpf/btf.h" /// Simple descriptor which replaces the kernel sk_buff structure. #define SK_BUFF struct __sk_buff /// From iproute2, annotate table with BTF which allows to read types at runtime. +/// TODO: Do we need this with Ubuntu 24.04? #define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val) \ struct ____btf_map_##name { \ type_key key; \ @@ -90,7 +78,22 @@ struct bpf_elf_map { ____btf_map_##name = {}; #define REGISTER_START() -#ifndef BTF +/// TODO: Do we need this with Ubuntu 24.04? +#if !defined(BTF) && (defined(__clang__) && (__clang_major__ < 12)) +/// A helper structure used by an eBPF C program +/// to describe map attributes for the elf_bpf loader +/// FIXME: We only need this because we are loading with iproute2. Remove with Ubuntu 24.04 +struct bpf_elf_map { + __u32 type; + __u32 size_key; + __u32 size_value; + __u32 max_elem; + __u32 flags; + __u32 id; + __u32 pinning; + __u32 inner_id; + __u32 inner_idx; +}; /// Note: pinning exports the table name globally, do not remove. #define REGISTER_TABLE(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES) \ struct bpf_elf_map SEC("maps") NAME = { \ @@ -139,7 +142,7 @@ struct { \ VALUE_TYPE *value; \ __uint(max_entries, MAX_ENTRIES); \ __uint(pinning, LIBBPF_PIN_BY_NAME); \ -} NAME SEC(".maps"); +} NAME SEC(MAPS_ELF_SEC); #define REGISTER_TABLE_FLAGS(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, FLAGS) \ struct { \ __uint(type, TYPE); \ @@ -148,14 +151,14 @@ struct { \ __uint(max_entries, MAX_ENTRIES); \ __uint(pinning, LIBBPF_PIN_BY_NAME); \ __uint(map_flags, FLAGS); \ -} NAME SEC(".maps"); +} NAME SEC(MAPS_ELF_SEC); #define REGISTER_TABLE_INNER(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, ID, INNER_IDX) \ struct NAME { \ __uint(type, TYPE); \ KEY_TYPE *key; \ VALUE_TYPE *value; \ __uint(max_entries, MAX_ENTRIES); \ -} NAME SEC(".maps"); +} NAME SEC(MAPS_ELF_SEC); #define REGISTER_TABLE_OUTER(NAME, TYPE, KEY_TYPE, VALUE_TYPE, MAX_ENTRIES, INNER_ID, INNER_NAME) \ struct { \ __uint(type, TYPE); \ @@ -164,7 +167,7 @@ struct { \ __uint(max_entries, MAX_ENTRIES); \ __uint(pinning, LIBBPF_PIN_BY_NAME); \ __array(values, struct INNER_NAME); \ -} NAME SEC(".maps"); +} NAME SEC(MAPS_ELF_SEC); #define REGISTER_TABLE_NO_KEY_TYPE(NAME, TYPE, KEY_SIZE, VALUE_TYPE, MAX_ENTRIES) \ struct { \ __uint(type, TYPE); \ @@ -172,7 +175,7 @@ struct { \ VALUE_TYPE *value; \ __uint(max_entries, MAX_ENTRIES); \ __uint(pinning, LIBBPF_PIN_BY_NAME); \ -} NAME SEC(".maps"); +} NAME SEC(MAPS_ELF_SEC); #endif #define REGISTER_END() diff --git a/backends/ebpf/runtime/kernel.mk b/backends/ebpf/runtime/kernel.mk index dd205709947..edfe554e725 100644 --- a/backends/ebpf/runtime/kernel.mk +++ b/backends/ebpf/runtime/kernel.mk @@ -3,7 +3,7 @@ ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) # Argument for the CLANG compiler LLC ?= llc CLANG ?= clang -override INCLUDES+= -I$(ROOT_DIR) -I$(ROOT_DIR)usr/include/ -I$(ROOT_DIR)contrib/libbpf/include/uapi/ +override INCLUDES+= -I$(ROOT_DIR) override LIBS+= # Optimization flags to save space override CFLAGS+= -O2 -g -c -D__KERNEL__ -D__ASM_SYSREG_H \ diff --git a/backends/ebpf/targets/ebpfenv.py b/backends/ebpf/targets/ebpfenv.py index 69b04f9026e..21112e43e92 100644 --- a/backends/ebpf/targets/ebpfenv.py +++ b/backends/ebpf/targets/ebpfenv.py @@ -96,7 +96,7 @@ def ns_proc_append(self, proc: subprocess.Popen, cmd: str) -> int: def ns_proc_close(self, proc: subprocess.Popen, **extra_args: Any) -> int: """Close and actually run the process in the namespace. Returns the exit code.""" - testutils.log.info("Executing command: %s", proc) + testutils.log.info("Executing command: %s", proc.args) result = testutils.run_process(proc, timeout=testutils.TIMEOUT, **extra_args) if result.returncode != testutils.SUCCESS: testutils.log.error( @@ -109,7 +109,7 @@ def _configure_bridge(self, br_name: str) -> int: avoid ICMPv6 spam.""" # We do not care about failures here self.ns_exec(f"ip link set dev {br_name} up") - self.ns_exec(f"ip link set dev {br_name} mtu 9000") + self.ns_exec(f"ip link set dev {br_name} mtu 1500") # Prevent the broadcasting of ipv6 link discovery messages self.ns_exec("sysctl -w net.ipv6.conf.all.disable_ipv6=1") self.ns_exec("sysctl -w net.ipv6.conf.default.disable_ipv6=1") @@ -130,7 +130,7 @@ def _configure_bridge_port(self, port_name: str) -> int: result = self.ns_exec(cmd) if result != testutils.SUCCESS: return result - cmd = f"ip link set dev {port_name} mtu 9000" + cmd = f"ip link set dev {port_name} mtu 1500" return self.ns_exec(cmd) def attach_interfaces(self, num_ifaces: int) -> int: diff --git a/backends/ebpf/targets/kernel_target.py b/backends/ebpf/targets/kernel_target.py index 571c0447bda..70f13c29e2d 100644 --- a/backends/ebpf/targets/kernel_target.py +++ b/backends/ebpf/targets/kernel_target.py @@ -20,6 +20,7 @@ import time from glob import glob from pathlib import Path +from typing import Optional from .ebpfenv import Bridge from .target import EBPFTarget @@ -33,10 +34,15 @@ class Target(EBPFTarget): - EBPF_MAP_PATH = "/sys/fs/bpf/tc/globals" + EBPF_PATH = Path("/sys/fs/bpf") def __init__(self, tmpdir, options, template): EBPFTarget.__init__(self, tmpdir, options, template) + self.bpftool = self.runtimedir.joinpath("install/bpftool") + if self.options.target == "xdp": + self.ebpf_map_path = self.EBPF_PATH.joinpath(f"xdp/globals") + else: + self.ebpf_map_path = self.EBPF_PATH.joinpath(f"tc/globals") def compile_dataplane(self): # Use clang to compile the generated C code to a LLVM IR @@ -68,10 +74,7 @@ def _create_runtime(self): args += "CFLAGS+=-DCONTROL_PLANE " # add the folder local to the P4 file to the list of includes args += f"INCLUDES+=-I{os.path.dirname(self.options.p4filename)} " - # some kernel specific includes for libbpf - args += f"INCLUDES+=-I{self.runtimedir}/usr/include " - args += f"INCLUDES+=-I{self.runtimedir}/contrib/libbpf/include/uapi " - args += f"LIBS+={self.runtimedir}/usr/lib64/libbpf.a " + args += f"LIBS+={self.runtimedir}/install/libbpf/libbpf.a " args += "LIBS+=-lz " args += "LIBS+=-lelf " result = testutils.exec_process(args) @@ -79,19 +82,25 @@ def _create_runtime(self): testutils.log.error("Failed to build the filter") return result.returncode - def _create_bridge(self): + def _create_bridge(self) -> Optional[Bridge]: # The namespace is the id of the process namespace = str(os.getpid()) # Number of input files direction = "in" num_files = len(glob(self.filename("*", direction))) # Create the namespace and the bridge with all its ports - br = Bridge(namespace) - result = br.create_virtual_env(num_files) + bridge = Bridge(namespace) + result = bridge.create_virtual_env(num_files) if result != testutils.SUCCESS: - br.ns_del() + bridge.ns_del() return None - return br + if self.options.target != "xdp": + # Add the qdisc. MUST be clsact layer. + for port_name in bridge.edge_ports: + result = bridge.ns_exec(f"tc qdisc add dev {port_name} clsact") + if result != testutils.SUCCESS: + return None + return bridge def _get_run_cmd(self): direction = "in" @@ -108,7 +117,7 @@ def _get_run_cmd(self): cmd += "-d" return cmd - def _kill_processes(self, procs): + def _kill_processes(self, procs) -> None: for proc in procs: # kill process, 15 is SIGTERM os.kill(proc.pid, 15) @@ -127,7 +136,7 @@ def _load_filter(self, bridge, proc, port_name): bridge.ns_exec(f"tc qdisc add dev {port_name} clsact") cmd = ( f"tc filter add dev {port_name} egress" - f" bpf da obj {self.template}.o section prog verbose" + f" bpf da obj {self.template}.o section tc verbose " ) return bridge.ns_proc_write(proc, cmd) @@ -145,6 +154,44 @@ def _attach_filters(self, bridge, proc): return result return testutils.SUCCESS + # def _attach_filters(self, bridge: Bridge, proc: subprocess.Popen) -> int: + # # Is this a XDP or TC (ebpf_filter) program? + # p_result = testutils.exec_process(f"objdump -hj xdp {self.template}.o").returncode + # is_xdp = p_result == testutils.SUCCESS + # # Load the specified eBPF object to "port_name" egress + # # As a side-effect, this may create maps in /sys/fs/bpf/ + # # Get the command to load eBPF code to all the attached ports + # result = bridge.ns_proc_write(proc, f"mount -t bpf none {self.EBPF_PATH}") + # if result != testutils.SUCCESS: + # return result + # result = bridge.ns_proc_append(proc, f"mkdir -p {self.ebpf_map_path}") + # if result != testutils.SUCCESS: + # return result + # load_type = "xdp" if is_xdp else "tc" + # cmd = f"{self.bpftool} prog load {self.template}.o {self.EBPF_PATH}/ebpf_filter pinmaps {self.ebpf_map_path} type {load_type}" + # result = bridge.ns_proc_append(proc, cmd) + # if result != testutils.SUCCESS: + # return result + + # attach_type = "xdp" if is_xdp else "tcx_egress" + # ports = bridge.br_ports if is_xdp else bridge.edge_ports + # if len(ports) > 0: + # for port_name in ports: + # result = bridge.ns_proc_append( + # proc, + # f"{self.bpftool} net attach {attach_type} pinned {self.EBPF_PATH}/ebpf_filter dev {port_name}", + # ) + # if result != testutils.SUCCESS: + # return result + # else: + # # No ports attached (no pcap files), load to bridge instead + # result = bridge.ns_proc_append( + # proc, + # f"{self.bpftool} net attach {attach_type} pinned {self.EBPF_PATH}/ebpf_filter dev {bridge.br_name}", + # ) + + # return result + def _run_tcpdump(self, bridge, filename, port): cmd = f"{bridge.get_ns_prefix()} tcpdump -w {filename} -i {port}" return subprocess.Popen(cmd.split()) @@ -166,14 +213,10 @@ def _run_in_namespace(self, bridge): return testutils.FAILURE dump_procs = self._init_tcpdump_listeners(bridge) result = self._attach_filters(bridge, proc) - if result != testutils.SUCCESS: - return result - # Check if eBPF maps have actually been created - result = bridge.ns_proc_write(proc, f"ls -1 {self.EBPF_MAP_PATH}") if result != testutils.SUCCESS: return result # Finally, append the actual runtime command to the process - result = bridge.ns_proc_append(proc, self._get_run_cmd()) + result = bridge.ns_proc_write(proc, self._get_run_cmd()) if result != testutils.SUCCESS: return result # Execute the command queue and close the process, retrieve result diff --git a/cmake/Linters.cmake b/cmake/Linters.cmake index ecab52b80e5..616f1e77b51 100644 --- a/cmake/Linters.cmake +++ b/cmake/Linters.cmake @@ -136,7 +136,7 @@ file( tools/*.py ) list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "backends/p4tools/submodules") -list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "backends/ebpf/runtime/contrib/libbpf") +list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "backends/ebpf/runtime/contrib") list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "backends/tofino/third_party") list(FILTER P4C_PYTHON_LINT_LIST EXCLUDE REGEX "tools/cpplint.py") diff --git a/testdata/extern_modules/extern-conntrack-ebpf.c b/testdata/extern_modules/extern-conntrack-ebpf.c index 2b90d0a3b7e..f5046fd6b85 100644 --- a/testdata/extern_modules/extern-conntrack-ebpf.c +++ b/testdata/extern_modules/extern-conntrack-ebpf.c @@ -29,7 +29,7 @@ struct connInfo { }; REGISTER_START() -REGISTER_TABLE(tcp_reg, BPF_MAP_TYPE_HASH, sizeof(u32), sizeof(struct connInfo), MAX_ENTRIES) +REGISTER_TABLE(tcp_reg, BPF_MAP_TYPE_HASH, u32, struct connInfo, MAX_ENTRIES) REGISTER_END() static inline u8 tcp_conntrack(struct Headers_t hdrs)