v0.2.5: split BPF datapath into fast_path + finalize via bpf_tail_call #135
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: QEMU verifier | |
| # SPEC.md §10.2: run the sudo-gated integration tests under two kernel | |
| # versions — 5.15 (EFG-parity) and 6.6 LTS (≥5.18 for | |
| # BPF_PROG_TEST_RUN_XDP_LIVE; the host CI job already covers whichever | |
| # kernel ubuntu-latest runners ship with, so this matrix targets | |
| # explicit EFG-parity + a modern LTS). | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: short | |
| # Pin versions so adding a matrix dimension doesn't drift toolchains. | |
| RUST_STABLE: "1.95.0" | |
| RUST_NIGHTLY: "nightly-2026-04-14" | |
| BPF_LINKER_VERSION: "0.10.3" | |
| jobs: | |
| qemu: | |
| name: qemu (kernel ${{ matrix.kernel_tag }}) | |
| runs-on: ubuntu-latest | |
| env: | |
| PACKETFRAME_BPF_REQUIRED: "1" | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # Canonical's mainline PPA hosts rebuilds of upstream releases. | |
| # We discover the actual .deb URL at runtime (build timestamps | |
| # vary per version) from the directory listing. | |
| kernel_tag: ["v5.15", "v6.6"] | |
| steps: | |
| - uses: actions/checkout@v6 | |
| # GitHub's `ubuntu-latest` runners ship with ~14 GB free after | |
| # the preinstalled toolchains (CodeQL, Android SDK, .NET, etc.). | |
| # Adding the Option F tokio/rtnetlink/futures dep graph pushes | |
| # target/ past that during debug-profile linking and we get | |
| # "ld: signal 7 (Bus error)" / "No space left on device". | |
| # Dropping the hosted-tool caches we don't use recovers ~20 GB, | |
| # which is plenty of headroom. Standard | |
| # jlumbroso/free-disk-space-style idiom, inlined to avoid | |
| # pulling a non-first-party action. | |
| - name: Free disk space on runner | |
| run: | | |
| set -eux | |
| sudo rm -rf /usr/share/dotnet | |
| sudo rm -rf /opt/ghc | |
| sudo rm -rf /usr/local/lib/android | |
| sudo rm -rf /opt/hostedtoolcache/CodeQL | |
| sudo rm -rf /usr/local/share/boost | |
| sudo docker system prune -af 2>/dev/null || true | |
| sudo apt-get clean | |
| df -h / | |
| - name: Install Rust stable + nightly (for BPF) | |
| run: | | |
| rustup install ${RUST_STABLE} --profile minimal | |
| rustup default ${RUST_STABLE} | |
| rustup install ${RUST_NIGHTLY} --profile minimal | |
| rustup component add --toolchain ${RUST_NIGHTLY} rust-src llvm-tools-preview | |
| - name: Cache cargo + BPF target | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/bin/ | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| target/ | |
| crates/modules/fast-path/bpf/target/ | |
| crates/modules/probe/bpf/target/ | |
| key: ${{ runner.os }}-cargo-qemu-${{ matrix.kernel_tag }}-${{ hashFiles('**/Cargo.lock', 'crates/modules/fast-path/bpf/Cargo.toml', 'crates/modules/probe/bpf/Cargo.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-qemu-${{ matrix.kernel_tag }}- | |
| ${{ runner.os }}-cargo-qemu- | |
| ${{ runner.os }}-cargo-check- | |
| - name: Install bpf-linker | |
| run: | | |
| if ! command -v bpf-linker >/dev/null 2>&1 || \ | |
| [ "$(bpf-linker --version 2>/dev/null | awk '{print $2}')" != "${BPF_LINKER_VERSION}" ]; then | |
| cargo install --locked --force bpf-linker --version ${BPF_LINKER_VERSION} | |
| fi | |
| - name: Install QEMU + virtme-ng | |
| run: | | |
| set -eux | |
| sudo apt-get update | |
| # virtme-ng is the maintained fork (classic virtme-run uses | |
| # -watchdog which QEMU 8.x dropped). Its `--run <deb>` accepts | |
| # a pre-built kernel package directly. | |
| sudo apt-get install -y qemu-system-x86 virtme-ng busybox-static | |
| virtme-ng --version || true | |
| - name: Fetch + install kernel + modules | |
| run: | | |
| set -eux | |
| BASE="https://kernel.ubuntu.com/mainline/${{ matrix.kernel_tag }}/amd64/" | |
| IMG_DEB=$(curl -fsSL "${BASE}" \ | |
| | grep -oE 'linux-image-unsigned-[0-9][^"]*generic_[^"]*_amd64\.deb' \ | |
| | sort -u | head -1) | |
| MOD_DEB=$(curl -fsSL "${BASE}" \ | |
| | grep -oE 'linux-modules-[0-9][^"]*generic_[^"]*_amd64\.deb' \ | |
| | sort -u | head -1) | |
| test -n "${IMG_DEB}" && test -n "${MOD_DEB}" | |
| curl -fsSL --retry 3 -o /tmp/kernel.deb "${BASE}${IMG_DEB}" | |
| curl -fsSL --retry 3 -o /tmp/modules.deb "${BASE}${MOD_DEB}" | |
| # Stage then cp (direct dpkg-deb -x to / clobbered /lib perms | |
| # on a previous iteration — see commit history). | |
| sudo rm -rf /tmp/kstage | |
| sudo mkdir -p /tmp/kstage | |
| sudo dpkg-deb -x /tmp/kernel.deb /tmp/kstage | |
| sudo dpkg-deb -x /tmp/modules.deb /tmp/kstage | |
| sudo cp -a /tmp/kstage/boot/. /boot/ | |
| sudo cp -a /tmp/kstage/lib/modules/. /lib/modules/ | |
| KVER=$(ls /tmp/kstage/lib/modules/ | grep -- '-generic$' | tail -1) | |
| test -n "${KVER}" | |
| sudo depmod -a "${KVER}" | |
| # Canonical ships vmlinuz as 0600 root; virtme-ng runs as the | |
| # non-root job user and can't read it otherwise. | |
| sudo chmod 0644 "/boot/vmlinuz-${KVER}" | |
| test -r "/boot/vmlinuz-${KVER}" | |
| test -d "/lib/modules/${KVER}" | |
| # Sanity: host toolchain still links. | |
| test -e /lib/x86_64-linux-gnu/libm.so.6 | |
| echo "KVER=${KVER}" >> "${GITHUB_ENV}" | |
| echo "KERNEL_IMG=/boot/vmlinuz-${KVER}" >> "${GITHUB_ENV}" | |
| - name: Build integration tests | |
| run: | | |
| # Build so the test binaries are ready to exec inside the VM. | |
| # `--no-run` produces the ELFs without executing them on the | |
| # host kernel. | |
| cargo test -p packetframe-fast-path --tests --no-run | |
| cargo test -p packetframe-probe --tests --no-run | |
| - name: Run integration tests in QEMU | |
| timeout-minutes: 10 | |
| run: | | |
| set -eux | |
| # Pass the installed vmlinuz path (not the .deb). virtme-ng | |
| # discovers matching modules at /lib/modules/$VER on the host | |
| # and packages them into its generated initramfs. | |
| # virtme-init starts the script with HOME=/ and `/` is RO | |
| # (only the listed overlay paths are writable). Point | |
| # HOME/CARGO_HOME/RUSTUP_HOME at the /home overlay so rustup | |
| # can write its state without going read-only. | |
| # --memory 4096 (up from 1024): virtme-ng sizes the in-VM | |
| # tmpfs against RAM, and the Option F dep graph (tokio + | |
| # rtnetlink + netlink-packet-route + futures) pushes | |
| # incremental-relink tmpfs usage past the old ~500 MB. | |
| # 4 GB RAM ⇒ ~2 GB writable tmpfs, comfortable headroom. | |
| virtme-ng \ | |
| --run "${KERNEL_IMG}" \ | |
| --pwd \ | |
| --memory 4096 \ | |
| --disable-kvm \ | |
| --verbose \ | |
| -- bash -c 'set -eux; export HOME=/home/runner; export CARGO_HOME="${HOME}/.cargo"; export RUSTUP_HOME="${HOME}/.rustup"; export PATH="${CARGO_HOME}/bin:${PATH}"; uname -a; which cargo; cargo test -p packetframe-fast-path --tests -- --ignored --nocapture; cargo test -p packetframe-probe --tests -- --ignored --nocapture' |