IP协议的CheckSum函数之理解

IP协议中的checksum


Author:zfive5(zhaozidong)

Email :zfive5@yahoo.com.cn

 

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

 

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

structIPHeader

{

   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数据

   */

}

<!--[if gte vml 1]> <![endif]-->

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

 

关于cksum_header的描述在《tcp/ip卷一》中是这样描述的:

  首部检验和字段是根据I P首部计算的检验和码。它不对首部后面的数据进行计算。I C M PI G M PU D PT C P在它们各自的首部中均含有同时覆盖首部和数据检验和码。

  为了计算一份数据报的I P检验和,首先把检验和字段置为0。然后,对首部中每个16 bit进行二进制反码求和(整个首部看成是由一串16 bit的字组成),结果存在检验和字段中。当收到一份I P数据报后,同样对首部中每个16 bit进行二进制反码的求和。由于接收方在计算过,程中包含了发送方存在首部中的检验和,因此,如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全1。如果结果不是全1(即检验和错误),那么I P就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。

 

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

 

正确的函数如下:

unsigned shortCheckSum(unsigned short *szBUF,intiSize)

{       

      unsigned longckSum=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
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
C语言实现IP数据包计算checksum可以使用以下步骤: 1. 首先,我们需要将IP数据包中的头部字段转换为16位字节序列。这些字段包括源IP地址、目标IP地址、协议类型、头部长度和服务类型等。 2. 创建一个累加和变量sum,初始化为0。 3. 将16位字节序列按照两个字节一组进行相加,将结果与sum相加。如果相加结果溢出16位,则将溢出的高位加到低位,即将16位结果与溢出高位相加。 4. 如果还存在剩下的单个字节,也将其加入sum中。 5. 以反码形式取sum并将结果赋值给checksum字段。 6. 最后,将checksum字段的值填入IP数据包的相应位置。 以下是一个示例代码: ```c #include <stdio.h> // 将16位字节序列按组相加 unsigned short sum(unsigned short *buf, int len) { unsigned long sum = 0; while (len > 1) { sum += *buf++; len -= 2; } if (len == 1) { sum += *(unsigned char*)buf; } // 将溢出的高位加到低位 sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); return (unsigned short)(~sum); } int main() { unsigned short data[] = {0x4500, 0x003C, 0x0000, 0x4000, 0x4011, 0xB861, 0xC0A8, 0x0001, 0xC0A8, 0x0002}; unsigned short checksum = sum(data, 10); printf("Calculated checksum: 0x%04X\n", checksum); return 0; } ``` 以上代码首先声明了一个sum函数,用于将16位字节序列按组相加,并返回校验和结果。 在main函数中,声明了一个包含IP头部字段的unsigned short类型的数据数组data。然后调用sum函数计算校验和,并打印结果。 运行代码后,将得到计算出的校验和结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值