0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/module.h>
0014 #include <linux/string.h>
0015
0016 #include <asm/byteorder.h>
0017
0018 static inline unsigned short from64to16(unsigned long x)
0019 {
0020
0021
0022
0023 union {
0024 unsigned long ul;
0025 unsigned int ui[2];
0026 unsigned short us[4];
0027 } in_v, tmp_v, out_v;
0028
0029 in_v.ul = x;
0030 tmp_v.ul = (unsigned long) in_v.ui[0] + (unsigned long) in_v.ui[1];
0031
0032
0033
0034 out_v.ul = (unsigned long) tmp_v.us[0] + (unsigned long) tmp_v.us[1]
0035 + (unsigned long) tmp_v.us[2];
0036
0037
0038 return out_v.us[0] + out_v.us[1];
0039 }
0040
0041
0042
0043
0044
0045 __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
0046 __u32 len, __u8 proto, __wsum sum)
0047 {
0048 return (__force __sum16)~from64to16(
0049 (__force u64)saddr + (__force u64)daddr +
0050 (__force u64)sum + ((len + proto) << 8));
0051 }
0052 EXPORT_SYMBOL(csum_tcpudp_magic);
0053
0054 __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
0055 __u32 len, __u8 proto, __wsum sum)
0056 {
0057 unsigned long result;
0058
0059 result = (__force u64)saddr + (__force u64)daddr +
0060 (__force u64)sum + ((len + proto) << 8);
0061
0062
0063
0064
0065 result = (result & 0xffffffff) + (result >> 32);
0066
0067 result = (result & 0xffffffff) + (result >> 32);
0068 return (__force __wsum)result;
0069 }
0070 EXPORT_SYMBOL(csum_tcpudp_nofold);
0071
0072
0073
0074
0075
0076
0077
0078
0079 static inline unsigned long do_csum(const unsigned char * buff, int len)
0080 {
0081 int odd, count;
0082 unsigned long result = 0;
0083
0084 if (len <= 0)
0085 goto out;
0086 odd = 1 & (unsigned long) buff;
0087 if (odd) {
0088 result = *buff << 8;
0089 len--;
0090 buff++;
0091 }
0092 count = len >> 1;
0093 if (count) {
0094 if (2 & (unsigned long) buff) {
0095 result += *(unsigned short *) buff;
0096 count--;
0097 len -= 2;
0098 buff += 2;
0099 }
0100 count >>= 1;
0101 if (count) {
0102 if (4 & (unsigned long) buff) {
0103 result += *(unsigned int *) buff;
0104 count--;
0105 len -= 4;
0106 buff += 4;
0107 }
0108 count >>= 1;
0109 if (count) {
0110 unsigned long carry = 0;
0111 do {
0112 unsigned long w = *(unsigned long *) buff;
0113 count--;
0114 buff += 8;
0115 result += carry;
0116 result += w;
0117 carry = (w > result);
0118 } while (count);
0119 result += carry;
0120 result = (result & 0xffffffff) + (result >> 32);
0121 }
0122 if (len & 4) {
0123 result += *(unsigned int *) buff;
0124 buff += 4;
0125 }
0126 }
0127 if (len & 2) {
0128 result += *(unsigned short *) buff;
0129 buff += 2;
0130 }
0131 }
0132 if (len & 1)
0133 result += *buff;
0134 result = from64to16(result);
0135 if (odd)
0136 result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
0137 out:
0138 return result;
0139 }
0140
0141
0142
0143
0144
0145 __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
0146 {
0147 return (__force __sum16)~do_csum(iph,ihl*4);
0148 }
0149 EXPORT_SYMBOL(ip_fast_csum);
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163 __wsum csum_partial(const void *buff, int len, __wsum sum)
0164 {
0165 unsigned long result = do_csum(buff, len);
0166
0167
0168 result += (__force u32)sum;
0169
0170 result = (result & 0xffffffff) + (result >> 32);
0171 return (__force __wsum)result;
0172 }
0173
0174 EXPORT_SYMBOL(csum_partial);
0175
0176
0177
0178
0179
0180 __sum16 ip_compute_csum(const void *buff, int len)
0181 {
0182 return (__force __sum16)~from64to16(do_csum(buff,len));
0183 }
0184 EXPORT_SYMBOL(ip_compute_csum);