Annotation of embedaddon/libnet/src/libnet_checksum.c, revision 1.1.1.2
1.1 misho 1: /*
1.1.1.2 ! misho 2: * $Id: libnet_checksum.c,v 1.14 2004/11/09 07:05:07 mike Exp $
1.1 misho 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
1.1.1.2 ! misho 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! */
1.1 misho 53: int
1.1.1.2 ! misho 54: libnet_in_cksum(uint16_t *addr, int len)
1.1 misho 55: {
56: int sum;
1.1.1.2 ! misho 57: #ifdef DEBIAN
! 58: uint16_t last_byte;
1.1 misho 59:
60: sum = 0;
1.1.1.2 ! misho 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
1.1 misho 71:
72: while (len > 1)
73: {
74: sum += *addr++;
75: len -= 2;
76: }
1.1.1.2 ! misho 77: #ifdef DEBIAN
1.1 misho 78: if (len == 1)
79: {
1.1.1.2 ! misho 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
1.1 misho 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:
1.1.1.2 ! misho 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:
1.1 misho 144:
1.1.1.2 ! misho 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: */
1.1 misho 152: int
1.1.1.2 ! misho 153: libnet_do_checksum(libnet_t *l, uint8_t *iphdr, int protocol, int h_len)
1.1 misho 154: {
1.1.1.2 ! misho 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)
1.1 misho 175:
176:
1.1.1.2 ! misho 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)
1.1 misho 195: {
196: snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
1.1.1.2 ! misho 197: "%s(): ipv4 hdr not inside packet (where %d, size %d)\n", __func__,
! 198: (int)(iphdr-beg), (int)(end-beg));
! 199: return -1;
1.1 misho 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: */
1.1.1.2 ! misho 206: if (iph_p->ip_v == 6)
1.1 misho 207: {
1.1.1.2 ! misho 208: ip6h_p = (struct libnet_ipv6_hdr *)iph_p;
! 209: iph_p = NULL;
1.1 misho 210: ip_hl = 40;
1.1.1.2 ! misho 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: }
1.1 misho 217: }
218: else
219: {
220: ip_hl = iph_p->ip_hl << 2;
221: }
222:
1.1.1.2 ! misho 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:
1.1 misho 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 =
1.1.1.2 ! misho 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();
1.1 misho 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) +
1.1.1.2 ! misho 257: (h_len - (tcph_p->th_off << 2));
1.1 misho 258: return (1);
259: }
260: #endif
1.1.1.2 ! misho 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: */
1.1 misho 268: tcph_p->th_sum = 0;
1.1.1.2 ! misho 269: if (ip6h_p)
1.1 misho 270: {
1.1.1.2 ! misho 271: sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
1.1 misho 272: }
273: else
274: {
1.1.1.2 ! misho 275: /* 8 = src and dst */
! 276: sum = libnet_in_cksum((uint16_t *)&iph_p->ip_src, 8);
1.1 misho 277: }
1.1.1.2 ! misho 278: sum += ntohs(IPPROTO_TCP + h_len);
! 279: sum += libnet_in_cksum((uint16_t *)tcph_p, h_len);
1.1 misho 280: tcph_p->th_sum = LIBNET_CKSUM_CARRY(sum);
1.1.1.2 ! misho 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
1.1 misho 288: break;
289: }
290: case IPPROTO_UDP:
291: {
292: struct libnet_udp_hdr *udph_p =
1.1.1.2 ! misho 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:
1.1 misho 299: udph_p->uh_sum = 0;
1.1.1.2 ! misho 300: if (ip6h_p)
1.1 misho 301: {
1.1.1.2 ! misho 302: sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
1.1 misho 303: }
304: else
305: {
1.1.1.2 ! misho 306: sum = libnet_in_cksum((uint16_t *)&iph_p->ip_src, 8);
1.1 misho 307: }
1.1.1.2 ! misho 308: sum += ntohs(IPPROTO_UDP + h_len);
! 309: sum += libnet_in_cksum((uint16_t *)udph_p, h_len);
1.1 misho 310: udph_p->uh_sum = LIBNET_CKSUM_CARRY(sum);
311: break;
312: }
313: case IPPROTO_ICMP:
314: {
315: struct libnet_icmpv4_hdr *icmph_p =
1.1.1.2 ! misho 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();
1.1 misho 321:
322: icmph_p->icmp_sum = 0;
1.1.1.2 ! misho 323: /* Hm, is this valid? Is the checksum algorithm for ICMPv6 encapsulated in IPv4
! 324: * actually defined?
! 325: */
! 326: if (ip6h_p)
1.1 misho 327: {
1.1.1.2 ! misho 328: sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
! 329: sum += ntohs(IPPROTO_ICMP6 + h_len);
1.1 misho 330: }
1.1.1.2 ! misho 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);
1.1 misho 351: icmph_p->icmp_sum = LIBNET_CKSUM_CARRY(sum);
352: break;
353: }
354: case IPPROTO_IGMP:
355: {
356: struct libnet_igmp_hdr *igmph_p =
1.1.1.2 ! misho 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();
1.1 misho 362:
363: igmph_p->igmp_sum = 0;
1.1.1.2 ! misho 364: sum = libnet_in_cksum((uint16_t *)igmph_p, h_len);
1.1 misho 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 =
1.1.1.2 ! misho 374: (struct libnet_gre_hdr *)(iphdr + ip_hl);
! 375: uint16_t fv = ntohs(greh_p->flags_ver);
! 376:
! 377: CHECK_IP_PAYLOAD_SIZE();
! 378:
1.1 misho 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: }
1.1.1.2 ! misho 386: sum = libnet_in_cksum((uint16_t *)greh_p, h_len);
1.1 misho 387: greh_p->gre_sum = LIBNET_CKSUM_CARRY(sum);
388: break;
389: }
390: case IPPROTO_OSPF:
391: {
392: struct libnet_ospf_hdr *oh_p =
1.1.1.2 ! misho 393: (struct libnet_ospf_hdr *)(iphdr + ip_hl);
! 394:
! 395: CHECK_IP_PAYLOAD_SIZE();
1.1 misho 396:
397: oh_p->ospf_sum = 0;
1.1.1.2 ! misho 398: sum += libnet_in_cksum((uint16_t *)oh_p, h_len);
1.1 misho 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 =
1.1.1.2 ! misho 405: (struct libnet_ospf_hdr *)(iphdr + ip_hl);
1.1 misho 406: struct libnet_lsa_hdr *lsa_p =
1.1.1.2 ! misho 407: (struct libnet_lsa_hdr *)(iphdr +
1.1 misho 408: ip_hl + oh_p->ospf_len);
409:
1.1.1.2 ! misho 410: /* FIXME need additional length check, to account for ospf_len */
1.1 misho 411: lsa_p->lsa_sum = 0;
1.1.1.2 ! misho 412: sum += libnet_in_cksum((uint16_t *)lsa_p, h_len);
1.1 misho 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;
1.1.1.2 ! misho 421: uint8_t *p, *p1, *p2, *p3;
1.1 misho 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: {
1.1.1.2 ! misho 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: }
1.1 misho 477: break;
478: }
479: case IPPROTO_VRRP:
480: {
481: struct libnet_vrrp_hdr *vrrph_p =
1.1.1.2 ! misho 482: (struct libnet_vrrp_hdr *)(iphdr + ip_hl);
! 483: CHECK_IP_PAYLOAD_SIZE();
1.1 misho 484:
485: vrrph_p->vrrp_sum = 0;
1.1.1.2 ! misho 486: sum = libnet_in_cksum((uint16_t *)vrrph_p, h_len);
1.1 misho 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? */
1.1.1.2 ! misho 492: /* FIXME you can't, checksumming non-IP protocols was not supported by libnet */
1.1 misho 493: struct libnet_cdp_hdr *cdph_p =
1.1.1.2 ! misho 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: }
1.1 misho 502:
503: cdph_p->cdp_sum = 0;
1.1.1.2 ! misho 504: sum = libnet_in_cksum((uint16_t *)cdph_p, h_len);
1.1 misho 505: cdph_p->cdp_sum = LIBNET_CKSUM_CARRY(sum);
506: break;
507: }
508: case LIBNET_PROTO_ISL:
509: {
1.1.1.2 ! misho 510: #if 0
! 511: struct libnet_isl_hdr *islh_p =
! 512: (struct libnet_isl_hdr *)buf;
! 513: #endif
1.1 misho 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,
1.1.1.2 ! misho 522: "%s(): unsupported protocol %d\n", __func__, protocol);
1.1 misho 523: return (-1);
524: }
525: }
526: return (1);
527: }
528:
529:
1.1.1.2 ! misho 530: uint16_t
! 531: libnet_ip_check(uint16_t *addr, int len)
1.1 misho 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>