From 957bf146705a2203598b6a5076bfe5bd267f1023 Mon Sep 17 00:00:00 2001 From: Jordan Rome Date: Sun, 18 Jan 2026 05:35:22 -0800 Subject: [PATCH] build: add nix support for development and builds This is so we don't have to rely on manual installation of dependencies to develop/build bpfilter, which can be problematic for many reasons (more details on https://nixos.org/). This also allows development on other linux distros. --- .github/CODEOWNERS | 1 + .github/workflows/nix.yaml | 52 ++++++++++++++++++ .gitignore | 4 +- CMakeLists.txt | 7 ++- derivation.nix | 94 +++++++++++++++++++++++++++++++ doc/developers/build.rst | 7 ++- flake.lock | 27 +++++++++ flake.nix | 110 +++++++++++++++++++++++++++++++++++++ 8 files changed, 298 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/nix.yaml create mode 100644 derivation.nix create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1a889125..2b3b4de5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,2 @@ * @qdeslandes +*.nix @jordalgo diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml new file mode 100644 index 00000000..89d11507 --- /dev/null +++ b/.github/workflows/nix.yaml @@ -0,0 +1,52 @@ +name: Nix Build + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build_nix: + name: "Build: Nix (${{ matrix.runner }})" + strategy: + matrix: + runner: [ubuntu-latest, ubuntu-24.04-arm] + runs-on: ${{ matrix.runner }} + + steps: + - name: Checkout bpfilter + uses: actions/checkout@v4 + - uses: nixbuild/nix-quick-install-action@v34 + with: + nix_conf: | + keep-env-derivations = true + keep-outputs = true + - name: Restore and save Nix store + uses: nix-community/cache-nix-action@v7 + with: + # restore and save a cache using this key + primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }} + # if there's no cache hit, restore a cache by this prefix + restore-prefixes-first-match: nix-${{ runner.os }}- + # collect garbage until the Nix store size (in bytes) is at most this number + # before trying to save a new cache + # 1G = 1073741824 + gc-max-store-size-linux: 1G + # do purge caches + purge: true + # purge all versions of the cache + purge-prefixes: nix-${{ runner.os }}- + # created more than this number of seconds ago + purge-created: 0 + # or, last accessed more than this number of seconds ago + # relative to the start of the `Post Restore and save Nix store` phase + purge-last-accessed: 0 + # except any version with the key that is the same as the `primary-key` + purge-primary-key: never + - name: Build & check + run: | + nix fmt -- --check . + nix build diff --git a/.gitignore b/.gitignore index add0e53c..65334861 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Usual build folder /build/ +# Nix +result* + # IDE configuration folder .zed .vscode @@ -14,4 +17,3 @@ __pycache__ # Local Claude instructions CLAUDE.local.md - diff --git a/CMakeLists.txt b/CMakeLists.txt index 37ff6947..325ee6d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,8 +15,11 @@ project(bpfilter list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/tools/cmake") -include(GitVersion) -get_version_from_git() +option(NO_GIT_VERSION "Disable git-based version detection" 0) +if (NOT NO_GIT_VERSION) + include(GitVersion) + get_version_from_git() +endif () message(NOTICE "bpfilter version ${PROJECT_VERSION}${PROJECT_VERSION_SUFFIX}") include(GNUInstallDirs) diff --git a/derivation.nix b/derivation.nix new file mode 100644 index 00000000..c71fbc07 --- /dev/null +++ b/derivation.nix @@ -0,0 +1,94 @@ +{ + stdenv, + lib, + cmake, + ninja, + pkg-config, + clang, + bison, + flex, + libbpf, + libnl, + elfutils, + openssl, + testers, + zlib, + zstd, + pcre2, + xxd, + version ? "0.0.1", +}: + +let + fs = lib.fileset; + + # zerocallusedregs is invalid with -target bpf + hardeningDisable = [ "zerocallusedregs" ]; + +in +{ + inherit hardeningDisable; + + package = stdenv.mkDerivation (finalAttrs: { + pname = "bpfilter"; + inherit version; + + src = fs.toSource { + root = ./.; + fileset = fs.unions [ + ./src + ./CMakeLists.txt + ./tools/cmake + ]; + }; + + inherit hardeningDisable; + + nativeBuildInputs = [ + cmake + ninja + pkg-config + clang # for building codegen BPF progs + bison + flex + ]; + + buildInputs = [ + libbpf + libnl + elfutils + openssl + zlib + zstd + pcre2 + xxd + ]; + + cmakeFlags = [ + "-DNO_GIT_VERSION=1" + "-DDEFAULT_PROJECT_VERSION=${finalAttrs.version}" + "-DNO_DOCS=1" + "-DNO_TESTS=1" + "-DNO_CHECKS=1" + "-DNO_BENCHMARKS=1" + ]; + + # We do not run the unit tests because the nix build sandbox doesn't + # have access to /sys/kernel/btf/vmlinux. + doCheck = false; + + meta.pkgConfigModules = [ "bpfilter" ]; + + passthru = { + tests.pkg-config = testers.testMetaPkgConfig finalAttrs.finalPackage; + }; + + preFixup = '' + substituteInPlace $out/lib/systemd/system/bpfilter.service \ + --replace-fail /usr/sbin/bpfilter $out/bin/bpfilter + + # workaround for https://github.com/NixOS/nixpkgs/issues/144170 + substituteInPlace $out/lib/pkgconfig/bpfilter.pc --replace-fail ''${prefix}/ "" + ''; + }); +} diff --git a/doc/developers/build.rst b/doc/developers/build.rst index 5c7a0ce7..af694c7c 100644 --- a/doc/developers/build.rst +++ b/doc/developers/build.rst @@ -1,7 +1,7 @@ Build from sources ================== -This document describes the process to build ``bpfilter`` from sources. While ``bpfilter`` can be built on most systems, a recent (6.6+) Linux kernel is required with ``libbpf`` 1.2+ to run the ``bpfilter`` daemon. ``bpfilter`` officially supports Fedora 40+, CentOS Stream 9+, and Ubuntu 24.04+. +This document describes the process to build ``bpfilter`` from sources. While ``bpfilter`` can be built on most systems, a recent (6.6+) Linux kernel is required with ``libbpf`` 1.2+ to run the ``bpfilter`` daemon. ``bpfilter`` officially supports Fedora 40+, CentOS Stream 9+, and Ubuntu 24.04+. There is also a nix flake which supports all the make targets except for ``doc``. If you want to perform a full build of ``bpfilter`` (including all test tests, code check, benchmarks, and documentation), the following dependencies are required: @@ -83,6 +83,11 @@ If you want to perform a full build of ``bpfilter`` (including all test tests, c sed \ xxd + # Nix (this is experimental) + nix develop + # or + nix build + You can then use CMake to generate the build system: .. code-block:: shell diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..3fcda0b5 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1768564909, + "narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..bec1d819 --- /dev/null +++ b/flake.nix @@ -0,0 +1,110 @@ +{ + description = "bpfilter - eBPF-based packet filtering framework"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = + { self, nixpkgs }: + let + systems = [ + "x86_64-linux" + "aarch64-linux" + ]; + forAllSystems = nixpkgs.lib.genAttrs systems; + nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); + + version = "0.0.1"; + + in + { + packages = forAllSystems ( + system: + let + pkgs = nixpkgsFor.${system}; + bpfilterLib = pkgs.callPackage ./derivation.nix { inherit version; }; + in + { + default = bpfilterLib.package; + bpfilter = bpfilterLib.package; + } + ); + + formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style); + + devShells = forAllSystems ( + system: + let + pkgs = nixpkgsFor.${system}; + bpfilterLib = pkgs.callPackage ./derivation.nix { inherit version; }; + in + { + default = pkgs.mkShell { + name = "bpfilter-dev"; + + inherit (bpfilterLib) hardeningDisable; + + inputsFrom = [ bpfilterLib.package ]; + + packages = with pkgs; [ + gnumake + clang-tools # clang-tidy, clang-format + include-what-you-use + gcc + autoconf + automake + libtool + + # Git (for GitVersion.cmake and benchmarks) + git + libgit2 + + # BPF tools + bpftools + + # Networking tools (for e2e tests) + iproute2 + iputils + + # Testing + cmocka + gbenchmark + + # Utilities + gawk + jq + gnused + procps + lcov + + # Documentation + # TODO: this (`make doc`) is still broken but should be fixed when linuxdoc is added to nixpkgs + doxygen + python3 + python3Packages.sphinx + # python3Packages.breathe + # python3Packages.furo + # python3Packages.linuxdoc + # python3Packages.gitpython + # python3Packages.scapy + # python3Packages.python-dateutil + # python3Packages.setuptools + # glibcLocales + ]; + + shellHook = '' + # Set locale for sphinx-build + export LOCALE_ARCHIVE="${pkgs.glibcLocales}/lib/locale/locale-archive" + export LC_ALL=C.UTF-8 + + # Add libbpf headers to include path for clang-tidy + export CPATH="${pkgs.libbpf}/include''${CPATH:+:$CPATH}" + + echo "bpfilter development environment" + ''; + }; + } + ); + }; +}