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

编译成功的BPF程序无法加载至内核的问题求助

BPF程序加载失败排查

问题描述

编写的BPF源码编译成功,但无法加载至内核,相关信息如下:

BPF源码

#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <netinet/in.h>
#include <linux/udp.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_perf_event.h>
#include <netinet/in.h>

struct bpf_map_def SEC("maps") timestamp_map = {
    .type = BPF_MAP_TYPE_PERCPU_ARRAY,
    .key_size = sizeof(int),
    .value_size = sizeof(uint64_t),
    .max_entries = 1,
};

SEC("filter")
int icmp_timestamp(struct __sk_buff *skb) {
    void *data_end = (void *)(long)skb->data_end;
    void *data = (void *)(long)skb->data;
    struct ethhdr *eth = data;

    if (eth->h_proto == htons(ETH_P_IP)) {
       struct iphdr *ip = data + sizeof(struct ethhdr);

        if (ip->protocol == IPPROTO_ICMP) {
            uint64_t timestamp = bpf_ktime_get_ns();
            int key = 0;
            bpf_map_update_elem(&timestamp_map, &key, &timestamp, BPF_ANY);
            return XDP_PASS;    
        }

    }
    return XDP_DROP;
}

char _license[] SEC("license") = "GPL";

加载命令

sudo bpftool prog load name_of_code.o /sys/fs/bpf/name_of_code

错误信息

libbpf: prog 'icmp_timestamp': BPF program load failed: Permission denied

libbpf: prog 'icmp_timestamp': -- BEGIN PROG LOAD LOG --
reg type unsupported for arg#0 function icmp_timestamp#26
0: R1=ctx(off=0,imm=0) R10=fp0
; void *data = (void *)(long)skb->data;
0: (61) r1 = *(u32 *)(r1 +76)
invalid bpf_context access off=76 size=4
processed 1 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
-- END PROG LOAD LOG --
libbpf: prog 'icmp_timestamp': failed to load: -13
libbpf: failed to load object 'rocode.o'

Error: failed to load object file

问题分析

核心错误是invalid bpf_context access off=76 size=4,原因是混淆了不同类型BPF程序的上下文和规则:

  • 代码声明挂载点为SEC("filter"),使用struct __sk_buff(TC程序上下文),但返回XDP_PASS/XDP_DROP(XDP程序返回值),属于跨类型混用。
  • struct __sk_buff和XDP程序的struct xdp_md内存布局完全不同,直接访问skb->data会触发内核无法识别的内存偏移,导致权限拒绝。

修复方案

根据需求选择以下两种路径之一:

路径1:改为XDP程序

若要在XDP层面实现ICMP过滤,修改代码如下:

#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <netinet/in.h>
#include <bpf/bpf_helpers.h>

struct bpf_map_def SEC("maps") timestamp_map = {
    .type = BPF_MAP_TYPE_PERCPU_ARRAY,
    .key_size = sizeof(int),
    .value_size = sizeof(uint64_t),
    .max_entries = 1,
};

// XDP程序挂载点为xdp
SEC("xdp")
int icmp_timestamp(struct xdp_md *ctx) {
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    struct ethhdr *eth = data;

    // 边界检查,避免越界访问
    if ((void *)eth + sizeof(*eth) > data_end)
        return XDP_DROP;

    if (eth->h_proto == htons(ETH_P_IP)) {
       struct iphdr *ip = data + sizeof(struct ethhdr);
       if ((void *)ip + sizeof(*ip) > data_end)
           return XDP_DROP;

        if (ip->protocol == IPPROTO_ICMP) {
            uint64_t timestamp = bpf_ktime_get_ns();
            int key = 0;
            bpf_map_update_elem(&timestamp_map, &key, &timestamp, BPF_ANY);
            return XDP_PASS;    
        }
    }
    return XDP_DROP;
}

char _license[] SEC("license") = "GPL";

加载时需绑定到网卡:

sudo bpftool prog load name_of_code.o /sys/fs/bpf/name_of_code && sudo bpftool net attach xdp id $(bpftool prog show name icmp_timestamp | awk '{print $1}') dev <你的网卡名>

路径2:改为TC Filter程序

若要在TC层面实现过滤,修改代码如下:

#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <netinet/in.h>
#include <linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>

struct bpf_map_def SEC("maps") timestamp_map = {
    .type = BPF_MAP_TYPE_PERCPU_ARRAY,
    .key_size = sizeof(int),
    .value_size = sizeof(uint64_t),
    .max_entries = 1,
};

// TC Filter程序挂载点为clsact
SEC("clsact")
int icmp_timestamp(struct __sk_buff *skb) {
    void *data_end = (void *)(long)skb->data_end;
    void *data = (void *)(long)skb->data;
    struct ethhdr *eth = data;

    // 边界检查,避免越界访问
    if ((void *)eth + sizeof(*eth) > data_end)
        return TC_ACT_SHOT;

    if (eth->h_proto == htons(ETH_P_IP)) {
       struct iphdr *ip = data + sizeof(struct ethhdr);
       if ((void *)ip + sizeof(*ip) > data_end)
           return TC_ACT_SHOT;

        if (ip->protocol == IPPROTO_ICMP) {
            uint64_t timestamp = bpf_ktime_get_ns();
            int key = 0;
            bpf_map_update_elem(&timestamp_map, &key, &timestamp, BPF_ANY);
            return TC_ACT_OK;    
        }
    }
    return TC_ACT_SHOT;
}

char _license[] SEC("license") = "GPL";

加载并挂载到TC钩子:

sudo bpftool prog load name_of_code.o /sys/fs/bpf/name_of_code
sudo tc qdisc add dev <你的网卡名> clsact
sudo tc filter add dev <你的网卡名> ingress bpf da obj name_of_code.o sec clsact

额外注意事项

  • 所有BPF程序必须添加边界检查(如(void *)eth + sizeof(*eth) > data_end),否则内核会拒绝加载。
  • 不同类型的BPF程序(XDP/TC/Tracepoint等)有专属的上下文结构、挂载点和返回值,不可混用。

内容的提问来源于stack exchange,提问作者Manideep G

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 06:25:25