一个IP检验和引起的问题

今天在QQ里有一个人问到ip检验和的问题,说自己抓包得到数据包,拿掉14字节的以太网头,取出20字节的IP头对其进行检验和(IP的检验和只是检验其头部,不包含数据),结果不对,问为什么?经过分析,我初步说是因为网络顺序和主机顺序的问题。最后自己测试发现不是。

1.首先理解ip检验和的算法

 在tcp/ip协议卷一中说明:数据局链路层的检验算法是crc,在IP,tcp,udp层用的是相同的算法。(计算一份IP数据报的校验和,首先将检验和字段设置为0,对首部中每个16位进行二制反码求和,结果存储在检验和字段。收到一份数据报同意)。

 

接受到数据后验证时也可以将检验和位设置为0

0x0000: 00 60 47 41 11 c9 00 09 6b 7a 5b 3b 08 00 45 00
0x0010: 00 1c 74 68 00 00 80 11 59 8f c0 a8 64 01 ab 46
0x0020: 9c e9 0f 3a 04 05 00 08 7f c5 00 00 00 00 00 00
0x0030: 00 00 00 00 00 00 00 00 00 00 00 00

在上面的16进制采样中,起始为Ethernet帧(DLC包)的开头。IPv4分组头从地址偏移量0x000e开始,第一个字节为0x45,最后一个字节为0xe9,即IPv4分组头到目标IP地址为止。根据以上的算法描述,我们可以作如下计算:

(1) 0x4500 + 0x001c + 0x7468 + 0x0000 + 0x8011 + 0x0000(累加和位置先置0) + 0xc0a8 + 0x6401 + 0xab46 + 0x9ce9 = 0x3a66d

(2) 0xa66d + 0x3 = 0xa670  //进位

(3) 0xffff - 0xa670 = 0x598f //检验和的值

 

计算出结果

2.实现,网上有,这里就直接拿来

//unsigned short   *buffer    ip头数据

//int   size   ip头数据的长度20字节

//返回值为计算的检验和

 

unsigned short   checksum(unsigned short   *buffer,   int   size)  
{  
 unsigned   long   cksum=0;   

 while   (size   >   1)   //求和  0x4500 +0x05d4+.........+0x00+0x65
 {   
  
   cksum   +=   *buffer++; 
   size     -=   sizeof(unsigned short);


 }

 if   (size)  
 {  
  cksum   +=   *(unsigned short *)buffer;        
 }  

 

//进位

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

 

//求反

 return   (unsigned short )(~cksum);    
}

 

这里要注意的是自己抓包得到的数据类型的定义,如果直接定义为unsigned short就有问题了,因为其大小为2字节。这样的后果是有一半的数据未叠加。intel系列的底层实现一般是小序列,也就是常说的主机序。因此这里看有将得到的数据存储为unsigned char,在传入的时候转换为unsigned short,这样不仅有利于算法的实现(对首部中每个16位进行二制反码求和),也可有实现网络序列和主机序列的转换。当将unsigned char 转换为unsigned short的时候,最终将以小序列存储。

如:unsigned char a[]={0x45,0x00},经过unsigned short *p = (unsigned short *)a;*p的值为0x0045.

 

char a[] = { 0x45 ,0x00,0x00,0x00 };
int *b = (int *)a; *b的值为0x00000045.

 

注意:抓包得到的是网络序列,转换后存储为主机序列,要正确的计算检验和,必须先将网络序列转换为主机序列,这样计算时才用的是网络序列在计算。才来要得到正确的结果。

 

在接收方进行检验和验证的时候,检验字段已经不为0了,这个时候如果ip包在传输过程中没有发生任何错误,则每个16位的二进制反码求和的结果应该是1.

 

例子:

抓包的到的IP头数据  unsigned char ipDataNet[]={0x45,0x00,0x05,0xd4,0xd5,0xea,0x40,0x00,0x39,0x06,0x3b,0x0e,0x77,0x2a,0xf2,0xf3,0xc0,0ca8,0x00,0x65};

转换为主机序列 unsigned char ipDataHost[] ={0x00,0x45,0xd4,0x05,0xea,0xd5,0x00,0x40,0x06,0x39,0x0e,0x3b,0x2a,0x77,0xf3,0xf2,0xa8,0xc0,0x65,0x00};

 

cksum((unsigned short *)ipDataHost , 20);

结果为1

 

 

这样最终计算的结果就是检验和。

 

3.这个就可以用到QQ协议的解析中,如:qq的版本号为  0x15 ox00

unsigned char QQVer[] = { 0x15 , 0x00};

 

unsigned short *pQqver = (unsigned short *)QQVer;

这样*pQqver的为位ox0015。

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值