使用eBPF在TC中修改端口与校验和后无法获取curl响应
问题:eBPF改写TCP目标端口后校验和异常
我想用eBPF将目标端口为80的数据包重定向到5432端口。本地未使用容器,直接部署了nginx(80端口)和postgresql(5432端口),期望执行curl localhost:80时,程序能将数据包的目标端口改为5432,从而得到与curl localhost:5432相同的结果,仅修改端口号,不改变IP地址。
由于本地三次握手通过lo接口完成,我编写了挂载到TC钩子的eBPF程序,挂载在iface=lo上,尝试改写所有目标端口为80的数据包。我尝试了两种方法重写端口后重新计算校验和,但均出现相同问题:
static __always_inline __u16 csum_fold_helper(__u32 csum) { __u32 sum; sum = (csum>>16) + (csum & 0xffff); sum += (sum>>16); return ~sum; } SEC("tc_cls") int tc_egress(struct __sk_buff *skb) { void *data_end = (void *)(long)skb->data_end; void *data = (void *)(long)skb->data; struct ethhdr *eth; struct iphdr *iph; struct tcphdr *tcph; eth = data; if ((void *)eth + sizeof(*eth) > data_end) { return TC_ACT_OK; } if (eth->h_proto != __bpf_htons(0x0800)) { return TC_ACT_OK; } iph = data + sizeof(*eth); if ((void *)iph + sizeof(*iph) > data_end) { return TC_ACT_OK; } if (iph->protocol != IPPROTO_TCP) { return TC_ACT_OK; } tcph = data + sizeof(*eth) + sizeof(*iph); if ((void *)tcph + sizeof(*tcph) > data_end) { return TC_ACT_OK; } if (tcph->dest == __bpf_htons(80)) { // update checksum method #1 // __u64 from = tcph->dest; // __u16 new_port = __bpf_htons(5432); // tcph->dest = new_port; // __u64 res = bpf_skb_store_bytes(skb, ETH_HLEN + sizeof(struct iphdr) + 2, &(new_port), 2, BPF_F_RECOMPUTE_CSUM); // update checksum method #2 __u16 new_port = __bpf_htons(5432); __u16 old_csum = tcph->check; __u32 sum = bpf_csum_diff(&tcph->dest, 2, &new_port, 2, 0); __u16 csum = csum_fold_helper(sum); tcph->dest = new_port; bpf_l4_csum_replace(skb, ETH_HLEN + sizeof(struct iphdr) + 16, old_csum, csum, BPF_F_PSEUDO_HDR); } return TC_ACT_OK; }
用tcpdump验证5432端口的活动时,能看到数据包已到达,但所有带S标志的SYN数据包校验和错误,带R标志的RST数据包校验和正确。然而卸载eBPF程序后直接执行curl localhost:5432,虽然tcpdump显示所有数据包校验和错误,但能正常得到响应。
现需帮忙检查校验和是否正确更新,以及程序是否遗漏其他步骤。
内容的提问来源于stack exchange,提问作者Angela
相关产品推荐
相关产品推荐

