1 /*
2 * $Id: libnet_checksum.c,v 1.14 2004/11/09 07:05:07 mike 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
libnet_in_cksum(uint16_t * addr,int len)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 */
540