]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet/in_cksum.c
c8a2d462fa042ae70420c12f9a1aa649b9698d4d
[apple/xnu.git] / bsd / netinet / in_cksum.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Copyright (c) 1988, 1992, 1993
24 * The Regents of the University of California. All rights reserved.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 * 3. All advertising materials mentioning features or use of this software
35 * must display the following acknowledgement:
36 * This product includes software developed by the University of
37 * California, Berkeley and its contributors.
38 * 4. Neither the name of the University nor the names of its contributors
39 * may be used to endorse or promote products derived from this software
40 * without specific prior written permission.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 *
54 * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
55 */
56
57 #include <sys/param.h>
58 #include <sys/mbuf.h>
59 #include <sys/kdebug.h>
60
61 #define DBG_FNC_IN_CKSUM NETDBG_CODE(DBG_NETIP, (3 << 8))
62
63 /*
64 * Checksum routine for Internet Protocol family headers (Portable Version).
65 *
66 * This routine is very heavily used in the network
67 * code and should be modified for each CPU to be as fast as possible.
68 */
69
70 union s_util {
71 char c[2];
72 u_short s;
73 };
74
75 union l_util {
76 u_int16_t s[2];
77 u_int32_t l;
78 };
79
80 union q_util {
81 u_int16_t s[4];
82 u_int32_t l[2];
83 u_int64_t q;
84 };
85
86 #define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
87
88 #define REDUCE32 \
89 { \
90 q_util.q = sum; \
91 sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
92 }
93 #define REDUCE16 \
94 { \
95 q_util.q = sum; \
96 l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
97 sum = l_util.s[0] + l_util.s[1]; \
98 ADDCARRY(sum); \
99 }
100
101 #define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
102
103
104 #if defined(ppc)
105
106 __inline unsigned short
107 in_addword(u_short a, u_short b)
108 {
109 union l_util l_util;
110 u_int32_t sum = a + b;
111 REDUCE;
112 return (sum);
113 }
114
115 __inline unsigned short
116 in_pseudo(u_int a, u_int b, u_int c)
117 {
118 u_int64_t sum;
119 union q_util q_util;
120 union l_util l_util;
121
122 sum = (u_int64_t) a + b + c;
123 REDUCE16;
124 return (sum);
125
126 }
127
128 int
129 in_cksum(m, len)
130 register struct mbuf *m;
131 register int len;
132 {
133 register u_short *w;
134 register int sum = 0;
135 register int mlen = 0;
136 int starting_on_odd = 0;
137
138
139 KERNEL_DEBUG(DBG_FNC_IN_CKSUM | DBG_FUNC_START, len,0,0,0,0);
140
141 for (;m && len; m = m->m_next) {
142 if (m->m_len == 0)
143 continue;
144 mlen = m->m_len;
145 w = mtod(m, u_short *);
146
147 if (len < mlen)
148 mlen = len;
149
150 sum = xsum_assym(w, mlen, sum, starting_on_odd);
151 len -= mlen;
152 if (mlen & 0x1)
153 {
154 if (starting_on_odd)
155 starting_on_odd = 0;
156 else
157 starting_on_odd = 1;
158 }
159 }
160
161 KERNEL_DEBUG(DBG_FNC_IN_CKSUM | DBG_FUNC_END, 0,0,0,0,0);
162 return (~sum & 0xffff);
163 }
164
165 u_short
166 in_cksum_skip(m, len, skip)
167 register struct mbuf *m;
168 register int len;
169 register int skip;
170 {
171 register u_short *w;
172 register int sum = 0;
173 register int mlen = 0;
174 int starting_on_odd = 0;
175
176 len -= skip;
177 for (; skip && m; m = m->m_next) {
178 if (m->m_len > skip) {
179 mlen = m->m_len - skip;
180 w = (u_short *)(m->m_data+skip);
181 goto skip_start;
182 } else {
183 skip -= m->m_len;
184 }
185 }
186 for (;m && len; m = m->m_next) {
187 if (m->m_len == 0)
188 continue;
189 mlen = m->m_len;
190 w = mtod(m, u_short *);
191
192 if (len < mlen)
193 mlen = len;
194 skip_start:
195 sum = xsum_assym(w, mlen, sum, starting_on_odd);
196 len -= mlen;
197 if (mlen & 0x1)
198 {
199 if (starting_on_odd)
200 starting_on_odd = 0;
201 else
202 starting_on_odd = 1;
203 }
204 }
205
206 return (~sum & 0xffff);
207 }
208 #else
209
210 u_short
211 in_addword(u_short a, u_short b)
212 {
213 union l_util l_util;
214 u_int32_t sum = a + b;
215 REDUCE(sum);
216 return (sum);
217 }
218
219 u_short
220 in_pseudo(u_int a, u_int b, u_int c)
221 {
222 u_int64_t sum;
223 union q_util q_util;
224 union l_util l_util;
225
226 sum = (u_int64_t) a + b + c;
227 REDUCE16;
228 return (sum);
229 }
230
231
232 int
233 in_cksum(m, len)
234 register struct mbuf *m;
235 register int len;
236 {
237 register u_short *w;
238 register int sum = 0;
239 register int mlen = 0;
240 int byte_swapped = 0;
241 union s_util s_util;
242 union l_util l_util;
243
244 KERNEL_DEBUG(DBG_FNC_IN_CKSUM | DBG_FUNC_START, len,0,0,0,0);
245
246 for (;m && len; m = m->m_next) {
247 if (m->m_len == 0)
248 continue;
249 w = mtod(m, u_short *);
250 if (mlen == -1) {
251 /*
252 * The first byte of this mbuf is the continuation
253 * of a word spanning between this mbuf and the
254 * last mbuf.
255 *
256 * s_util.c[0] is already saved when scanning previous
257 * mbuf.
258 */
259 s_util.c[1] = *(char *)w;
260 sum += s_util.s;
261 w = (u_short *)((char *)w + 1);
262 mlen = m->m_len - 1;
263 len--;
264 } else
265 mlen = m->m_len;
266 if (len < mlen)
267 mlen = len;
268 len -= mlen;
269 /*
270 * Force to even boundary.
271 */
272 if ((1 & (int) w) && (mlen > 0)) {
273 REDUCE;
274 sum <<= 8;
275 s_util.c[0] = *(u_char *)w;
276 w = (u_short *)((char *)w + 1);
277 mlen--;
278 byte_swapped = 1;
279 }
280 /*
281 * Unroll the loop to make overhead from
282 * branches &c small.
283 */
284 while ((mlen -= 32) >= 0) {
285 sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
286 sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
287 sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
288 sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
289 w += 16;
290 }
291 mlen += 32;
292 while ((mlen -= 8) >= 0) {
293 sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
294 w += 4;
295 }
296 mlen += 8;
297 if (mlen == 0 && byte_swapped == 0)
298 continue;
299 REDUCE;
300 while ((mlen -= 2) >= 0) {
301 sum += *w++;
302 }
303 if (byte_swapped) {
304 REDUCE;
305 sum <<= 8;
306 byte_swapped = 0;
307 if (mlen == -1) {
308 s_util.c[1] = *(char *)w;
309 sum += s_util.s;
310 mlen = 0;
311 } else
312 mlen = -1;
313 } else if (mlen == -1)
314 s_util.c[0] = *(char *)w;
315 }
316 if (len)
317 printf("cksum: out of data\n");
318 if (mlen == -1) {
319 /* The last mbuf has odd # of bytes. Follow the
320 standard (the odd byte may be shifted left by 8 bits
321 or not as determined by endian-ness of the machine) */
322 s_util.c[1] = 0;
323 sum += s_util.s;
324 }
325 REDUCE;
326 KERNEL_DEBUG(DBG_FNC_IN_CKSUM | DBG_FUNC_END, 0,0,0,0,0);
327 return (~sum & 0xffff);
328 }
329
330 int
331 in_cksum_skip(m, len, skip)
332 register struct mbuf *m;
333 register u_short len;
334 register u_short skip;
335 {
336 register u_short *w;
337 register int sum = 0;
338 register int mlen = 0;
339 int byte_swapped = 0;
340 union s_util s_util;
341 union l_util l_util;
342
343 KERNEL_DEBUG(DBG_FNC_IN_CKSUM | DBG_FUNC_START, len,0,0,0,0);
344
345 len -= skip;
346 for (; skip && m; m = m->m_next) {
347 if (m->m_len > skip) {
348 mlen = m->m_len - skip;
349 w = (u_short *)(m->m_data+skip);
350 goto skip_start;
351 } else {
352 skip -= m->m_len;
353 }
354 }
355 for (;m && len; m = m->m_next) {
356 if (m->m_len == 0)
357 continue;
358 w = mtod(m, u_short *);
359
360 if (mlen == -1) {
361 /*
362 * The first byte of this mbuf is the continuation
363 * of a word spanning between this mbuf and the
364 * last mbuf.
365 *
366 * s_util.c[0] is already saved when scanning previous
367 * mbuf.
368 */
369 s_util.c[1] = *(char *)w;
370 sum += s_util.s;
371 w = (u_short *)((char *)w + 1);
372 mlen = m->m_len - 1;
373 len--;
374 } else {
375 mlen = m->m_len;
376 }
377 if (len < mlen)
378 mlen = len;
379 skip_start:
380
381 len -= mlen;
382 /*
383 * Force to even boundary.
384 */
385 if ((1 & (int) w) && (mlen > 0)) {
386 REDUCE;
387 sum <<= 8;
388 s_util.c[0] = *(u_char *)w;
389 w = (u_short *)((char *)w + 1);
390 mlen--;
391 byte_swapped = 1;
392 }
393 /*
394 * Unroll the loop to make overhead from
395 * branches &c small.
396 */
397 while ((mlen -= 32) >= 0) {
398 sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
399 sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
400 sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
401 sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
402 w += 16;
403 }
404 mlen += 32;
405 while ((mlen -= 8) >= 0) {
406 sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
407 w += 4;
408 }
409 mlen += 8;
410 if (mlen == 0 && byte_swapped == 0)
411 continue;
412 REDUCE;
413 while ((mlen -= 2) >= 0) {
414 sum += *w++;
415 }
416 if (byte_swapped) {
417 REDUCE;
418 sum <<= 8;
419 byte_swapped = 0;
420 if (mlen == -1) {
421 s_util.c[1] = *(char *)w;
422 sum += s_util.s;
423 mlen = 0;
424 } else
425 mlen = -1;
426 } else if (mlen == -1)
427 s_util.c[0] = *(char *)w;
428 }
429 if (len)
430 printf("cksum: out of data\n");
431 if (mlen == -1) {
432 /* The last mbuf has odd # of bytes. Follow the
433 standard (the odd byte may be shifted left by 8 bits
434 or not as determined by endian-ness of the machine) */
435 s_util.c[1] = 0;
436 sum += s_util.s;
437 }
438 REDUCE;
439 KERNEL_DEBUG(DBG_FNC_IN_CKSUM | DBG_FUNC_END, 0,0,0,0,0);
440 return (~sum & 0xffff);
441 }
442
443 #endif