linuxebpfbpfnetfilter

Setting up BPF Netfilter hooks environment/dependencies


I'm trying to create a simple BPF program to drop incoming/outgoing packets and accept forwarded packets only, to be loaded by bpftool. I'm using Ubuntu 24.04 with Linux kernel version 6.8. This is my program:

#include <linux/bpf.h>
  
#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
#include <linux/netfilter.h>

SEC("netfilter") /* hint to BPF loader that this is a netfilter BPF program */
int filter(const struct bpf_nf_ctx *ctx) {
  const struct nf_hook_state *state = ctx->state;
  unsigned int routing_decision = state->hook;

  if (routing_decision == NF_INET_LOCAL_IN ||
      routing_decision == NF_INET_LOCAL_OUT)
    return NF_DROP;

  return NF_ACCEPT;
}

char LICENSE[] SEC("license") = "GPL";

However when I try to compile it into an object file, I get:

clang --target=bpf -O2 -Wall -I/usr/include/x86_64-linux-gnu -c filter.c -o filter.bpf.o
filter.c:8:27: warning: declaration of 'struct bpf_nf_ctx' will not be visible outside of this function [-Wvisibility]
    8 | int filter(const struct bpf_nf_ctx *ctx) {
      |                           ^
filter.c:9:42: error: incomplete definition of type 'struct bpf_nf_ctx'
    9 |   const struct nf_hook_state *state = ctx->state;
      |                                       ~~~^
filter.c:8:27: note: forward declaration of 'struct bpf_nf_ctx'
    8 | int filter(const struct bpf_nf_ctx *ctx) {
      |                           ^
filter.c:10:29: error: incomplete definition of type 'struct bpf_nf_ctx'
   10 |   int routing_decision = ctx->state->hook;
      |                          ~~~^
filter.c:8:27: note: forward declaration of 'struct bpf_nf_ctx'
    8 | int filter(const struct bpf_nf_ctx *ctx) {
      |                           ^
1 warning and 2 errors generated.

After realizing struct bpf_nf_ctx is defined in nf_bpf_link.h, I added the proper includes (#include <net/netfilter/nf_bpf_link.h>, when the full path is /usr/src/linux-headers-6.8.0-48/include/net/netfilter/nf_bpf_link.h) and this was the output:

clang --target=bpf -O2 -Wall -I/usr/include/x86_64-linux-gnu -I/usr/src/linux-headers-6.8.0-48/include -c filter.c -o filter.bpf.o
In file included from filter.c:1:
In file included from /usr/src/linux-headers-6.8.0-48/include/linux/bpf.h:8:
In file included from /usr/src/linux-headers-6.8.0-48/include/uapi/linux/filter.h:9:
/usr/src/linux-headers-6.8.0-48/include/linux/compiler.h:251:10: fatal error: 'asm/rwonce.h' file not found
  251 | #include <asm/rwonce.h>
      |          ^~~~~~~~~~~~~~
1 error generated.
make: *** [Makefile:7: all] Error 1

How do I properly set up my environment to compile a BPF program using netfilter hooks?


Solution

  • You are including internal kernel headers (non uapi headers). These need a bunch of generated dependencies to work which is why you are getting include issues.

    For the netfilter programs the kernel devs decided to not use projection types like __sk_buff but give you access to the real, internal sk_buff struct. To get the definitions for these they expect you to use a vmlinux.h or manually define the structs you need just like when writing tracing programs that access internal kernel types.

    For the uninitiated, you can generate a vmlinux.h file from your local kernel with bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

    A quote from the patch set that introduced it:

    The new program type is 'tracing style', i.e. there is no context access rewrite done by verifier, the function argument (struct bpf_nf_ctx) isn't stable. There is no support for direct packet access, dynptr api should be used instead.

    That also means that you should use CO-RE do do reads if you need this program to work on future kernels.