Implementation Notes¶
BPF Naming Conventions¶
The BPF-to-Rust-skeleton compilation uses the name of the .bpf.c file
as the module name and prefixes for module functions.
my_bpf_program.bpf.c will be converted into mod MyBpfProgram;
and MyBpfProgramSkelBuilder and other MyBpfProgram... prefixes.
Adding new hooks¶
When adding new LSM hooks, you have to be careful because the semantics of LSM hooks can change between kernel versions. Although changes are uncommon, they do happen. Make sure the test your code on all kernel versions when adding new hooks.
If a hook changes between kernel versions, then you need to define two versions of the hook and determine which one to use at compile time. While there are some advantages to compiling multiple versions and choosing the correct version at runtime. This approach ran into other difficulties. This means that currently SeaBee must be compiled for the specific kernel version it is going to run on. For more information on this decision, see issue #9
Logging¶
So you've written up a new BPF program, that's great!
Unfortunately, you've been using bpf_printk in your code.
While this is nice for quick testing of your prototype,
it isn't a great long term solution.
We want to have custom logs for your BPF program in a designated location.
However, setting that up takes a couple steps.
Don't worry though, it'll look great when we're done!
Logging A New BPF Hook¶
- Add a new entry to the
log_typeenum inbpf/include/logging_types.h. This allows translation between the C log structs and the generated Rust log structs. - Add a new struct in
bpf/include/logging_types.hwith the name*_logwhere*is the name of the BPF program. This struct describes any contextual info relevant to the hook. At a minimum, it should contain the info used to make the access control decision. - Create a logging function in
bpf/src/seabee_enforce/self_enforce_log.h. Follow the pattern of the other functions already present. Instantiate the struct from step 2 and send it to the ring buffer. Use thelog_typedefined in the first step to aid the C-to-Rust translation. - Replace
bpf_printkcalls with the new log function. Choose areasonand alevelfor each call. Thereasonexplains why the log is being printed. For example,LOG_REASON_DENY, suggests that some action was attempted and denied. Theleveldefines a relative level of importance of the log. This allows some customization of how many logs are printed. Typically only the most critical logs will be printed, but if a problem is being debugged, including less important logs may be helpful. In order to differentiate between different reasons and levels, code may need to be restructured. - Add the struct to the
get_log_structfunction inbpf/src/logging/mod.rs. Following the pattern of other logs, add a case to the match statement and include the newlog_typeenum value and log struct. TheToStringtrait must also be implemented to print the log. Follow the pattern of other structs in the file. Note: The name of the struct andlog_typeneed to match in Rust and C, otherwise it will fail to compile.
Logging for a new eBPF Skeleton¶
Note: SeaBee no longer uses multiple skeletons
- Setup logging in skeleton code (c code)
#include "logging.h"- create a global variable for log level:
u32 log_level; - add the ringbuf:
struct log_ringbuf log_ringbuf SEC(".maps");
- Setup the userspace code
- configure skeleton log level:
open_skel.bss_mut().log_level = ...; - configure skeleton ringbuf:
open_skel.maps().log_ringbuf().reuse_fd(<original log ringbuf>.as_fd())?;
- configure skeleton log level: