The Pedigree Project 0.1
inet_chksum.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
35/*
36 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
37 * All rights reserved.
38 *
39 * Redistribution and use in source and binary forms, with or without modification,
40 * are permitted provided that the following conditions are met:
41 *
42 * 1. Redistributions of source code must retain the above copyright notice,
43 * this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright notice,
45 * this list of conditions and the following disclaimer in the documentation
46 * and/or other materials provided with the distribution.
47 * 3. The name of the author may not be used to endorse or promote products
48 * derived from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
51 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
53 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
54 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
55 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
58 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
59 * OF SUCH DAMAGE.
60 *
61 * This file is part of the lwIP TCP/IP stack.
62 *
63 * Author: Adam Dunkels <adam@sics.se>
64 *
65 */
66
67#include "lwip/opt.h"
68
69#include "lwip/inet_chksum.h"
70#include "lwip/def.h"
71#include "lwip/ip_addr.h"
72
73#include <string.h>
74
75#ifndef LWIP_CHKSUM
76# define LWIP_CHKSUM lwip_standard_chksum
77# ifndef LWIP_CHKSUM_ALGORITHM
78# define LWIP_CHKSUM_ALGORITHM 2
79# endif
80u16_t lwip_standard_chksum(const void *dataptr, int len);
81#endif
82/* If none set: */
83#ifndef LWIP_CHKSUM_ALGORITHM
84# define LWIP_CHKSUM_ALGORITHM 0
85#endif
86
87#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
98u16_t
99lwip_standard_chksum(const void *dataptr, int len)
100{
101 u32_t acc;
102 u16_t src;
103 const u8_t *octetptr;
104
105 acc = 0;
106 /* dataptr may be at odd or even addresses */
107 octetptr = (const u8_t*)dataptr;
108 while (len > 1) {
109 /* declare first octet as most significant
110 thus assume network order, ignoring host order */
111 src = (*octetptr) << 8;
112 octetptr++;
113 /* declare second octet as least significant */
114 src |= (*octetptr);
115 octetptr++;
116 acc += src;
117 len -= 2;
118 }
119 if (len > 0) {
120 /* accumulate remaining octet */
121 src = (*octetptr) << 8;
122 acc += src;
123 }
124 /* add deferred carry bits */
125 acc = (acc >> 16) + (acc & 0x0000ffffUL);
126 if ((acc & 0xffff0000UL) != 0) {
127 acc = (acc >> 16) + (acc & 0x0000ffffUL);
128 }
129 /* This maybe a little confusing: reorder sum using lwip_htons()
130 instead of lwip_ntohs() since it has a little less call overhead.
131 The caller must invert bits for Internet sum ! */
132 return lwip_htons((u16_t)acc);
133}
134#endif
135
136#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
137/*
138 * Curt McDowell
139 * Broadcom Corp.
140 * csm@broadcom.com
141 *
142 * IP checksum two bytes at a time with support for
143 * unaligned buffer.
144 * Works for len up to and including 0x20000.
145 * by Curt McDowell, Broadcom Corp. 12/08/2005
146 *
147 * @param dataptr points to start of data to be summed at any boundary
148 * @param len length of data to be summed
149 * @return host order (!) lwip checksum (non-inverted Internet sum)
150 */
151u16_t
152lwip_standard_chksum(const void *dataptr, int len)
153{
154 const u8_t *pb = (const u8_t *)dataptr;
155 const u16_t *ps;
156 u16_t t = 0;
157 u32_t sum = 0;
158 int odd = ((mem_ptr_t)pb & 1);
159
160 /* Get aligned to u16_t */
161 if (odd && len > 0) {
162 ((u8_t *)&t)[1] = *pb++;
163 len--;
164 }
165
166 /* Add the bulk of the data */
167 ps = (const u16_t *)(const void *)pb;
168 while (len > 1) {
169 sum += *ps++;
170 len -= 2;
171 }
172
173 /* Consume left-over byte, if any */
174 if (len > 0) {
175 ((u8_t *)&t)[0] = *(const u8_t *)ps;
176 }
177
178 /* Add end bytes */
179 sum += t;
180
181 /* Fold 32-bit sum to 16 bits
182 calling this twice is probably faster than if statements... */
183 sum = FOLD_U32T(sum);
184 sum = FOLD_U32T(sum);
185
186 /* Swap if alignment was odd */
187 if (odd) {
188 sum = SWAP_BYTES_IN_WORD(sum);
189 }
190
191 return (u16_t)sum;
192}
193#endif
194
195#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
207u16_t
208lwip_standard_chksum(const void *dataptr, int len)
209{
210 const u8_t *pb = (const u8_t *)dataptr;
211 const u16_t *ps;
212 u16_t t = 0;
213 const u32_t *pl;
214 u32_t sum = 0, tmp;
215 /* starts at odd byte address? */
216 int odd = ((mem_ptr_t)pb & 1);
217
218 if (odd && len > 0) {
219 ((u8_t *)&t)[1] = *pb++;
220 len--;
221 }
222
223 ps = (const u16_t *)(const void*)pb;
224
225 if (((mem_ptr_t)ps & 3) && len > 1) {
226 sum += *ps++;
227 len -= 2;
228 }
229
230 pl = (const u32_t *)(const void*)ps;
231
232 while (len > 7) {
233 tmp = sum + *pl++; /* ping */
234 if (tmp < sum) {
235 tmp++; /* add back carry */
236 }
237
238 sum = tmp + *pl++; /* pong */
239 if (sum < tmp) {
240 sum++; /* add back carry */
241 }
242
243 len -= 8;
244 }
245
246 /* make room in upper bits */
247 sum = FOLD_U32T(sum);
248
249 ps = (const u16_t *)pl;
250
251 /* 16-bit aligned word remaining? */
252 while (len > 1) {
253 sum += *ps++;
254 len -= 2;
255 }
256
257 /* dangling tail byte remaining? */
258 if (len > 0) { /* include odd byte */
259 ((u8_t *)&t)[0] = *(const u8_t *)ps;
260 }
261
262 sum += t; /* add end bytes */
263
264 /* Fold 32-bit sum to 16 bits
265 calling this twice is probably faster than if statements... */
266 sum = FOLD_U32T(sum);
267 sum = FOLD_U32T(sum);
268
269 if (odd) {
270 sum = SWAP_BYTES_IN_WORD(sum);
271 }
272
273 return (u16_t)sum;
274}
275#endif
276
278static u16_t
279inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
280{
281 struct pbuf *q;
282 u8_t swapped = 0;
283
284 /* iterate through all pbuf in chain */
285 for (q = p; q != NULL; q = q->next) {
286 LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
287 (void *)q, (void *)q->next));
288 acc += LWIP_CHKSUM(q->payload, q->len);
289 /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
290 /* just executing this next line is probably faster that the if statement needed
291 to check whether we really need to execute it, and does no harm */
292 acc = FOLD_U32T(acc);
293 if (q->len % 2 != 0) {
294 swapped = 1 - swapped;
295 acc = SWAP_BYTES_IN_WORD(acc);
296 }
297 /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
298 }
299
300 if (swapped) {
301 acc = SWAP_BYTES_IN_WORD(acc);
302 }
303
304 acc += (u32_t)lwip_htons((u16_t)proto);
305 acc += (u32_t)lwip_htons(proto_len);
306
307 /* Fold 32-bit sum to 16 bits
308 calling this twice is probably faster than if statements... */
309 acc = FOLD_U32T(acc);
310 acc = FOLD_U32T(acc);
311 LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
312 return (u16_t)~(acc & 0xffffUL);
313}
314
315#if LWIP_IPV4
316/* inet_chksum_pseudo:
317 *
318 * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
319 * IP addresses are expected to be in network byte order.
320 *
321 * @param p chain of pbufs over that a checksum should be calculated (ip data part)
322 * @param src source ip address (used for checksum of pseudo header)
323 * @param dst destination ip address (used for checksum of pseudo header)
324 * @param proto ip protocol (used for checksum of pseudo header)
325 * @param proto_len length of the ip data part (used for checksum of pseudo header)
326 * @return checksum (as u16_t) to be saved directly in the protocol header
327 */
328u16_t
329inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
330 const ip4_addr_t *src, const ip4_addr_t *dest)
331{
332 u32_t acc;
333 u32_t addr;
334
335 addr = ip4_addr_get_u32(src);
336 acc = (addr & 0xffffUL);
337 acc += ((addr >> 16) & 0xffffUL);
338 addr = ip4_addr_get_u32(dest);
339 acc += (addr & 0xffffUL);
340 acc += ((addr >> 16) & 0xffffUL);
341 /* fold down to 16 bits */
342 acc = FOLD_U32T(acc);
343 acc = FOLD_U32T(acc);
344
345 return inet_cksum_pseudo_base(p, proto, proto_len, acc);
346}
347#endif /* LWIP_IPV4 */
348
349#if LWIP_IPV6
361u16_t
362ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
363 const ip6_addr_t *src, const ip6_addr_t *dest)
364{
365 u32_t acc = 0;
366 u32_t addr;
367 u8_t addr_part;
368
369 for (addr_part = 0; addr_part < 4; addr_part++) {
370 addr = src->addr[addr_part];
371 acc += (addr & 0xffffUL);
372 acc += ((addr >> 16) & 0xffffUL);
373 addr = dest->addr[addr_part];
374 acc += (addr & 0xffffUL);
375 acc += ((addr >> 16) & 0xffffUL);
376 }
377 /* fold down to 16 bits */
378 acc = FOLD_U32T(acc);
379 acc = FOLD_U32T(acc);
380
381 return inet_cksum_pseudo_base(p, proto, proto_len, acc);
382}
383#endif /* LWIP_IPV6 */
384
385/* ip_chksum_pseudo:
386 *
387 * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
388 * IP addresses are expected to be in network byte order.
389 *
390 * @param p chain of pbufs over that a checksum should be calculated (ip data part)
391 * @param src source ip address (used for checksum of pseudo header)
392 * @param dst destination ip address (used for checksum of pseudo header)
393 * @param proto ip protocol (used for checksum of pseudo header)
394 * @param proto_len length of the ip data part (used for checksum of pseudo header)
395 * @return checksum (as u16_t) to be saved directly in the protocol header
396 */
397u16_t
398ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
399 const ip_addr_t *src, const ip_addr_t *dest)
400{
401#if LWIP_IPV6
402 if (IP_IS_V6(dest)) {
403 return ip6_chksum_pseudo(p, proto, proto_len, ip_2_ip6(src), ip_2_ip6(dest));
404 }
405#endif /* LWIP_IPV6 */
406#if LWIP_IPV4 && LWIP_IPV6
407 else
408#endif /* LWIP_IPV4 && LWIP_IPV6 */
409#if LWIP_IPV4
410 {
411 return inet_chksum_pseudo(p, proto, proto_len, ip_2_ip4(src), ip_2_ip4(dest));
412 }
413#endif /* LWIP_IPV4 */
414}
415
417static u16_t
418inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
419 u16_t chksum_len, u32_t acc)
420{
421 struct pbuf *q;
422 u8_t swapped = 0;
423 u16_t chklen;
424
425 /* iterate through all pbuf in chain */
426 for (q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
427 LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
428 (void *)q, (void *)q->next));
429 chklen = q->len;
430 if (chklen > chksum_len) {
431 chklen = chksum_len;
432 }
433 acc += LWIP_CHKSUM(q->payload, chklen);
434 chksum_len -= chklen;
435 LWIP_ASSERT("delete me", chksum_len < 0x7fff);
436 /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
437 /* fold the upper bit down */
438 acc = FOLD_U32T(acc);
439 if (q->len % 2 != 0) {
440 swapped = 1 - swapped;
441 acc = SWAP_BYTES_IN_WORD(acc);
442 }
443 /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
444 }
445
446 if (swapped) {
447 acc = SWAP_BYTES_IN_WORD(acc);
448 }
449
450 acc += (u32_t)lwip_htons((u16_t)proto);
451 acc += (u32_t)lwip_htons(proto_len);
452
453 /* Fold 32-bit sum to 16 bits
454 calling this twice is probably faster than if statements... */
455 acc = FOLD_U32T(acc);
456 acc = FOLD_U32T(acc);
457 LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
458 return (u16_t)~(acc & 0xffffUL);
459}
460
461#if LWIP_IPV4
462/* inet_chksum_pseudo_partial:
463 *
464 * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
465 * IP addresses are expected to be in network byte order.
466 *
467 * @param p chain of pbufs over that a checksum should be calculated (ip data part)
468 * @param src source ip address (used for checksum of pseudo header)
469 * @param dst destination ip address (used for checksum of pseudo header)
470 * @param proto ip protocol (used for checksum of pseudo header)
471 * @param proto_len length of the ip data part (used for checksum of pseudo header)
472 * @return checksum (as u16_t) to be saved directly in the protocol header
473 */
474u16_t
475inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
476 u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest)
477{
478 u32_t acc;
479 u32_t addr;
480
481 addr = ip4_addr_get_u32(src);
482 acc = (addr & 0xffffUL);
483 acc += ((addr >> 16) & 0xffffUL);
484 addr = ip4_addr_get_u32(dest);
485 acc += (addr & 0xffffUL);
486 acc += ((addr >> 16) & 0xffffUL);
487 /* fold down to 16 bits */
488 acc = FOLD_U32T(acc);
489 acc = FOLD_U32T(acc);
490
491 return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
492}
493#endif /* LWIP_IPV4 */
494
495#if LWIP_IPV6
509u16_t
510ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
511 u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest)
512{
513 u32_t acc = 0;
514 u32_t addr;
515 u8_t addr_part;
516
517 for (addr_part = 0; addr_part < 4; addr_part++) {
518 addr = src->addr[addr_part];
519 acc += (addr & 0xffffUL);
520 acc += ((addr >> 16) & 0xffffUL);
521 addr = dest->addr[addr_part];
522 acc += (addr & 0xffffUL);
523 acc += ((addr >> 16) & 0xffffUL);
524 }
525 /* fold down to 16 bits */
526 acc = FOLD_U32T(acc);
527 acc = FOLD_U32T(acc);
528
529 return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
530}
531#endif /* LWIP_IPV6 */
532
533/* ip_chksum_pseudo_partial:
534 *
535 * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
536 *
537 * @param p chain of pbufs over that a checksum should be calculated (ip data part)
538 * @param src source ip address (used for checksum of pseudo header)
539 * @param dst destination ip address (used for checksum of pseudo header)
540 * @param proto ip protocol (used for checksum of pseudo header)
541 * @param proto_len length of the ip data part (used for checksum of pseudo header)
542 * @return checksum (as u16_t) to be saved directly in the protocol header
543 */
544u16_t
545ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
546 u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest)
547{
548#if LWIP_IPV6
549 if (IP_IS_V6(dest)) {
550 return ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip6(src), ip_2_ip6(dest));
551 }
552#endif /* LWIP_IPV6 */
553#if LWIP_IPV4 && LWIP_IPV6
554 else
555#endif /* LWIP_IPV4 && LWIP_IPV6 */
556#if LWIP_IPV4
557 {
558 return inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip4(src), ip_2_ip4(dest));
559 }
560#endif /* LWIP_IPV4 */
561}
562
563/* inet_chksum:
564 *
565 * Calculates the Internet checksum over a portion of memory. Used primarily for IP
566 * and ICMP.
567 *
568 * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
569 * @param len length of the buffer to calculate the checksum
570 * @return checksum (as u16_t) to be saved directly in the protocol header
571 */
572
573u16_t
574inet_chksum(const void *dataptr, u16_t len)
575{
576 return (u16_t)~(unsigned int)LWIP_CHKSUM(dataptr, len);
577}
578
586u16_t
588{
589 u32_t acc;
590 struct pbuf *q;
591 u8_t swapped;
592
593 acc = 0;
594 swapped = 0;
595 for (q = p; q != NULL; q = q->next) {
596 acc += LWIP_CHKSUM(q->payload, q->len);
597 acc = FOLD_U32T(acc);
598 if (q->len % 2 != 0) {
599 swapped = 1 - swapped;
600 acc = SWAP_BYTES_IN_WORD(acc);
601 }
602 }
603
604 if (swapped) {
605 acc = SWAP_BYTES_IN_WORD(acc);
606 }
607 return (u16_t)~(acc & 0xffffUL);
608}
609
610/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
611 * like MEMCPY but generates a checksum at the same time. Since this is a
612 * performance-sensitive function, you might want to create your own version
613 * in assembly targeted at your hardware by defining it in lwipopts.h:
614 * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
615 */
616
617#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
622u16_t
623lwip_chksum_copy(void *dst, const void *src, u16_t len)
624{
625 MEMCPY(dst, src, len);
626 return LWIP_CHKSUM(dst, len);
627}
628#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
#define LWIP_DEBUGF(debug, message)
Definition debug.h:183
#define INET_DEBUG
Definition opt.h:2706
u16_t inet_chksum_pbuf(struct pbuf *p)
#define SWAP_BYTES_IN_WORD(w)
Definition inet_chksum.h:66
#define FOLD_U32T(u)
Definition inet_chksum.h:71
Definition pbuf.h:161
struct pbuf * next
Definition pbuf.h:163
u16_t len
Definition pbuf.h:178
void * payload
Definition pbuf.h:166