在ebpf tc egress方向修改了tcp的payload信息,我通过bpf_skb_change_tail将tcp payload从尾部减少了diff_len(测试中该值为88)。
常规思路:我都修改了tcp的包了,肯定要需要重新计算checksum ,所以我按照常规方法计算了checksum ,发现返回包传到客户端时,用wireshark抓包,发现多次请求的checksum 都是一样的,这就非常违反常理,按理说checksum 在客户端每次不同的请求下(客户端使用的随机端口号)都是应该不同的。同时我在ebpf/tc程序中打印了我计算的checksum前后的值
每次请求
我在ebpf中打印的old checksum都是855e,在客户端wireshark抓包的checksum都是5e2d
我在ebpf中计算的checksum恰好为wireshark期望的正确checksum,图中为3fbf和bf3f(字节序不同,值相同),这个值是随着不同请求而变化的,checksum变化符合我们的预期
于是灵机一动,是不是tcp的checksum是在tc hook之后才计算的,我在ebpf/tc里计算完之后恰好和tc hook点之后内核计算的重复了,两者“抵消”,导致客户端抓包每次都是一样的checksum。
ebpf程序中每次打印的old checksum都恒定也印证了这一问题,如果我在ebpf程序中不修改checksum,只减少payload,发现每次请求客户端抓包的checksum都是不同,且实际值和期望的正确值差了58。
于是又联想了old checksum 855e转换字节序后为5e85。这个值和wireshark每次抓到的恒定值5e2d也差58,这58到底是什么含义呢,突然想到0x58的十进制不就是88,不就是我减少的payload的长度吗。
linux5.3版本
于是得出结论:ebpf/tc egress 中checksum不需要重新计算,但是要加减长度(我这只有减的情况,加的不知道),如下代码即可解决问题
tcp->check = tcp->check - bpf_htons(old_tcp_payload_len - new_tcp_payload_len);
结论有点草率,仅供参考,可能和内核版本啥的也有关。