免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 28065 | 回复: 12
打印 上一主题 下一主题

IP校验和详解 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-07-12 14:02 |只看该作者 |倒序浏览
原文见my blog:http://blog.chinaunix.net/u3/94771/showart.php?id=1993908

IP校验和详解

一、校验和算法
   之前一直只知道IP校验和算法反码求和相关的,但具体细节不清楚,今天了解了下。
   IP校验和主要是用来保证数据(IP包头)的完整性的.它用的算法非常简单,就是反码求和校验.需要注意的是反码求和又叫1的补码(one'scomplement),而2的补码就是我们通常说的补码求和了.校验算法具体如下.

1、发送方
   i)将校验和字段置为0,然后将IP包头按16比特分成多个单元,如包头长度不是16比特的倍数,则用0比特填充到16比特的倍数;
   ii)对各个单元采用反码加法运算(即高位溢出位会加到低位,通常的补码运算是直接丢掉溢出的高位),将得到的和的反码填入校验和字段;
   iii)发送数据包.

2、接收方
   i)将IP包头按16比特分成多个单元,如包头长度不是16比特的倍数,则用0比特填充到16比特的倍数;
   ii)对各个单元采用反码加法运算,检查得到的和是否符合是全1(有的实现可能对得到的和会取反码,然后判断最终值是不是全0);
   iii)如果是全1则进行下步处理,否则意味着包已变化从而丢弃之.

   需要强调的是反码和是采用高位溢出加到低位的,如3比特的反码和运算:100b+101b=010b(因为100b+101b=1001b,高位溢出1,其应该加到低位,即001b+1b(高位溢出位)=010b),具体细节请参考文章:http://blog.chinaunix.net/u/20/showart_438418.html


二、校验和源码
   网上流传多组实现,常见的有如下两种(如追求效率可改写为汇编代码):
1、RFC1071源码
unsigned short csum(unsigned char *addr, int count)
{
       /* Compute Internet Checksum for "count" bytes
        * beginning at location "addr".
       */

       register long sum = 0;

       while( count > 1 )

       {
           /* This is the inner loop */
           sum += * (unsigned short) addr++;
           count -= 2;
        }

       /* Add left-over byte, if any */
       if( count > 0 )
           sum += * (unsigned char *) addr;

       /* Fold 32-bit sum to 16 bits */
       while (sum>>16)
           sum = (sum & 0xffff) + (sum >> 16);

       return ~sum;
}

    第一个while循环是做普通加法(2进制补码加法),因为IP包头和TCP整个报文段比较短(没达到2^17数量级),所以不可能导致4字节的sum溢出(unsigned long 一般至少为4字节)).
    紧接着的一个判断语句是为了能处理输入数据是奇数个字节的这种情况.
    再接着的数据循环是实现反码算法(在前面的普通加法得到的数据的基础上),由反码和的高位溢出加到低位的性质,可得到"32位的数据的高位比特移位16比特,再加上原来的低16比特,不影响最终结果"这个等价运算,因为sum的最初值(刚开始循环时)可能很大,所以这个等价运算需循环进行,直到sum的高比特(16比特以上)全为0.对于32位的sum,事实上这个运算循环至多只有两轮,所以也有程序直接用两条"sum = (sum & 0xffff) + (sum >> 16);"代替了整个循环.
    最后,对和取反返回.

2、对数据长度没限制的实现

unsigned short cksum (struct ip *ip, int len)
{
      long sum = 0; /* assume 32 bit long, 16 bit short */

      while ( len >1 )

      {

         sum += *((unsigned short *) ip)++;

         if (sum & 8x00000000) /* if high-order bit set, fold */
             sum = (sum & 0xFFFF) + (sum>> 16) ;

         len -= 2;

       }

       if ( len ) /* take care of left over byte */
            sum += ( unsigned short ) * (unsignedl char *) ip;

       while ( sum >> 16)
           sum =(sum & 0xFFFF) + (sum>> 16);

       return ~sum;

}

   这个实现与前面的一个的最大的不同是对数据的长度没什么限制了,因为它在第一个循环的加法运算中实时检测sum的高位的值,一旦发现其有溢出的危险,就及时运用等价运算关系消除了这个危险.

三、几个细节问题
  1、数据部分改变时的重校验
   考虑这样的应用场景:路由器转发IP报文时,有可能只更改了IP数据包头的部分内容(如更改了TTL,分片了或SNAT更改了源IP等~~~),却需要重校验的问题.为提高转发效率,要求重校验算法尽可能快,故出现了如下所示的重校验算法(只是一个简单的示例):
UpdateTTL(struct ip_hdr *ipptr, unsigned char n)     
{
     unsigned long sum;
     unsigned short old;

     old = ntohs(*(unsigned short *)&ipptr->ttl);
     ipptr->ttl -= n;
     sum = old + (~ntohs(*(unsigned short *)&ipptr->ttl) & 0xffff);
     sum += ntohs(ipptr->Checksum);
     sum = (sum & 0xffff) + (sum>>16);
     ipptr->Checksum = htons(sum + (sum>>16));

}

    算法的实现依据是这样的.假设包头原校验和为~C,改变的字段的原始值是m,更改后的值是m',设~C'为重校验和,则有 ~C' = ~(C+(-m)+m') = ~C+(m-m') = ~C+m+~m'
    等价关系的成立基于反码的运算性质:取反运算满足结合律,按位取反运算与符号取反(及相反数)是等价的(即~C=-C).

    如果有多个字段改变,只是上面的公式中的m和m'有多个而已,直接用反码加法搞定即可。

2、为什么采用反码和运算
   IP数据包校验要求速度快,所以只采用了简单的和校验,为什么采用反码和而不是补码和呢?
   i)反码和的溢出有后效性(蔓延性)
    反码和将高位溢出加到低位,导致这个溢出会对后面操作有永久影响,有后效性;而补码和直接将高位和溢出,导致这个溢出对后面的操作再无影响,因此无后效性
   ii)反码校验无需考虑字节序
