编译成功的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(×tamp_map, &key, ×tamp, 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(×tamp_map, &key, ×tamp, 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(×tamp_map, &key, ×tamp, 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
相关产品推荐
相关产品推荐

