File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libnet / src / libnet_checksum.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:11:38 2023 UTC (9 months ago) by misho
Branches: libnet, MAIN
CVS tags: v1_2p1, HEAD
Version 1.2p1

    1: /*
    2:  *  $Id: libnet_checksum.c,v 1.1.1.3 2023/09/27 11:11:38 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: #include "common.h"
   34: 
   35: /* Note: len is in bytes, not 16-bit words! */
   36: int
   37: libnet_in_cksum(uint16_t *addr, int len)
   38: {
   39:     int sum = 0;
   40:     union
   41:     {
   42:         uint16_t s;
   43:         uint8_t b[2];
   44:     } pad;
   45: 
   46:     sum = 0;
   47: 
   48:     while (len > 1)
   49:     {
   50:         sum += *addr++;
   51:         len -= 2;
   52:     }
   53: 
   54:     if (len == 1)
   55:     {
   56:         pad.b[0] = *(uint8_t *)addr;
   57:         pad.b[1] = 0;
   58:         sum += pad.s;
   59:     }
   60: 
   61:     return (sum);
   62: }
   63: 
   64: int
   65: libnet_toggle_checksum(libnet_t *l, libnet_ptag_t ptag, int mode)
   66: {
   67:     libnet_pblock_t *p;
   68: 
   69:     p = libnet_pblock_find(l, ptag);
   70:     if (p == NULL)
   71:     {
   72:         /* err msg set in libnet_pblock_find() */
   73:         return (-1);
   74:     }
   75:     if (mode == LIBNET_ON)
   76:     {
   77:         if ((p->flags) & LIBNET_PBLOCK_DO_CHECKSUM)
   78:         {
   79:             return (1);
   80:         }
   81:         else
   82:         {
   83:             (p->flags) |= LIBNET_PBLOCK_DO_CHECKSUM;
   84:             return (1);
   85:         }
   86:     }
   87:     else
   88:     {
   89:         if ((p->flags) & LIBNET_PBLOCK_DO_CHECKSUM)
   90:         {
   91:             (p->flags) &= ~LIBNET_PBLOCK_DO_CHECKSUM;
   92:             return (1);
   93:         }
   94:         else
   95:         {
   96:             return (1);
   97:         }
   98:     }
   99: }
  100: 
  101: 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)
  102: {
  103:     if((iphdr+ip_hl+h_len) > end)
  104:     {
  105:         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  106:                 "%s(): ip payload not inside packet (pktsz %d, iphsz %d, payloadsz %d)", func,
  107: 		(int)(end - iphdr), ip_hl, h_len);
  108:         return -1;
  109:     }
  110: 
  111:     return 0;
  112: }
  113: 
  114: 
  115: /*
  116:  * For backwards binary compatibility. The calculations done here can easily
  117:  * result in buffer overreads and overwrites. You have been warned. And no, it
  118:  * is not possible to fix, the API contains no information on the buffer's
  119:  * boundary. libnet itself calls the safe function, libnet_inet_checksum(). So
  120:  * should you.
  121:  */
  122: int
  123: libnet_do_checksum(libnet_t *l, uint8_t *iphdr, int protocol, int h_len)
  124: {
  125:     uint16_t ip_len = 0;
  126:     struct libnet_ipv4_hdr* ip4 = (struct libnet_ipv4_hdr *)iphdr;
  127:     struct libnet_ipv6_hdr* ip6 = (struct libnet_ipv6_hdr *)iphdr;
  128: 
  129:     if(ip4->ip_v == 6) {
  130:         ip_len = ntohs(ip6->ip_len);
  131:     } else {
  132:         ip_len = ntohs(ip4->ip_len);
  133:     }
  134: 
  135:     return libnet_inet_checksum(l, iphdr, protocol, h_len,
  136:             iphdr, iphdr + ip_len
  137:             );
  138: }
  139: 
  140: 
  141: #define CHECK_IP_PAYLOAD_SIZE() do { \
  142:     int e=check_ip_payload_size(l,iphdr,ip_hl, h_len, end, __func__);\
  143:     if(e) return e;\
  144: } while(0)
  145: 
  146: 
  147: /*
  148:  * We are checksumming pblock "q"
  149:  *
  150:  * iphdr is the pointer to it's encapsulating IP header
  151:  * protocol describes the type of "q", expressed as an IPPROTO_ value
  152:  * h_len is the h_len from "q"
  153:  */
  154: int
  155: libnet_inet_checksum(libnet_t *l, uint8_t *iphdr, int protocol, int h_len, const uint8_t *beg, const uint8_t * end)
  156: {
  157:     /* will need to update this for ipv6 at some point */
  158:     struct libnet_ipv4_hdr *iph_p = (struct libnet_ipv4_hdr *)iphdr;
  159:     struct libnet_ipv6_hdr *ip6h_p = NULL; /* default to not using IPv6 */
  160:     int ip_hl = 0;
  161:     int sum = 0;
  162:     uint8_t ip_nh = 0;
  163: 
  164:     /* Check for memory under/over reads/writes. */
  165:     if(iphdr < beg || (iphdr+sizeof(*iph_p)) > end)
  166:     {
  167:         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  168:             "%s(): ipv4 hdr not inside packet (where %d, size %d)", __func__,
  169: 	    (int)(iphdr-beg), (int)(end-beg));
  170:         return -1;
  171:     }
  172: 
  173:     /*
  174:      *  Figure out which IP version we're dealing with.  We'll assume v4
  175:      *  and overlay a header structure to yank out the version.
  176:      */
  177:     if (iph_p->ip_v == 6)
  178:     {
  179:         ip6h_p = (struct libnet_ipv6_hdr *)iph_p;
  180:         iph_p = NULL;
  181:         ip_hl   = 40;
  182:         ip_nh = ip6h_p->ip_nh;
  183: 
  184:         if((uint8_t*)(ip6h_p+1) > end)
  185:         {
  186:             snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  187:                     "%s(): ipv6 hdr not inside packet", __func__);
  188:             return -1;
  189:         }
  190: 
  191:         /* FIXME this entire fragile exercise would be avoided if we just passed
  192:          * in the pointer to the protocol block 'q' we are checksumming, which
  193:          * we know.
  194:          */
  195:         while (ip_nh != protocol && (uint8_t*)ip6h_p + ip_hl + 1 < end)
  196:         {
  197:             /* next header is not the upper layer protocol */
  198:            switch (ip_nh)
  199:            {
  200:               case IPPROTO_DSTOPTS:
  201:               case IPPROTO_HOPOPTS:
  202:               case IPPROTO_ROUTING:
  203:               case IPPROTO_FRAGMENT:
  204:               case IPPROTO_AH:
  205:               case IPPROTO_ESP:
  206:               case IPPROTO_MH:
  207:                  /*
  208:                   * count option headers to the header length for
  209:                   * checksum processing
  210:                   */
  211:                  /* Common structure of ipv6 ext headers is:
  212:                   *  uint8: next header protocol
  213:                   *  uint8: length of this header, in multiples of 8, not
  214:                   *    including first eight octets
  215:                   * The pointer arithmetic below follows from above.
  216:                   */
  217:                  ip_nh = *((uint8_t*)ip6h_p+ip_hl); /* next next header */
  218:                  ip_hl += (*((uint8_t*)ip6h_p+ip_hl+1)+1)*8; /* ext header length */
  219:                  break;
  220:               default:
  221:                  snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  222:                      "%s(): unsupported extension header (%d)", __func__, ip_nh);
  223:                  return -1;
  224:            }
  225: 
  226:         }
  227:     }
  228:     else
  229:     {
  230:         ip_hl = iph_p->ip_hl << 2;
  231:     }
  232: 
  233:     if((iphdr+ip_hl) > end)
  234:     {
  235:         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  236:             "%s(): ip hdr len not inside packet", __func__);
  237:         return -1;
  238:     }
  239: 
  240:     /*
  241:      *  Dug Song came up with this very cool checksuming implementation
  242:      *  eliminating the need for explicit psuedoheader use.  Check it out.
  243:      */
  244:     switch (protocol)
  245:     {
  246:         case IPPROTO_TCP:
  247:         {
  248:             struct libnet_tcp_hdr *tcph_p =
  249:                 (struct libnet_tcp_hdr *)(iphdr + ip_hl);
  250: 
  251: 	    h_len = (int)(end - (uint8_t*) tcph_p); /* ignore h_len, sum the packet we've coalesced */
  252: 
  253:             CHECK_IP_PAYLOAD_SIZE();
  254: 
  255: #if (STUPID_SOLARIS_CHECKSUM_BUG)
  256:             tcph_p->th_sum = tcph_p->th_off << 2;
  257:             return (1);
  258: #endif /* STUPID_SOLARIS_CHECKSUM_BUG */
  259: #if (HAVE_HPUX11)   
  260:             if (l->injection_type != LIBNET_LINK)
  261:             {
  262:                 /*
  263:                  *  Similiar to the Solaris Checksum bug - but need to add
  264:                  *  the size of the TCP payload (only for raw sockets).
  265:                  */
  266:                 tcph_p->th_sum = (tcph_p->th_off << 2) +
  267:                         (h_len - (tcph_p->th_off << 2));
  268:                 return (1); 
  269:             }
  270: #endif
  271:             /* TCP checksum is over the IP pseudo header:
  272:              * ip src
  273:              * ip dst
  274:              * tcp protocol (IPPROTO_TCP)
  275:              * tcp length, including the header
  276:              * + the TCP header (with checksum set to zero) and data
  277:              */
  278:             tcph_p->th_sum = 0;
  279:             if (ip6h_p)
  280:             {
  281:                 sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
  282:             }
  283:             else
  284:             {
  285:                 /* 8 = src and dst */
  286:                 sum = libnet_in_cksum((uint16_t *)&iph_p->ip_src, 8);
  287:             }
  288:             sum += ntohs(IPPROTO_TCP + h_len);
  289:             sum += libnet_in_cksum((uint16_t *)tcph_p, h_len);
  290:             tcph_p->th_sum = LIBNET_CKSUM_CARRY(sum);
  291: #if 0
  292:             printf("tcp sum calculated: %#x/%d h_len %d\n",
  293:                     ntohs(tcph_p->th_sum),
  294:                     ntohs(tcph_p->th_sum),
  295:                     h_len
  296:                   );
  297: #endif
  298:             break;
  299:         }
  300:         case IPPROTO_UDP:
  301:         {
  302:             struct libnet_udp_hdr *udph_p =
  303:                 (struct libnet_udp_hdr *)(iphdr + ip_hl);
  304: 
  305: 	    h_len = (int)(end - (uint8_t*) udph_p); /* ignore h_len, sum the packet we've coalesced */
  306: 
  307:             CHECK_IP_PAYLOAD_SIZE();
  308: 
  309:             udph_p->uh_sum = 0;
  310:             if (ip6h_p)
  311:             {
  312:                 sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
  313:             }
  314:             else
  315:             {
  316:                 sum = libnet_in_cksum((uint16_t *)&iph_p->ip_src, 8);
  317:             }
  318:             sum += ntohs(IPPROTO_UDP + h_len);
  319:             sum += libnet_in_cksum((uint16_t *)udph_p, h_len);
  320:             udph_p->uh_sum = LIBNET_CKSUM_CARRY(sum);
  321:             break;
  322:         }
  323:         case IPPROTO_ICMP:
  324:         {
  325:             struct libnet_icmpv4_hdr *icmph_p =
  326:                 (struct libnet_icmpv4_hdr *)(iphdr + ip_hl);
  327: 
  328:             h_len = (int)(end - (uint8_t*) icmph_p); /* ignore h_len, sum the packet we've coalesced */
  329: 
  330:             CHECK_IP_PAYLOAD_SIZE();
  331: 
  332:             icmph_p->icmp_sum = 0;
  333:             /* Hm, is this valid? Is the checksum algorithm for ICMPv6 encapsulated in IPv4
  334:              * actually defined?
  335:              */
  336:             if (ip6h_p)
  337:             {
  338:                 sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
  339:                 sum += ntohs(IPPROTO_ICMP6 + h_len);
  340:             }
  341:             sum += libnet_in_cksum((uint16_t *)icmph_p, h_len);
  342:             icmph_p->icmp_sum = LIBNET_CKSUM_CARRY(sum);
  343:             break;
  344:         }
  345:         case IPPROTO_ICMPV6:
  346:         {
  347:             struct libnet_icmpv6_hdr *icmph_p =
  348:                 (struct libnet_icmpv6_hdr *)(iphdr + ip_hl);
  349: 
  350:             h_len = (int)(end - (uint8_t*) icmph_p); /* ignore h_len, sum the packet we've coalesced */
  351: 
  352:             CHECK_IP_PAYLOAD_SIZE();
  353: 
  354:             icmph_p->icmp_sum = 0;
  355:             if (ip6h_p)
  356:             {
  357:                 sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
  358:                 sum += ntohs(IPPROTO_ICMP6 + h_len);
  359:             }
  360:             sum += libnet_in_cksum((uint16_t *)icmph_p, h_len);
  361:             icmph_p->icmp_sum = LIBNET_CKSUM_CARRY(sum);
  362:             break;
  363:         }
  364:         case IPPROTO_IGMP:
  365:         {
  366:             struct libnet_igmp_hdr *igmph_p =
  367:                 (struct libnet_igmp_hdr *)(iphdr + ip_hl);
  368: 
  369: 	    h_len = (int)(end - (uint8_t*) igmph_p); /* ignore h_len, sum the packet we've coalesced */
  370: 
  371:             CHECK_IP_PAYLOAD_SIZE();
  372: 
  373:             igmph_p->igmp_sum = 0;
  374:             sum = libnet_in_cksum((uint16_t *)igmph_p, h_len);
  375:             igmph_p->igmp_sum = LIBNET_CKSUM_CARRY(sum);
  376:             break;
  377:         }
  378: 	case IPPROTO_GRE:
  379: 	{
  380:             /* checksum is always at the same place in GRE header
  381:              * in the multiple RFC version of the protocol ... ouf !!!
  382:              */
  383: 	    struct libnet_gre_hdr *greh_p = 
  384: 		(struct libnet_gre_hdr *)(iphdr + ip_hl);
  385: 	    uint16_t fv = ntohs(greh_p->flags_ver);
  386: 
  387:             CHECK_IP_PAYLOAD_SIZE();
  388: 
  389: 	    if (!(fv & (GRE_CSUM|GRE_ROUTING | GRE_VERSION_0)) ||
  390:                 !(fv & (GRE_CSUM|GRE_VERSION_1)))
  391: 	    {
  392: 		snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  393:                 "%s(): can't compute GRE checksum (wrong flags_ver bits: 0x%x )",  __func__, fv);
  394: 		return (-1);
  395: 	    }
  396: 	    sum = libnet_in_cksum((uint16_t *)greh_p, h_len);
  397: 	    greh_p->gre_sum = LIBNET_CKSUM_CARRY(sum);
  398: 	    break;
  399: 	}
  400:         case IPPROTO_OSPF:
  401:         {
  402:             struct libnet_ospf_hdr *oh_p =
  403:                 (struct libnet_ospf_hdr *)(iphdr + ip_hl);
  404: 
  405:             CHECK_IP_PAYLOAD_SIZE();
  406: 
  407:             oh_p->ospf_sum = 0;
  408:             sum += libnet_in_cksum((uint16_t *)oh_p, h_len);
  409:             oh_p->ospf_sum = LIBNET_CKSUM_CARRY(sum);
  410:             break;
  411:         }
  412:         case IPPROTO_OSPF_LSA:
  413:         {
  414:             struct libnet_ospf_hdr *oh_p =
  415:                 (struct libnet_ospf_hdr *)(iphdr + ip_hl);
  416:             struct libnet_lsa_hdr *lsa_p =
  417:                 (struct libnet_lsa_hdr *)(iphdr + 
  418:                 ip_hl + oh_p->ospf_len);
  419: 
  420:             /* FIXME need additional length check, to account for ospf_len */
  421:             lsa_p->lsa_sum = 0;
  422:             sum += libnet_in_cksum((uint16_t *)lsa_p, h_len);
  423:             lsa_p->lsa_sum = LIBNET_CKSUM_CARRY(sum);
  424:             break;
  425: #if 0
  426:             /*
  427:              *  Reworked fletcher checksum taken from RFC 1008.
  428:              */
  429:             int c0, c1;
  430:             struct libnet_lsa_hdr *lsa_p = (struct libnet_lsa_hdr *)buf;
  431:             uint8_t *p, *p1, *p2, *p3;
  432: 
  433:             c0 = 0;
  434:             c1 = 0;
  435: 
  436:             lsa_p->lsa_cksum = 0;
  437: 
  438:             p = buf;
  439:             p1 = buf;
  440:             p3 = buf + len;             /* beginning and end of buf */
  441: 
  442:             while (p1 < p3)
  443:             {
  444:                 p2 = p1 + LIBNET_MODX;
  445:                 if (p2 > p3)
  446:                 {
  447:                     p2 = p3;
  448:                 }
  449:   
  450:                 for (p = p1; p < p2; p++)
  451:                 {
  452:                     c0 += (*p);
  453:                     c1 += c0;
  454:                 }
  455: 
  456:                 c0 %= 255;
  457:                 c1 %= 255;      /* modular 255 */
  458:  
  459:                 p1 = p2;
  460:             }
  461: 
  462: #if AWR_PLEASE_REWORK_THIS
  463:             lsa_p->lsa_cksum[0] = (((len - 17) * c0 - c1) % 255);
  464:             if (lsa_p->lsa_cksum[0] <= 0)
  465:             {
  466:                 lsa_p->lsa_cksum[0] += 255;
  467:             }
  468: 
  469:             lsa_p->lsa_cksum[1] = (510 - c0 - lsa_p->lsa_cksum[0]);
  470:             if (lsa_p->lsa_cksum[1] > 255)
  471:             {
  472:                 lsa_p->lsa_cksum[1] -= 255;
  473:             }
  474: #endif
  475:             break;
  476: #endif
  477:         }
  478:         case IPPROTO_IP:
  479:         {
  480:             if(!iph_p) {
  481:                 /* IPv6 doesn't have a checksum */
  482:             } else {
  483:                 iph_p->ip_sum = 0;
  484:                 sum = libnet_in_cksum((uint16_t *)iph_p, ip_hl);
  485:                 iph_p->ip_sum = LIBNET_CKSUM_CARRY(sum);
  486:             }
  487:             break;
  488:         }
  489:         case IPPROTO_VRRP:
  490:         {
  491:             struct libnet_vrrp_hdr *vrrph_p =
  492:                 (struct libnet_vrrp_hdr *)(iphdr + ip_hl);
  493:             CHECK_IP_PAYLOAD_SIZE();
  494: 
  495:             vrrph_p->vrrp_sum = 0;
  496:             sum = libnet_in_cksum((uint16_t *)vrrph_p, h_len);
  497:             vrrph_p->vrrp_sum = LIBNET_CKSUM_CARRY(sum);
  498:             break;
  499:         }
  500:         case LIBNET_PROTO_CDP:
  501:         {   /* XXX - Broken: how can we easily get the entire packet size? */
  502: 	    /* FIXME you can't, checksumming non-IP protocols was not supported by libnet */
  503:             struct libnet_cdp_hdr *cdph_p =
  504:                 (struct libnet_cdp_hdr *)iphdr;
  505: 
  506:             if((iphdr+h_len) > end)
  507:             {
  508:                 snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  509:                         "%s(): cdp payload not inside packet", __func__);
  510:                 return -1;
  511:             }
  512: 
  513:             cdph_p->cdp_sum = 0;
  514:             sum = libnet_in_cksum((uint16_t *)cdph_p, h_len);
  515:             cdph_p->cdp_sum = LIBNET_CKSUM_CARRY(sum);
  516:             break;
  517:         }
  518:         case LIBNET_PROTO_ISL:
  519:         {
  520: #if 0
  521:             struct libnet_isl_hdr *islh_p =
  522:                 (struct libnet_isl_hdr *)buf;
  523: #endif
  524:             /*
  525:              *  Need to compute 4 byte CRC for the ethernet frame and for
  526:              *  the ISL frame itself.  Use the libnet_crc function.
  527:              */
  528:         }
  529:         default:
  530:         {
  531:             snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
  532:                 "%s(): unsupported protocol %d", __func__, protocol);
  533:             return (-1);
  534:         }
  535:     }
  536:     return (1);
  537: }
  538: 
  539: 
  540: uint16_t
  541: libnet_ip_check(uint16_t *addr, int len)
  542: {
  543:     int sum;
  544: 
  545:     sum = libnet_in_cksum(addr, len);
  546:     return (LIBNET_CKSUM_CARRY(sum));
  547: }
  548: 
  549: /**
  550:  * Local Variables:
  551:  *  indent-tabs-mode: nil
  552:  *  c-file-style: "stroustrup"
  553:  * End:
  554:  */

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