| 1 | |
| 2 | |
| 3 | *
|
| 4 | * INET An implementation of the TCP/IP protocol suite for the LINUX
|
| 5 | * operating system. INET is implemented using the BSD Socket
|
| 6 | * interface as the means of communication with the user level.
|
| 7 | *
|
| 8 | * IP/TCP/UDP checksumming routines
|
| 9 | *
|
| 10 | * Authors: Jorge Cwik, <[email protected]>
|
| 11 | * Arnt Gulbrandsen, <[email protected]>
|
| 12 | * Tom May, <[email protected]>
|
| 13 | * Andreas Schwab, <[email protected]>
|
| 14 | * Lots of code moved from tcp.c and ip.c; see those files
|
| 15 | * for more names.
|
| 16 | *
|
| 17 | * 03/02/96 Jes Sorensen, Andreas Schwab, Roman Hodek:
|
| 18 | * Fixed some nasty bugs, causing some horrible crashes.
|
| 19 | * A: At some points, the sum (%0) was used as
|
| 20 | * length-counter instead of the length counter
|
| 21 | * (%1). Thanks to Roman Hodek for pointing this out.
|
| 22 | * B: GCC seems to mess up if one uses too many
|
| 23 | * data-registers to hold input values and one tries to
|
| 24 | * specify d0 and d1 as scratch registers. Letting gcc
|
| 25 | * choose these registers itself solves the problem.
|
| 26 |
|
| 27 |
|
| 28 | |
| 29 | kills, so most of the assembly has to go.
|
| 30 |
|
| 31 | #include <linux/export.h>
|
| 32 | #include <net/checksum.h>
|
| 33 |
|
| 34 | #include <asm/byteorder.h>
|
| 35 |
|
| 36 | #ifndef do_csum
|
| 37 | static unsigned int do_csum(const unsigned char *buff, int len)
|
| 38 | {
|
| 39 | int odd;
|
| 40 | unsigned int result = 0;
|
| 41 |
|
| 42 | if (len <= 0)
|
| 43 | goto out;
|
| 44 | odd = 1 & (unsigned long) buff;
|
| 45 | if (odd) {
|
| 46 | #ifdef __LITTLE_ENDIAN
|
| 47 | result += (*buff << 8);
|
| 48 | #else
|
| 49 | result = *buff;
|
| 50 | #endif
|
| 51 | len--;
|
| 52 | buff++;
|
| 53 | }
|
| 54 | if (len >= 2) {
|
| 55 | if (2 & (unsigned long) buff) {
|
| 56 | result += *(unsigned short *) buff;
|
| 57 | len -= 2;
|
| 58 | buff += 2;
|
| 59 | }
|
| 60 | if (len >= 4) {
|
| 61 | const unsigned char *end = buff + ((unsigned)len & ~3);
|
| 62 | unsigned int carry = 0;
|
| 63 | do {
|
| 64 | unsigned int w = *(unsigned int *) buff;
|
| 65 | buff += 4;
|
| 66 | result += carry;
|
| 67 | result += w;
|
| 68 | carry = (w > result);
|
| 69 | } while (buff < end);
|
| 70 | result += carry;
|
| 71 | result = (result & 0xffff) + (result >> 16);
|
| 72 | }
|
| 73 | if (len & 2) {
|
| 74 | result += *(unsigned short *) buff;
|
| 75 | buff += 2;
|
| 76 | }
|
| 77 | }
|
| 78 | if (len & 1)
|
| 79 | #ifdef __LITTLE_ENDIAN
|
| 80 | result += *buff;
|
| 81 | #else
|
| 82 | result += (*buff << 8);
|
| 83 | #endif
|
| 84 | result = csum_from32to16(result);
|
| 85 | if (odd)
|
| 86 | result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
|
| 87 | out:
|
| 88 | return result;
|
| 89 | }
|
| 90 | #endif
|
| 91 |
|
| 92 | #ifndef ip_fast_csum
|
| 93 | |
| 94 | * This is a version of ip_compute_csum() optimized for IP headers,
|
| 95 | * which always checksum on 4 octet boundaries.
|
| 96 |
|
| 97 | __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
|
| 98 | {
|
| 99 | return (__force __sum16)~do_csum(iph, ihl*4);
|
| 100 | }
|
| 101 | EXPORT_SYMBOL(ip_fast_csum);
|
| 102 | #endif
|
| 103 |
|
| 104 | |
| 105 | * computes the checksum of a memory block at buff, length len,
|
| 106 | * and adds in "sum" (32-bit)
|
| 107 | *
|
| 108 | * returns a 32-bit number suitable for feeding into itself
|
| 109 | * or csum_tcpudp_magic
|
| 110 | *
|
| 111 | * this function must be called with even lengths, except
|
| 112 | * for the last fragment, which may be odd
|
| 113 | *
|
| 114 | * it's best to have buff aligned on a 32-bit boundary
|
| 115 |
|
| 116 | __wsum csum_partial(const void *buff, int len, __wsum wsum)
|
| 117 | {
|
| 118 | unsigned int sum = (__force unsigned int)wsum;
|
| 119 | unsigned int result = do_csum(buff, len);
|
| 120 |
|
| 121 |
|
| 122 | result += sum;
|
| 123 | if (sum > result)
|
| 124 | result += 1;
|
| 125 | return (__force __wsum)result;
|
| 126 | }
|
| 127 | EXPORT_SYMBOL(csum_partial);
|
| 128 |
|
| 129 | |
| 130 | * this routine is used for miscellaneous IP-like checksums, mainly
|
| 131 | * in icmp.c
|
| 132 |
|
| 133 | __sum16 ip_compute_csum(const void *buff, int len)
|
| 134 | {
|
| 135 | return (__force __sum16)~do_csum(buff, len);
|
| 136 | }
|
| 137 | EXPORT_SYMBOL(ip_compute_csum);
|
| 138 |
|
| 139 | #ifndef csum_tcpudp_nofold
|
| 140 | static inline u32 from64to32(u64 x)
|
| 141 | {
|
| 142 |
|
| 143 | x = (x & 0xffffffff) + (x >> 32);
|
| 144 |
|
| 145 | x = (x & 0xffffffff) + (x >> 32);
|
| 146 | return (u32)x;
|
| 147 | }
|
| 148 |
|
| 149 | __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
|
| 150 | __u32 len, __u8 proto, __wsum sum)
|
| 151 | {
|
| 152 | unsigned long long s = (__force u32)sum;
|
| 153 |
|
| 154 | s += (__force u32)saddr;
|
| 155 | s += (__force u32)daddr;
|
| 156 | #ifdef __BIG_ENDIAN
|
| 157 | s += proto + len;
|
| 158 | #else
|
| 159 | s += (proto + len) << 8;
|
| 160 | #endif
|
| 161 | return (__force __wsum)from64to32(s);
|
| 162 | }
|
| 163 | EXPORT_SYMBOL(csum_tcpudp_nofold);
|
| 164 | #endif
|