How to calculate TCP/UDP checksum for non-linear skb / payload?
我正在尝试计算正在发送的 skb 上的 TCP/UDP 标头校验和。
基本上,有两个函数应该完成所有工作
(在这个 stackoverflow 问题和这个问题中也提到过):
-
csum_tcpudp_magic -
csum_partial
例如,对于 TCP:
1 2 3 4 5 6 7 8 | tcph->check = 0; tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, tcp_packet_len, IPPROTO_TCP, csum_partial((unsigned char *)tcph, tcp_packet_len, 0)); |
这很好用——我能够传输 TCP/UDP 数据包并在接收端使用wireshark 验证校验和。但是,我找不到任何关于碎片化 SKB 的信息。
这两个"魔术"函数会处理 TCP/UDP 负载分片的情况吗?
此外,TCP/UDP 标头可能驻留在 SKB 的非线性部分(也可能是分段的)。
这些函数也会处理这种情况吗?
我确实知道我可以尝试使用更大的数据包,这会导致它们碎片化,但是现在错误处理的代价是内核 oops,我不能让系统崩溃。
经过大量挖掘,我发现了这一点。
如果要使用它们来计算 TCP/UDP 校验和,我需要使所有数据包有效负载驻留在 skb 的线性部分中。
幸运的是,Linux 中有一个实用函数可以做到这一点 -
所以基本上,流程是:
1 2 3 4 5 6 7 8 9 10 11 12 | if (unlikely(skb_linearize(skb) != 0)) ... goto drop - no memory...; ... do whatever you want with data ... tcph->check = 0; tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, tcp_packet_len, IPPROTO_TCP, csum_partial((unsigned char *)tcph, tcp_packet_len, 0)); |
如果无法进行线性化(性能、内存或其他原因),则需要"手动"计算校验和。
到此代码运行时,数据包已重新组装。