如何在另一用户态程序中获取eBPF Maps的MAP FD
问题描述
我已在网络接口上挂载了eBPF XDP程序(port_filter_kern.c):
port_filter_kern.c:丢弃发往特定端口的入站流量,端口号以键的形式存储在名为port_map的eBPF map中。port_filter_user.c:负责将eBPF程序加载到指定接口,读取端口号文本文件后更新port_map,代码片段如下:
map_fd = bpf_object__find_map_fd_by_name(obj, "port_map"); printf("map_fd %d ",map_fd); // 打印map的文件描述符 int result = bpf_map_update_elem(map_fd, &portkey, &value, BPF_ANY);
现在需要通过另一个用户态程序port_filter_runtime.c,在运行时从用户输入/文本文件获取端口号,更新同一个port_map,从而丢弃新增端口的入站流量。但尝试以下两种方式获取的map FD和第一个程序打印的FD不一致,无法正确更新:
方式一:
struct bpf_object *obj = bpf_object__open_file("port_filter_kern.o", NULL); struct bpf_map *map = bpf_object__find_map_by_name(obj, "port_map"); int map_fd = bpf_map__fd(map); printf("map_fd %d ",map_fd); // 打印map的文件描述符
方式二:
struct bpf_object *obj = bpf_object__open_file("port_filter_kern.o", NULL); int map_fd = bpf_object__find_map_fd_by_name(obj, "port_map"); printf("map_fd %d ",map_fd); // 打印map的文件描述符
解决方案
你之前的方法错误在于:bpf_object__open_file只是从目标文件创建了一个新的BPF对象实例,对应的是未加载到内核的map定义,而非已经由port_filter_user.c加载到内核中的那个实际map实例,因此得到的FD完全不同。
要访问内核中已存在的port_map,需要直接通过map的名称查找内核中的实例,而不是重新打开目标文件。以下是两种可行的实现方式:
方法1:使用libbpf的bpf_get_map_fd_by_name函数
libbpf提供了直接通过名称获取内核中已存在map的FD的函数,代码示例:
#include <bpf/libbpf.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> int main() { int map_fd = bpf_get_map_fd_by_name("port_map"); if (map_fd < 0) { fprintf(stderr, "Failed to get map FD: %s\n", strerror(-map_fd)); return 1; } printf("map_fd %d\n", map_fd); // 示例:添加端口8080到过滤列表 __u32 portkey = 8080; __u32 value = 1; // 假设value非0即表示需要丢弃该端口流量 int result = bpf_map_update_elem(map_fd, &portkey, &value, BPF_ANY); if (result < 0) { fprintf(stderr, "Failed to update map: %s\n", strerror(errno)); close(map_fd); return 1; } close(map_fd); return 0; }
编译时需要链接libbpf相关库:
gcc port_filter_runtime.c -o port_filter_runtime -lbpf -lelf -lz
方法2:通过sysfs手动查找并打开map
如果你的libbpf版本较低不支持bpf_get_map_fd_by_name,可以通过BPF文件系统手动查找:
- 确保BPF文件系统已挂载:
mount -t bpf bpf /sys/fs/bpf/ - 直接打开
/sys/fs/bpf/下对应port_map的文件(如果是命名map会直接以名称作为文件名),代码示例:
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> #include <errno.h> int main() { const char* map_path = "/sys/fs/bpf/port_map"; int map_fd = open(map_path, O_RDWR); if (map_fd < 0) { fprintf(stderr, "Failed to open map: %s\n", strerror(errno)); return 1; } printf("map_fd %d\n", map_fd); // 后续更新map的逻辑同上 close(map_fd); return 0; }
注意事项
- 运行程序的用户需要足够权限(通常为root或拥有
CAP_BPF权限); - 如果
port_map未设置命名属性,无法通过名称直接查找,可在第一个程序加载时将map ID写入文件,供第二个程序读取后通过/sys/fs/bpf/map_{ID}路径打开; - 更新map时注意键值的类型要和内核程序中定义的
port_map一致。
内容的提问来源于stack exchange,提问作者Paddy
相关产品推荐
相关产品推荐

