lax
lax

Reputation: 23

eBPF: How to attach xdp with BPF_LINK_CREATE

I know how to attach xdp with netlink, but I don't want to use that since the attaching keep existing even if the loader exits. Then I found another way to attach the xdp in the official documents, which is by BPF_LINK_CREATE, here is the link and original statement:

There are two ways of attaching XDP programs to network devices, the legacy way of doing 
is is via a netlink socket the details of which are complex. Examples of libraries that 
implement netlink XDP attaching are vishvananda/netlink and libbpf.

The modern and recommended way is to use BPF links. Doing so is as easy as calling 
BPF_LINK_CREATE with the target_ifindex set to the network interface target, attach_type
set to BPF_LINK_TYPE_XDP and the same flags as would be used for the netlink approach.

The description about attaching xdp with BPF_LINK_CREATE is too simple for me to write a workable loader, and I still didn't find a official demo about its usage, but I tried, here are my codes:

test.bpf.c

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

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

SEC("xdp")
int xdp_func(struct xdp_md *ctx) {
    bpf_printk("hello xdp");
    return XDP_PASS;
}

test.cpp

// loader
#include <stdio.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/syscall.h>
#include <net/if.h>

#include "test.skel.h"

#define error(fmt, args...) printf("error: " fmt, ##args)

static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
                          unsigned int size)
{
    return syscall(__NR_bpf, cmd, attr, size);
}

#define bpf_syscall(cmd, attr)                          \
    ({                                                  \
        int fd;                                         \
        fd = sys_bpf(cmd, &attr, sizeof(attr));         \
        if (fd < 0)                                     \
        {                                               \
            error("bpf syscall failure(%s): cmd: " #cmd \
                  " line: %d\n",                        \
                  strerror(errno), __LINE__);           \
            exit(-1);                                   \
        }                                               \
        (fd);                                           \
    })

#define bpf_get_prog_fd(prog)                                \
    ({                                                       \
        int fd;                                              \
        fd = bpf_program__fd(prog);                          \
        if (fd < 0)                                          \
        {                                                    \
            error("fail to get bpf program fd " #prog "\n"); \
            exit(-1);                                        \
        }                                                    \
        fd;                                                  \
    })

void read_trace_pipe(FILE *fp = nullptr)
{
    int trace_fd = open("/sys/kernel/debug/tracing/trace_pipe", O_RDONLY, 0);
    assert(trace_fd > 0);
    if (fp == nullptr)
        fp = stdout;

    while (1)
    {
        static char buf[4096];
        ssize_t sz = read(trace_fd, buf, sizeof(buf));
        if (sz > 0)
            fwrite(buf, 1, sz, fp);
        if (sz < 0)
            break;
    }
}

int main(void)
{
    test_bpf *obj = test_bpf::open_and_load();
    assert(obj);
    assert(test_bpf::attach(obj) == 0);
    union bpf_attr attr = {};
    int ifindex = if_nametoindex("lo");
    printf("ifindex: %d\n", ifindex);
    attr.link_create.target_ifindex = ifindex;
    attr.link_create.attach_type = BPF_XDP;
    attr.link_create.prog_fd = bpf_get_prog_fd(obj->progs.xdp_func);
    bpf_syscall(BPF_LINK_CREATE, attr);
    read_trace_pipe();
}

Makefile

all: test
    sudo ./test

test: test.cpp test.skel.h
    g++ -g $<  -o $@ -lbpf

test.bpf.o: test.bpf.c vmlinux.h
    clang -g -O2 -D__x86_64__ -target bpf -c $< -o $@

test.skel.h: test.bpf.o
    bpftool gen skeleton $< > $@

vmlinux.h: /sys/kernel/btf/vmlinux
    bpftool btf dump file $< format c > $@

clean:
    rm -f vmlinux.h test.skel.h test.bpf.o test

When I run make, the program works fine, but the bpf_printk("hello xdp") wasn't called, here is the fd table of loader:

# ll /proc/`pgrep test`/fd
total 0
lrwx------ 1 root root 64 Dec 10 13:38 0 -> /dev/pts/2
lrwx------ 1 root root 64 Dec 10 13:38 1 -> /dev/pts/2
lrwx------ 1 root root 64 Dec 10 13:38 2 -> /dev/pts/2
lrwx------ 1 root root 64 Dec 10 13:38 3 -> anon_inode:bpf-map
lr-x------ 1 root root 64 Dec 10 13:38 4 -> anon_inode:btf
lrwx------ 1 root root 64 Dec 10 13:38 5 -> anon_inode:bpf-prog
lr-x------ 1 root root 64 Dec 10 13:38 6 -> anon_inode:bpf_link
lr-x------ 1 root root 64 Dec 10 13:38 7 -> /sys/kernel/debug/tracing/trace_pipe

output of bpftool net list

# bpftool net list
xdp:
lo(1) generic id 528

tc:

flow_dissector:

netfilter:

The fd anon_inode:bpf_link shows that the link is established successfully, but why the xdp hook isn't invoked(tested by ping 127.0.0.1), where might be the problem?

Upvotes: 1

Views: 89

Answers (1)

lax
lax

Reputation: 23

I've solved it myself, it was a mistake, the tracing_on was turned off by me accidently, waste my a whole day. Turn it on, and everything works as expected.

Upvotes: 0

Related Questions