You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

问题原因

  1. memset参数顺序错误:代码中memset(&attr, sizeof(union bpf_attr), 0);的参数顺序颠倒,正确用法是memset(目标地址, 填充值, 长度),错误的参数导致bpf_attr结构被错误初始化,触发errno=7。
  2. 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不支持预分配内存

修正后的可用示例

#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 14:40:37