File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libnet / src / libnet_checksum.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 11:54:42 2013 UTC (10 years, 11 months ago) by misho
Branches: libnet, MAIN
CVS tags: v1_1_6p5, v1_1_6p4, v1_1_6p0, v1_1_6, HEAD
1.1.6

    1: /*
    2:  *  $Id: libnet_checksum.c,v 1.1.1.2 2013/07/22 11:54:42 misho Exp $
    3:  *
    4:  *  libnet
    5:  *  libnet_checksum.c - checksum routines
    6:  *
    7:  *  Copyright (c) 1998 - 2004 Mike D. Schiffman <mike@infonexus.com>
    8:  *  All rights reserved.
    9:  *
   10:  * Redistribution and use in source and binary forms, with or without
   11:  * modification, are permitted provided that the following conditions
   12:  * are met:
   13:  * 1. Redistributions of source code must retain the above copyright
   14:  *    notice, this list of conditions and the following disclaimer.
   15:  * 2. Redistributions in binary form must reproduce the above copyright
   16:  *    notice, this list of conditions and the following disclaimer in the
   17:  *    documentation and/or other materials provided with the distribution.
   18:  *
   19:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   20:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   23:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29:  * SUCH DAMAGE.
   30:  *
   31:  */
   32: 
   33: #if (HAVE_CONFIG_H)
   34: #include "../include/config.h"
   35: #endif
   36: #if (!(_WIN32) || (__CYGWIN__)) 
   37: #include "../include/libnet.h"
   38: #else
   39: #include "../include/win32/libnet.h"
   40: #endif
   41: 
   42: /* FIXME - unit test these - 0 is debian's version, else is -RC1's */
   43: /* Note about aliasing warning:
   44:  *
   45:  *   http://mail.opensolaris.org/pipermail/tools-gcc/2005-August/000047.html
   46:  *
   47:  * See RFC 1071, and:
   48:  *
   49:  *   http://mathforum.org/library/drmath/view/54379.html
   50:  */
   51: #undef DEBIAN
   52: /* Note: len is in bytes, not 16-bit words! */
   53: int
   54: libnet_in_cksum(uint16_t *addr, int len)
   55: {
   56:     int sum;
   57: #ifdef DEBIAN
   58:     uint16_t last_byte;
   59: 
   60:     sum = 0;
   61:     last_byte = 0;
   62: #else
   63:     union
   64:     {
   65:         uint16_t s;
   66:         uint8_t b[2];
   67:     }pad;
   68: 
   69:     sum = 0;
   70: #endif
   71: 
   72:     while (len > 1)
   73:     {
   74:         sum += *addr++;
   75:         len -= 2;
   76:     }
   77: #ifdef DEBIAN
   78:     if (len == 1)
   79:     {
   80:         *(uint8_t *)&last_byte = *(uint8_t *)addr;
   81:         sum += last_byte;
   82: #else
   83:     if (len == 1)
   84:     {
   85:         pad.b[0] = *(uint8_t *)addr;
   86:         pad.b[1] = 0;
   87:         sum += pad.s;
   88: #endif
   89:     }
   90: 
   91:     return (sum);
   92: }
   93: 
   94: int
   95: libnet_toggle_checksum(libnet_t *l, libnet_ptag_t ptag, int mode)
   96: {
   97:     libnet_pblock_t *p;
   98: 
   99:     p = libnet_pblock_find(l, ptag);
  100:     if (p == NULL)
  101:     {
  102:         /* err msg set in libnet_pblock_find() */
  103:         return (-1);
  104:     }
  105:     if (mode == LIBNET_ON)
  106:     {
  107:         if ((p->flags) & LIBNET_PBLOCK_DO_CHECKSUM)
  108:         {
  109:             return (1);
  110:         }
  111:         else
  112:         {
  113:             (p->flags) |= LIBNET_PBLOCK_DO_CHECKSUM;
  114:             return (1);
  115:         }
  116:     }
  117:     else
  118:     {
  119:         if ((p->flags) & LIBNET_PBLOCK_DO_CHECKSUM)
  120:         {
  121:             (p->flags) &= ~LIBNET_PBLOCK_DO_CHECKSUM;
  122:             return (1);
  123:         }
  124:         else
  125:         {
  126:             return (1);
  127:         }
  128:     }
  129: }
  130: 
  131: static int check_ip_payload_size(libnet_t*l, const uint8_t *iphdr, int ip_hl, int h_len, const uint8_t * end, const char* func)
  132: {
  133:     if((iphdr+ip_hl+h_len) > end)
  134:     {
  135:         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  136:                 "%s(): ip payload not inside packet (pktsz %d, iphsz %d, payloadsz %d)\n", func,
  137: 		(int)(end - iphdr), ip_hl, h_len);
  138:         return -1;
  139:     }
  140: 
  141:     return 0;
  142: }
  143: 
  144: 
  145: /*
  146:  * For backwards binary compatibility. The calculations done here can easily
  147:  * result in buffer overreads and overwrites. You have been warned. And no, it
  148:  * is not possible to fix, the API contains no information on the buffer's
  149:  * boundary. libnet itself calls the safe function, libnet_inet_checksum(). So
  150:  * should you.
  151:  */
  152: int
  153: libnet_do_checksum(libnet_t *l, uint8_t *iphdr, int protocol, int h_len)
  154: {
  155:     uint16_t ip_len = 0;
  156:     struct libnet_ipv4_hdr* ip4 = (struct libnet_ipv4_hdr *)iphdr;
  157:     struct libnet_ipv6_hdr* ip6 = (struct libnet_ipv6_hdr *)iphdr;
  158: 
  159:     if(ip4->ip_v == 6) {
  160:         ip_len = ntohs(ip6->ip_len);
  161:     } else {
  162:         ip_len = ntohs(ip4->ip_len);
  163:     }
  164: 
  165:     return libnet_inet_checksum(l, iphdr, protocol, h_len,
  166:             iphdr, iphdr + ip_len
  167:             );
  168: }
  169: 
  170: 
  171: #define CHECK_IP_PAYLOAD_SIZE() do { \
  172:     int e=check_ip_payload_size(l,iphdr,ip_hl, h_len, end, __func__);\
  173:     if(e) return e;\
  174: } while(0)
  175: 
  176: 
  177: /*
  178:  * We are checksumming pblock "q"
  179:  *
  180:  * iphdr is the pointer to it's encapsulating IP header
  181:  * protocol describes the type of "q", expressed as an IPPROTO_ value
  182:  * h_len is the h_len from "q"
  183:  */
  184: int
  185: libnet_inet_checksum(libnet_t *l, uint8_t *iphdr, int protocol, int h_len, const uint8_t *beg, const uint8_t * end)
  186: {
  187:     /* will need to update this for ipv6 at some point */
  188:     struct libnet_ipv4_hdr *iph_p = (struct libnet_ipv4_hdr *)iphdr;
  189:     struct libnet_ipv6_hdr *ip6h_p = NULL; /* default to not using IPv6 */
  190:     int ip_hl   = 0;
  191:     int sum     = 0;
  192: 
  193:     /* Check for memory under/over reads/writes. */
  194:     if(iphdr < beg || (iphdr+sizeof(*iph_p)) > end)
  195:     {
  196:         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  197:             "%s(): ipv4 hdr not inside packet (where %d, size %d)\n", __func__,
  198: 	    (int)(iphdr-beg), (int)(end-beg));
  199:         return -1;
  200:     }
  201: 
  202:     /*
  203:      *  Figure out which IP version we're dealing with.  We'll assume v4
  204:      *  and overlay a header structure to yank out the version.
  205:      */
  206:     if (iph_p->ip_v == 6)
  207:     {
  208:         ip6h_p = (struct libnet_ipv6_hdr *)iph_p;
  209:         iph_p = NULL;
  210:         ip_hl   = 40;
  211:         if((uint8_t*)(ip6h_p+1) > end)
  212:         {
  213:             snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  214:                     "%s(): ipv6 hdr not inside packet\n", __func__);
  215:             return -1;
  216:         }
  217:     }
  218:     else
  219:     {
  220:         ip_hl = iph_p->ip_hl << 2;
  221:     }
  222: 
  223:     if((iphdr+ip_hl) > end)
  224:     {
  225:         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  226:             "%s(): ip hdr len not inside packet\n", __func__);
  227:         return -1;
  228:     }
  229: 
  230:     /*
  231:      *  Dug Song came up with this very cool checksuming implementation
  232:      *  eliminating the need for explicit psuedoheader use.  Check it out.
  233:      */
  234:     switch (protocol)
  235:     {
  236:         case IPPROTO_TCP:
  237:         {
  238:             struct libnet_tcp_hdr *tcph_p =
  239:                 (struct libnet_tcp_hdr *)(iphdr + ip_hl);
  240: 
  241: 	    h_len = end - (uint8_t*) tcph_p; /* ignore h_len, sum the packet we've coalesced */
  242: 
  243:             CHECK_IP_PAYLOAD_SIZE();
  244: 
  245: #if (STUPID_SOLARIS_CHECKSUM_BUG)
  246:             tcph_p->th_sum = tcph_p->th_off << 2;
  247:             return (1);
  248: #endif /* STUPID_SOLARIS_CHECKSUM_BUG */
  249: #if (HAVE_HPUX11)   
  250:             if (l->injection_type != LIBNET_LINK)
  251:             {
  252:                 /*
  253:                  *  Similiar to the Solaris Checksum bug - but need to add
  254:                  *  the size of the TCP payload (only for raw sockets).
  255:                  */
  256:                 tcph_p->th_sum = (tcph_p->th_off << 2) +
  257:                         (h_len - (tcph_p->th_off << 2));
  258:                 return (1); 
  259:             }
  260: #endif
  261:             /* TCP checksum is over the IP pseudo header:
  262:              * ip src
  263:              * ip dst
  264:              * tcp protocol (IPPROTO_TCP)
  265:              * tcp length, including the header
  266:              * + the TCP header (with checksum set to zero) and data
  267:              */
  268:             tcph_p->th_sum = 0;
  269:             if (ip6h_p)
  270:             {
  271:                 sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
  272:             }
  273:             else
  274:             {
  275:                 /* 8 = src and dst */
  276:                 sum = libnet_in_cksum((uint16_t *)&iph_p->ip_src, 8);
  277:             }
  278:             sum += ntohs(IPPROTO_TCP + h_len);
  279:             sum += libnet_in_cksum((uint16_t *)tcph_p, h_len);
  280:             tcph_p->th_sum = LIBNET_CKSUM_CARRY(sum);
  281: #if 0
  282:             printf("tcp sum calculated: %#x/%d h_len %d\n",
  283:                     ntohs(tcph_p->th_sum),
  284:                     ntohs(tcph_p->th_sum),
  285:                     h_len
  286:                   );
  287: #endif
  288:             break;
  289:         }
  290:         case IPPROTO_UDP:
  291:         {
  292:             struct libnet_udp_hdr *udph_p =
  293:                 (struct libnet_udp_hdr *)(iphdr + ip_hl);
  294: 
  295: 	    h_len = end - (uint8_t*) udph_p; /* ignore h_len, sum the packet we've coalesced */
  296: 
  297:             CHECK_IP_PAYLOAD_SIZE();
  298: 
  299:             udph_p->uh_sum = 0;
  300:             if (ip6h_p)
  301:             {
  302:                 sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
  303:             }
  304:             else
  305:             {
  306:                 sum = libnet_in_cksum((uint16_t *)&iph_p->ip_src, 8);
  307:             }
  308:             sum += ntohs(IPPROTO_UDP + h_len);
  309:             sum += libnet_in_cksum((uint16_t *)udph_p, h_len);
  310:             udph_p->uh_sum = LIBNET_CKSUM_CARRY(sum);
  311:             break;
  312:         }
  313:         case IPPROTO_ICMP:
  314:         {
  315:             struct libnet_icmpv4_hdr *icmph_p =
  316:                 (struct libnet_icmpv4_hdr *)(iphdr + ip_hl);
  317: 
  318:             h_len = end - (uint8_t*) icmph_p; /* ignore h_len, sum the packet we've coalesced */
  319: 
  320:             CHECK_IP_PAYLOAD_SIZE();
  321: 
  322:             icmph_p->icmp_sum = 0;
  323:             /* Hm, is this valid? Is the checksum algorithm for ICMPv6 encapsulated in IPv4
  324:              * actually defined?
  325:              */
  326:             if (ip6h_p)
  327:             {
  328:                 sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
  329:                 sum += ntohs(IPPROTO_ICMP6 + h_len);
  330:             }
  331:             sum += libnet_in_cksum((uint16_t *)icmph_p, h_len);
  332:             icmph_p->icmp_sum = LIBNET_CKSUM_CARRY(sum);
  333:             break;
  334:         }
  335:         case IPPROTO_ICMPV6:
  336:         {
  337:             struct libnet_icmpv6_hdr *icmph_p =
  338:                 (struct libnet_icmpv6_hdr *)(iphdr + ip_hl);
  339: 
  340:             h_len = end - (uint8_t*) icmph_p; /* ignore h_len, sum the packet we've coalesced */
  341: 
  342:             CHECK_IP_PAYLOAD_SIZE();
  343: 
  344:             icmph_p->icmp_sum = 0;
  345:             if (ip6h_p)
  346:             {
  347:                 sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
  348:                 sum += ntohs(IPPROTO_ICMP6 + h_len);
  349:             }
  350:             sum += libnet_in_cksum((uint16_t *)icmph_p, h_len);
  351:             icmph_p->icmp_sum = LIBNET_CKSUM_CARRY(sum);
  352:             break;
  353:         }
  354:         case IPPROTO_IGMP:
  355:         {
  356:             struct libnet_igmp_hdr *igmph_p =
  357:                 (struct libnet_igmp_hdr *)(iphdr + ip_hl);
  358: 
  359: 	    h_len = end - (uint8_t*) igmph_p; /* ignore h_len, sum the packet we've coalesced */
  360: 
  361:             CHECK_IP_PAYLOAD_SIZE();
  362: 
  363:             igmph_p->igmp_sum = 0;
  364:             sum = libnet_in_cksum((uint16_t *)igmph_p, h_len);
  365:             igmph_p->igmp_sum = LIBNET_CKSUM_CARRY(sum);
  366:             break;
  367:         }
  368: 	case IPPROTO_GRE:
  369: 	{
  370:             /* checksum is always at the same place in GRE header
  371:              * in the multiple RFC version of the protocol ... ouf !!!
  372:              */
  373: 	    struct libnet_gre_hdr *greh_p = 
  374: 		(struct libnet_gre_hdr *)(iphdr + ip_hl);
  375: 	    uint16_t fv = ntohs(greh_p->flags_ver);
  376: 
  377:             CHECK_IP_PAYLOAD_SIZE();
  378: 
  379: 	    if (!(fv & (GRE_CSUM|GRE_ROUTING | GRE_VERSION_0)) ||
  380:                 !(fv & (GRE_CSUM|GRE_VERSION_1)))
  381: 	    {
  382: 		snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  383:                 "%s(): can't compute GRE checksum (wrong flags_ver bits: 0x%x )\n",  __func__, fv);
  384: 		return (-1);
  385: 	    }
  386: 	    sum = libnet_in_cksum((uint16_t *)greh_p, h_len);
  387: 	    greh_p->gre_sum = LIBNET_CKSUM_CARRY(sum);
  388: 	    break;
  389: 	}
  390:         case IPPROTO_OSPF:
  391:         {
  392:             struct libnet_ospf_hdr *oh_p =
  393:                 (struct libnet_ospf_hdr *)(iphdr + ip_hl);
  394: 
  395:             CHECK_IP_PAYLOAD_SIZE();
  396: 
  397:             oh_p->ospf_sum = 0;
  398:             sum += libnet_in_cksum((uint16_t *)oh_p, h_len);
  399:             oh_p->ospf_sum = LIBNET_CKSUM_CARRY(sum);
  400:             break;
  401:         }
  402:         case IPPROTO_OSPF_LSA:
  403:         {
  404:             struct libnet_ospf_hdr *oh_p =
  405:                 (struct libnet_ospf_hdr *)(iphdr + ip_hl);
  406:             struct libnet_lsa_hdr *lsa_p =
  407:                 (struct libnet_lsa_hdr *)(iphdr + 
  408:                 ip_hl + oh_p->ospf_len);
  409: 
  410:             /* FIXME need additional length check, to account for ospf_len */
  411:             lsa_p->lsa_sum = 0;
  412:             sum += libnet_in_cksum((uint16_t *)lsa_p, h_len);
  413:             lsa_p->lsa_sum = LIBNET_CKSUM_CARRY(sum);
  414:             break;
  415: #if 0
  416:             /*
  417:              *  Reworked fletcher checksum taken from RFC 1008.
  418:              */
  419:             int c0, c1;
  420:             struct libnet_lsa_hdr *lsa_p = (struct libnet_lsa_hdr *)buf;
  421:             uint8_t *p, *p1, *p2, *p3;
  422: 
  423:             c0 = 0;
  424:             c1 = 0;
  425: 
  426:             lsa_p->lsa_cksum = 0;
  427: 
  428:             p = buf;
  429:             p1 = buf;
  430:             p3 = buf + len;             /* beginning and end of buf */
  431: 
  432:             while (p1 < p3)
  433:             {
  434:                 p2 = p1 + LIBNET_MODX;
  435:                 if (p2 > p3)
  436:                 {
  437:                     p2 = p3;
  438:                 }
  439:   
  440:                 for (p = p1; p < p2; p++)
  441:                 {
  442:                     c0 += (*p);
  443:                     c1 += c0;
  444:                 }
  445: 
  446:                 c0 %= 255;
  447:                 c1 %= 255;      /* modular 255 */
  448:  
  449:                 p1 = p2;
  450:             }
  451: 
  452: #if AWR_PLEASE_REWORK_THIS
  453:             lsa_p->lsa_cksum[0] = (((len - 17) * c0 - c1) % 255);
  454:             if (lsa_p->lsa_cksum[0] <= 0)
  455:             {
  456:                 lsa_p->lsa_cksum[0] += 255;
  457:             }
  458: 
  459:             lsa_p->lsa_cksum[1] = (510 - c0 - lsa_p->lsa_cksum[0]);
  460:             if (lsa_p->lsa_cksum[1] > 255)
  461:             {
  462:                 lsa_p->lsa_cksum[1] -= 255;
  463:             }
  464: #endif
  465:             break;
  466: #endif
  467:         }
  468:         case IPPROTO_IP:
  469:         {
  470:             if(!iph_p) {
  471:                 /* IPv6 doesn't have a checksum */
  472:             } else {
  473:                 iph_p->ip_sum = 0;
  474:                 sum = libnet_in_cksum((uint16_t *)iph_p, ip_hl);
  475:                 iph_p->ip_sum = LIBNET_CKSUM_CARRY(sum);
  476:             }
  477:             break;
  478:         }
  479:         case IPPROTO_VRRP:
  480:         {
  481:             struct libnet_vrrp_hdr *vrrph_p =
  482:                 (struct libnet_vrrp_hdr *)(iphdr + ip_hl);
  483:             CHECK_IP_PAYLOAD_SIZE();
  484: 
  485:             vrrph_p->vrrp_sum = 0;
  486:             sum = libnet_in_cksum((uint16_t *)vrrph_p, h_len);
  487:             vrrph_p->vrrp_sum = LIBNET_CKSUM_CARRY(sum);
  488:             break;
  489:         }
  490:         case LIBNET_PROTO_CDP:
  491:         {   /* XXX - Broken: how can we easily get the entire packet size? */
  492: 	    /* FIXME you can't, checksumming non-IP protocols was not supported by libnet */
  493:             struct libnet_cdp_hdr *cdph_p =
  494:                 (struct libnet_cdp_hdr *)iphdr;
  495: 
  496:             if((iphdr+h_len) > end)
  497:             {
  498:                 snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  499:                         "%s(): cdp payload not inside packet\n", __func__);
  500:                 return -1;
  501:             }
  502: 
  503:             cdph_p->cdp_sum = 0;
  504:             sum = libnet_in_cksum((uint16_t *)cdph_p, h_len);
  505:             cdph_p->cdp_sum = LIBNET_CKSUM_CARRY(sum);
  506:             break;
  507:         }
  508:         case LIBNET_PROTO_ISL:
  509:         {
  510: #if 0
  511:             struct libnet_isl_hdr *islh_p =
  512:                 (struct libnet_isl_hdr *)buf;
  513: #endif
  514:             /*
  515:              *  Need to compute 4 byte CRC for the ethernet frame and for
  516:              *  the ISL frame itself.  Use the libnet_crc function.
  517:              */
  518:         }
  519:         default:
  520:         {
  521:             snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  522:                 "%s(): unsupported protocol %d\n", __func__, protocol);
  523:             return (-1);
  524:         }
  525:     }
  526:     return (1);
  527: }
  528: 
  529: 
  530: uint16_t
  531: libnet_ip_check(uint16_t *addr, int len)
  532: {
  533:     int sum;
  534: 
  535:     sum = libnet_in_cksum(addr, len);
  536:     return (LIBNET_CKSUM_CARRY(sum));
  537: }
  538: 
  539: /* EOF */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>