Blame view

lib/checksum.c 4.15 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
26a28fa4f   Arnd Bergmann   add generic lib/c...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  /*
   *
   * 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.
26a28fa4f   Arnd Bergmann   add generic lib/c...
26
27
28
29
   */
  
  /* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access
   kills, so most of the assembly has to go. */
8bc3bcc93   Paul Gortmaker   lib: reduce the u...
30
  #include <linux/export.h>
26a28fa4f   Arnd Bergmann   add generic lib/c...
31
32
33
  #include <net/checksum.h>
  
  #include <asm/byteorder.h>
20c1f641b   Arnd Bergmann   lib/checksum.c: m...
34
  #ifndef do_csum
c44ba9f66   Arnd Bergmann   lib/checksum.c: u...
35
  static inline unsigned short from32to16(unsigned int x)
26a28fa4f   Arnd Bergmann   add generic lib/c...
36
37
38
39
40
41
42
43
44
45
  {
  	/* 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)
  {
be0e1e788   Ian Abbott   lib/checksum.c: o...
46
  	int odd;
c44ba9f66   Arnd Bergmann   lib/checksum.c: u...
47
  	unsigned int result = 0;
26a28fa4f   Arnd Bergmann   add generic lib/c...
48
49
50
51
52
  
  	if (len <= 0)
  		goto out;
  	odd = 1 & (unsigned long) buff;
  	if (odd) {
32a9ff9cc   Arnd Bergmann   lib/checksum.c: f...
53
  #ifdef __LITTLE_ENDIAN
32a9ff9cc   Arnd Bergmann   lib/checksum.c: f...
54
  		result += (*buff << 8);
0a5549ed1   Arnd Bergmann   lib/checksum: fix...
55
56
  #else
  		result = *buff;
32a9ff9cc   Arnd Bergmann   lib/checksum.c: f...
57
  #endif
26a28fa4f   Arnd Bergmann   add generic lib/c...
58
59
60
  		len--;
  		buff++;
  	}
be0e1e788   Ian Abbott   lib/checksum.c: o...
61
  	if (len >= 2) {
26a28fa4f   Arnd Bergmann   add generic lib/c...
62
63
  		if (2 & (unsigned long) buff) {
  			result += *(unsigned short *) buff;
26a28fa4f   Arnd Bergmann   add generic lib/c...
64
65
66
  			len -= 2;
  			buff += 2;
  		}
be0e1e788   Ian Abbott   lib/checksum.c: o...
67
68
  		if (len >= 4) {
  			const unsigned char *end = buff + ((unsigned)len & ~3);
c44ba9f66   Arnd Bergmann   lib/checksum.c: u...
69
  			unsigned int carry = 0;
26a28fa4f   Arnd Bergmann   add generic lib/c...
70
  			do {
c44ba9f66   Arnd Bergmann   lib/checksum.c: u...
71
  				unsigned int w = *(unsigned int *) buff;
26a28fa4f   Arnd Bergmann   add generic lib/c...
72
73
74
75
  				buff += 4;
  				result += carry;
  				result += w;
  				carry = (w > result);
be0e1e788   Ian Abbott   lib/checksum.c: o...
76
  			} while (buff < end);
26a28fa4f   Arnd Bergmann   add generic lib/c...
77
78
79
80
81
82
83
84
85
  			result += carry;
  			result = (result & 0xffff) + (result >> 16);
  		}
  		if (len & 2) {
  			result += *(unsigned short *) buff;
  			buff += 2;
  		}
  	}
  	if (len & 1)
32a9ff9cc   Arnd Bergmann   lib/checksum.c: f...
86
87
88
  #ifdef __LITTLE_ENDIAN
  		result += *buff;
  #else
26a28fa4f   Arnd Bergmann   add generic lib/c...
89
  		result += (*buff << 8);
32a9ff9cc   Arnd Bergmann   lib/checksum.c: f...
90
  #endif
26a28fa4f   Arnd Bergmann   add generic lib/c...
91
92
93
94
95
96
  	result = from32to16(result);
  	if (odd)
  		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
  out:
  	return result;
  }
20c1f641b   Arnd Bergmann   lib/checksum.c: m...
97
  #endif
26a28fa4f   Arnd Bergmann   add generic lib/c...
98

64e69073c   Vineet Gupta   asm-generic heade...
99
  #ifndef ip_fast_csum
26a28fa4f   Arnd Bergmann   add generic lib/c...
100
101
102
103
104
105
106
107
108
  /*
   *	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);
64e69073c   Vineet Gupta   asm-generic heade...
109
  #endif
26a28fa4f   Arnd Bergmann   add generic lib/c...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  
  /*
   * 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);
26a28fa4f   Arnd Bergmann   add generic lib/c...
145
  #ifndef csum_tcpudp_nofold
9ce357795   karl beldan   lib/checksum.c: f...
146
147
148
149
150
151
152
153
  static inline u32 from64to32(u64 x)
  {
  	/* add up 32-bit and 32-bit for 32+c bit */
  	x = (x & 0xffffffff) + (x >> 32);
  	/* add up carry.. */
  	x = (x & 0xffffffff) + (x >> 32);
  	return (u32)x;
  }
26a28fa4f   Arnd Bergmann   add generic lib/c...
154
  __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
01cfbad79   Alexander Duyck   ipv4: Update para...
155
  			  __u32 len, __u8 proto, __wsum sum)
26a28fa4f   Arnd Bergmann   add generic lib/c...
156
157
158
159
160
161
162
163
164
165
  {
  	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
150ae0e94   karl beldan   lib/checksum.c: f...
166
  	return (__force __wsum)from64to32(s);
26a28fa4f   Arnd Bergmann   add generic lib/c...
167
168
169
  }
  EXPORT_SYMBOL(csum_tcpudp_nofold);
  #endif