Annotation of embedaddon/libnet/src/libnet_checksum.c, revision 1.1.1.3
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:
1.1.1.3 ! misho 33: #include "common.h"
1.1.1.2 misho 34:
35: /* Note: len is in bytes, not 16-bit words! */
1.1 misho 36: int
1.1.1.2 misho 37: libnet_in_cksum(uint16_t *addr, int len)
1.1 misho 38: {
1.1.1.3 ! misho 39: int sum = 0;
1.1.1.2 misho 40: union
41: {
42: uint16_t s;
43: uint8_t b[2];
1.1.1.3 ! misho 44: } pad;
1.1.1.2 misho 45:
46: sum = 0;
1.1 misho 47:
48: while (len > 1)
49: {
50: sum += *addr++;
51: len -= 2;
52: }
1.1.1.3 ! misho 53:
1.1.1.2 misho 54: if (len == 1)
55: {
56: pad.b[0] = *(uint8_t *)addr;
57: pad.b[1] = 0;
58: sum += pad.s;
1.1 misho 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:
1.1.1.2 misho 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,
1.1.1.3 ! misho 106: "%s(): ip payload not inside packet (pktsz %d, iphsz %d, payloadsz %d)", func,
1.1.1.2 misho 107: (int)(end - iphdr), ip_hl, h_len);
108: return -1;
109: }
110:
111: return 0;
112: }
113:
1.1 misho 114:
1.1.1.2 misho 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: */
1.1 misho 122: int
1.1.1.2 misho 123: libnet_do_checksum(libnet_t *l, uint8_t *iphdr, int protocol, int h_len)
1.1 misho 124: {
1.1.1.2 misho 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)
1.1 misho 145:
146:
1.1.1.2 misho 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 */
1.1.1.3 ! misho 160: int ip_hl = 0;
! 161: int sum = 0;
! 162: uint8_t ip_nh = 0;
1.1.1.2 misho 163:
164: /* Check for memory under/over reads/writes. */
165: if(iphdr < beg || (iphdr+sizeof(*iph_p)) > end)
1.1 misho 166: {
167: snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
1.1.1.3 ! misho 168: "%s(): ipv4 hdr not inside packet (where %d, size %d)", __func__,
1.1.1.2 misho 169: (int)(iphdr-beg), (int)(end-beg));
170: return -1;
1.1 misho 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: */
1.1.1.2 misho 177: if (iph_p->ip_v == 6)
1.1 misho 178: {
1.1.1.2 misho 179: ip6h_p = (struct libnet_ipv6_hdr *)iph_p;
180: iph_p = NULL;
1.1 misho 181: ip_hl = 40;
1.1.1.3 ! misho 182: ip_nh = ip6h_p->ip_nh;
! 183:
1.1.1.2 misho 184: if((uint8_t*)(ip6h_p+1) > end)
185: {
186: snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
1.1.1.3 ! misho 187: "%s(): ipv6 hdr not inside packet", __func__);
1.1.1.2 misho 188: return -1;
189: }
1.1.1.3 ! misho 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: }
1.1 misho 227: }
228: else
229: {
230: ip_hl = iph_p->ip_hl << 2;
231: }
232:
1.1.1.2 misho 233: if((iphdr+ip_hl) > end)
234: {
235: snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
1.1.1.3 ! misho 236: "%s(): ip hdr len not inside packet", __func__);
1.1.1.2 misho 237: return -1;
238: }
239:
1.1 misho 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 =
1.1.1.2 misho 249: (struct libnet_tcp_hdr *)(iphdr + ip_hl);
250:
1.1.1.3 ! misho 251: h_len = (int)(end - (uint8_t*) tcph_p); /* ignore h_len, sum the packet we've coalesced */
1.1.1.2 misho 252:
253: CHECK_IP_PAYLOAD_SIZE();
1.1 misho 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) +
1.1.1.2 misho 267: (h_len - (tcph_p->th_off << 2));
1.1 misho 268: return (1);
269: }
270: #endif
1.1.1.2 misho 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: */
1.1 misho 278: tcph_p->th_sum = 0;
1.1.1.2 misho 279: if (ip6h_p)
1.1 misho 280: {
1.1.1.2 misho 281: sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
1.1 misho 282: }
283: else
284: {
1.1.1.2 misho 285: /* 8 = src and dst */
286: sum = libnet_in_cksum((uint16_t *)&iph_p->ip_src, 8);
1.1 misho 287: }
1.1.1.2 misho 288: sum += ntohs(IPPROTO_TCP + h_len);
289: sum += libnet_in_cksum((uint16_t *)tcph_p, h_len);
1.1 misho 290: tcph_p->th_sum = LIBNET_CKSUM_CARRY(sum);
1.1.1.2 misho 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
1.1 misho 298: break;
299: }
300: case IPPROTO_UDP:
301: {
302: struct libnet_udp_hdr *udph_p =
1.1.1.2 misho 303: (struct libnet_udp_hdr *)(iphdr + ip_hl);
304:
1.1.1.3 ! misho 305: h_len = (int)(end - (uint8_t*) udph_p); /* ignore h_len, sum the packet we've coalesced */
1.1.1.2 misho 306:
307: CHECK_IP_PAYLOAD_SIZE();
308:
1.1 misho 309: udph_p->uh_sum = 0;
1.1.1.2 misho 310: if (ip6h_p)
1.1 misho 311: {
1.1.1.2 misho 312: sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
1.1 misho 313: }
314: else
315: {
1.1.1.2 misho 316: sum = libnet_in_cksum((uint16_t *)&iph_p->ip_src, 8);
1.1 misho 317: }
1.1.1.2 misho 318: sum += ntohs(IPPROTO_UDP + h_len);
319: sum += libnet_in_cksum((uint16_t *)udph_p, h_len);
1.1 misho 320: udph_p->uh_sum = LIBNET_CKSUM_CARRY(sum);
321: break;
322: }
323: case IPPROTO_ICMP:
324: {
325: struct libnet_icmpv4_hdr *icmph_p =
1.1.1.2 misho 326: (struct libnet_icmpv4_hdr *)(iphdr + ip_hl);
327:
1.1.1.3 ! misho 328: h_len = (int)(end - (uint8_t*) icmph_p); /* ignore h_len, sum the packet we've coalesced */
1.1.1.2 misho 329:
330: CHECK_IP_PAYLOAD_SIZE();
1.1 misho 331:
332: icmph_p->icmp_sum = 0;
1.1.1.2 misho 333: /* Hm, is this valid? Is the checksum algorithm for ICMPv6 encapsulated in IPv4
334: * actually defined?
335: */
336: if (ip6h_p)
1.1 misho 337: {
1.1.1.2 misho 338: sum = libnet_in_cksum((uint16_t *)&ip6h_p->ip_src, 32);
339: sum += ntohs(IPPROTO_ICMP6 + h_len);
1.1 misho 340: }
1.1.1.2 misho 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:
1.1.1.3 ! misho 350: h_len = (int)(end - (uint8_t*) icmph_p); /* ignore h_len, sum the packet we've coalesced */
1.1.1.2 misho 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);
1.1 misho 361: icmph_p->icmp_sum = LIBNET_CKSUM_CARRY(sum);
362: break;
363: }
364: case IPPROTO_IGMP:
365: {
366: struct libnet_igmp_hdr *igmph_p =
1.1.1.2 misho 367: (struct libnet_igmp_hdr *)(iphdr + ip_hl);
368:
1.1.1.3 ! misho 369: h_len = (int)(end - (uint8_t*) igmph_p); /* ignore h_len, sum the packet we've coalesced */
1.1.1.2 misho 370:
371: CHECK_IP_PAYLOAD_SIZE();
1.1 misho 372:
373: igmph_p->igmp_sum = 0;
1.1.1.2 misho 374: sum = libnet_in_cksum((uint16_t *)igmph_p, h_len);
1.1 misho 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 =
1.1.1.2 misho 384: (struct libnet_gre_hdr *)(iphdr + ip_hl);
385: uint16_t fv = ntohs(greh_p->flags_ver);
386:
387: CHECK_IP_PAYLOAD_SIZE();
388:
1.1 misho 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,
1.1.1.3 ! misho 393: "%s(): can't compute GRE checksum (wrong flags_ver bits: 0x%x )", __func__, fv);
1.1 misho 394: return (-1);
395: }
1.1.1.2 misho 396: sum = libnet_in_cksum((uint16_t *)greh_p, h_len);
1.1 misho 397: greh_p->gre_sum = LIBNET_CKSUM_CARRY(sum);
398: break;
399: }
400: case IPPROTO_OSPF:
401: {
402: struct libnet_ospf_hdr *oh_p =
1.1.1.2 misho 403: (struct libnet_ospf_hdr *)(iphdr + ip_hl);
404:
405: CHECK_IP_PAYLOAD_SIZE();
1.1 misho 406:
407: oh_p->ospf_sum = 0;
1.1.1.2 misho 408: sum += libnet_in_cksum((uint16_t *)oh_p, h_len);
1.1 misho 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 =
1.1.1.2 misho 415: (struct libnet_ospf_hdr *)(iphdr + ip_hl);
1.1 misho 416: struct libnet_lsa_hdr *lsa_p =
1.1.1.2 misho 417: (struct libnet_lsa_hdr *)(iphdr +
1.1 misho 418: ip_hl + oh_p->ospf_len);
419:
1.1.1.2 misho 420: /* FIXME need additional length check, to account for ospf_len */
1.1 misho 421: lsa_p->lsa_sum = 0;
1.1.1.2 misho 422: sum += libnet_in_cksum((uint16_t *)lsa_p, h_len);
1.1 misho 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;
1.1.1.2 misho 431: uint8_t *p, *p1, *p2, *p3;
1.1 misho 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: {
1.1.1.2 misho 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: }
1.1 misho 487: break;
488: }
489: case IPPROTO_VRRP:
490: {
491: struct libnet_vrrp_hdr *vrrph_p =
1.1.1.2 misho 492: (struct libnet_vrrp_hdr *)(iphdr + ip_hl);
493: CHECK_IP_PAYLOAD_SIZE();
1.1 misho 494:
495: vrrph_p->vrrp_sum = 0;
1.1.1.2 misho 496: sum = libnet_in_cksum((uint16_t *)vrrph_p, h_len);
1.1 misho 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? */
1.1.1.2 misho 502: /* FIXME you can't, checksumming non-IP protocols was not supported by libnet */
1.1 misho 503: struct libnet_cdp_hdr *cdph_p =
1.1.1.2 misho 504: (struct libnet_cdp_hdr *)iphdr;
505:
506: if((iphdr+h_len) > end)
507: {
508: snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
1.1.1.3 ! misho 509: "%s(): cdp payload not inside packet", __func__);
1.1.1.2 misho 510: return -1;
511: }
1.1 misho 512:
513: cdph_p->cdp_sum = 0;
1.1.1.2 misho 514: sum = libnet_in_cksum((uint16_t *)cdph_p, h_len);
1.1 misho 515: cdph_p->cdp_sum = LIBNET_CKSUM_CARRY(sum);
516: break;
517: }
518: case LIBNET_PROTO_ISL:
519: {
1.1.1.2 misho 520: #if 0
521: struct libnet_isl_hdr *islh_p =
522: (struct libnet_isl_hdr *)buf;
523: #endif
1.1 misho 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,
1.1.1.3 ! misho 532: "%s(): unsupported protocol %d", __func__, protocol);
1.1 misho 533: return (-1);
534: }
535: }
536: return (1);
537: }
538:
539:
1.1.1.2 misho 540: uint16_t
541: libnet_ip_check(uint16_t *addr, int len)
1.1 misho 542: {
543: int sum;
544:
545: sum = libnet_in_cksum(addr, len);
546: return (LIBNET_CKSUM_CARRY(sum));
547: }
548:
1.1.1.3 ! misho 549: /**
! 550: * Local Variables:
! 551: * indent-tabs-mode: nil
! 552: * c-file-style: "stroustrup"
! 553: * End:
! 554: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>