You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 16:42:04