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

TC eBPF修改目的IP后数据包丢失问题排查求助

问题:TC eBPF修改目的IP后数据包被内核丢弃,无法从egress发出

我基于TC eBPF编写了一款代理,可根据特定条件修改数据包的目的IP。修改完成后,程序已修复IP校验和并返回TC_ACT_OK。我将该代理挂载至ingress路径,但修改后的数据包似乎被内核丢弃,始终无法从egress路径发出。以下是简化后的eBPF函数,挂载后仍存在相同问题(该函数将目的IP静态设置为10.0.0.2)。我的环境中存在同一网段、IP为10.0.0.2的接口,该接口通过虚拟网桥连接(使用Linux虚拟命名空间),原本预期数据包会从egress路径发出。请问问题可能出在哪里?如何排查内核丢弃数据包的原因?

SEC("tc")
int handle_ingress(struct __sk_buff *skb) {
    void *data = (void *)(long)skb->data;
    void *data_end = (void *)(long)skb->data_end;

    struct ethhdr *eth = data;
    if ((void *)eth + sizeof(*eth) > data_end)
        return TC_ACT_SHOT; 

    if (eth->h_proto != bpf_htons(ETH_P_IP))
        return TC_ACT_OK; 

    struct iphdr *ip = data + sizeof(*eth);
    if ((void *)ip + sizeof(*ip) > data_end)
        return TC_ACT_SHOT;

    __be32 new_dst = bpf_htonl(0x0A000002);  // 10.0.0.2 in network byte order

    // Recalculate the IP checksum
    __s64 sum = bpf_csum_diff((void *)&ip->daddr, 4, (void *)&new_dst, 4, 0);
    if (bpf_skb_store_bytes(skb, sizeof(struct ethhdr) + offsetof(struct iphdr, daddr), (void *)&new_dst, 4, 0) < 0)
        return TC_ACT_SHOT; // Drop packet if modification fails
    bpf_l3_csum_replace(skb, sizeof(struct ethhdr) + offsetof(struct iphdr, check), 0, sum, 0);

    return TC_ACT_OK;
}

可能的问题点

1. 以太网帧目的MAC未更新

修改IP后,内核不会自动触发ARP查询更新二层目的MAC。原数据包的目的MAC仍是修改前的地址,导致二层转发失败,内核直接丢弃数据包。

2. 路由与IP转发配置缺失

  • 当前命名空间中可能没有到10.0.0.2的有效路由,或者路由指向的出口不是目标网桥接口。
  • 系统未开启IP转发:如果当前节点是路由角色,net.ipv4.ip_forward参数若为0,会阻止跨接口转发数据包。

3. 校验和更新逻辑错误

虽然调用了bpf_l3_csum_replace,但参数或计算逻辑可能有误:

  • bpf_csum_diff返回的是校验和增量,需确认是否正确应用到原校验和上;
  • 若数据包开启了硬件校验和卸载,可能需要额外设置BPF_F_HW_CSUM标志(但IPv4校验和通常由软件计算)。

4. 内核过滤规则拦截

iptables/nftables的INPUT、FORWARD链可能存在DROP规则,拦截了修改后的数据包;ebtables也可能对二层流量做了限制。

5. 虚拟网桥转发限制

网桥可能未开启转发功能,或者STP协议阻塞了端口,导致数据包无法通过网桥转发到目标命名空间。


排查与验证步骤

  • 查看内核丢弃日志:执行dmesg -w实时监控内核日志,内核通常会打印数据包丢弃的具体原因(如校验和错误、MAC不匹配、路由不存在等)。
  • 多节点抓包验证:
    • 在ingress接口抓包,确认eBPF已成功修改目的IP;
    • 在网桥接口抓包,检查数据包是否进入网桥;
    • 在目标命名空间的接口抓包,验证数据包是否被转发过来。
  • 手动设置目的MAC测试:如果已知10.0.0.2对应的MAC地址,在eBPF程序中添加修改以太网目的MAC的逻辑,测试是否能正常转发。
  • 检查路由与转发配置:
    • 执行ip route show确认存在到10.0.0.2的路由;
    • 执行sysctl net.ipv4.ip_forward,若结果为0,执行sysctl -w net.ipv4.ip_forward=1开启转发。
  • 校验和正确性验证:用tcpdump抓包后,查看IPv4头部的校验和是否正确,排除校验和错误导致的丢弃。

修复示例:添加MAC地址更新逻辑

如果已知目标IP的MAC地址,可在eBPF程序中直接修改:

SEC("tc")
int handle_ingress(struct __sk_buff *skb) {
    void *data = (void *)(long)skb->data;
    void *data_end = (void *)(long)skb->data_end;

    struct ethhdr *eth = data;
    if ((void *)eth + sizeof(*eth) > data_end)
        return TC_ACT_SHOT; 

    if (eth->h_proto != bpf_htons(ETH_P_IP))
        return TC_ACT_OK; 

    struct iphdr *ip = data + sizeof(*eth);
    if ((void *)ip + sizeof(*ip) > data_end)
        return TC_ACT_SHOT;

    __be32 new_dst = bpf_htonl(0x0A000002);  // 10.0.0.2 in network byte order
    // 手动设置10.0.0.2对应的MAC地址
    unsigned char new_dst_mac[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};

    // 更新目的MAC
    if (bpf_skb_store_bytes(skb, offsetof(struct ethhdr, h_dest), new_dst_mac, 6, 0) < 0)
        return TC_ACT_SHOT;

    // Recalculate the IP checksum
    __s64 sum = bpf_csum_diff((void *)&ip->daddr, 4, (void *)&new_dst, 4, 0);
    if (bpf_skb_store_bytes(skb, sizeof(struct ethhdr) + offsetof(struct iphdr, daddr), (void *)&new_dst, 4, 0) < 0)
        return TC_ACT_SHOT; // Drop packet if modification fails
    bpf_l3_csum_replace(skb, sizeof(struct ethhdr) + offsetof(struct iphdr, check), 0, sum, 0);

    return TC_ACT_OK;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:04:51