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
相关产品推荐
相关产品推荐

