使用bpftool加载eBPF程序时触发Invalid argument错误求助
问题描述
我正在学习TC ingress与egress数据路径,编写了一段用于在TC层拦截特定IP的eBPF程序,编译无异常。但执行以下bpftool命令加载时出现Invalid argument错误:
bpftool net detach xdp dev lo rm -f /sys/fs/bpf/$(TARGET) bpftool prog load $(BPF_OBJ) /sys/fs/bpf/$(TARGET) bpftool net attach xdp pinned /sys/fs/bpf/$(TARGET) dev lo
程序代码如下:
struct { __uint(type, BPF_MAP_TYPE_ARRAY); __uint(max_entries, 20000); __type(key, __u32); __type(value, __u32); } blocked SEC(".maps"); SEC("cgroup_skb/ingress") int process_ingress_pkt(struct __sk_buff *skb){ void *data = (void *)(long)skb->data; void *data_end = (void *)(long)skb->data_end; if (data > data_end) { return TC_ACT_SHOT; } struct ethhdr *eth_hdr = data; if ((void *) (eth_hdr + 1) > data_end) { return TC_ACT_OK; } if (eth_hdr->h_proto != bpf_htons(ETH_P_IP)) { return TC_ACT_OK; } if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end){ return TC_ACT_SHOT; } long *blocked_ip; struct iphdr *ip = (data + sizeof(struct ethhdr)); if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) { return TC_ACT_SHOT; } blocked_ip = bpf_map_lookup_elem(&blocked, &(ip->saddr)); if (blocked_ip == NULL) { return TC_ACT_OK; }else{ return TC_ACT_SHOT; } } char __license[] SEC("license") = "GPL";
bpftool返回的错误日志:
libbpf: prog 'process_ingress_pkt': BPF program load failed: Invalid argument libbpf: prog 'process_ingress_pkt': -- BEGIN PROG LOAD LOG -- ; int process_ingress_pkt(struct __sk_buff *skb){ 0: (b7) r0 = 2 ; void *data_end = (void *)(long)skb->data_end; 1: (61) r3 = *(u32 *)(r1 +80) ; void *data = (void *)(long)skb->data; 2: (61) r2 = *(u32 *)(r1 +76) ; if (data > data_end) { 3: (2d) if r2 > r3 goto pc+22 R0_w=inv2 R1=ctx(id=0,off=0,imm=0) R2_w=pkt(id=0,off=0,r=0,imm=0) R3_w=pkt_end(id=0,off=0,imm=0) R10=fp0 4: (b7) r0 = 0 ; if ((void *) (eth_hdr + 1) > data_end) { 5: (bf) r1 = r2 6: (07) r1 += 14 ; if ((void *) (eth_hdr + 1) > data_end) { 7: (2d) if r1 > r3 goto pc+18 R0_w=inv0 R1_w=pkt(id=0,off=14,r=14,imm=0) R2_w=pkt(id=0,off=0,r=14,imm=0) R3_w=pkt_end(id=0,off=0,imm=0) R10=fp0 ; if (eth_hdr->h_proto != bpf_htons(ETH_P_IP)) { 8: (71) r1 = *(u8 *)(r2 +12) 9: (71) r4 = *(u8 *)(r2 +13) 10: (67) r4 <<= 8 11: (4f) r4 |= r1 ; if (eth_hdr->h_proto != bpf_htons(ETH_P_IP)) { 12: (55) if r4 != 0x8 goto pc+13 R0=inv0 R1=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R2=pkt(id=0,off=0,r=14,imm=0) R3=pkt_end(id=0,off=0,imm=0) R4=inv8 R10=fp0 ; if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end){ 13: (bf) r1 = r2 14: (07) r1 += 34 15: (b7) r0 = 2 ; if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end){ 16: (2d) if r1 > r3 goto pc+9 R0_w=inv2 R1_w=pkt(id=0,off=34,r=34,imm=0) R2=pkt(id=0,off=0,r=34,imm=0) R3_w=pkt_end(id=0,off=0,imm=0) R4=inv8 R10=fp0 ; blocked_ip = bpf_map_lookup_elem(&blocked, &(ip->saddr)); 17: (07) r2 += 26 ; blocked_ip = bpf_map_lookup_elem(&blocked, &(ip->saddr)); 18: (18) r1 = 0xffffacebc2209000 20: (85) call bpf_map_lookup_elem#1 21: (bf) r1 = r0 22: (b7) r0 = 1 23: (55) if r1 != 0x0 goto pc+1 from 23 to 25: R0_w=inv1 R1_w=map_value(id=0,off=0,ks=4,vs=4,imm=0) R10=fp0 ; 25: (67) r0 <<= 1 ; } 26: (95) exit At program exit the register R0 has value (0x2; 0x0) should have been in (0x0; 0x1) processed 28 insns (limit 1000000) max_states_per_insn 0 total_states 2 peak_states 2 mark_read 1 -- END PROG LOAD LOG -- libbpf: prog 'process_ingress_pkt': failed to load: -22 libbpf: failed to load object 'firewall.o' Error: failed to load object file
我推测错误日志中At program exit the register R0 has value (0x2; 0x0) should have been in (0x0; 0x1)是返回值不符合预期,但不确定具体原因,希望得到解决方法。
解决方法
1. 修正eBPF程序的返回值类型
你的程序使用了SEC("cgroup_skb/ingress"),这是cgroup套接字钩子,并非TC/XDP钩子。这类程序的返回值规则和TC程序完全不同:
- 返回
0:允许数据包通过 - 返回
1:阻止数据包通过
你代码中使用的TC_ACT_SHOT(值为2)、TC_ACT_OK(值为0)是TC程序的返回码,不符合cgroup_skb钩子的要求,这正是错误日志提示返回值不在(0x0; 0x1)范围内的核心原因。
修改代码中的返回值:
if (data > data_end) { return 1; // 替换原TC_ACT_SHOT } if ((void *) (eth_hdr + 1) > data_end) { return 0; // 替换原TC_ACT_OK } if (eth_hdr->h_proto != bpf_htons(ETH_P_IP)) { return 0; // 替换原TC_ACT_OK } if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end){ return 1; // 替换原TC_ACT_SHOT } if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) { return 1; // 替换原TC_ACT_SHOT } blocked_ip = bpf_map_lookup_elem(&blocked, &(ip->saddr)); if (blocked_ip == NULL) { return 0; // 替换原TC_ACT_OK }else{ return 1; // 替换原TC_ACT_SHOT }
2. 修正加载命令(适配cgroup_skb钩子)
你当前使用的是XDP的加载命令,但你的程序是cgroup_skb类型,需要挂载到cgroup上,而非直接绑定到网络设备:
步骤如下:
- 创建并挂载一个cgroupv2目录:
mkdir -p /sys/fs/cgroup/test mount -t cgroup2 none /sys/fs/cgroup/test
- 将当前shell(或目标进程)加入该cgroup:
echo $$ > /sys/fs/cgroup/test/cgroup.procs
- 加载eBPF程序并挂载到cgroup的ingress钩子:
bpftool prog load $(BPF_OBJ) /sys/fs/bpf/$(TARGET) bpftool cgroup attach /sys/fs/cgroup/test ingress pinned /sys/fs/bpf/$(TARGET)
3. 可选:改为TC程序(如果目标是TC层)
如果你确实想在TC层运行程序,需要修改SEC段为TC钩子类型,比如SEC("tc/ingress"),同时保留TC_ACT_*返回码,然后用TC命令加载:
# 先移除原有XDP程序 bpftool net detach xdp dev lo # 加载程序并挂载到TC ingress链 tc qdisc add dev lo clsact tc filter add dev lo ingress bpf da obj $(BPF_OBJ) sec tc/ingress
内容的提问来源于stack exchange,提问作者Kwaku Biney
相关产品推荐
相关产品推荐

