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

使用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上,而非直接绑定到网络设备:

步骤如下:

  1. 创建并挂载一个cgroupv2目录:
mkdir -p /sys/fs/cgroup/test
mount -t cgroup2 none /sys/fs/cgroup/test
  1. 将当前shell(或目标进程)加入该cgroup:
echo $$ > /sys/fs/cgroup/test/cgroup.procs
  1. 加载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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 03:35:58