Home | History | Annotate | Download | only in ipv4
      1 /**
      2  * @file
      3  * Incluse internet checksum functions.
      4  *
      5  */
      6 
      7 /*
      8  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
      9  * All rights reserved.
     10  *
     11  * Redistribution and use in source and binary forms, with or without modification,
     12  * are permitted provided that the following conditions are met:
     13  *
     14  * 1. Redistributions of source code must retain the above copyright notice,
     15  *    this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright notice,
     17  *    this list of conditions and the following disclaimer in the documentation
     18  *    and/or other materials provided with the distribution.
     19  * 3. The name of the author may not be used to endorse or promote products
     20  *    derived from this software without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     23  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     24  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
     25  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
     27  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
     30  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
     31  * OF SUCH DAMAGE.
     32  *
     33  * This file is part of the lwIP TCP/IP stack.
     34  *
     35  * Author: Adam Dunkels <adam (at) sics.se>
     36  *
     37  */
     38 
     39 #include "lwip/opt.h"
     40 
     41 #include "lwip/inet_chksum.h"
     42 #include "lwip/def.h"
     43 
     44 #include <stddef.h>
     45 #include <string.h>
     46 
     47 /* These are some reference implementations of the checksum algorithm, with the
     48  * aim of being simple, correct and fully portable. Checksumming is the
     49  * first thing you would want to optimize for your platform. If you create
     50  * your own version, link it in and in your cc.h put:
     51  *
     52  * #define LWIP_CHKSUM <your_checksum_routine>
     53  *
     54  * Or you can select from the implementations below by defining
     55  * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
     56  */
     57 
     58 #ifndef LWIP_CHKSUM
     59 # define LWIP_CHKSUM lwip_standard_chksum
     60 # ifndef LWIP_CHKSUM_ALGORITHM
     61 #  define LWIP_CHKSUM_ALGORITHM 2
     62 # endif
     63 #endif
     64 /* If none set: */
     65 #ifndef LWIP_CHKSUM_ALGORITHM
     66 # define LWIP_CHKSUM_ALGORITHM 0
     67 #endif
     68 
     69 #if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
     70 /**
     71  * lwip checksum
     72  *
     73  * @param dataptr points to start of data to be summed at any boundary
     74  * @param len length of data to be summed
     75  * @return host order (!) lwip checksum (non-inverted Internet sum)
     76  *
     77  * @note accumulator size limits summable length to 64k
     78  * @note host endianess is irrelevant (p3 RFC1071)
     79  */
     80 static u16_t
     81 lwip_standard_chksum(void *dataptr, u16_t len)
     82 {
     83   u32_t acc;
     84   u16_t src;
     85   u8_t *octetptr;
     86 
     87   acc = 0;
     88   /* dataptr may be at odd or even addresses */
     89   octetptr = (u8_t*)dataptr;
     90   while (len > 1) {
     91     /* declare first octet as most significant
     92        thus assume network order, ignoring host order */
     93     src = (*octetptr) << 8;
     94     octetptr++;
     95     /* declare second octet as least significant */
     96     src |= (*octetptr);
     97     octetptr++;
     98     acc += src;
     99     len -= 2;
    100   }
    101   if (len > 0) {
    102     /* accumulate remaining octet */
    103     src = (*octetptr) << 8;
    104     acc += src;
    105   }
    106   /* add deferred carry bits */
    107   acc = (acc >> 16) + (acc & 0x0000ffffUL);
    108   if ((acc & 0xffff0000UL) != 0) {
    109     acc = (acc >> 16) + (acc & 0x0000ffffUL);
    110   }
    111   /* This maybe a little confusing: reorder sum using htons()
    112      instead of ntohs() since it has a little less call overhead.
    113      The caller must invert bits for Internet sum ! */
    114   return htons((u16_t)acc);
    115 }
    116 #endif
    117 
    118 #if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
    119 /*
    120  * Curt McDowell
    121  * Broadcom Corp.
    122  * csm (at) broadcom.com
    123  *
    124  * IP checksum two bytes at a time with support for
    125  * unaligned buffer.
    126  * Works for len up to and including 0x20000.
    127  * by Curt McDowell, Broadcom Corp. 12/08/2005
    128  *
    129  * @param dataptr points to start of data to be summed at any boundary
    130  * @param len length of data to be summed
    131  * @return host order (!) lwip checksum (non-inverted Internet sum)
    132  */
    133 
    134 static u16_t
    135 lwip_standard_chksum(void *dataptr, int len)
    136 {
    137   u8_t *pb = (u8_t *)dataptr;
    138   u16_t *ps, t = 0;
    139   u32_t sum = 0;
    140   int odd = ((mem_ptr_t)pb & 1);
    141 
    142   /* Get aligned to u16_t */
    143   if (odd && len > 0) {
    144     ((u8_t *)&t)[1] = *pb++;
    145     len--;
    146   }
    147 
    148   /* Add the bulk of the data */
    149   ps = (u16_t *)(void *)pb;
    150   while (len > 1) {
    151     sum += *ps++;
    152     len -= 2;
    153   }
    154 
    155   /* Consume left-over byte, if any */
    156   if (len > 0) {
    157     ((u8_t *)&t)[0] = *(u8_t *)ps;
    158   }
    159 
    160   /* Add end bytes */
    161   sum += t;
    162 
    163   /* Fold 32-bit sum to 16 bits
    164      calling this twice is propably faster than if statements... */
    165   sum = FOLD_U32T(sum);
    166   sum = FOLD_U32T(sum);
    167 
    168   /* Swap if alignment was odd */
    169   if (odd) {
    170     sum = SWAP_BYTES_IN_WORD(sum);
    171   }
    172 
    173   return (u16_t)sum;
    174 }
    175 #endif
    176 
    177 #if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
    178 /**
    179  * An optimized checksum routine. Basically, it uses loop-unrolling on
    180  * the checksum loop, treating the head and tail bytes specially, whereas
    181  * the inner loop acts on 8 bytes at a time.
    182  *
    183  * @arg start of buffer to be checksummed. May be an odd byte address.
    184  * @len number of bytes in the buffer to be checksummed.
    185  * @return host order (!) lwip checksum (non-inverted Internet sum)
    186  *
    187  * by Curt McDowell, Broadcom Corp. December 8th, 2005
    188  */
    189 
    190 static u16_t
    191 lwip_standard_chksum(void *dataptr, int len)
    192 {
    193   u8_t *pb = (u8_t *)dataptr;
    194   u16_t *ps, t = 0;
    195   u32_t *pl;
    196   u32_t sum = 0, tmp;
    197   /* starts at odd byte address? */
    198   int odd = ((mem_ptr_t)pb & 1);
    199 
    200   if (odd && len > 0) {
    201     ((u8_t *)&t)[1] = *pb++;
    202     len--;
    203   }
    204 
    205   ps = (u16_t *)pb;
    206 
    207   if (((mem_ptr_t)ps & 3) && len > 1) {
    208     sum += *ps++;
    209     len -= 2;
    210   }
    211 
    212   pl = (u32_t *)ps;
    213 
    214   while (len > 7)  {
    215     tmp = sum + *pl++;          /* ping */
    216     if (tmp < sum) {
    217       tmp++;                    /* add back carry */
    218     }
    219 
    220     sum = tmp + *pl++;          /* pong */
    221     if (sum < tmp) {
    222       sum++;                    /* add back carry */
    223     }
    224 
    225     len -= 8;
    226   }
    227 
    228   /* make room in upper bits */
    229   sum = FOLD_U32T(sum);
    230 
    231   ps = (u16_t *)pl;
    232 
    233   /* 16-bit aligned word remaining? */
    234   while (len > 1) {
    235     sum += *ps++;
    236     len -= 2;
    237   }
    238 
    239   /* dangling tail byte remaining? */
    240   if (len > 0) {                /* include odd byte */
    241     ((u8_t *)&t)[0] = *(u8_t *)ps;
    242   }
    243 
    244   sum += t;                     /* add end bytes */
    245 
    246   /* Fold 32-bit sum to 16 bits
    247      calling this twice is propably faster than if statements... */
    248   sum = FOLD_U32T(sum);
    249   sum = FOLD_U32T(sum);
    250 
    251   if (odd) {
    252     sum = SWAP_BYTES_IN_WORD(sum);
    253   }
    254 
    255   return (u16_t)sum;
    256 }
    257 #endif
    258 
    259 /* inet_chksum_pseudo:
    260  *
    261  * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
    262  * IP addresses are expected to be in network byte order.
    263  *
    264  * @param p chain of pbufs over that a checksum should be calculated (ip data part)
    265  * @param src source ip address (used for checksum of pseudo header)
    266  * @param dst destination ip address (used for checksum of pseudo header)
    267  * @param proto ip protocol (used for checksum of pseudo header)
    268  * @param proto_len length of the ip data part (used for checksum of pseudo header)
    269  * @return checksum (as u16_t) to be saved directly in the protocol header
    270  */
    271 u16_t
    272 inet_chksum_pseudo(struct pbuf *p,
    273        ip_addr_t *src, ip_addr_t *dest,
    274        u8_t proto, u16_t proto_len)
    275 {
    276   u32_t acc;
    277   u32_t addr;
    278   struct pbuf *q;
    279   u8_t swapped;
    280 
    281   acc = 0;
    282   swapped = 0;
    283   /* iterate through all pbuf in chain */
    284   for(q = p; q != NULL; q = q->next) {
    285     LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
    286       (void *)q, (void *)q->next));
    287     acc += LWIP_CHKSUM(q->payload, q->len);
    288     /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
    289     /* just executing this next line is probably faster that the if statement needed
    290        to check whether we really need to execute it, and does no harm */
    291     acc = FOLD_U32T(acc);
    292     if (q->len % 2 != 0) {
    293       swapped = 1 - swapped;
    294       acc = SWAP_BYTES_IN_WORD(acc);
    295     }
    296     /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
    297   }
    298 
    299   if (swapped) {
    300     acc = SWAP_BYTES_IN_WORD(acc);
    301   }
    302   addr = ip4_addr_get_u32(src);
    303   acc += (addr & 0xffffUL);
    304   acc += ((addr >> 16) & 0xffffUL);
    305   addr = ip4_addr_get_u32(dest);
    306   acc += (addr & 0xffffUL);
    307   acc += ((addr >> 16) & 0xffffUL);
    308   acc += (u32_t)htons((u16_t)proto);
    309   acc += (u32_t)htons(proto_len);
    310 
    311   /* Fold 32-bit sum to 16 bits
    312      calling this twice is propably faster than if statements... */
    313   acc = FOLD_U32T(acc);
    314   acc = FOLD_U32T(acc);
    315   LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
    316   return (u16_t)~(acc & 0xffffUL);
    317 }
    318 
    319 /* inet_chksum_pseudo:
    320  *
    321  * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
    322  * IP addresses are expected to be in network byte order.
    323  *
    324  * @param p chain of pbufs over that a checksum should be calculated (ip data part)
    325  * @param src source ip address (used for checksum of pseudo header)
    326  * @param dst destination ip address (used for checksum of pseudo header)
    327  * @param proto ip protocol (used for checksum of pseudo header)
    328  * @param proto_len length of the ip data part (used for checksum of pseudo header)
    329  * @return checksum (as u16_t) to be saved directly in the protocol header
    330  */
    331 u16_t
    332 inet_chksum_pseudo_partial(struct pbuf *p,
    333        ip_addr_t *src, ip_addr_t *dest,
    334        u8_t proto, u16_t proto_len, u16_t chksum_len)
    335 {
    336   u32_t acc;
    337   u32_t addr;
    338   struct pbuf *q;
    339   u8_t swapped;
    340   u16_t chklen;
    341 
    342   acc = 0;
    343   swapped = 0;
    344   /* iterate through all pbuf in chain */
    345   for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
    346     LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
    347       (void *)q, (void *)q->next));
    348     chklen = q->len;
    349     if (chklen > chksum_len) {
    350       chklen = chksum_len;
    351     }
    352     acc += LWIP_CHKSUM(q->payload, chklen);
    353     chksum_len -= chklen;
    354     LWIP_ASSERT("delete me", chksum_len < 0x7fff);
    355     /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
    356     /* fold the upper bit down */
    357     acc = FOLD_U32T(acc);
    358     if (q->len % 2 != 0) {
    359       swapped = 1 - swapped;
    360       acc = SWAP_BYTES_IN_WORD(acc);
    361     }
    362     /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
    363   }
    364 
    365   if (swapped) {
    366     acc = SWAP_BYTES_IN_WORD(acc);
    367   }
    368   addr = ip4_addr_get_u32(src);
    369   acc += (addr & 0xffffUL);
    370   acc += ((addr >> 16) & 0xffffUL);
    371   addr = ip4_addr_get_u32(dest);
    372   acc += (addr & 0xffffUL);
    373   acc += ((addr >> 16) & 0xffffUL);
    374   acc += (u32_t)htons((u16_t)proto);
    375   acc += (u32_t)htons(proto_len);
    376 
    377   /* Fold 32-bit sum to 16 bits
    378      calling this twice is propably faster than if statements... */
    379   acc = FOLD_U32T(acc);
    380   acc = FOLD_U32T(acc);
    381   LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
    382   return (u16_t)~(acc & 0xffffUL);
    383 }
    384 
    385 /* inet_chksum:
    386  *
    387  * Calculates the Internet checksum over a portion of memory. Used primarily for IP
    388  * and ICMP.
    389  *
    390  * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
    391  * @param len length of the buffer to calculate the checksum
    392  * @return checksum (as u16_t) to be saved directly in the protocol header
    393  */
    394 
    395 u16_t
    396 inet_chksum(void *dataptr, u16_t len)
    397 {
    398   return ~LWIP_CHKSUM(dataptr, len);
    399 }
    400 
    401 /**
    402  * Calculate a checksum over a chain of pbufs (without pseudo-header, much like
    403  * inet_chksum only pbufs are used).
    404  *
    405  * @param p pbuf chain over that the checksum should be calculated
    406  * @return checksum (as u16_t) to be saved directly in the protocol header
    407  */
    408 u16_t
    409 inet_chksum_pbuf(struct pbuf *p)
    410 {
    411   u32_t acc;
    412   struct pbuf *q;
    413   u8_t swapped;
    414 
    415   acc = 0;
    416   swapped = 0;
    417   for(q = p; q != NULL; q = q->next) {
    418     acc += LWIP_CHKSUM(q->payload, q->len);
    419     acc = FOLD_U32T(acc);
    420     if (q->len % 2 != 0) {
    421       swapped = 1 - swapped;
    422       acc = SWAP_BYTES_IN_WORD(acc);
    423     }
    424   }
    425 
    426   if (swapped) {
    427     acc = SWAP_BYTES_IN_WORD(acc);
    428   }
    429   return (u16_t)~(acc & 0xffffUL);
    430 }
    431 
    432 /* These are some implementations for LWIP_CHKSUM_COPY, which copies data
    433  * like MEMCPY but generates a checksum at the same time. Since this is a
    434  * performance-sensitive function, you might want to create your own version
    435  * in assembly targeted at your hardware by defining it in lwipopts.h:
    436  *   #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
    437  */
    438 
    439 #if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
    440 /** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
    441  * For architectures with big caches, data might still be in cache when
    442  * generating the checksum after copying.
    443  */
    444 u16_t
    445 lwip_chksum_copy(void *dst, const void *src, u16_t len)
    446 {
    447   MEMCPY(dst, src, len);
    448   return LWIP_CHKSUM(dst, len);
    449 }
    450 #endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
    451