Skip to content

Remove bpfilter daemon and consolidate every feature in libbpfilter#467

Open
qdeslandes wants to merge 6 commits intofacebook:mainfrom
qdeslandes:daemon-less
Open

Remove bpfilter daemon and consolidate every feature in libbpfilter#467
qdeslandes wants to merge 6 commits intofacebook:mainfrom
qdeslandes:daemon-less

Conversation

@qdeslandes
Copy link
Contributor

  • Eliminate the bpfilter daemon process; all filtering logic now lives in libbpfilter and is called directly by bfcli
  • Move the code generation engine (cgen/), BPF stubs (bpf/), context management (ctx.c), and ELF stub handling from src/bpfilter/ into src/libbpfilter/
  • Remove the IPC layer (request.c, response.c, io.c, ns.c) and the daemon entry point, systemd service, and socket
  • Rewrite bfcli to initialise the library context on startup, call libbpfilter APIs directly, and tear down on exit
  • Adapt all e2e tests to run without a daemon: helpers no longer start/stop a background process, and daemon-specific tests are replaced by equivalent persistence and namespace tests
  • Scrub every remaining "daemon" reference from comments, docs, and user-facing strings (external kernel headers excluded)

As it is now, this PR can be merged into main, but more polishing will be done before tagging a new release.

The cgen/, bpf/, ctx, and xlate modules were compiled directly into the
bpfilter binary. Move them into libbpfilter so the filtering logic lives
entirely in the shared library and the daemon is a thin entry point.
Replace the daemon IPC path in libbpfilter/cli.c with direct calls to
the cgen and ctx APIs, allowing bfcli to load, attach, and manage chains
without a running daemon.

Move ctx and elfstub headers to the public libbpfilter API, pull BTF
lifetime into bf_ctx_setup/teardown, remove namespace switching and
transient mode, and extend bfcli's stage-1 parser with global flags
(--verbose, --bpffs-path, --with-bpf-token, deprecated compat options).
Replace daemon e2e tests with namespace and persistence tests that
exercise bpffs-based chain discovery directly, without starting or
stopping the daemon process.

Remove the daemon lifecycle (start_bpfilter/stop_bpfilter) from the
test harness and propagate --bpffs-path through the ${BFCLI} variable
so all test invocations use the sandbox-local bpffs mount.

Skip bf_ctx_setup in bfcli when --dry-run is used so matcher tests
no longer require bpffs access. Fix clang-analyzer BitwiseShift
warning in --verbose option parsing.

Drop namespace unit tests and request namespace accessors that are no
longer part of the public API.
The daemon has been removed in favour of direct library calls. Update
comments, documentation, and user-facing strings that still referred to
"the daemon" so the terminology is consistent with the new architecture.

External kernel headers (src/external/) are left unchanged.
Lock bpfilter directory in bpffs to prevent concurrent access to the
chains and BPF objects.
`make test` runs all the tests, including fuzzing the bfcli parser for
60 seconds (`make fuzzing` target). This operation takes more time than
all the other tests combined which providing little value (fuzzing for a
few hours could be more interesting).

Disable fuzzing in the `make test` target.
@meta-cla meta-cla bot added the cla signed label Mar 10, 2026
Comment on lines +393 to +397
_bf_global_ctx = _ctx;

if (!bf_ctx_is_transient()) {
r = _bf_ctx_discover();
if (r) {
_bf_ctx_free(&_bf_global_ctx);
return bf_err_r(r, "failed to discover chains");
}
pindir_fd = bf_ctx_get_pindir_fd();
if (pindir_fd < 0)
return bf_err_r(pindir_fd, "failed to get pin directory FD");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: Dangling _bf_global_ctx on new error paths (must-fix)

Commit 5 adds two early-return paths (here and line 401) after _bf_global_ctx = _ctx (line 393) but before TAKE_PTR(_ctx) (line 411). When either return fires, the _free_bf_ctx_ cleanup on _ctx frees the context object, but _bf_global_ctx still points to the freed memory. Any subsequent access (e.g. a retry of bf_ctx_setup(), or bf_ctx_teardown()) is use-after-free.

The _bf_ctx_discover() error path (line 405) already handles this correctly with _bf_ctx_free(&_bf_global_ctx).

Possible fixes:

  1. Move _bf_global_ctx = _ctx to just before TAKE_PTR(_ctx) (after all fallible operations), or
  2. Add _bf_global_ctx = NULL before each early return on the new paths.

Comment on lines +27 to +28
static int copy_hookopts(struct bf_hookopts **dest,
const struct bf_hookopts *src)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: Naming convention violation (must-fix)

Static functions copy_hookopts (line 27) and copy_set (line 377) lack the required _bf_ prefix for internal functions in libbpfilter. Every other static function in the library follows this convention (e.g. _bf_ctx_free, _bf_ctx_gen_token, _bf_rule_references_empty_set).

Suggested change
static int copy_hookopts(struct bf_hookopts **dest,
const struct bf_hookopts *src)
static int _bf_copy_hookopts(struct bf_hookopts **dest,

}

int bf_chain_update(const struct bf_chain *chain)
static int copy_set(struct bf_set **dest, const struct bf_set *src)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: Naming convention violation (must-fix)

Same as copy_hookopts above -- should be _bf_copy_set.

Suggested change
static int copy_set(struct bf_set **dest, const struct bf_set *src)
static int _bf_copy_set(struct bf_set **dest, const struct bf_set *src)

Comment on lines +6 to +10
Environment variables
---------------------

``bfcli`` reads the following environment variables to configure the library before processing commands. These variables are ignored when ``--dry-run`` is used.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: "Environment variables" section is inaccurate (must-fix)

BF_BPFFS_PATH, BF_WITH_BPF_TOKEN, and BF_VERBOSE are not environment variables -- they are command-line options (--bpffs-path, --with-bpf-token, --verbose) parsed via argp in src/bfcli/opts.c. There are no getenv() calls in src/bfcli/ or src/libbpfilter/.

The e2e test harness does export BF_BPFFS_PATH=... but only uses it as a shell variable to construct the --bpffs-path CLI argument in e2e_test_util.sh.

This section should be renamed (e.g. "Configuration options") and rewritten to document the actual CLI flags (--bpffs-path PATH, --with-bpf-token, --verbose FLAG).

Additionally, BF_VERBOSE is described as a "comma-separated list", but the code in opts.c (lines 362-374) parses each --verbose invocation as a single enum value. Users must pass --verbose multiple times (e.g., --verbose debug --verbose bpf).

-------------

From here on, we assume **bpfilter** has been installed on your system. If you build it locally, you will need to substitute the ``bpfilter`` command with ``$BUILD_DIR/output/sbin/bpfilter``, same for ``bfcli``. The example below is meant to familiarize you with **bpfilter**, more in-depth information can be found throughout the documentation.
From here on, we assume **bpfilter** has been installed on your system. If you build it locally, you will need to substitute the ``bfcli`` command with ``$BUILD_DIR/output/bin/bfcli``. The example below is meant to familiarize you with **bpfilter**, more in-depth information can be found throughout the documentation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: Wrong binary path (must-fix)

$BUILD_DIR/output/bin/bfcli should be $BUILD_DIR/output/sbin/bfcli. CMAKE_RUNTIME_OUTPUT_DIRECTORY is set to ${CMAKE_BINARY_DIR}/output/sbin (root CMakeLists.txt line 59), and bfcli installs to CMAKE_INSTALL_SBINDIR (src/bfcli/CMakeLists.txt line 81). The README.md already uses the correct sbin path.

/// BPF token file descriptor
int token_fd;

int lock_fd;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: Missing field documentation (suggestion)

Adjacent fields (token_fd, with_bpf_token, bpffs_path, verbose) all have /// Doxygen comments. Consider adding one here for consistency, e.g.:

Suggested change
int lock_fd;
/// File descriptor for the exclusive bpffs lock
int lock_fd;


r = flock(pindir_fd, LOCK_EX | LOCK_NB);
if (r)
return bf_err_r(-errno, "failed to lock pin directory");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude: Unhelpful error message for contention case (suggestion)

When flock() fails with EWOULDBLOCK (another bfcli instance holds the lock), the message "failed to lock pin directory" doesn't indicate contention. Since this is now the primary coordination mechanism (replacing the daemon's single-instance guarantee), a more specific message would aid debugging, e.g.: "pin directory is locked, another bfcli instance may be running".

