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>