Inspect and Safely Manage BPF struct_ops with bpftool
You will learn to list and inspect the BPF struct_ops objects currently registered with the kernel, register an existing ELF object, optionally pin its links, and unregister the right object afterwards. The commands below follow the bpftool-struct_ops(8) manpage installed with linux-tools-common version 6.8.0-139.139. Allow about 15 minutes if the object is already built. Building a suitable BPF object is a separate job.
The route
Jump straight to the step you need, or tick off Done means at the end.
1. Check the local tool and its contract
Start with ordinary, read-only commands. The subcommand is part of bpftool, rather than a separate executable named bpftool-struct_ops.
$ command -v bpftool
$ bpftool version
$ bpftool struct_ops help
On the reference installation, the package version is 6.8.0-139.139. The actual binary may come from a kernel-specific linux-tools package, so a package version and a runnable binary are not the same check. If bpftool version warns that it cannot find the tool for the running kernel, stop and install the matching package through your normal system administration process. Do not work around that warning by copying a random binary into place.
2. See what is registered
Listing does not change kernel state and normally needs no elevated privileges. It prints the map ID first, then the map name and the kernel type.
$ bpftool struct_ops show
100: dctcp tcp_congestion_ops
105: cubic tcp_congestion_ops
The exact IDs and names vary by host. list is an alias for the same brief view. Save the output before making a change, especially if more than one object has a similar name.
$ bpftool struct_ops list
$ bpftool -j struct_ops show
The JSON form is useful for scripts. Add -p when you want human-readable JSON. An empty result can be perfectly valid: it means no struct_ops objects are currently visible to the command.
3. Inspect one object before changing it
Use the saved numeric ID or an exact map name. The dump command shows detail for one object, or for all objects when you omit the selector.
$ bpftool struct_ops dump id 105
$ bpftool struct_ops dump name cubic
Use the dump to confirm that the object you intend to remove belongs to the expected subsystem. Do not infer an ID from an old terminal capture: IDs can change as objects are registered and unregistered.
4. Register an existing struct_ops object
Registration changes kernel state and normally requires elevated privileges. It loads the BPF struct_ops found in the ELF sections named .struct_ops and .struct_ops.link. Replace the placeholder with a real object produced by your build.
$ sudo bpftool struct_ops register /path/to/bpf_struct_ops.o
Registered tcp_congestion_ops cubic id 110
A single object can contain more than one struct_ops definition. The command registers all of them. A verifier, BTF, permissions or subsystem error means registration did not complete; keep the diagnostic output and fix the object or its environment before retrying.
If the ELF contains entries in .struct_ops.link, pass a directory to pin the created links:
$ sudo install -d -m 0755 /run/bpf/struct_ops
$ sudo bpftool struct_ops register /path/to/bpf_struct_ops.o /run/bpf/struct_ops
$ sudo find /run/bpf/struct_ops -maxdepth 1 -type f -print
The links are pinned there using the corresponding map names. Choose a directory owned by the service that needs the pins, and record it in that service's configuration. The manpage specifies the directory argument, but it does not create a lifecycle policy for the pins.
5. Verify the new state
After registration, use a fresh listing rather than trusting a success message alone.
$ bpftool struct_ops show
$ bpftool struct_ops dump name cubic
$ bpftool -j struct_ops show
Match the returned name and kernel type to the object you meant to register. If you are automating this, parse JSON and treat a changed ID as expected. The ID is an identifier for the current kernel object, not a durable name.
6. Unregister with a deliberate recovery plan
Unregistering is service-disrupting when the struct_ops supplies live kernel behaviour. Check the name, ID and owning service first. This command requires elevated privileges on most systems and removes the registration from the kernel subsystem.
$ sudo bpftool struct_ops show
$ sudo bpftool struct_ops unregister id 105
Unregistered tcp_congestion_ops cubic id 105
You can use name instead when the name is unambiguous:
$ sudo bpftool struct_ops unregister name cubic
There is no rollback command in this interface. The practical undo is to register the original ELF object again, provided it is still available and the subsystem accepts it. Recheck the result:
$ bpftool struct_ops show
$ test -e /run/bpf/struct_ops/cubic && echo "pin remains" || echo "pin absent"
If you pinned links, account for their files separately. Unregistering an object and removing a pin are different lifecycle actions. Do not delete pins merely to make a directory look tidy while a consumer may still depend on them.
Common traps
- Confusing the manpage name with the command: run
bpftool struct_ops, notbpftool-struct_ops. - Using a stale ID: list again immediately before an unregister operation.
- Assuming
registerloads every BPF section: this command targets the.struct_opsand.struct_ops.linksections described by the manpage. - Expecting plain output to be stable for a script: request JSON with
-jand handle an empty object list. - Debugging too late: add
-dto print libbpf and verifier diagnostics when a load fails, but avoid treating verbose logs as proof that registration succeeded.
Done means
- You can identify each visible struct_ops by a fresh ID, name and kernel type.
- You inspected the intended object with
dumpbefore changing state. - Any registration used the intended ELF file and, if needed, an explicit pin directory.
- You verified the post-change listing and kept the original ELF available for recovery.