三、几个细节问题
1、数据部分改变时的重校验
考虑这样的应用场景:路由器转发IP报文时,有可能只更改了IP数据包头的部分内容(如更改了TTL,分片了或SNAT更改了源IP等~~~),却需要重校验的问题.为提高转发效率,要求重校验算法尽可能快,故出现了如下所示的重校验算法(只是一个简单的示例):
{
unsigned long sum;
unsigned short old;
old = ntohs(*(unsigned short *)&ipptr->ttl);
ipptr->ttl -= n;
sum = old + (~ntohs(*(unsigned short *)&ipptr->ttl) & 0xffff);
sum += ntohs(ipptr->Checksum);
sum = (sum & 0xffff) + (sum>>16);
ipptr->Checksum = htons(sum + (sum>>16));
}
算法的实现依据是这样的.假设包头原校验和为~C,改变的字段的原始值是m,更改后的值是m',设~C'为重校验和,则有 ~C' = ~(C+(-m)+m') = ~C+(m-m') = ~C+m+~m'等价关系的成立基于反码的运算性质:取反运算满足结合律,按位取反运算与符号取反(及相反数)是等价的(即~C=-C).
如果有多个字段改变,只是上面的公式中的m和m'有多个而已,直接用反码加法搞定即可。
2、为什么采用反码和运算
IP数据包校验要求速度快,所以只采用了简单的和校验,为什么采用反码和而不是补码和呢?
i)反码和的溢出有后效性(蔓延性)
反码和将高位溢出加到低位,导致这个溢出会对后面操作有永久影响,有后效性;而补码和直接将高位和溢出,导致这个溢出对后面的操作再无影响,因此无后效性
ii)反码校验无需考虑字节序
正因为反码和的溢出有后效性,导致大端字节序(big-endian)和小端字节序(little-endian)对同一数据序列(如两个16比特的序列)产生的校验和也只是字节序相反,而补码和因为将溢出丢掉了,不同字节序之间的校验和大不相同且没什么联系。
基于以上的理由,校验和运算既可选择在数据被转换成网络字节序前,也可选择在之后,只要保证被校验的字段和填写的校验和字段的字节序保持一致就可以了。(这其实可以看作是负负得正,计算校验和与字节序有关,然后写校验和字段与字节序有关,然后直接计算校验和再写校验和字段则与字节序无关。)
四、参考文章
http://blog.chinaunix.net/u/20/showart_438512.html,关于IP校验和的
http://blog.chinaunix.net/u/12313/showart_176114.html,关于网络校验和的
http://www.wesoho.com/article/Delphi/2143.htm,关于IP校验和的
http://blog.chinaunix.net/u/20/showart_438418.html,关于补码和反码的