想起 eBPF Talk: 达成内核 bpf 子系统贡献者成就 给内核新增了一个 XDP 安装失败的 tracepoint;现在想来,这个 tracepoint 有点鸡肋,因为可以通过 kprobe 一些函数就能解决该 tracepoint 所要解决的问题。

进来,特意学习了一些网卡驱动里的 XDP 安装过程,觉得可以 kprobe 这些网卡驱动的 XDP 安装函数,来实现一个 XDP 安装过程的跟踪工具,便有了 xdpsnoop 这个工具。

xdpsnoop 简介

xdpsnoop kprobe 了所有网卡驱动的 XDP 安装函数,即使这些网卡驱动都是内核模块;前提是,这些函数能够被 kprobe。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# ./xdpsnoop --verbose
2024/03/03 15:15:24 Attached kprobe to tun_xdp
2024/03/03 15:15:24 Attached kretprobe to tun_xdp
2024/03/03 15:15:24 Attached kprobe to generic_xdp_install
2024/03/03 15:15:24 Attached kretprobe to generic_xdp_install
2024/03/03 15:15:24 Attached kprobe to virtnet_xdp
2024/03/03 15:15:25 Attached kretprobe to virtnet_xdp
2024/03/03 15:15:25 Attached kprobe to xennet_xdp
2024/03/03 15:15:25 Attached kretprobe to xennet_xdp
2024/03/03 15:15:25 Listening for events...
2024/03/03 15:15:28 Installed XDP to ifindex=2 ifname=ens33 bpf_prog_id=448 bpf_prog_name=dummy
2024/03/03 15:15:28 Removed XDP from ifindex=2 ifname=ens33

P.S. 后续会增加对 dev_xdp_attach 函数的 kprobe。

xdpsnoop 实现

xdpsnoop 的实现并不复杂,主要是 kprobe 那些网卡驱动的 XDP 安装函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
SEC("kprobe")
int BPF_KPROBE(k_xdp_install, struct net_device *dev, struct netdev_bpf *bpf)
{
    struct xdp_install_args *args;

    args = get_install_args();
    if (!args)
        return BPF_OK;

    if (!is_setup_xdp(bpf)) {
        clear_install_args(args);
        return BPF_OK;
    }

    args->dev = dev;
    args->bpf = bpf;

    return BPF_OK;
}

static __always_inline void
output_netdev(struct event *ev, struct net_device *dev)
{
    BPF_CORE_READ_INTO(&ev->ifindex, dev, ifindex);
    bpf_probe_read_kernel_str(ev->ifname, sizeof(ev->ifname), dev->name);
}

static __always_inline void
output_bpf(struct event *ev, struct netdev_bpf *bpf)
{
    BPF_CORE_READ_INTO(&ev->prog_id, bpf, prog, aux, id);
    bpf_probe_read_kernel_str(ev->prog, sizeof(ev->prog), BPF_CORE_READ(bpf, prog, aux, name));
}

static __always_inline void
output_errmsg(struct event *ev, int retval, struct netdev_bpf *bpf)
{
    if (retval == 0)
        return;

    bpf_probe_read_kernel_str(ev->errmsg, sizeof(ev->errmsg), ev->errmsg);
}

SEC("kretprobe")
int BPF_KRETPROBE(kr_xdp_install, int retval)
{
    struct xdp_install_args *args;

    args = get_install_args();
    if (!args || !args->dev || !args->bpf)
        return BPF_OK;

    struct event ev = {};

    output_netdev(&ev, args->dev);
    output_bpf(&ev, args->bpf);
    ev.retval = retval;
    output_errmsg(&ev, retval, args->bpf);

    bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &ev, sizeof(ev));

    return BPF_OK;
}

然后,在 Go 里,查找并 kprobe 所有参数为 struct net_device *, struct netdev_bpf * 的函数。

具体的源代码,请查看 xdpsnoop

小结

xdpsnoop 是一个简单的 XDP 安装过程的跟踪工具,通过 kprobe 网卡驱动的 XDP 安装函数,来实现对 XDP 安装过程的跟踪。