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