IP数据报的首部检验和

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IP数据报的首部检验和相关的知识,希望对你有一定的参考价值。

如图所示

按单位(半字节、字节、双字节)换算,然后以双字节(16位整型,Big Endian)为单位进行累加。最后取反。
例如:
第一个域长度是4位,内容是十进制的4,二进制是0100;第二个域长度是4位,内容是十进制的5,二进制是0101;第三个域长度是8位,内容是十进制的0,二进制是0000 0000。

因此第一个16位值是 0100 0101 0000 0000。
第四个域长度是16位,内容是十进制的28,二进制是 0000 0000 0001 1100。

因此第二个16位值是 0000 0000 0001 1100。
……以此类推。
参考技术A 好处是,可以减少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。

关于二进制反码求和运算需要说明的一点是,先取反后相加与先相加后取反,得到的结果是一样的。

It may look awkword to use a 1\'s complement addition on 2\'s complement machines. This method however has its own benefits.
Probably the most important is that it is endian independent. Little Endian computers store hex numbers with the LSB last (Intel processors for example). Big Endian computers put the LSB first (IBM mainframes for example). When carry is added to the LSB to form the 1\'s complement sum (see the example) it doesn\'t matter if we add 03 + 01 or 01 + 03. The result is the same.
Other benefits include the easiness of checking the transmission and the checksum calculation plus a variety of ways to speed up the calculation by updating only IP fields that have changed.

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
  得到的结果是全一,正确。
 

 IP/UDP/TCP/ICMP校验和异同

四种报文的校验和算法一样,但在作用范围存在不同:IP校验和只校验20字节的IP报头;而ICMP校验和覆盖整个报文(ICMP报头+ICMP数据);UDP和TCP校验和不仅覆盖整个报文,而且还有12字节的IP伪首部,包括源IP地址(4字节)、目的IP地址(4字节)、协议(2字节,第一字节补0)和TCP/UDP包长(2字节,包含协议头和数据)。另外UDP、TCP数据报的长度可以为奇数字节,所以在计算校验和时需要在最后增加填充字节0(注意,填充字节只是为了计算校验和,可以不被传送)。
这里还要提一点,UDP的校验和是可选的,当校验和字段为0时,表明该UDP报文未使用校验和,接收方就不需要校验和检查了!那如果UDP校验和的计算结果是0时怎么办呢?书上有这么一句话:“如果校验和的计算结果为0,则存入的值为全1(65535),这在二进制反码计算中是等效的。”
TCP校验和示例
由于TCP首部中不包含源地址与目标地址等信息,为了保证TCP校验的有效性,在进行TCP校验和的计算时,需要增加一个TCP伪首部的校验和,定义如下:
struct 
{
    unsigned long saddr;  //源地址

    unsigned long daddr; //目的地址

    char mbz; //强制置空

    char ptcl; //协议类型

    unsigned short tcpl; //TCP长度

}psd_header;

然后我们将这两个字段复制到同一个缓冲区SendBuf中并计算TCP校验和:
memcpy(SendBuf, &psd_header, sizeof(psd_header)); 
memcpy(SendBuf+sizeof(psd_header), &tcp_header, sizeof(tcp_header));
tcp_header.th_sum=checksum((unsigned short *)SendBuf, sizeof(psd_header)+sizeof(tcp_header));

 

参考:

图解TCP/IP

TCP/IP详解卷一

IP头校验和

https://zhidao.baidu.com/question/331155022269467205.html

以上是关于IP数据报的首部检验和的主要内容,如果未能解决你的问题,请参考以下文章

IP数据报的格式

计算机网络针对性复习

计算机网络针对性复习

计算机网络针对性复习

ip数据报里面的首部长度问题

IP数据包的格式