正因为反码和的溢出有后效性,导致大端字节序(big-endian)和小端字节序(little-endian)对同一数据序列(如两个16比特的序列)产生的校验和也只是字节序相反,而补码和因为将溢出丢掉了,不同字节序之间的校验和大不相同且没什么联系。
   基于以上的理由,校验和运算既可选择在数据被转换成网络字节序前,也可选择在之后,只要保证被校验的字段和填写的校验和字段的字节序保持一致就可以了。(这其实可以看作是负负得正,计算校验和与字节序有关,然后写校验和字段与字节序有关,然后直接计算校验和再写校验和字段则与字节序无关了~~)




四、参考文章
http://blog.chinaunix.net/u/20/showart_438512.html,关于IP校验和的
http://blog.chinaunix.net/u/12313/showart_176114.html,关于网络校验和的
http://www.wesoho.com/article/Delphi/2143.htm,关于IP校验和的
http://blog.chinaunix.net/u/20/showart_438418.html,关于补码和反码的


[ 本帖最后由 bripengandre 于 2009-7-13 09:53 编辑 ]

论坛徽章:
5
IT运维版块每日发帖之星
日期:2015-08-06 06:20:00IT运维版块每日发帖之星
日期:2015-08-10 06:20:00IT运维版块每日发帖之星
日期:2015-08-23 06:20:00IT运维版块每日发帖之星
日期:2015-08-24 06:20:00IT运维版块每日发帖之星
日期:2015-11-12 06:20:00
2 [报告]
发表于 2009-07-12 21:52 |只看该作者
UpdateTTL(struct ip_hdr *ipptr, unsigned char n)     
{
     unsigned long sum;
     unsigned short old;

     old = ntohs(*(unsigned short *)&ipptr->ttl);
     ipptr->ttl -= n;
     sum = old + (~ntohs(*(unsigned short *)&ipptr->ttl) & 0xffff);
     sum += ntohs(ipptr->Checksum);
     sum = (sum & 0xffff) + (sum>>16);
     ipptr->Checksum = htons(sum + (sum>>16));

}

这个算法好像只能修正一个字节的变化,对于超过1个字节的改变好像还是要用那个标准的算法的。 不知道说的对否?

论坛徽章:
0
3 [报告]
发表于 2009-07-13 09:37 |只看该作者
原帖由 ssffzz1 于 2009-7-12 21:52 发表
UpdateTTL(struct ip_hdr *ipptr, unsigned char n)     
{
     unsigned long sum;
     unsigned short old;

     old = ntohs(*(unsigned short *)&ipptr->ttl);
     ipptr->ttl -= n;
     sum = ...

这个小代码确实只是示例当TTL字段改变时的重校验,有多个字段改变时,可以用~C' = ~(C+(-m)+m') = ~C+(m-m') = ~C+m+~m'
来构造算法吧,这个就是简单的反码加法,这对校验字段多的tcp校验估计更有用吧~~

论坛徽章:
0
4 [报告]
发表于 2009-07-13 10:54 |只看该作者
来学习了

论坛徽章:
0
5 [报告]
发表于 2009-09-26 18:05 |只看该作者

xx

8x00000000
到底是什么意思啊?????????????????
难道就我一个人愚笨吗? 大家都知道是什么意思?

论坛徽章:
5
IT运维版块每日发帖之星
日期:2015-08-06 06:20:00IT运维版块每日发帖之星
日期:2015-08-10 06:20:00IT运维版块每日发帖之星
日期:2015-08-23 06:20:00IT运维版块每日发帖之星
日期:2015-08-24 06:20:00IT运维版块每日发帖之星
日期:2015-11-12 06:20:00
6 [报告]
发表于 2009-09-27 09:27 |只看该作者
原帖由 shenfeng128 于 2009-9-26 18:05 发表
8x00000000
到底是什么意思啊?????????????????
难道就我一个人愚笨吗? 大家都知道是什么意思?



不是你笨,是我也笨。 对算法基本没研究,你找那个RFC文档看看吧。

论坛徽章:
0
7 [报告]
发表于 2009-10-09 12:49 |只看该作者
原帖由 shenfeng128 于 2009-9-26 18:05 发表
8x00000000
到底是什么意思啊?????????????????
难道就我一个人愚笨吗? 大家都知道是什么意思?



很明显,他写错了,后面的注释很清楚了啊!

应该0x80000000

论坛徽章:
0
8 [报告]
发表于 2009-10-09 13:53 |只看该作者
原帖由 evil_knight 于 2009-10-9 12:49 发表



很明显,他写错了,后面的注释很清楚了啊!

应该0x80000000


这个问题前段时间shenfeng128还分贴讨论过,当初我也疑惑,现在看到原内容应该是0x80000000,这样就好理解了

[ 本帖最后由 hyagami 于 2009-10-9 14:25 编辑 ]

论坛徽章:
0
9 [报告]
发表于 2010-06-25 09:43 |只看该作者
3Q 学习

论坛徽章:
0
10 [报告]
发表于 2010-08-28 09:59 |只看该作者
首先谢谢楼主,弄不明白再请教楼主。
学习中……,内容我转载到个人空间去,注明来源与去处,请楼主理解!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP