Prevent bf_map and bf_link serialization#420
Prevent bf_map and bf_link serialization#420qdeslandes wants to merge 12 commits intofacebook:mainfrom
bf_map and bf_link serialization#420Conversation
bf_program creates the bf_link object in bf_program_new(), for every new program. If the program is not attached, then program->link->hookopts is NULL, and that's how we know. This approach is not ideal as we might create a bf_link object we don't need. Additionally, it leaves bf_link in a temporary state as long as we don't attach the program to the kernel: the bf_link is in the bf_program object and serialized, but not materialized. This commit only create bf_link when the BPF link object is needed. Calling bf_link_new() will create the BPF link, and fail on error. Doing so, we know that any bf_link object refers to a program attached to the kernel.
bf_program creates the necessary maps objects by default (in bf_program_new()), then update them with the proper size and number of elements. This commit improves this behaviour by following bf_link's example: bf_map are not created until they are required by the program. Calling bf_map_new() will create the BPF map object, which makes the behaviour consistent with bf_link.
Create bf_handle to store references to all the BPF objects defined for a chain, instead of centralizing them in bf_program. The benefit is that bf_cgen can take the ownership of bf_handle and discard the bf_program object that becomes irrelevant once the bytecode is loaded.
Replace bf_program in bf_cgen with bf_handle. The bytecode generation context (bf_program) don't need to be stored anymore, as we can keep a reference to the BPF objects.
Refactor BTF data handling to always create BTF data for BPF maps. The main purpose is to define a decl tag defining the map's bpfilter type.
Use the BPF map FD to restore the handle's maps instead of the serialized context. Most of the information from bf_map can be collected using BPF_OBJ_GET_INFO_BY_FD, the internal map type is stored as a decl tag in the associated BTF data.
Similarly to bf_map, restore bf_handle.link from the link's FD (up to two links). The hook options can't be restored fully from the link's data for now, so we serialize handle->link->hookopts directly from bf_handle for now.
There was a problem hiding this comment.
Pull request overview
This pull request prevents serialization of bf_map and bf_link objects, instead relying on BPF object metadata from the kernel. This is a significant refactoring that introduces bf_handle to manage BPF object references (prog_fd, maps, link) separately from the bytecode generation context (bf_program).
Changes:
- Introduces
bf_handleabstraction to hold BPF object references that persist after program generation - Maps and links are now restored from pinned BPF objects using BTF metadata and kernel info queries rather than from serialized data
- BTF data is created for all maps (except ring buffers) to enable map type identification during restoration
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/bpfilter/cgen/handle.h | New header defining bf_handle structure and API |
| src/bpfilter/cgen/handle.c | Implementation of bf_handle with restoration from pinned objects |
| src/bpfilter/cgen/program.h | Refactored to remove map/link storage, add handle reference |
| src/bpfilter/cgen/program.c | Maps now created during load; program takes/releases handle |
| src/bpfilter/cgen/prog/map.h | Removed map lifecycle functions, added bf_map_new_from_fd |
| src/bpfilter/cgen/prog/map.c | BTF creation for maps, type detection from BTF, restoration from FD |
| src/bpfilter/cgen/prog/link.h | Link creation now includes attach, added bf_link_new_from_fd |
| src/bpfilter/cgen/prog/link.c | Link creation merged with attach, restoration from FD |
| src/bpfilter/cgen/cgen.h | Changed from bf_program to bf_handle reference |
| src/bpfilter/cgen/cgen.c | Updated to use bf_handle operations |
| src/bpfilter/xlate/cli.c | Updated to access link/maps through handle |
| src/libbpfilter/bpf.h | Added bf_bpf_obj_get_info, map/BTF fd_by_id functions |
| src/libbpfilter/bpf.c | Implemented new BPF syscall wrappers |
| src/libbpfilter/include/bpfilter/btf.h | Changed BTF type IDs from uint32_t to int |
| src/libbpfilter/include/bpfilter/bpf_types.h | Added BPF command constants for new operations |
| tests/e2e/daemon/restore_with_sets.sh | New test for set restoration |
| tests/e2e/daemon/restore_attached.sh | Enhanced test verification |
| tests/e2e/matchers/set.sh | Updated to query maps via program instead of pinned paths |
| tests/e2e/e2e_test_util.sh | Added --skip-cleanup flag for stop_bpfilter |
| tests/unit/libbpfilter/bpf.c | Updated bf_bpf_btf_load signature with length parameter |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (type != BF_MAP_TYPE_LOG && key_size == 0) | ||
| return bf_err_r(-EINVAL, "bf_map %s: key size can't be 0", name); | ||
| if (type == BF_MAP_TYPE_LOG && key_size != 0) | ||
| return bf_err_r(-EINVAL, "bf_map %s: key size must be", name); |
There was a problem hiding this comment.
The error message is incomplete. It should read "bf_map %s: key size must be 0" but it's missing "0" at the end.
| return bf_err_r(-EINVAL, "bf_map %s: key size must be", name); | |
| return bf_err_r(-EINVAL, "bf_map %s: key size must be 0", name); |
| if (handle->lmap) { | ||
| DUMP(prefix, "lmap: struct bf_map *"); | ||
| bf_dump_prefix_push(prefix); | ||
| bf_map_dump(handle->lmap, bf_dump_prefix_last(prefix)); | ||
| bf_dump_prefix_pop(prefix); | ||
| } else { | ||
| DUMP(prefix, "lmap: struct bf_map * (NULL)"); | ||
| } | ||
|
|
There was a problem hiding this comment.
The lmap is dumped twice (lines 232-239 and 241-248). This is duplicated code and should be removed. Only one of these blocks should remain.
| if (handle->lmap) { | |
| DUMP(prefix, "lmap: struct bf_map *"); | |
| bf_dump_prefix_push(prefix); | |
| bf_map_dump(handle->lmap, bf_dump_prefix_last(prefix)); | |
| bf_dump_prefix_pop(prefix); | |
| } else { | |
| DUMP(prefix, "lmap: struct bf_map * (NULL)"); | |
| } |
| case BPF_LINK_TYPE_TCX: | ||
| if (info.tcx.attach_type == BF_BPF_TCX_INGRESS) | ||
| _link->hook = BF_HOOK_TC_INGRESS; | ||
| else if (info.tcx.attach_type == BF_BPF_TCX_ENGRESS) |
There was a problem hiding this comment.
Typo in the constant name: "ENGRESS" should be "EGRESS". This appears to be comparing against a constant defined elsewhere in the codebase.
| else if (info.tcx.attach_type == BF_BPF_TCX_ENGRESS) | |
| else if (info.tcx.attach_type == BF_BPF_TCX_EGRESS) |
| struct cgid_file_handle handle = {.handle_bytes = 128}; | ||
| int mount_id; | ||
|
|
||
| if (!((*hookopts)->used_opts & BF_FLAG(BF_HOOKOPTS_PRIORITIES))) |
There was a problem hiding this comment.
The condition check is incorrect. The code checks for BF_HOOKOPTS_PRIORITIES flag but then tries to access cgpath. It should check for BF_HOOKOPTS_CGPATH flag instead. The error message says "missing cgpath in hookopts" but the flag being checked is PRIORITIES.
| if (!((*hookopts)->used_opts & BF_FLAG(BF_HOOKOPTS_PRIORITIES))) | |
| if (!((*hookopts)->used_opts & BF_FLAG(BF_HOOKOPTS_CGPATH))) |
bf_maprepresent BPF map, andbf_linkBPF links. Both those objects are part of the runtime context of the daemon required for every chain, as such they are serialized on disk.Eventually, this creates 2 sources of truth for the chain: the BPF object and its metadata running on the system, the serialized version of the bpfilter runtime object.
This change disables the serialization of the
bf_mapandbf_linkobjects, and rely on the BPF objects metadata instead, reducing the size of the serialized context, as well as the potential discrepancies between the sources of truth.This change requires #419