IP校验和详解

文见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).


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-12 14:16 编辑 ]

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux IP层的代码实现主要在`net/ipv4`目录下,下面是一些主要的源代码文件及其功能: 1. `ip_input.c`:IP数据包输入处理,包括IP首部校验、路由选择、分片重组等。 2. `ip_output.c`:IP数据包输出处理,包括路由选择、TTL递减、分片等。 3. `ip_fragment.c`:IP数据包分片处理。 4. `ip_forward.c`:IP数据包转发处理,包括路由选择、ARP解析、TTL递减、分片等。 5. `ip_route.c`:IP路由表管理,包括路由表项的添加、删除、修改等操作。 6. `ipmr.c`:IP组播路由处理。 7. `tcp_ipv4.c`:TCP协议的IP层实现,包括TCP数据包输入处理和输出处理。 8. `udp.c`:UDP协议的IP层实现,包括UDP数据包输入处理和输出处理。 9. `icmp.c`:ICMP协议的IP层实现,包括ICMP数据包输入处理和输出处理。 10. `arp.c`:ARP协议的实现,包括ARP请求和应答的发送和接收。 11. `devinet.c`:设备接口的IP层实现,包括设备的IP地址和路由表的管理。 12. `netfilter_ipv4.c`:IP层的Netfilter框架实现,包括数据包的过滤、修改等操作。 除了上述文件外,还有一些与IP层相关的文件,如`net/ipv4/inet_hashtable.c`、`net/ipv4/tcp_input.c`、`net/ipv4/udp_impl.h`等。这些文件都是为了支持IP层的实现而存在的。 总的来说,Linux IP层的代码实现非常庞大和复杂,但是非常高效和灵活,支持多种路由协议和高级路由功能,是Linux网络子系统的核心之一。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值