为什么我的 BPF_PROG_TYPE_CGROUP_SKB 程序无法在容器中运行?

dip*_*ark 3 linux linux-kernel docker bpf ebpf

我编写了以下 eBPF 程序来对数据包进行计数:

#include <linux/version.h>
#include <uapi/linux/bpf.h>

#include "include/bpf_map.h"
#include "include/bpf_helpers.h"

struct bpf_map_def SEC("maps/count") count_map = {
    .type = BPF_MAP_TYPE_ARRAY,
    .key_size = sizeof(int),
    .value_size = sizeof(__u64),
    .max_entries = 1024,
};

SEC("cgroup/skb")
int count_packets(struct __sk_buff *skb) {
    char debug[] = "count_packets\n";
    bpf_trace_printk(debug, sizeof(debug));

    int packets_key = 0;
    __u64 *packets = 0;

    packets = bpf_map_lookup_elem(&count_map, &packets_key);
    if (packets == 0)
        return 0;

    *packets += 1;

    // allow access
    return 1;
}

char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
Run Code Online (Sandbox Code Playgroud)

我还有一个用户空间组件,它将程序加载为 a BPF_PROG_TYPE_CGROUP_SKB,使用 Attach type 将其附加到 v2 cgroup ( /sys/fs/cgroup/unified/foo) BPF_CGROUP_INET_EGRESS,将其自己的 PID 添加到该 cgroup 并开始创建网络流量。

当我在容器外部运行此用户空间组件时,它按预期工作,并且我看到我的程序通过执行以下操作来调用cat /sys/kernel/debug/tracing/trace_pipe。

但是,当我在容器中运行程序时,我看不到任何输出。

我按如下方式运行容器:

docker run -it \
        --privileged \
        --pid=host \
        --net=host \
        -v /sys/fs/cgroup/unified:/sys/fs/cgroup/unified \
        ${IMAGE}
Run Code Online (Sandbox Code Playgroud)

我使用主机网络和 PID 命名空间来避免它们可能导致的任何潜在问题。

为什么我的程序似乎无法在容器内运行?

uname -a:Linux ubuntu-bionic 4.18.0-16-generic #17~18.04.1-Ubuntu SMP 2月12日星期二13:35:51 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

dip*_*ark 7

这是因为 docker 使用net_prio和net_cls控制器来覆盖用于 cgroup2 匹配的数据。从这里

虽然用户态可以随时开始使用 net_prio 或 net_cls,但一旦使用其中任何一个,cgroup2 匹配就不再起作用。

我的解决方案是使用启动标志禁用这些控制器:cgroup_no_v1=net_prio,net_cls。更好的解决方案是停止 docker 使用它们,但我不知道该怎么做。