@github-actions
Copy link

Claude PR Review — #467

PR: Remove bpfilter daemon and consolidate every feature in libbpfilter
HEAD: ed7cc0640de258b54be0cb47b33fe3b1618b0c4d
Reviewed: 6 commits, ~10900 lines of diff


Summary

This PR eliminates the bpfilter daemon, moving all filtering logic into libbpfilter as a shared library. bfcli now calls libbpfilter directly. The IPC layer (request/response/io/ns) is removed, e2e tests are adapted, and all "daemon" references are scrubbed from code and docs. Commit 5 adds flock-based locking on the bpffs pin directory for concurrency control. Commit 6 excludes fuzzing from make test.


Findings

Must-fix (4)

# File Issue
1 src/libbpfilter/ctx.c:393-397 Dangling _bf_global_ctx on error paths. Commit 5 adds early returns after _bf_global_ctx = _ctx but before TAKE_PTR(_ctx). On failure, auto-cleanup frees _ctx while _bf_global_ctx still points to freed memory → use-after-free.
2 src/libbpfilter/cli.c:27,377 Static functions copy_hookopts and copy_set lack _bf_ prefix. Every other static function in libbpfilter follows the _bf_ naming convention.
3 doc/usage/bfcli.rst:6-17 "Environment variables" section is wrong. BF_BPFFS_PATH, BF_WITH_BPF_TOKEN, BF_VERBOSE are CLI options (--bpffs-path, --with-bpf-token, --verbose), not env vars. No getenv() calls exist. Also, --verbose takes one value per invocation, not a comma-separated list.
4 doc/usage/index.rst:31 Wrong binary path. $BUILD_DIR/output/bin/bfcli$BUILD_DIR/output/sbin/bfcli (per CMAKE_RUNTIME_OUTPUT_DIRECTORY and CMAKE_INSTALL_SBINDIR).

Suggestions (2)

# File Issue
5 src/libbpfilter/ctx.c:42 New lock_fd field lacks /// Doxygen comment; adjacent fields all have one.
6 src/libbpfilter/ctx.c:401 flock() error message is generic. For EWOULDBLOCK, a message like "pin directory is locked, another bfcli instance may be running" would aid debugging.

Commit messages (2)

# Commit Issue
7 5/6 (31553a8) Typo in subject: "bffps" → "bpffs". The body uses the correct spelling. (must-fix)
8 2/6 (af1b97b) Component prefix daemon: is misleading — 26/27 changed files are in src/libbpfilter/ or src/bfcli/, not the daemon. Consider lib,cli:. (suggestion)

Minor (3, not posted inline)

  • Commit 3/6: component prefix tests,cli omits tools (benchmark files also changed).
  • Commit 6/6: body grammar "which providing" → "while providing".
  • src/bfcli/opts.h:18: Doxygen line is 81 chars (1 over limit), though ReflowComments: false means clang-format won't flag it.

Overall Status: FAIL

4 must-fix issues block merge. The dangling _bf_global_ctx pointer (finding #1) is a correctness regression introduced by commit 5. The naming and documentation issues (findings #2-4, #7) require straightforward fixes.


Reviewed at ed7cc064 · 7 inline comments posted

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant