[2/2] Add CGROUP_SOCK_ADDR Logging Infrastructure#491
[2/2] Add CGROUP_SOCK_ADDR Logging Infrastructure#491yaakov-stein wants to merge 13 commits intofacebook:mainfrom
CGROUP_SOCK_ADDR Logging Infrastructure#491Conversation
Claude review of PR #491 (3ca48eb)Suggestions
Nits
|
CGROUP_SOCK_ADDR Logging] Enable LoggingCGROUP_SOCK_ADDR Logging] Add CGROUP_SOCK_ADDR Logging Infrastructure
| } | ||
|
|
||
| if (log_opts & (1 << BF_LOG_OPT_INTERNET)) { | ||
| __builtin_memcpy(log->payload.sock_addr.saddr, ctx->sock_addr.saddr, |
There was a problem hiding this comment.
Claude: must-fix: For IPv4 hooks the prologue writes only 4 bytes into ctx->sock_addr.saddr and ctx->sock_addr.daddr, but here sizeof(ctx->sock_addr.saddr) is 16. The __builtin_memcpy unconditionally copies all 16 bytes, so for IPv4 the remaining 12 bytes per address are uninitialized BPF stack data copied into the ring buffer entry.
For CONNECT4/CONNECT6 hooks, has_saddr is false so ctx->sock_addr.saddr is never written at all — the entire 16 bytes are uninitialized. This is either a BPF verifier rejection (if the verifier tracks the uninitialized read) or uninitialized stack data submitted to userspace via bpf_ringbuf_submit.
Consider either: (a) zero-initializing the sock_addr region in the prologue before writing the partial address, or (b) passing the address size to the stub so it copies only the correct number of bytes.
There was a problem hiding this comment.
Currently, the packet logging does the same thing for headers that are not requested. This is not a verifier rejection in either case, but we do submit uninitialized stack data in both cases. As long as this doesn't become a verifier issue, I think it can be left as is given that both option add some amount of BPF instructions. Happy to make a change though if others think it makes sense.
CGROUP_SOCK_ADDR Logging] Add CGROUP_SOCK_ADDR Logging InfrastructureCGROUP_SOCK_ADDR Logging Infrastructure
Rename the packet logging ELF stub from `log` to `pkt_log` to establish naming symmetry with the upcoming `sock_log` stub for socket-based hooks.
Add a bf_log_type discriminator and restructure bf_log as a tagged union with packet and socket variants. Packet-specific fields (pkt_size, headers, l2hdr/l3hdr/l4hdr) move into the pkt variant. The sock variant (pid, comm) is defined but not yet populated. The struct sizes to the larger packet variant, so total size is unchanged for ring buffer reservations.
Move packet.c/h from cgen/matcher/ to cgen/ and rename bf_matcher_generate_packet() to bf_packet_gen_inline_matcher(). This file provides shared codegen utilities for all packet-based flavors, not a matcher implementation, and will host additional shared packet codegen like bf_packet_gen_inline_log() in subsequent commits.
Add struct _bf_runtime_sock_addr to bf_runtime with saddr, daddr, and dport fields. The cgroup_sock_addr inline codegen will pre-read context fields into these before calling the sock_addr_log ELF stub, avoiding per-hook-type verifier restrictions on bpf_sock_addr field access.
Add a BPF ELF stub for cgroup_sock_addr logging. Captures process metadata and socket address fields pre-read into bf_runtime by the prologue.
7606403 to
fd68815
Compare
Add BF_LOG_OPT_PID and BF_LOG_OPT_COMM to the bf_log_opt enum for socket-based hooks that log process metadata instead of packet headers.
Reject invalid log options during chain creation: packet hooks only support link/internet/transport, socket hooks only support internet/transport/pid/comm.
80e428a to
0827510
Compare
| #define static_assert _Static_assert | ||
| #endif | ||
|
|
||
| #include <linux/in6.h> |
There was a problem hiding this comment.
Claude: nit: This is the first <linux/...> include in any public header under include/bpfilter/. It is only needed for sizeof(struct in6_addr) in _bf_log_sock_addr.saddr and .daddr. The sibling struct _bf_runtime_sock_addr in cgen/runtime.h already uses the literal 16 for the same purpose. Using 16 here (with a comment referencing sizeof(struct in6_addr)) would avoid introducing a kernel UAPI header dependency into the public API surface.
Update log action description with the new l2/l3/l4/pid/comm options and document socket hook log output in the chain logs section.
0827510 to
3ca48eb
Compare
| /* Copy context fields into the runtime for the logging stub. | ||
| * The verifier restricts context access by attach type, so the shared | ||
| * stub reads from here instead of the context directly. */ | ||
| if (program->runtime.chain->flags & BF_FLAG(BF_CHAIN_LOG)) { |
There was a problem hiding this comment.
Claude: suggestion: The context field copies (dport, saddr, daddr) are guarded by BF_CHAIN_LOG, which is set if any rule in the chain has logging enabled. This means every connection through the chain pays the cost of the BPF loads and stores, even if the matched rule does not have logging or only requests pid,comm.
For chains where logging is rare (e.g., one log rule among many), consider computing the union of all rule->log options at chain creation time and only emitting copies for actually-needed fields. Not a correctness issue, but the number of instructions per invocation could be reduced.
| struct _bf_log_pkt | ||
| { | ||
| /** Total size of the packet, including the payload. */ | ||
| __u64 pkt_size; |
There was a problem hiding this comment.
Claude: suggestion: struct _bf_log_pkt, struct _bf_log_sock_addr (here in runtime.h), and struct _bf_runtime_sock_addr (in cgen/runtime.h) use a leading-underscore prefix for struct names. The project style guide reserves leading underscores for static functions and variables (e.g., _bf_ctx_free()), and all existing struct types in the codebase use the plain bf_ prefix (e.g., struct bf_chain, struct bf_runtime, struct bf_log).
Consider struct bf_log_pkt, struct bf_log_sock_addr, and struct bf_runtime_sock_addr for consistency.
| } | ||
|
|
||
| if (req & (1 << BF_LOG_OPT_COMM)) { | ||
| (void)fprintf(stdout, " Process : %s%.16s%s\n", |
There was a problem hiding this comment.
Claude: nit: The format specifier %.16s hardcodes the comm buffer size. BF_COMM_LEN is defined to 16 in this PR and could be used here via %.*s to stay in sync if the buffer size ever changes:
(void)fprintf(stdout, " Process : %s%.*s%s\n",
bf_logger_get_color(BF_COLOR_LIGHT_GREEN, BF_STYLE_BOLD),
BF_COMM_LEN, log->payload.sock_addr.comm,
bf_logger_get_color(BF_COLOR_RESET, BF_STYLE_RESET));
Stack:
CGROUP_SOCK_ADDRLogging Infrastructure #491Summary
This adds the remaining infrastructure to go from the hard-coded packet-specific logging to a flavor-specific logging approach that makes it easy to enable logging for
CGROUP_SOCK_ADDR:We add a new
sock_addr_logELF stub, agen_inline_logcallback (see #486), CLI formatting for socket log entries, and per-hook log option validation (e.g.BF_LOG_OPT_LINKis rejected on socket hooks,BF_LOG_OPT_PIDis rejected on packet hooks). It also introduces two new log options,BF_LOG_OPT_PIDandBF_LOG_OPT_COMM, which use thebpf_get_current_pid_tgid()andbpf_get_current_comm()helper calls in the stub.One of the challenges here is that the BPF verifier restricts which
bpf_sock_addrcontext fields a program can access based on its attach type. For example,msg_src_ip4is only valid for SENDMSG4 anduser_ip4is only valid for IPv4 hooks. The verifier checks all code paths statically regardless of runtime reachability, so a shared ELF stub compiled once and linked into all hook types cannot read these fields directly. To solve this, the per-hook inline codegen in the prologue copies the permitted fields into a new_bf_runtime_sock_addrstruct on the BPF stack, and the stub copies from there into the log entry. The copying is done once per program invocation, guarded by the existingBF_CHAIN_LOGflag, so programs without logging rules pay zero cost.Adding
_bf_runtime_sock_addrto bf_runtime increases the stack frame for all flavors by 40 bytes (with alignment), but the struct remains well within the BPF 512-byte stack limit.Testing
sendmsg4log:Note
connect4readingmsg_src_ip6), so we'll need to add it to the stack frame, similar to how we do with packets. This leads to a new question - we really should have differentbf_runtimefor packet flavors andCGROUP_SOCK_ADDRgiven how different they are and the different data they use (can be done in a similar type of way we did it inbf_log, sharing common fields and then using a union). However, I don't think know is the right time to make this invasive change when a less invasive approach works, albeit in a less clean/proper way. Imo we can add this as a follow-up.