checksum.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:3k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * INET An implementation of the TCP/IP protocol suite for the LINUX
  3.  * operating system.  INET is implemented using the  BSD Socket
  4.  * interface as the means of communication with the user level.
  5.  *
  6.  * MIPS specific IP/TCP/UDP checksumming routines
  7.  *
  8.  * Authors: Ralf Baechle, <ralf@waldorf-gmbh.de>
  9.  * Lots of code moved from tcp.c and ip.c; see those files
  10.  * for more names.
  11.  *
  12.  * This program is free software; you can redistribute it and/or
  13.  * modify it under the terms of the GNU General Public License
  14.  * as published by the Free Software Foundation; either version
  15.  * 2 of the License, or (at your option) any later version.
  16.  *
  17.  * $Id: checksum.c,v 1.3 1997/12/01 17:57:34 ralf Exp $
  18.  */
  19. #include <net/checksum.h>
  20. #include <linux/types.h>
  21. #include <asm/byteorder.h>
  22. #include <asm/string.h>
  23. #include <asm/uaccess.h>
  24. static inline unsigned short from32to16(unsigned int x)
  25. {
  26. /* 32 bits --> 16 bits + carry */
  27. x = (x & 0xffff) + (x >> 16);
  28. /* 16 bits + carry --> 16 bits including carry */
  29. x = (x & 0xffff) + (x >> 16);
  30. return (unsigned short)x;
  31. }
  32. static inline unsigned int do_csum(const unsigned char * buff, int len)
  33. {
  34. int odd, count;
  35. unsigned int result = 0;
  36. if (len <= 0)
  37. goto out;
  38. odd = 1 & (unsigned long) buff;
  39. if (odd) {
  40. result = be16_to_cpu(*buff);
  41. len--;
  42. buff++;
  43. }
  44. count = len >> 1; /* nr of 16-bit words.. */
  45. if (count) {
  46. if (2 & (unsigned long) buff) {
  47. result += *(unsigned short *) buff;
  48. count--;
  49. len -= 2;
  50. buff += 2;
  51. }
  52. count >>= 1; /* nr of 32-bit words.. */
  53. if (count) {
  54. unsigned int carry = 0;
  55. do {
  56. unsigned int w = *(unsigned int *) buff;
  57. count--;
  58. buff += 4;
  59. result += carry;
  60. result += w;
  61. carry = (w > result);
  62. } while (count);
  63. result += carry;
  64. result = (result & 0xffff) + (result >> 16);
  65. }
  66. if (len & 2) {
  67. result += *(unsigned short *) buff;
  68. buff += 2;
  69. }
  70. }
  71. if (len & 1)
  72. result += le16_to_cpu(*buff);
  73. result = from32to16(result);
  74. if (odd)
  75. result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
  76. out:
  77. return result;
  78. }
  79. /*
  80.  * computes a partial checksum, e.g. for TCP/UDP fragments
  81.  */
  82. unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum)
  83. {
  84. unsigned int result = do_csum(buff, len);
  85. /* add in old sum, and carry.. */
  86. result += sum;
  87. if(sum > result)
  88. result += 1;
  89. return result;
  90. }
  91. /*
  92.  * copy while checksumming, otherwise like csum_partial
  93.  */
  94. unsigned int csum_partial_copy(const char *src, char *dst, 
  95.                                int len, unsigned int sum)
  96. {
  97. /*
  98.  * It's 2:30 am and I don't feel like doing it real ...
  99.  * This is lots slower than the real thing (tm)
  100.  */
  101. sum = csum_partial(src, len, sum);
  102. memcpy(dst, src, len);
  103. return sum;
  104. }
  105. /*
  106.  * Copy from userspace and compute checksum.  If we catch an exception
  107.  * then zero the rest of the buffer.
  108.  */
  109. unsigned int csum_partial_copy_from_user (const char *src, char *dst,
  110.                                           int len, unsigned int sum,
  111.                                           int *err_ptr)
  112. {
  113. int missing;
  114. missing = copy_from_user(dst, src, len);
  115. if (missing) {
  116. memset(dst + len - missing, 0, missing);
  117. *err_ptr = -EFAULT;
  118. }
  119. return csum_partial(dst, len, sum);
  120. }