linux内核校验算法

http://www.cs.fsu.edu/~baker/devices/lxr/http/source/linux/lib/checksum.c#L49 考虑了字节对齐的校验和源码
http://blog.csdn.net/qy532846454/article/details/7010852 内核分析

 

 /*
  *
  * INET         An implementation of the TCP/IP protocol suite for the LINUX
  *              operating system.  INET is implemented using the  BSD Socket
  *              interface as the means of communication with the user level.
  *
  *              IP/TCP/UDP checksumming routines
  *
  * Authors:     Jorge Cwik, <jorge@laser.satlink.net>
  *              Arnt Gulbrandsen, <agulbra@nvg.unit.no>
  *              Tom May, <ftom@netcom.com>
  *              Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
  *              Lots of code moved from tcp.c and ip.c; see those files
  *              for more names.
  *
  * 03/02/96     Jes Sorensen, Andreas Schwab, Roman Hodek:
  *              Fixed some nasty bugs, causing some horrible crashes.
  *              A: At some points, the sum (%0) was used as
  *              length-counter instead of the length counter
  *              (%1). Thanks to Roman Hodek for pointing this out.
  *              B: GCC seems to mess up if one uses too many
  *              data-registers to hold input values and one tries to
  *              specify d0 and d1 as scratch registers. Letting gcc
  *              choose these registers itself solves the problem.
  *
  *              This program is free software; you can redistribute it and/or
  *              modify it under the terms of the GNU General Public License
  *              as published by the Free Software Foundation; either version
  *              2 of the License, or (at your option) any later version.
  */
 
 /* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access
  kills, so most of the assembly has to go. */

//字节对齐这块儿,确实还不懂,但是这个源码也说明了校验需要考虑大端小端的情况 
 #include <linux/module.h>
 #include <net/checksum.h>
 #include <asm/byteorder.h>
 
 static inline unsigned short from32to16(unsigned long x)
 {
         /* add up 16-bit and 16-bit for 16+c bit */
         x = (x & 0xffff) + (x >> 16);
         /* add up carry.. */
         x = (x & 0xffff) + (x >> 16);
         return x;
 }
 
 static unsigned int do_csum(const unsigned char *buff, int len)
 {
         int odd, count;
         unsigned long result = 0;
 
         if (len <= 0)
                 goto out;
         odd = 1 & (unsigned long) buff;
         if (odd) {
 #ifdef __LITTLE_ENDIAN
                 result = *buff;
 #else
                 result += (*buff << 8);
 #endif
                 len--;
                 buff++;
         }

         count = len >> 1;               /* nr of 16-bit words.. */
         if (count) {
                 if (2 & (unsigned long) buff) {
                         result += *(unsigned short *) buff;
                         count--;
                         len -= 2;
                         buff += 2;
                 }
                 count >>= 1;            /* nr of 32-bit words.. */
                 if (count) {
                         unsigned long carry = 0;
                         do {
                                 unsigned long w = *(unsigned int *) buff;
                                 count--;
                                 buff += 4;
                                 result += carry;
                                 result += w;
                                 carry = (w > result);
                         } while (count);
                         result += carry;
                         result = (result & 0xffff) + (result >> 16);//加高位进位的1
                 }
                 if (len & 2) {
                         result += *(unsigned short *) buff;
                         buff += 2;
                 }
         }
         if (len & 1)
 #ifdef __LITTLE_ENDIAN
                 result += *buff;
 #else
                 result += (*buff << 8);
 #endif
         result = from32to16(result);
         if (odd)
                 result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);//从奇数地址开始校验,最后需要高低交换
 out:
         return result;//这个结果在调用后需要取反
 }
 
 /*
  *      This is a version of ip_compute_csum() optimized for IP headers,
  *      which always checksum on 4 octet boundaries.
  */
 __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
 {
         return (__force __sum16)~do_csum(iph, ihl*4);
 }
 EXPORT_SYMBOL(ip_fast_csum);
 
 /*
  * computes the checksum of a memory block at buff, length len,
  * and adds in "sum" (32-bit)
  *
  * returns a 32-bit number suitable for feeding into itself
  * or csum_tcpudp_magic
  *
  * this function must be called with even lengths, except
  * for the last fragment, which may be odd
  *
  * it's best to have buff aligned on a 32-bit boundary
  */
 __wsum csum_partial(const void *buff, int len, __wsum wsum)
 {
         unsigned int sum = (__force unsigned int)wsum;
         unsigned int result = do_csum(buff, len);
 
         /* add in old sum, and carry.. */
         result += sum;
         if (sum > result)
                 result += 1;
         return (__force __wsum)result;
 }
 EXPORT_SYMBOL(csum_partial);
 
 /*
  * this routine is used for miscellaneous IP-like checksums, mainly
  * in icmp.c
  */
 __sum16 ip_compute_csum(const void *buff, int len)
 {
         return (__force __sum16)~do_csum(buff, len);
 }
 EXPORT_SYMBOL(ip_compute_csum);
 
 /*
  * copy from fs while checksumming, otherwise like csum_partial
  */
 __wsum
 csum_partial_copy_from_user(const void __user *src, void *dst, int len,
                                                 __wsum sum, int *csum_err)
 {
         int missing;
 
         missing = __copy_from_user(dst, src, len);
         if (missing) {
                 memset(dst + len - missing, 0, missing);
                 *csum_err = -EFAULT;
         } else
                 *csum_err = 0;
 
         return csum_partial(dst, len, sum);
 }
 EXPORT_SYMBOL(csum_partial_copy_from_user);
 
 /*
  * copy from ds while checksumming, otherwise like csum_partial
  */
 __wsum
 csum_partial_copy(const void *src, void *dst, int len, __wsum sum)
 {
         memcpy(dst, src, len);
         return csum_partial(dst, len, sum);
 }
 EXPORT_SYMBOL(csum_partial_copy);
 
 #ifndef csum_tcpudp_nofold
 __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
                         unsigned short len,
                         unsigned short proto,
                         __wsum sum)
 {
         unsigned long long s = (__force u32)sum;
 
         s += (__force u32)saddr;
         s += (__force u32)daddr;
 #ifdef __BIG_ENDIAN
         s += proto + len;
 #else
         s += (proto + len) << 8;
 #endif
         s += (s >> 32);
         return (__force __wsum)s;
 }
 EXPORT_SYMBOL(csum_tcpudp_nofold);
 #endif
 
Tagged on: ,

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据