使用BPF_LINK_CREATE挂载XDP程序后未触发执行的问题排查
XDP程序挂载成功但bpf_printk无输出的问题排查
我原本通过netlink挂载XDP程序,但该方式在加载器退出后挂载状态仍会保留,因此转而采用官方推荐的BPF_LINK_CREATE方式。编写对应的eBPF代码、加载器代码及Makefile后,程序运行时:
- fd表显示已创建
anon_inode:bpf_link bpftool net list显示lo网卡已挂载XDP程序(generic模式)
但执行ping 127.0.0.1时,bpf_printk无日志输出。
附相关代码:
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
运行信息:
- 加载器fd表显示存在anon_inode:bpf_link
bpftool net list输出:
# bpftool net list xdp: lo(1) generic id 528 tc: flow_dissector: netfilter:
可能的原因及解决方法
1. bpf_printk输出被内核次数限制
内核默认对bpf_printk的输出次数设有限制(默认通常为1000次),超过限制后会停止输出。
- 排查:执行
sysctl kernel.bpf_printk_limit查看当前限制值。 - 解决:执行
sudo sysctl -w kernel.bpf_printk_limit=-1取消输出次数限制,之后重新运行程序并测试。
2. Generic XDP未处理lo网卡回环包
部分内核版本或配置下,Generic模式的XDP可能不会处理本地回环(lo)网卡的数据包。
- 解决:将XDP程序挂载到物理网卡(替换代码中的
lo为物理网卡名,如eth0),然后ping该网卡的IP地址,验证是否有输出。
3. Skeleton的attach操作冗余且可能干扰
test_bpf::attach(obj)调用是冗余的:bpftool生成的skeleton默认不会为XDP程序执行挂载操作(需指定网卡参数),该调用实际无有效操作,反而可能引入未知问题。
- 解决:注释掉
assert(test_bpf::attach(obj) == 0);这一行,重新编译运行程序。
4. Trace_pipe读取逻辑未清空旧数据
当前read_trace_pipe函数启动后会先读取trace_pipe中的历史数据,可能导致新的bpf_printk输出被延迟读取或被历史数据覆盖。
- 解决:修改
read_trace_pipe函数,先清空现有trace_pipe内容,再进入循环读取新输出:
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; // 清空trace_pipe历史内容 char buf[4096]; while (read(trace_fd, buf, sizeof(buf)) > 0); while (1) { ssize_t sz = read(trace_fd, buf, sizeof(buf)); if (sz > 0) fwrite(buf, 1, sz, fp); if (sz < 0) break; } }
内容的提问来源于stack exchange,提问作者lax
相关产品推荐
相关产品推荐

