XDP minimal example

First written 2020-11-15; Last updated 2020-11-15

This note shows a minimal XDP (eXpress Data Path) setup with libbpf and BTF/BPF CO-RE because it took me a while to collect all the pieces for a working program. It should also serve as starting point for BPF in general. It also lists some useful BPF-related resources.

Note
BPF is a fast moving target so this information might be outdated.

Resources

See libbpf for the requirements to use BTF/BPF CO-RE and to run the minimal example below.

Minimal example

The full source for this example is licensed under LGPL-2.1-or-later and BSD-2-Clause and is available at my Gitweb and can be cloned with:

git clone https://ruderich.org/simon/bpf/xdp-example.git/

BPF side: example.bpf.c

The minimal BPF side is very simple.

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>

SEC("xdp")
int xdp_prog(struct xdp_md *ctx) {
    return XDP_PASS;
}
char LICENSE[] SEC("license") = "GPL";

XDP_PASS accepts all packets making this XDP program a NOP.

User-space side: example.c

The user-space is a bit more complicated. The following code omits error checks for brevity, see the Git repository for the full code.

#include <net/if.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>

#include <bpf/bpf.h>
#include <linux/if_link.h>

libbpf’s skeleton is used to reduce the boilerplate. The skeleton is generated by the Makefile from example.bpf.c with bpftool gen skeleton.

#include "example.skel.h"

int main(int argc, char **argv) {

The interface index is required to attach the XDP program.

    unsigned int ifindex = if_nametoindex(argv[1]);

Larger BPF programs require more memory.

    struct rlimit rlim = {
        .rlim_cur = RLIM_INFINITY,
        .rlim_max = RLIM_INFINITY,
    };
    setrlimit(RLIMIT_MEMLOCK, &rlim);

Load the skeleton and compile the BPF.

    int err;
    struct example_bpf *obj;

    obj = example_bpf__open();
    err = example_bpf__load(obj);

Attach the BPF program to the network interface.

    int flags = XDP_FLAGS_SKB_MODE;
    int fd = bpf_program__fd(obj->progs.xdp_prog);

    err = bpf_set_link_xdp_fd(ifindex, fd, flags);

After attaching the XDP program there are two possibilities: Either exit, this will keep the XDP program attached to the interface, or, enter a main loop and interact with the XDP program (e.g. using maps). For this example we just sleep ten seconds.

    sleep(10);

For the latter type of XDP programs we must now detach from the network interface.

    fd = -1;
    err = bpf_set_link_xdp_fd(ifindex, fd, flags);

    example_bpf__destroy(obj);
}

Run

$ make vmlinux.h
$ make
$ ./example eth0

Open another terminal and run ip link, you should see something like this:

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 xdpgeneric [...]
    link/ether [...] brd ff:ff:ff:ff:ff:ff
    prog/xdp id 5520 tag 3b185187f1855c4c

Last updated 2020-11-15

Impressum Datenschutzerklärung