make: fix access to env vars
[netsniff.git] / src / layer3.c
blobd05aa3c35f5069383cac315785835da6f06a1fb4
1 /*
2 * Mausezahn - A fast versatile traffic generator
3 * Copyright (C) 2008-2010 Herbert Haas
4 *
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License version 2 as published by the
7 * Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 * details.
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html
21 // ***************************************************************************
22 // This sections contains functions to send various L3-based PDUs such as
23 //
24 // * IP
25 //
26 // (ahem, yes this is currently all here...)
27 //
28 // ***************************************************************************
30 #include "mz.h"
31 #include "cli.h"
33 #define MZ_IP_HELP \
34 "| IP type: Send raw IP packets.\n" \
35 "|\n" \
36 "| Supports L3 mode (automatic L2 creation) or 'L2-L3' mode (MAC addresses must be provided).\n" \
37 "| In L3 mode the IP checksum and length cannot be manipulated to wrong values (currently).\n" \
38 "| The L2-L3 mode is activated when specifying any MAC addresses on the command line\n" \
39 "| (options -a, -b). \n" \
40 "|\n" \
41 "| The IP addresses can be specified via the -A and -B options, which identify the source\n" \
42 "| and destination addresses, respectively. A dotted decimal notation, an IP range, or a\n" \
43 "| FQDN can be used. The source address can also be random (-A rand).\n" \
44 "|\n" \
45 "| ARGUMENT SYNTAX: [<comma separated parameter list>]\n" \
46 "|\n" \
47 "| Parameters:\n" \
48 "|\n" \
49 "| len 0-65535 Only accessible in L2 mode\n" \
50 "| sum 0-65535 Only accessible in L2 mode (0 means auto-calculation)\n" \
51 "| tos 00-ff Full 8-bit control via hex input (use this also for ECN bits).\n" \
52 "| dscp 0-63 Allows easier specification of DSCP (PHB and Drop Propability)\n" \
53 "| ttl 0-255\n" \
54 "| proto 0-255\n" \
55 "| frag 0-65535 Includes flags (MSB) and offset (LSB)\n" \
56 "| df Sets the \"Don't Fragment\" flag\n" \
57 "| mf Sets the \"More Fragments\" flag\n" \
58 "| rf Sets the reserved flag.\n" \
59 "| id 0-65535\n" \
60 "| loose <addresses> Loose Source Route (LSR) option; specify a sequence of hops\n" \
61 "| using the notation: 1.1.1.1+2.2.2.2+3.3.3.3+...\n" \
62 "| strict <addresses> Strict Source Route (SSR) option; same address notation as above\n" \
63 "| option <hex_string> Specify any IP option using a hexadecimal string (aa:bb:cc:...)\n" \
64 "|\n" \
65 "| Additionally the Ethertype can be specified:\n" \
66 "|\n" \
67 "| ether_type 00:00-ff:ff Only accessible in L2 mode (default = 08:00 = IPv4)\n" \
68 "| \n"
71 #define MZ_IP6_HELP \
72 "| IP type: Send raw IPv6 packets.\n" \
73 "|\n" \
74 "| Supports L3 mode (automatic L2 creation) or 'L2-L3' mode (MAC addresses must be provided).\n" \
75 "| In L3 mode the IP checksum and length cannot be manipulated to wrong values (currently).\n" \
76 "| The L2-L3 mode is activated when specifying any MAC addresses on the command line\n" \
77 "| (options -a, -b). \n" \
78 "|\n" \
79 "| ARGUMENT SYNTAX: [<comma separated parameter list>]\n" \
80 "|\n" \
81 "| Parameters:\n" \
82 "|\n" \
83 "| len 0-65535 Only accessible in L2 mode\n" \
84 "| sum 0-65535 Only accessible in L2 mode (0 means auto-calculation)\n" \
85 "| tos 00-ff Full 8-bit control via hex input (use this also for ECN bits).\n" \
86 "| dscp 0-63 Allows easier specification of DSCP (PHB and Drop Propability)\n" \
87 "| flow 0-1048575 Flow label\n" \
88 "| hop 0-255 Hop limit\n" \
89 "| next 0-255 Next protocol or header type\n" \
90 "| frag 0-65535 Includes flags (MSB) and offset (LSB)\n" \
91 "| mf Sets the \"More Fragments\" flag\n" \
92 "| frag_res1 Sets the reserved flag 1.\n" \
93 "| frag_res2 Sets the reserved flag 2.\n" \
94 "| id 0-65535 Fragment ID\n" \
95 "| loose <addresses> Source Routing Header\n" \
96 "| rtype 0,2 Source Routing Type: 0 (Deprecated in RFC 5095) or 2 for Mobile IP\n" \
97 "| segments 0-255 Number of route segments left, used by RH0\n" \
98 "|\n" \
99 "| Additionally the Ethertype can be specified:\n" \
100 "|\n" \
101 "| ether_type 00:00-ff:ff Only accessible in L2 mode (default = 86:dd = IPv6)\n" \
102 "| \n"
105 // Only used to simplify initialization of libnet
106 // Return pointer to context
107 libnet_t* get_link_context()
109 libnet_t * l;
110 char errbuf[LIBNET_ERRBUF_SIZE];
112 // Don't open context if only a help text is requested
113 if (getarg(tx.arg_string,"help", NULL)==1)
115 return NULL;
119 if (tx.packet_mode)
120 { // Let libnet create an appropriate Ethernet frame
121 if (ipv6_mode)
122 l = libnet_init (LIBNET_RAW6_ADV, tx.device, errbuf);
123 else
124 l = libnet_init (LIBNET_RAW4_ADV, tx.device, errbuf);
126 else // User specified Ethernet header details (src or dst)
128 l = libnet_init (LIBNET_LINK_ADV, tx.device, errbuf);
131 if (l == NULL)
133 fprintf(stderr, "%s", errbuf);
134 exit(EXIT_FAILURE);
136 return l;
140 //////////////////////////////////////////////////////////////////////////////
141 // Prepare IP packet
142 libnet_ptag_t create_ip_packet (libnet_t *l)
144 libnet_ptag_t t;
145 char argval[MAX_PAYLOAD_SIZE];
146 int i, T; // only an abbreviation for tx.packet_mode
148 if (ipv6_mode)
149 return create_ip6_packet(l);
151 // Default IP header fields
152 tx.ip_len = LIBNET_IPV4_H; // Don't forget to add payload length
153 tx.ip_id = 0;
154 tx.ip_frag = 0; // Flags and Offset !!!
155 tx.ip_sum = 0; // default: automatically calculate checksum
156 tx.ip_tos = 0;
157 tx.ip_ttl = 255;
160 // temporary variables
161 unsigned int dummy;
162 size_t len;
163 char *s;
166 T = tx.packet_mode; // >0 means automatic L2 creation
168 if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==IP) )
170 if (mz_port)
172 cli_print(gcli, "%s", MZ_IP_HELP);
173 return -1;
175 else
178 fprintf(stderr,"\n"
179 MAUSEZAHN_VERSION
180 "\n%s", MZ_IP_HELP);
182 exit(0);
186 // Check if hex_payload already specified (externally)
187 if (tx.hex_payload_s)
189 memcpy( (void*) tx.ip_payload, (void*) tx.hex_payload, tx.hex_payload_s);
190 tx.ip_payload_s = tx.hex_payload_s;
194 // Evaluate CLI parameters:
196 if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1))
198 if (mode==IP)
199 tx.ip_payload_s = str2hex (argval, tx.ip_payload, MAX_PAYLOAD_SIZE);
201 // else payload has been specified as ASCII text via -P option
204 // NOTE: If 'mode' is NOT IP (e. g. UDP or TCP or something else)
205 // then the argument 'len' and 'sum' is NOT meant for the IP header!
206 // Instead the user can use 'iplen' and 'ipsum'.
207 if (mode==IP)
209 if (getarg(tx.arg_string,"len", argval)==1)
211 if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n");
212 tx.ip_len = (u_int16_t) str2int(argval);
214 else
216 tx.ip_len = LIBNET_IPV4_H + tx.ip_payload_s;
219 if (getarg(tx.arg_string,"sum", argval)==1)
221 if (T) fprintf(stderr, " IP_Warning: 'sum' cannot be set in this mode.\n");
222 tx.ip_sum = (u_int16_t) str2int(argval);
225 else // mode is NOT IP
227 if (getarg(tx.arg_string,"iplen", argval)==1)
229 if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n");
230 tx.ip_len = (u_int16_t) str2int(argval);
232 else
234 tx.ip_len = LIBNET_IPV4_H + tx.ip_payload_s;
237 if (getarg(tx.arg_string,"ipsum", argval)==1)
239 if (T) fprintf(stderr, " IP_Warning: 'sum' cannot be set in this mode.\n");
240 tx.ip_sum = (u_int16_t) str2int(argval);
245 if (getarg(tx.arg_string,"tos", argval)==1)
247 tx.ip_tos = (u_int8_t) strtol(argval,NULL,16);
248 dummy = (unsigned int) strtol(argval,NULL,16);
249 if (dummy > 255) fprintf(stderr, " IP_Warning: 'tos' too big, adjusted to LSBs\n");
252 if (getarg(tx.arg_string,"dscp", argval)==1)
254 dummy = (unsigned int) str2int(argval);
255 if (dummy > 63)
257 fprintf(stderr, " IP_Warning: 'dscp' too big, adjusted to 63\n");
258 dummy = 63;
260 tx.ip_tos = (u_int8_t) dummy*4;
263 if (getarg(tx.arg_string,"id", argval)==1)
265 tx.ip_id = (u_int16_t) str2int(argval);
268 if (getarg(tx.arg_string,"frag", argval)==1)
270 tx.ip_frag = (u_int16_t) str2int(argval);
273 if (getarg(tx.arg_string,"df", NULL)==1)
275 tx.ip_frag |= 0x4000;
278 if (getarg(tx.arg_string,"mf", NULL)==1)
280 tx.ip_frag |= 0x2000;
283 if (getarg(tx.arg_string,"rf", NULL)==1)
285 tx.ip_frag |= 0x8000;
289 if (getarg(tx.arg_string,"ttl", argval)==1)
291 tx.ip_ttl = (u_int8_t) str2int(argval);
294 if (getarg(tx.arg_string,"proto", argval)==1)
296 tx.ip_proto = (u_int8_t) str2int(argval);
300 if ((tx.ascii)&&(mode==IP)) // ASCII PAYLOAD overrides hex payload
302 strncpy((char *)tx.ip_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE);
303 tx.ip_payload_s = strlen((char *)tx.ascii_payload);
304 tx.ip_len += tx.ip_payload_s;
308 /////////
309 // Want some padding? The specified number of padding bytes are ADDED to the
310 // payload. Note that this is only evaluated if we are in IP mode because
311 // UDP and TCP already might have been padded and set the ip_payload_s.
312 // (Note the difference in send_eth() where you specified the total number
313 // of bytes in the frame)
315 if ((tx.padding)&&(mode==IP))
317 for (i=0; i<tx.padding; i++)
319 tx.ip_payload[tx.ip_payload_s+i] = 0x42; // pad with THE ANSWER (why random?)
321 tx.ip_payload_s += tx.padding;
322 tx.ip_len += tx.padding;
329 // Loose and Strict Source Route
330 // See RFC 791 for most the detailed description
332 if ( (getarg(tx.arg_string,"loose", argval)==1) ||
333 (getarg(tx.arg_string,"strict", argval)==1) )
335 len = strlen(argval);
337 if (len<7) // not even a single dotted decimal IP address given!
339 fprintf(stderr, " IP_Warning: Source route option requires at least one IP address!\n");
340 // But we allow this :-)
344 // determine how many IP addresses have been specified
345 dummy=0;
346 for (i=0; i<len; i++)
348 if (ispunct(*(argval+i))) dummy++ ;
350 dummy = (dummy+1) / 4; // the number of IP addresses
352 // Specify: type code, length, pointer
353 if (getarg(tx.arg_string,"loose", argval)==1)
355 tx.ip_option[0] = 131; // loose source route
357 else
359 tx.ip_option[0] = 137; // strict source route
361 tx.ip_option[1] = 3+(dummy*4); // length
362 tx.ip_option[2] = 4; // Use first IP address as next hop
363 //tx.ip_option[2] = 4+4*dummy; // smallest pointer, points to first address, which is
364 // the 4th byte within this option
366 tx.ip_option_s = 3;
367 s = strtok(argval, ".+-:;/>");
370 len--;
371 tx.ip_option[tx.ip_option_s] = (u_int8_t) str2int(s);
372 tx.ip_option_s++;
373 } while ( (s=strtok(NULL, ".+-:;/>")) != NULL );
375 tx.ip_option_s++; // EOL
377 // add empty space for record route: //// NONSENSE? /////
379 for (i=0; i<(4*dummy); i++)
381 tx.ip_option[tx.ip_option_s] = 0x00;
382 tx.ip_option_s++;
389 // Allow any IP option specified as hex string
390 // An option can be a single byte or consist of multiple bytes in which case
391 // a length field is needed, see RFC 791.
392 if (getarg(tx.arg_string,"option", argval)==1)
394 // check if conflicting with argument "loose" or "strict"
395 if (tx.ip_option_s)
397 fprintf(stderr, " IP_Error: Another IP option already specified. Please check your arguments.\n");
398 exit(1);
401 tx.ip_option_s = str2hex (argval, tx.ip_option, 1023);
406 if (tx.ip_option_s)
408 t = libnet_build_ipv4_options (tx.ip_option,
409 tx.ip_option_s,
412 tx.ip_len += tx.ip_option_s;
416 ///////
417 // Did the user specify ANY payload? We require at least one byte!
419 if (!tx.ip_payload_s)
421 tx.ip_payload[0] = 0x42;
422 tx.ip_payload_s = 1;
426 t = libnet_build_ipv4 (tx.ip_len,
427 tx.ip_tos,
428 tx.ip_id,
429 tx.ip_frag,
430 tx.ip_ttl,
431 tx.ip_proto,
432 tx.ip_sum,
433 tx.ip_src, // init.c defaults this to own SA
434 tx.ip_dst, // init.c defaults this to 255.255.255.255
435 (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, // if e.g. mode=UDP ignore payload argument
436 (mode==IP) ? tx.ip_payload_s : 0,
439 (mode==IP) ? tx.ip_payload : NULL, // if e.g. mode=UDP ignore payload argument
440 (mode==IP) ? tx.ip_payload_s : 0,
446 if (t == -1)
448 fprintf(stderr, " mz/create_ip_packet: Can't build IP header: %s\n", libnet_geterror(l));
449 exit (0);
453 return t;
457 //////////////////////////////////////////////////////////////////////////////
458 // Prepare IPv6 packet
459 libnet_ptag_t create_ip6_packet (libnet_t *l)
461 libnet_ptag_t t;
462 char argval[MAX_PAYLOAD_SIZE];
463 int i, T; // only an abbreviation for tx.packet_mode
465 // Default IP header fields
466 tx.ip_len = 0;
467 tx.ip_id = 0;
468 tx.ip6_segs = 0;
469 tx.ip6_rtype = 0;
470 tx.ip6_id = 0;
471 tx.ip_frag = 0; // Flags and Offset !!!
472 tx.ip_tos = 0;
473 tx.ip_ttl = 255;
475 // temporary variables
476 unsigned int dummy;
477 size_t len;
478 char *s;
480 T = tx.packet_mode; // >0 means automatic L2 creation
482 if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==IP) )
484 if (mz_port)
486 cli_print(gcli, "%s", MZ_IP6_HELP);
487 return -1;
489 else
491 fprintf(stderr,"\n"
492 MAUSEZAHN_VERSION
493 "\n%s", MZ_IP6_HELP);
495 exit(0);
499 // Check if hex_payload already specified (externally)
500 if (tx.hex_payload_s)
502 memcpy( (void*) tx.ip_payload, (void*) tx.hex_payload, tx.hex_payload_s);
503 tx.ip_payload_s = tx.hex_payload_s;
506 // Evaluate CLI parameters:
507 if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1))
509 if (mode==IP)
510 tx.ip_payload_s = str2hex (argval, tx.ip_payload, MAX_PAYLOAD_SIZE);
512 // else payload has been specified as ASCII text via -P option
514 // NOTE: If 'mode' is NOT IP (e. g. UDP or TCP or something else)
515 // then the argument 'len' and 'sum' is NOT meant for the IP header!
516 // Instead the user can use 'iplen' and 'ipsum'.
517 if (mode==IP)
519 if (getarg(tx.arg_string,"len", argval)==1)
521 if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n");
522 tx.ip_len = (u_int16_t) str2int(argval);
524 else
526 tx.ip_len += tx.ip_payload_s;
529 else // mode is NOT IP
531 if (getarg(tx.arg_string,"iplen", argval)==1)
533 if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n");
534 tx.ip_len = (u_int16_t) str2int(argval);
536 else
538 tx.ip_len += tx.ip_payload_s;
543 if (getarg(tx.arg_string,"tos", argval)==1)
545 tx.ip_tos = (u_int8_t) strtol(argval,NULL,16);
546 dummy = (unsigned int) strtol(argval,NULL,16);
547 if (dummy > 255) fprintf(stderr, " IP_Warning: 'tos' too big, adjusted to LSBs\n");
550 if (getarg(tx.arg_string,"flow", argval)==1)
552 dummy = (unsigned int) strtol(argval,NULL,16);
553 if (dummy > 1048575)
555 fprintf(stderr, " IP_Warning: 'flow label' too big, adjusted to 0xfffff\n");
556 dummy = 0xfffff;
558 tx.ip_flow = dummy;
561 if (getarg(tx.arg_string,"dscp", argval)==1)
563 dummy = (unsigned int) str2int(argval);
564 if (dummy > 63)
566 fprintf(stderr, " IP_Warning: 'dscp' too big, adjusted to 63\n");
567 dummy = 63;
569 tx.ip_tos = (u_int8_t) dummy*4;
572 if (getarg(tx.arg_string,"id", argval)==1)
574 tx.ip6_id = str2int(argval);
577 if (getarg(tx.arg_string,"frag", argval)==1)
579 tx.ip_frag = ((u_int16_t) str2int(argval)) << 3;
582 if (getarg(tx.arg_string,"mf", NULL)==1)
584 tx.ip_frag |= 0x0001;
587 if (getarg(tx.arg_string,"frag_res1", NULL)==1)
589 tx.ip_frag |= 0x0002;
592 if (getarg(tx.arg_string,"frag_res2", NULL)==1)
594 tx.ip_frag |= 0x0004;
597 if (getarg(tx.arg_string,"hop", argval)==1)
599 tx.ip_ttl = (u_int8_t) str2int(argval);
602 if (getarg(tx.arg_string,"next", argval)==1)
604 tx.ip_proto = (u_int8_t) str2int(argval);
606 else if (mode==IP)
608 tx.ip_proto = 59; // No Next Header for IPv6
612 if ((tx.ascii)&&(mode==IP)) // ASCII PAYLOAD overrides hex payload
614 strncpy((char *)tx.ip_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE);
615 tx.ip_payload_s = strlen((char *)tx.ascii_payload);
616 tx.ip_len += tx.ip_payload_s;
620 /////////
621 // Want some padding? The specified number of padding bytes are ADDED to the
622 // payload. Note that this is only evaluated if we are in IP mode because
623 // UDP and TCP already might have been padded and set the ip_payload_s.
624 // (Note the difference in send_eth() where you specified the total number
625 // of bytes in the frame)
627 if ((tx.padding)&&(mode==IP))
629 for (i=0; i<tx.padding; i++)
631 tx.ip_payload[tx.ip_payload_s+i] = 0x42; // pad with THE ANSWER (why random?)
633 tx.ip_payload_s += tx.padding;
634 tx.ip_len += tx.padding;
637 if (tx.ip6_id) {
638 t = libnet_build_ipv6_frag (tx.ip_proto,
640 htons(tx.ip_frag),
641 htonl(tx.ip6_id),
642 (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL,
643 (mode==IP) ? tx.ip_payload_s : 0,
646 tx.ip_len += LIBNET_IPV6_FRAG_H;
647 tx.ip_payload_s = 0;
648 tx.ip_proto = LIBNET_IPV6_NH_FRAGMENT;
651 // See RFC 2460 Routing Header
653 if ( (getarg(tx.arg_string,"segments", argval)==1) )
655 dummy = (unsigned int) str2int(argval);
656 if (dummy > 255) {
657 fprintf(stderr, " IP_Error: Maximal Routing Segments are 255!\n");
658 exit(1);
660 tx.ip6_segs = dummy;
663 if ( (getarg(tx.arg_string,"rtype", argval)==1) )
665 dummy = (unsigned int) str2int(argval);
666 if (dummy > 255) {
667 fprintf(stderr, " IP_Error: Maximum Routing Type is 255!\n");
668 exit(1);
670 tx.ip6_segs = dummy;
673 if ( (getarg(tx.arg_string,"loose", argval)==1) )
675 // Fill reserved
676 memset(tx.ip_option, 0, 4);
677 tx.ip_option_s=4;
679 len = strlen(argval);
680 s = strtok(argval, ".+-;/>");
683 len--;
684 *((struct libnet_in6_addr *) &tx.ip_option[tx.ip_option_s]) = libnet_name2addr6 (l, s, LIBNET_DONT_RESOLVE);
685 tx.ip_option_s += 16;
686 } while ( (s=strtok(NULL, ".+-;/>")) != NULL );
688 if (!tx.ip_option_s) {
689 fprintf(stderr, " IP_Error: No Routing Hops found!\n");
690 exit(1);
693 if (mode==IP && tx.ip_payload_s)
694 memmove(tx.ip_payload+tx.ip_option_s, tx.ip_payload, tx.ip_payload_s);
695 else
696 tx.ip_payload_s = 0;
698 memcpy(tx.ip_payload, tx.ip_option, tx.ip_option_s);
699 tx.ip_payload_s += tx.ip_option_s;
701 t = libnet_build_ipv6_routing(tx.ip_proto,
702 (tx.ip_option_s -4) / 8,
703 tx.ip6_rtype,
704 tx.ip6_segs,
705 tx.ip_payload,
706 tx.ip_payload_s,
709 tx.ip_len += LIBNET_IPV6_ROUTING_H + tx.ip_option_s;
710 tx.ip_payload_s = 0;
711 tx.ip_proto = LIBNET_IPV6_NH_ROUTING;
714 t = libnet_build_ipv6 (tx.ip_tos,
715 tx.ip_flow,
716 tx.ip_len,
717 tx.ip_proto,
718 tx.ip_ttl,
719 tx.ip6_src,
720 tx.ip6_dst,
721 (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL,
722 (mode==IP) ? tx.ip_payload_s : 0,
726 if (t == -1)
728 fprintf(stderr, " mz/create_ip_packet: Can't build IPv6 header: %s\n", libnet_geterror(l));
729 exit (0);
732 return t;