如何使用BPF_MAP_TYPE_SOCKHASH?创建失败问题求助
BPF_MAP_TYPE_SOCKHASH创建失败问题排查与解决
问题描述
- 开启quic_bpf测试nginx_quic时,BPF_MAP_CREATE调用失败,错误码(errno)为22(无效参数)
- 编写创建BPF_MAP_TYPE_SOCKHASH类型映射的Demo后,出现错误码(errno)为7(参数列表过长)
- 运行环境:Linux内核5.4.0-126-generic(Ubuntu 18.04),
uname -a输出:Linux h3c-R4900 5.4.0-126-generic #142~18.04.1-Ubuntu SMP Thu Sep 1 16:25:16 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
用户提供的错误代码:
#include <sys/socket.h> #include <sys/syscall.h> #include <linux/bpf.h> #include <string.h> #include <stdint.h> #include <errno.h> #include <stdio.h> int main(){ union bpf_attr attr; memset(&attr, sizeof(union bpf_attr), 0); attr.map_type = BPF_MAP_TYPE_SOCKHASH; attr.key_size = sizeof(uint64_t); attr.value_size = sizeof(uint64_t); attr.max_entries = 64; attr.map_flags = 0; int ret = syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr)); printf("ret = %d, errno = %d\n", ret, errno); return 0; }
运行结果:ret = -1, errno = 7
问题原因
- memset参数顺序错误:代码中
memset(&attr, sizeof(union bpf_attr), 0);的参数顺序颠倒,正确用法是memset(目标地址, 填充值, 长度),错误的参数导致bpf_attr结构被错误初始化,触发errno=7。 - SOCKHASH参数不符合内核要求:
- 内核5.4中,BPF_MAP_TYPE_SOCKHASH的key必须是
struct bpf_sock_tuple类型(对应套接字五元组),不能用uint64_t - value需要存储socket文件描述符,应为
sizeof(int)而非uint64_t - 需设置
BPF_F_NO_PREALLOC标志,SOCKHASH不支持预分配内存
- 内核5.4中,BPF_MAP_TYPE_SOCKHASH的key必须是
修正后的可用示例
#include <sys/socket.h> #include <sys/syscall.h> #include <linux/bpf.h> #include <linux/bpf_common.h> #include <string.h> #include <stdint.h> #include <errno.h> #include <stdio.h> // 对应内核中的struct bpf_sock_tuple(IPv4版本) struct bpf_sock_tuple_v4 { __be32 src_ip; __be32 dst_ip; __be16 src_port; __be16 dst_port; }; int main(){ union bpf_attr attr = {0}; // 直接初始化比memset更安全 attr.map_type = BPF_MAP_TYPE_SOCKHASH; // 使用IPv4的tuple大小,若需支持IPv6则用对应结构体大小 attr.key_size = sizeof(struct bpf_sock_tuple_v4); attr.value_size = sizeof(int); // 存储socket fd attr.max_entries = 64; attr.map_flags = BPF_F_NO_PREALLOC; // SOCKHASH必须设置此标志 attr.numa_node = 0; // 指定NUMA节点,默认0即可 int ret = syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr)); printf("ret = %d, errno = %d\n", ret, errno); if (ret >= 0) { printf("SOCKHASH map created successfully, fd: %d\n", ret); // 后续可操作此map,最后记得close(ret) } return 0; }
关于libbpf的依赖说明
- 直接调用
syscall可以实现BPF操作,但容易出现参数错误、内核版本兼容性问题。 - 推荐使用libbpf(尤其是libbpf-bootstrap),它会自动处理结构体初始化、参数校验、内核特性适配等细节,大幅降低开发难度。例如用libbpf创建SOCKHASH时,只需调用
bpf_map_create并传入对应参数,无需手动处理bpf_attr的复杂配置。
内容的提问来源于stack exchange,提问作者lyra
相关产品推荐
相关产品推荐

