未创建 eBPF/XDP 映射

eBPF / XDP map not getting created

我在 BPF 中为 XDP 实现了一个实现,其中我指定要创建的五个映射如下:

    struct bpf_map_def SEC("maps") servers = {
        .type = BPF_MAP_TYPE_HASH,
        .key_size = sizeof(struct ip_key),
        .value_size = sizeof(struct dest_info),
        .max_entries = MAX_SERVERS,
    };
    
    struct bpf_map_def SEC("maps") server_ips = {
        .type = BPF_MAP_TYPE_HASH,
        .key_size = sizeof(struct ip_key),
        .value_size = sizeof(struct server_ip_key),
        .max_entries = MAX_SERVERS,
    };
    
    struct bpf_map_def SEC("maps") client_addrs = {
        .type = BPF_MAP_TYPE_HASH,
        .key_size = sizeof(struct port_key),
        .value_size = sizeof(struct client_port_addr),
        .max_entries = MAX_CLIENTS,
    };
    
    struct bpf_map_def SEC("maps") stoc_port_maps = {
        .type = BPF_MAP_TYPE_HASH,
        .key_size = sizeof(struct port_key),
        .value_size = sizeof(struct port_map),
        .max_entries = MAX_FLOWS,
    };
    
    struct bpf_map_def SEC("maps") ctos_port_maps = {
        .type = BPF_MAP_TYPE_HASH,
        .key_size = sizeof(struct port_key),
        .value_size = sizeof(struct port_map),
        .max_entries = MAX_FLOWS,
    };

但是,无论我做什么,servers 地图都没有创建。当我 运行 bpftool map show 时,我只得到如下输出:

root@balancer:/xdp# bpftool map list  
68: hash  name client_addrs  flags 0x0
        key 8B  value 16B  max_entries 4096  memlock 98304B
69: hash  name ctos_port_maps  flags 0x0
        key 8B  value 20B  max_entries 4096  memlock 131072B
70: hash  name server_ips  flags 0x0
        key 8B  value 8B  max_entries 512  memlock 8192B
73: hash  name stoc_port_maps  flags 0x0
        key 8B  value 20B  max_entries 4096  memlock 131072B
74: array  name xdp_lb_k.rodata  flags 0x480
        key 4B  value 50B  max_entries 1  memlock 4096B
        frozen
root@balancer:/xdp#

值得注意的是,每个键或值结构都已填充到最接近的八字节倍数,并且没有编译或验证器错误。我也在 运行 将程序安装在 docker 容器上。到目前为止,我已经尝试在我的代码中移动 servers 地图定义,注释掉其他地图定义,只保留 servers 定义活动,将名称更改为其他组合,以及其他一些小改动但到目前为止没有任何效果。

如果您需要我的代码或信息的任何其他部分以便更好地分析情况,请告诉我。


附录一: 我正在使用此 Makefile 规则编译目标文件:

xdp_lb_kern.o: xdp_lb_kern.c 
    clang -S \
        -target bpf \
        -D __BPF_TRACING__ \
        -I../../libbpf/src \
        -I../../custom-headers \
        -Wall \
        -Wno-unused-value \
        -Wno-pointer-sign \
        -Wno-compare-distinct-pointer-types \
        -O2 -emit-llvm -c -o ${@:.o=.ll} $<
    llc -march=bpf -filetype=obj -o $@ ${@:.o=.ll}

然后,在容器的环境中,我使用此规则加载程序:

load_balancer:
    bpftool net detach xdpgeneric dev eth0
    rm -f /sys/fs/bpf/xdp_lb
    bpftool prog load xdp_lb_kern.o /sys/fs/bpf/xdp_lb
    bpftool net attach xdpgeneric pinned /sys/fs/bpf/xdp_lb dev eth0

编译过程生成一个 .o 和一个 .ll 输出文件。 .ll 输出文件的开头行,其中地图定义可见,如下所示:

; ModuleID = 'xdp_lb_kern.c'
source_filename = "xdp_lb_kern.c"
target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128"
target triple = "bpf"

%struct.bpf_map_def = type { i32, i32, i32, i32, i32 }
%struct.xdp_md = type { i32, i32, i32, i32, i32 }
%struct.ip_key = type { i32, i32 }
%struct.port_key = type { i16, [3 x i16] }
%struct.ethhdr = type { [6 x i8], [6 x i8], i16 }
%struct.iphdr = type { i8, i8, i16, i16, i16, i8, i8, i16, i32, i32 }

@servers = dso_local global %struct.bpf_map_def { i32 1, i32 8, i32 32, i32 512, i32 0 }, section "maps", align 4
@server_ips = dso_local global %struct.bpf_map_def { i32 1, i32 8, i32 8, i32 512, i32 0 }, section "maps", align 4
@client_addrs = dso_local global %struct.bpf_map_def { i32 1, i32 8, i32 16, i32 4096, i32 0 }, section "maps", align 4
@stoc_port_maps = dso_local global %struct.bpf_map_def { i32 1, i32 8, i32 20, i32 4096, i32 0 }, section "maps", align 4
@ctos_port_maps = dso_local global %struct.bpf_map_def { i32 1, i32 8, i32 20, i32 4096, i32 0 }, section "maps", align 4
@loadbal.____fmt = internal constant [24 x i8] c"balancer got something![=14=]", align 1
@_license = dso_local global [4 x i8] c"GPL[=14=]", section "license", align 1
@process_packet.____fmt = internal constant [26 x i8] c"it's an ip packet from %x[=14=]", align 1
@llvm.used = appending global [7 x i8*] [i8* getelementptr inbounds ([4 x i8], [4 x i8]* @_license, i32 0, i32 0), i8* bitcast (%struct.bpf_map_def* @client_addrs to i8*), i8* bitcast (%struct.bpf_map_def* @ctos_port_maps to i8*), i8* bitcast (i32 (%struct.xdp_md*)* @loadbal to i8*), i8* bitcast (%struct.bpf_map_def* @server_ips to i8*), i8* bitcast (%struct.bpf_map_def* @servers to i8*), i8* bitcast (%struct.bpf_map_def* @stoc_port_maps to i8*)], section "llvm.metadata"

; Function Attrs: nounwind
define dso_local i32 @loadbal(%struct.xdp_md* nocapture readonly %0) #0 section "xdp" {
  %2 = alloca %struct.ip_key, align 4
  %3 = alloca %struct.port_key, align 2
  %4 = alloca %struct.port_key, align 2
  %5 = getelementptr inbounds %struct.xdp_md, %struct.xdp_md* %0, i64 0, i32 1
  %6 = load i32, i32* %5, align 4, !tbaa !2
  %7 = zext i32 %6 to i64
  %8 = inttoptr i64 %7 to i8*
  %9 = getelementptr inbounds %struct.xdp_md, %struct.xdp_md* %0, i64 0, i32 0
  %10 = load i32, i32* %9, align 4, !tbaa !7

根据评论中的讨论,未创建地图,因为它实际上并未在您的 eBPF 代码中使用(问题中未提供)。

正如您自己意识到的那样,您的代码中调用地图的分支实际上是无法访问的。基于此,很可能是 clang 编译出了这部分代码,并且在生成的 eBPF 字节码中没有使用该映射。在准备加载您的程序时,bpftool (libbpf) 会查看哪些映射是必需的,并且只创建您的程序所需的映射。如果没有程序使用它们,它可能会跳过 ELF 文件中定义的映射。

这里的一个提示是,如果程序有效地使用了地图,它 couldn't load successfully if the map was missing:假设您的程序已加载,则地图在需要时必然存在。请注意,bpftool prog show 将向您显示程序使用的地图的 ID。