有没有一种方法可以在 ebpf 程序和用户空间程序之间共享一些 ebpf 映射,这些程序具有使用 libbpf 作为键的值结构

is there a way I can share some ebpf map between ebpf program and userspace program that has value struct using libbpf for keys

所以我创建了一个 BPF_MAP_TYPE_ARRAY 类型的地图。

struct share_me
{

    struct iphdr dest_ip;
};

struct bpf_map_def SEC("maps") ip_map = {
    .type = BPF_MAP_TYPE_ARRAY,
    .key_size = sizeof(int),
    .value_size = sizeof(struct share_me),
    .max_entries = 64,  /* Assume netdev has no more than 64 queues */
};

所以 ip_map 是我的地图及其定义的和 SEC elf 部分,用于在上述定义中创建地图

在我正在做的ebpf程序函数中

SEC("xdp_sock")
int xdp_sock_prog(struct xdp_md *ctx)
{
    int index = ctx->rx_queue_index;
    __u32 *pkt_count;

    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;  
    struct share_me me; 

    if ((void *)eth + sizeof(*eth) <= data_end)
    {

        struct iphdr *ip = data + sizeof(*eth);
        //me.dest_ip=ip;    
        memcpy(&me.dest_ip,ip,sizeof(struct iphdr));    
        bpf_map_lookup_elem(&ip_map, &index);
        bpf_map_update_elem(&ip_map,&index,&me,0);  

所以我正在用我的结构对象

更新ip_map键的当前值

这就是我在 usespace 程序中所做的

        bpf_obj = load_bpf_and_xdp_attach(&cfg);
        if (!bpf_obj) {
            /* Error handling done in load_bpf_and_xdp_attach() */
            exit(EXIT_FAILURE);
        }

        /* We also need to load the xsks_map */
        
        map1 = bpf_object__find_map_by_name(bpf_obj, "ip_map");
        xsks_map_fd = bpf_map__fd(map);
        map_fd = bpf_map__fd(map1);
        if(map_fd<0)
        {
            printf("map_fd <0\n");
            exit(0);
        }
        
        if (xsks_map_fd < 0) {
            fprintf(stderr, "ERROR: no xsks map found: %s\n",
                strerror(xsks_map_fd));
            exit(EXIT_FAILURE);
        }

这是 load_bpf_and_xdp_attach 函数,只是 bpf/libbpf 的包装器,因此调用 load_bpf_object_file 等,

struct bpf_object *load_bpf_and_xdp_attach(struct config *cfg)
{
    struct bpf_program *bpf_prog;
    struct bpf_object *bpf_obj;
    int offload_ifindex = 0;
    int prog_fd = -1;
    int err;

    /* If flags indicate hardware offload, supply ifindex */
    if (cfg->xdp_flags & XDP_FLAGS_HW_MODE)
        offload_ifindex = cfg->ifindex;

    /* Load the BPF-ELF object file and get back libbpf bpf_object */
    if (cfg->reuse_maps)
        bpf_obj = load_bpf_object_file_reuse_maps(cfg->filename,
                              offload_ifindex,
                              cfg->pin_dir);
    else
        bpf_obj = load_bpf_object_file(cfg->filename, offload_ifindex);
    if (!bpf_obj) {
        fprintf(stderr, "ERR: loading file: %s\n", cfg->filename);
        exit(EXIT_FAIL_BPF);
    }
    /* At this point: All XDP/BPF programs from the cfg->filename have been
     * loaded into the kernel, and evaluated by the verifier. Only one of
     * these gets attached to XDP hook, the others will get freed once this
     * process exit.
     */

    if (cfg->progsec[0])
        /* Find a matching BPF prog section name */
        bpf_prog = bpf_object__find_program_by_title(bpf_obj, cfg->progsec);
    else
        /* Find the first program */
        bpf_prog = bpf_program__next(NULL, bpf_obj);

    if (!bpf_prog) {
        fprintf(stderr, "ERR: couldn't find a program in ELF section '%s'\n", cfg->progsec);
        exit(EXIT_FAIL_BPF);
    }

    strncpy(cfg->progsec, bpf_program__title(bpf_prog, false), sizeof(cfg->progsec));

    prog_fd = bpf_program__fd(bpf_prog);
    if (prog_fd <= 0) {
        fprintf(stderr, "ERR: bpf_program__fd failed\n");
        exit(EXIT_FAIL_BPF);
    }

    /* At this point: BPF-progs are (only) loaded by the kernel, and prog_fd
     * is our select file-descriptor handle. Next step is attaching this FD
     * to a kernel hook point, in this case XDP net_device link-level hook.
     */
    err = xdp_link_attach(cfg->ifindex, cfg->xdp_flags, prog_fd);
    if (err)
        exit(err);

    return bpf_obj;
}

但我收到错误

libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
; int index = ctx->rx_queue_index;
0: (61) r2 = *(u32 *)(r1 +16)
; int index = ctx->rx_queue_index;
1: (63) *(u32 *)(r10 -4) = r2
; void *data_end = (void *)(long)ctx->data_end;
2: (61) r2 = *(u32 *)(r1 +4)
; void *data = (void *)(long)ctx->data;
3: (61) r1 = *(u32 *)(r1 +0)
; if ((void *)eth + sizeof(*eth) <= data_end)
4: (07) r1 += 14
; if ((void *)eth + sizeof(*eth) <= data_end)
5: (2d) if r1 > r2 goto pc+25
 R1_w=pkt(id=0,off=14,r=14,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R10=fp0 fp-8=mmmm????
; memcpy(&me.dest_ip,ip,sizeof(struct iphdr));  
6: (61) r2 = *(u32 *)(r1 +16)
invalid access to packet, off=30 size=4, R1(id=0,off=30,r=14)
R1 offset is outside of the packet
processed 7 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

libbpf: -- END LOG --
libbpf: failed to load program 'xdp_sock'
libbpf: failed to load object 'af_xdp_kern.o'
ERR: loading BPF-OBJ file(af_xdp_kern.o) (-22): Invalid argument
ERR: loading file: af_xdp_kern.o

所以我在加载我的 ebpf 程序时得到 invalid access to packet, off=30 size=4, R1(id=0,off=30,r=14) R1 offset is outside of the packet 我有点确定错误是因为我使用结构值作为类型 BPF_MAP_TYPE_ARRY 值

的映射

所以我想知道如果我使用允许结构作为映射键值的 libbpf,是否还有其他 MAP 类型

TL;DR. 问题是您正在 out-of-bound 访问来自验证者的数据包 观点看法。您需要首先检查数据包是否足够长以实际包含 IP header。


正在读取验证程序错误消息。

; memcpy(&me.dest_ip,ip,sizeof(struct iphdr));  
6: (61) r2 = *(u32 *)(r1 +16)
invalid access to packet, off=30 size=4, R1(id=0,off=30,r=14)
R1 offset is outside of the packet

验证器在指令 6 上出错,对应于 memcpy 语句。它指出您正在对数据包进行无效访问,R1 中的偏移量超出了数据包的已知范围。

您检查过数据包是否至少足以容纳以太网 header,但您从未检查过它是否足够长以容纳 IP header。所以验证者看到你试图访问以太网之外的字节 header(最多偏移量 30)和错误。


正在更新数据包边界检查。

如果您从一开始就知道您需要阅读以太网和 IP headers,您可以从以下位置更新您的检查:

if ((void *)eth + sizeof(*eth) <= data_end)

至:

if ((void *)eth + sizeof(*eth) + sizeof(struct iphdr) <= data_end)