首页 > 代码库 > 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;
}

示例

  IP头:
  45 00    00 31
  89 F5    00 00
  6E 06    00 00(校验字段)
  DE B7   45 5D       ->    222.183.69.93
  C0 A8   00 DC     ->    192.168.0.220
  计算:  
  4500 + 0031 +89F5 + 0000 + 6e06+ 0000 + DEB7 + 455D + C0A8 + 00DC =3 22C4
  0003 + 22C4 = 22C7
  ~22C7 = DD38      ->即为应填充的校验和
  当接受到IP数据包时,要检查IP头是否正确,则对IP头进行检验,方法同上:
  计算:
  4500 + 0031 +89F5 + 0000 + 6E06+ DD38 + DEB7 + 455D + C0A8 + 00DC =3 FFFC
  0003 + FFFC = FFFF
  得到的结果是全一,正确。
 

参考:

图解TCP/IP

TCP/IP详解卷一

IP头校验和

IP校验和