eBPF / XDP 映射未创建

发布于 2025-01-15 07:56:40 字数 5421 浏览 3 评论 0原文

我在 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 定义处于活动状态,将名称更改为其他组合,以及其他一些小的改变,但到目前为止没有任何效果。

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


附录1: 我使用此 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!\00", align 1
@_license = dso_local global [4 x i8] c"GPL\00", section "license", align 1
@process_packet.____fmt = internal constant [26 x i8] c"it's an ip packet from %x\00", 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

I have an implementation in BPF for XDP, wherein I specify five maps to be created as follows:

    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,
    };

However, no matter what I do, the servers map is not getting created. When I run bpftool map show, I only get such output as the following:

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#

It is notable that each of the key or value structs have been padded to the closest multiple of eight bytes, and there are no compile or verifier errors. I am also running the program on docker containers. So far, I have tried moving the servers map definition around in my code, commenting out the other map definitions leaving only the servers definition active, changing the name to other combinations, and a few other minor changes but nothing has worked so far.

Please let me know if you would need any other portion of my code or information for a better analysis of the situation.


Appendix 1:
I am compiling the object file using this Makefile rule:

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} 
lt;
    llc -march=bpf -filetype=obj -o $@ ${@:.o=.ll}

Then, in the container's environment, I load the program using this rule:

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

The compilation process generates a .o and a .ll output file. The beginning lines of the .ll output file, where the map definitions are visible, are shown below:

; 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!\00", align 1
@_license = dso_local global [4 x i8] c"GPL\00", section "license", align 1
@process_packet.____fmt = internal constant [26 x i8] c"it's an ip packet from %x\00", 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

困倦 2025-01-22 07:56:40

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

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

这里的一个提示是,如果程序有效地使用了映射,则它 如果地图丢失,则无法成功加载:考虑到您的程序已加载,如果需要,地图必然会存在。请注意,bpftool prog show 将向您显示程序使用的映射的 ID。

As per the discussion in the comments, the map is not created because it is not actually used in your eBPF code (not provided in the question).

As you realised yourself, the branch in your code that was calling the map was in fact unreachable. Based on that, it's likely that clang compiled out this portion of code, and that the map is not used in the resulting eBPF bytecode. When preparing to load your program, bpftool (libbpf) looks at what maps are necessary, and only creates the ones that are needed for your program. It may skip maps that are defined in the ELF file if no program uses them.

One hint here is that, if the program was effectively using the map, it couldn't load successfully if the map was missing: given that your program loads, the map would necessarily be present if it was needed. Note that bpftool prog show will show you the ids of the maps used by a program.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文