如何计算IP或ICMP协议首部里的checksum字段
程序员文章站
2022-07-10 14:33:15
...
协议层的首部里都会有一个校验字段checksum,由发送端计算和存储,由接收端验证,用来保证报文在传输过程中的完整性
checksum的计算方法多样,而对于IP Header和ICMP Header中的checksum,采用相同的计算方法-加法校验和算法
计算校验和
IP Header中的checksum只校验IP首部,不校验数据部分
ICMP Header中的checksum校验ICMP首部和数据部分
反码求和法
反码求和法,是先求和,然后再取反
发送时
- 将校验和字段设置为0
- 每16个bit(即2个字节)组成一个数,相加
- 取反码,填入校验和字段
书上或网上都这样描述,大致是没错的,但是实际经不起推敲,有一些细节并没有关注到
比如不管是IP Header还是ICMP Header中的checksum都是16个bit,而这些数字相加有可能超过16个bit,怎么办?难道只是简单地把高位数字省略?
当然不是
IP首部的checksum只计算IP首部的数据20个字节,每两个字节组成一个数,这当然比较好分配
然而像ICMP首部中的checksum计算的是首部和数据部分,有可能有奇数个字节,每2个字节组成一个数,最后还会剩下一个字节,这最后一个字节是简单地相加吗?
当然也不是(尤其这个问题,让我搞了很久)
所以总结下计算方法
- 将校验和字段设置为0
- 每16个bit(即2个字节)组成一个数,相加,如果超过16个bit,把超过的高位值加到这16个bit值上,得到的新值再和下一个值相加
- 如果最后还剩8个bit值,不能简单的加到低位,要把这8个bit当成高位值,再用0填充一个16个bit值,相加
- 最后取反,填充到校验和字段
capl如何计算
word GetOrVerifyChecksumValue(byte data[], long length)
{
int i = 0;
dword temp = 0;
word checksum = 0;
while (i < length)
{
if ((i+1) < length)
{
temp = checksum + ((data[i]<<8) + data[i+1]);
checksum = (temp & 0xffff) + (temp >> 16);
i = i + 2;
}
else
{
temp = checksum + (data[i] << 8);
checksum = (temp & 0xffff) + (temp >> 16);
i = i + 1;
}
}
checksum = ~checksum;
return checksum;
}
注意:
(data[i]<<8)一定要加括号,不然会先计算后面的加号
验证校验和
验证校验和的方法也就简单明了了
- 把校验和值填入校验和字段
- 每16个bit组成一个数,相加,如果超过16个bit,把超过的加到低位,得到的新值再和下一个值相加
- 最后的结果为0,说明所有的值都是正确的
如果用capl函数表示,和上面的函数相同,只是计算时要把校验和字段设置为0,返回值是校验和,验证时要把校验和字段设置checksum值,返回值是0
了解这个有什么好处?
capl中提供了方法组装报文,校验和会自动计算出,不需要手动计算,但是,在应用层有可能有一些自定义协议,如果采用了这种算法,就需要自己手动计算了