eBPF / XDP 映射未创建
我在 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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
根据评论中的讨论,未创建地图,因为它实际上并未在您的 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.