首页 > 代码库 > IP校验和
IP校验和
首部检验和字段是根据 I P首部计算的检验和码,它不对首部后面的数据进行计算。
I C M P、I G M P、U D P和T C P在它们各自的首部中均含有同时覆盖首部和数据检验和码。
为了计算一份数据报的 I P检验和,首先把检验和字段置为 0。然后,对首部中每个 16 bit进行二进制反码求和(整个首部看成是由一串 16 bit的字组成),结果存在检验和字段中。
当收到一份 I P数据报后,同样对首部中每个 16 bit进行二进制反码的求和。由于接收方在计算过程中包含了发送方存在首部中的检验和,因此,如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全 1。如果结果不是全 1(即检验和错误),那么I P就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。
I C M P、I G M P、U D P和T C P都采用相同的检验和算法,尽管 T C P和U D P除了本身的首部和数据外,在 I P首部中还包含不同的字段。在 RFC 1071[Braden, Borman and Patridge 1988]中有关于如何计算 I n t e r n e t检验和的实现技术。由于路由器经常只修改 T T L字段(减 1),因此当路由器转发一份报文时可以增加它的检验和,而不需要对I P 整个首部进行重新计算。 R F C1141[Mallory and Kullberg 1990]为此给出了一个很有效的方法。但是,标准的BSD实现在转发数据报时并不是采用这种增加的办法。
二进制反码求和
先进行二进制求和,然后对和取反。对一个无符号的数,先求其反码,然后从低位到高位,按位相加,有益处则向高位进1(和一般的二进制法则一样),若最高位有进位,则向最低位进1。
关于二进制反码求和运算需要说明的一点是,先取反后相加与先相加后取反,得到的结果是一样的。
a、 不依赖系统是大端小端。即无论你是发送方计算机或者接收方检查校验和时,都不要调用htons或者ntohs,直接通过上面的算法就可以得到正确的结果。这个问题你可以自己举个例子,用反码求和时,交换16位数的字节顺序,得到的结果相同,只是字节顺序相应地也交换了;而如果使用原码或者补码求和,得到的结果可能就不同。
b、 计算和验证校验和比较简单、快递。
具体方法
在发送数据时,为了计算数IP据报的校验和。应该按如下步骤:
(1)把IP数据报的首部都置为0,包括校验和字段。
(2)把首部看成以16位为单位的数字组成,依次进行二进制反码求和。
(3)把得到的结果存入校验和字段中。
在接收数据时,计算数据报的校验和相对简单,按如下步骤:
(1)把首部看成以16位为单位的数字组成,依次进行二进制反码求和,包括校验和字段。
(2)检查计算出的校验和的结果是否全为1。
(3)如果全为1,说明被整除,校验和正确。否则,校验和就是错误的,协议栈要抛弃这个数据包
接收方计算校验和时的首部与发送方计算校验和时的首部相比,多了一个发送方计算出来的校验和。因此,如果首部在传输过程中没有发生差错,那么接收方计算的结果应该为全一,因为接收方计算除校验和以外的部分得到值是校验和的反码,再加多出来的校验和当然是全一了。
计算对IP首部检验和的算法如下:
(1)把IP数据包的校验和字段置为0;
(2)把首部看成以16位为单位的数字组成,依次进行二进制求和(注意:求和时应将最高位的进位保存,所以加法应采用32位加法);
(3)将上述加法过程中产生的进位(最高位的进位)加到低16位(采用32位加法时,即为将高16位与低16位相加,之后还要把该次加法最高位产生的进位加到低16位)
(4)将上述的和取反,即得到校验和。
程序
unsigned short cal_chksum(unsigned short *addr, int len) { int nleft = len; int sum = 0; unsigned short *w=addr; unsigned short answer = 0; while(nleft > 1){ // 16bit为单位累加运算 sum += *w++; nleft -= 2; } if(nleft == 1){ //若addr奇数个字节,会剩下最后一字节. sum + =*(unsigned char *)w; } sum = (sum>>16) + (sum&0xffff); sum += (sum>>16); answer = ~sum; return answer; }
示例
参考:
图解TCP/IP
TCP/IP详解卷一
IP头校验和
IP校验和