IP 协议中的checksum

  

Author:zfive5(zhaozidong)

Email :zfive5@yahoo.com.cn

 

最近一段时间,对网络又开始追根溯源,最好的办法就是打开开源协议栈看一个究竟,不求写一个完整的ip协议栈,但求通达解惑!

 

众所周知,IP头定义如下:

struct IPHeader

{

   unsigned char  ver_hlen;  

   unsigned char  tos;

   unsigned short  len;

   unsigned short  id;

   unsigned short  offset;

   unsigned char   ttl;

   unsigned char   type;

   unsigned short   cksum_header;

   unsigned long   ipsrc;

   unsigned long   ipdest;

   /*

     后面可能存在option数据

   */

}

IP头中的大多字段都好理解,只要一本TCP/IP入门的书就可以明明白白了,对cksum字段理解,如果只是看书,到头来很可能还不清楚怎么算它!

 

关于cksum_header的描述在《tcp/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 就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。

 

不知道有多少能人看完此描述后,写出算法或函数来!

 

正确的函数如下:

unsigned short CheckSum(unsigned short *szBUF,int iSize)

{       

       unsigned long ckSum=0;

    for(;iSize>1;iSize-=sizeof(unsigned short))

              ckSum+=*szBUF++;

 

       if(iSize==1)

              ckSum+=*(unsigned char *)szBUF;

 

       ckSum=(ckSum>>16)+(ckSum&0xffff);

       ckSum+=(ckSum>>16);

       return(unsigned short )(~ckSum);

}

 

让我们假设一个IP头数据,来解cksum的惑!

 

IP头数据:

 

01000101   / ver_hlen; / 

00000000   /tos*/

00000000 00000010/*len*/

00000000 00000000/*id*/

00000000 00000000/*offset*/

00000001/*ttl*/

00010001/*type*/

00000000 00000000/*cksum0)*/

01111111 00000000 00000000 0000001/*sip*/

01111111 00000000 00000000 0000001/*dip*/

 

运算过程(注意是大端格式加):

 

01000101 00000000

01000101 00000000

00000000 00000010

01000101 00000010

00000000 00000000

01000101 00000010

00000000 00000000

01000101 00000010

00000100 00010001

01001001 00010011

00000000 00000000

01001001 00010011

01111111 00000000

11001000 00010011

00000000 00000001

11001000 00010100

01111111 00000000

01000111 00010100

00000000 00000001

和: 101000111 00010101

                  

ckSum=(ckSum>>16)+(ckSum&0xffff)后:

 

   00000000 00000001                1

 01000111 00010101

和: 01000111 00010110

 

ckSum+=(ckSum>>16)后:

   

   01000111 00010110

   00000000 00000000

和: 01000111 00010110

 

~: 10111000 11101000(效检和)

 

 

运算过程(注意用小端格式加):

 

00000000 01000101

00000000 01000101

00000010 00000000

00000010 01000101

00000000 00000000

00000010 01000101

00000000 00000000

00000010 01000101

00010001 00000100

00010011 01001001

00000000 00000000  

00010011 01001001

00000000 01111111  

00010011  11001000

00000001 00000000  

00010100 11001000

00000000 01111111  

00010101 01000111

00000001 00000000  

和: 00010110 01000111

ckSum=(ckSum>>16)+(ckSum&0xffff)后:

 

   00000000 00000000                1

 00010110 01000111

和: 00010110 01000111

 

ckSum+=(ckSum>>16)后:

   

   00010110 01000111

   00000000 00000000

和: 00010110 01000111

 

~: 11101001 10111000(效检和)

 

 

checksum211101000 10111000(小端)

 

checksum110111000 11101000(大端)

 

算法一样,说白了就是循环加,加到没有进位为止,然后在取反!

 

在现在《TCPIP卷二》中的cksum实现有以下语句:

while(sum>>16)

 sum=(sum&0xffff)+(sum>>16);

 

通过它更能说明就是在作循环加操作,现在又有一个疑问:

       ckSum=(ckSum>>16)+(ckSum&0xffff);

       ckSum+=(ckSum>>16);         

while(sum>>16)

   sum=(sum&0xffff)+(sum>>16);

等价吗?

 

等价,在一定条件下等价,大家都知道ip理论上最长是0xffff

那么

checksum最大不会超过:0xffff0000

这样>>16后为0xffff

0xffff+checksum最大0x1fffe

0x1fffe >>16+0x1fffe=0xffff

注意了没有了进位

所以得到等价的结论!

 

 

以前每读到cksum注解时,书上只是草草曰16位反码和之云云,没有强调进位也要参加运算一点征兆,直到最近写了不少程序才看清楚这个细节!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值