/src/wireshark/epan/in_cksum.c
Line | Count | Source |
1 | | /* in_cksum.c |
2 | | * 4.4-Lite-2 Internet checksum routine, modified to take a vector of |
3 | | * pointers/lengths giving the pieces to be checksummed. Also using |
4 | | * Tahoe/CGI version of ADDCARRY(x) macro instead of from portable version. |
5 | | * |
6 | | * Copyright (c) 1988, 1992, 1993 |
7 | | * The Regents of the University of California. All rights reserved. |
8 | | * |
9 | | * SPDX-License-Identifier: BSD-3-Clause |
10 | | * |
11 | | * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 |
12 | | */ |
13 | | |
14 | | #include "config.h" |
15 | | |
16 | | #include <glib.h> |
17 | | |
18 | | #include <epan/tvbuff.h> |
19 | | #include <epan/in_cksum.h> |
20 | | |
21 | | /* |
22 | | * Checksum routine for Internet Protocol family headers (Portable Version). |
23 | | * |
24 | | * This routine is very heavily used in the network |
25 | | * code and should be modified for each CPU to be as fast as possible. |
26 | | */ |
27 | | |
28 | 56.3k | #define ADDCARRY(x) {if ((x) > 65535) (x) -= 65535;} |
29 | 56.3k | #define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} |
30 | | |
31 | | /* |
32 | | * Linux and Windows, at least, when performing Local Checksum Offload |
33 | | * store the one's complement sum (not inverted to its bitwise complement) |
34 | | * of the pseudo header in the checksum field (instead of initializing |
35 | | * to zero), allowing the device driver to calculate the real checksum |
36 | | * later without needing knowledge of the pseudoheader itself. |
37 | | * (This is presumably why GSO requires equal length buffers - so that the |
38 | | * pseudo header contribution to the checksum, which includes the payload |
39 | | * length, is the same.) |
40 | | * |
41 | | * We can output this partial checksum as an intermediate result, |
42 | | * assuming that the pseudo header is all but the last chunk in the vector. |
43 | | * Note that unlike the final output it is not inverted, and that it |
44 | | * (like the final computed checksum) is is network byte order. |
45 | | */ |
46 | | int |
47 | | in_cksum_ret_partial(const vec_t *vec, int veclen, uint16_t *partial) |
48 | 21.8k | { |
49 | 21.8k | register const uint16_t *w; |
50 | 21.8k | register int sum = 0; |
51 | 21.8k | register int mlen = 0; |
52 | 21.8k | int byte_swapped = 0; |
53 | | |
54 | 21.8k | union { |
55 | 21.8k | uint8_t c[2]; |
56 | 21.8k | uint16_t s; |
57 | 21.8k | } s_util; |
58 | 21.8k | union { |
59 | 21.8k | uint16_t s[2]; |
60 | 21.8k | uint32_t l; |
61 | 21.8k | } l_util; |
62 | | |
63 | 71.3k | for (; veclen != 0; vec++, veclen--) { |
64 | 49.5k | if (veclen == 1 && partial) { |
65 | 0 | REDUCE; |
66 | 0 | *partial = sum; |
67 | 0 | } |
68 | 49.5k | if (vec->len == 0) |
69 | 3.15k | continue; |
70 | 46.3k | w = (const uint16_t *)(const void *)vec->ptr; |
71 | 46.3k | if (mlen == -1) { |
72 | | /* |
73 | | * The first byte of this chunk is the continuation |
74 | | * of a word spanning between this chunk and the |
75 | | * last chunk. |
76 | | * |
77 | | * s_util.c[0] is already saved when scanning previous |
78 | | * chunk. |
79 | | */ |
80 | 0 | s_util.c[1] = *(const uint8_t *)w; |
81 | 0 | sum += s_util.s; |
82 | 0 | w = (const uint16_t *)(const void *)((const uint8_t *)w + 1); |
83 | 0 | mlen = vec->len - 1; |
84 | 0 | } else |
85 | 46.3k | mlen = vec->len; |
86 | | /* |
87 | | * Force to even boundary. |
88 | | */ |
89 | 46.3k | if ((1 & (intptr_t)w) && (mlen > 0)) { |
90 | 2.11k | REDUCE; |
91 | 2.11k | sum <<= 8; |
92 | 2.11k | s_util.c[0] = *(const uint8_t *)w; |
93 | 2.11k | w = (const uint16_t *)(const void *)((const uint8_t *)w + 1); |
94 | 2.11k | mlen--; |
95 | 2.11k | byte_swapped = 1; |
96 | 2.11k | } |
97 | | /* |
98 | | * Unroll the loop to make overhead from |
99 | | * branches &c small. |
100 | | */ |
101 | 260k | while ((mlen -= 32) >= 0) { |
102 | 213k | sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; |
103 | 213k | sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; |
104 | 213k | sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; |
105 | 213k | sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; |
106 | 213k | w += 16; |
107 | 213k | } |
108 | 46.3k | mlen += 32; |
109 | 98.8k | while ((mlen -= 8) >= 0) { |
110 | 52.5k | sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; |
111 | 52.5k | w += 4; |
112 | 52.5k | } |
113 | 46.3k | mlen += 8; |
114 | 46.3k | if (mlen == 0 && byte_swapped == 0) |
115 | 16.0k | continue; |
116 | 30.2k | REDUCE; |
117 | 75.4k | while ((mlen -= 2) >= 0) { |
118 | 45.1k | sum += *w++; |
119 | 45.1k | } |
120 | 30.2k | if (byte_swapped) { |
121 | 2.11k | REDUCE; |
122 | 2.11k | sum <<= 8; |
123 | 2.11k | byte_swapped = 0; |
124 | 2.11k | if (mlen == -1) { |
125 | 1.56k | s_util.c[1] = *(const uint8_t *)w; |
126 | 1.56k | sum += s_util.s; |
127 | 1.56k | mlen = 0; |
128 | 1.56k | } else |
129 | 551 | mlen = -1; |
130 | 28.1k | } else if (mlen == -1) |
131 | 8.15k | s_util.c[0] = *(const uint8_t *)w; |
132 | 30.2k | } |
133 | 21.8k | if (mlen == -1) { |
134 | | /* The last mbuf has odd # of bytes. Follow the |
135 | | standard (the odd byte may be shifted left by 8 bits |
136 | | or not as determined by endian-ness of the machine) */ |
137 | 8.70k | s_util.c[1] = 0; |
138 | 8.70k | sum += s_util.s; |
139 | 8.70k | } |
140 | 21.8k | REDUCE; |
141 | 21.8k | return (~sum & 0xffff); |
142 | 21.8k | } |
143 | | |
144 | | int |
145 | | in_cksum(const vec_t *vec, int veclen) |
146 | 20.1k | { |
147 | 20.1k | return in_cksum_ret_partial(vec, veclen, NULL); |
148 | 20.1k | } |
149 | | |
150 | | uint16_t |
151 | | ip_checksum(const uint8_t *ptr, int len) |
152 | 309 | { |
153 | 309 | vec_t cksum_vec[1]; |
154 | | |
155 | 309 | SET_CKSUM_VEC_PTR(cksum_vec[0], ptr, len); |
156 | 309 | return in_cksum_ret_partial(&cksum_vec[0], 1, NULL); |
157 | 309 | } |
158 | | |
159 | | uint16_t |
160 | | ip_checksum_tvb(tvbuff_t *tvb, int offset, int len) |
161 | 1.40k | { |
162 | 1.40k | vec_t cksum_vec[1]; |
163 | | |
164 | 1.40k | SET_CKSUM_VEC_TVB(cksum_vec[0], tvb, offset, len); |
165 | 1.40k | return in_cksum_ret_partial(&cksum_vec[0], 1, NULL); |
166 | 1.40k | } |
167 | | |
168 | | /* |
169 | | * Given the host-byte-order value of the checksum field in a packet |
170 | | * header, and the network-byte-order computed checksum of the data |
171 | | * that the checksum covers (including the checksum itself), compute |
172 | | * what the checksum field *should* have been. |
173 | | * |
174 | | * This always returns +0 (0x0000) not -0 (0xffff). The few protocols, |
175 | | * like ICMP, that can have an all zero packet (aside from the checksum |
176 | | * field) such that 0xffff is the correct result should test for that |
177 | | * case before, after, or instead of calling this method. |
178 | | */ |
179 | | uint16_t |
180 | | in_cksum_shouldbe(uint16_t sum, uint16_t computed_sum) |
181 | 11.6k | { |
182 | 11.6k | uint32_t shouldbe; |
183 | | |
184 | | /* |
185 | | * The value that should have gone into the checksum field |
186 | | * is the negative of the value gotten by summing up everything |
187 | | * *but* the checksum field. |
188 | | * |
189 | | * We can compute that by subtracting the value of the checksum |
190 | | * field from the sum of all the data in the packet, and then |
191 | | * computing the negative of that value. |
192 | | * |
193 | | * "sum" is the value of the checksum field, and "computed_sum" |
194 | | * is the negative of the sum of all the data in the packets, |
195 | | * so that's -(-computed_sum - sum), or (sum + computed_sum). |
196 | | * |
197 | | * All the arithmetic in question is one's complement, so the |
198 | | * addition must include an end-around carry; we do this by |
199 | | * doing the arithmetic in 32 bits (with no sign-extension), |
200 | | * and then adding the upper 16 bits of the sum, which contain |
201 | | * the carry, to the lower 16 bits of the sum, and then do it |
202 | | * again in case *that* sum produced a carry. (XXX - It won't. |
203 | | * It can't be any larger than 0xFFFF + 0xFFFF = 0x1FFFE, |
204 | | * which only carries once back to 0xFFFF.) |
205 | | * |
206 | | * Also, since all the arithmetic is one's complement, +0 (0x0000) |
207 | | * and -0 (0xFFFF) are indistinguishable. (Different ways of |
208 | | * performing the calculation can yield one zero or the other for |
209 | | * different inputs.) Between the two, +0 is the representation we |
210 | | * want unless all the bits of the packet except for the checksum |
211 | | * field are zero, but we can't know that from our inputs here. |
212 | | * Most protocols which use this checksum require at least some |
213 | | * nonzero bits (a version, a length field, something either directly |
214 | | * or in a pseudoheader), and so +0 is correct. Despite this, some |
215 | | * networking stacks put 0xFFFF when 0x0000 is appropriate, see RFC |
216 | | * 1624. |
217 | | * |
218 | | * As RFC 1071 notes, the checksum can be computed without |
219 | | * byte-swapping the 16-bit words; summing 16-bit words |
220 | | * on a big-endian machine gives a big-endian checksum, which |
221 | | * can be directly stuffed into the big-endian checksum fields |
222 | | * in protocol headers, and summing words on a little-endian |
223 | | * machine gives a little-endian checksum, which must be |
224 | | * byte-swapped before being stuffed into a big-endian checksum |
225 | | * field. |
226 | | * |
227 | | * "computed_sum" is a network-byte-order value, so we must put |
228 | | * it in host byte order before subtracting it from the |
229 | | * host-byte-order value from the header; the adjusted checksum |
230 | | * will be in host byte order, which is what we'll return. |
231 | | */ |
232 | 11.6k | shouldbe = sum; |
233 | 11.6k | shouldbe += g_ntohs(computed_sum); |
234 | 11.6k | shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16); |
235 | 11.6k | shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16); // XXX - Unneeded. |
236 | | /* Always return +0, not -0. |
237 | | * There are some other ways to always return +0, such as |
238 | | * calculating -(-computed_sum + -sum) instead; the fastest |
239 | | * approach is likely CPU and compiler dependent. |
240 | | */ |
241 | 11.6k | return shouldbe == 0xFFFF ? 0 : shouldbe; |
242 | 11.6k | } |
243 | | |
244 | | /* |
245 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
246 | | * |
247 | | * Local variables: |
248 | | * c-basic-offset: 8 |
249 | | * tab-width: 8 |
250 | | * indent-tabs-mode: t |
251 | | * End: |
252 | | * |
253 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
254 | | * :indentSize=8:tabSize=8:noTabs=false: |
255 | | */ |