mausezahn: use getopt_long instead of getopt
[netsniff-ng.git] / staging / layer3.c
blob7dabc26db67d1f431659d42813c01f9f08b8b4a3
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(void)
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);
137 if (tx.prio != 0 &&
138 setsockopt (libnet_getfd (l), SOL_SOCKET, SO_PRIORITY, &tx.prio,
139 sizeof tx.prio) < 0)
141 perror("setsockopt SO_PRIORITY");
142 exit(EXIT_FAILURE);
145 return l;
149 //////////////////////////////////////////////////////////////////////////////
150 // Prepare IP packet
151 libnet_ptag_t create_ip_packet (libnet_t *l)
153 libnet_ptag_t t;
154 char argval[MAX_PAYLOAD_SIZE];
155 int i, T; // only an abbreviation for tx.packet_mode
157 if (ipv6_mode)
158 return create_ip6_packet(l);
160 // Default IP header fields
161 tx.ip_len = LIBNET_IPV4_H; // Don't forget to add payload length
162 tx.ip_id = 0;
163 tx.ip_frag = 0; // Flags and Offset !!!
164 tx.ip_sum = 0; // default: automatically calculate checksum
165 tx.ip_tos = 0;
167 // temporary variables
168 unsigned int dummy;
169 size_t len;
170 char *s;
172 T = tx.packet_mode; // >0 means automatic L2 creation
174 if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==IP) )
176 if (mz_port)
178 cli_print(gcli, "%s", MZ_IP_HELP);
179 return -1;
181 else
184 fprintf(stderr,"\n"
185 MAUSEZAHN_VERSION
186 "\n%s", MZ_IP_HELP);
188 exit(0);
192 // Check if hex_payload already specified (externally)
193 if (tx.hex_payload_s)
195 memcpy( (void*) tx.ip_payload, (void*) tx.hex_payload, tx.hex_payload_s);
196 tx.ip_payload_s = tx.hex_payload_s;
200 // Evaluate CLI parameters:
202 if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1))
204 if (mode==IP)
205 tx.ip_payload_s = str2hex (argval, tx.ip_payload, MAX_PAYLOAD_SIZE);
207 // else payload has been specified as ASCII text via -P option
210 // NOTE: If 'mode' is NOT IP (e. g. UDP or TCP or something else)
211 // then the argument 'len' and 'sum' is NOT meant for the IP header!
212 // Instead the user can use 'iplen' and 'ipsum'.
213 if (mode==IP)
215 if (getarg(tx.arg_string,"len", argval)==1)
217 if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n");
218 tx.ip_len = (u_int16_t) str2int(argval);
220 else
222 tx.ip_len = LIBNET_IPV4_H + tx.ip_payload_s;
225 if (getarg(tx.arg_string,"sum", argval)==1)
227 if (T) fprintf(stderr, " IP_Warning: 'sum' cannot be set in this mode.\n");
228 tx.ip_sum = (u_int16_t) str2int(argval);
231 else // mode is NOT IP
233 if (getarg(tx.arg_string,"iplen", argval)==1)
235 if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n");
236 tx.ip_len = (u_int16_t) str2int(argval);
238 else
240 tx.ip_len = LIBNET_IPV4_H + tx.ip_payload_s;
243 if (getarg(tx.arg_string,"ipsum", argval)==1)
245 if (T) fprintf(stderr, " IP_Warning: 'sum' cannot be set in this mode.\n");
246 tx.ip_sum = (u_int16_t) str2int(argval);
251 if (getarg(tx.arg_string,"tos", argval)==1)
253 tx.ip_tos = (u_int8_t) strtol(argval,NULL,16);
254 dummy = (unsigned int) strtol(argval,NULL,16);
255 if (dummy > 255) fprintf(stderr, " IP_Warning: 'tos' too big, adjusted to LSBs\n");
258 if (getarg(tx.arg_string,"dscp", argval)==1)
260 dummy = (unsigned int) str2int(argval);
261 if (dummy > 63)
263 fprintf(stderr, " IP_Warning: 'dscp' too big, adjusted to 63\n");
264 dummy = 63;
266 tx.ip_tos = (u_int8_t) dummy*4;
269 if (getarg(tx.arg_string,"id", argval)==1)
271 tx.ip_id = (u_int16_t) str2int(argval);
274 if (getarg(tx.arg_string,"frag", argval)==1)
276 tx.ip_frag = (u_int16_t) str2int(argval);
279 if (getarg(tx.arg_string,"df", NULL)==1)
281 tx.ip_frag |= 0x4000;
284 if (getarg(tx.arg_string,"mf", NULL)==1)
286 tx.ip_frag |= 0x2000;
289 if (getarg(tx.arg_string,"rf", NULL)==1)
291 tx.ip_frag |= 0x8000;
294 if (getarg(tx.arg_string, "ttl", argval) == 1)
295 tx.ip_ttl = (u_int8_t)str2int(argval);
296 else if (tx.ip_ttl == 0)
297 tx.ip_ttl = 255;
299 if (getarg(tx.arg_string,"proto", argval)==1)
301 tx.ip_proto = (u_int8_t) str2int(argval);
305 if ((tx.ascii)&&(mode==IP)) // ASCII PAYLOAD overrides hex payload
307 strncpy((char *)tx.ip_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE);
308 tx.ip_payload_s = strlen((char *)tx.ascii_payload);
309 tx.ip_len += tx.ip_payload_s;
313 /////////
314 // Want some padding? The specified number of padding bytes are ADDED to the
315 // payload. Note that this is only evaluated if we are in IP mode because
316 // UDP and TCP already might have been padded and set the ip_payload_s.
317 // (Note the difference in send_eth() where you specified the total number
318 // of bytes in the frame)
320 if ((tx.padding)&&(mode==IP))
322 for (i=0; i<tx.padding; i++)
324 tx.ip_payload[tx.ip_payload_s+i] = 0x42; // pad with THE ANSWER (why random?)
326 tx.ip_payload_s += tx.padding;
327 tx.ip_len += tx.padding;
334 // Loose and Strict Source Route
335 // See RFC 791 for most the detailed description
337 if ( (getarg(tx.arg_string,"loose", argval)==1) ||
338 (getarg(tx.arg_string,"strict", argval)==1) )
340 len = strlen(argval);
342 if (len<7) // not even a single dotted decimal IP address given!
344 fprintf(stderr, " IP_Warning: Source route option requires at least one IP address!\n");
345 // But we allow this :-)
349 // determine how many IP addresses have been specified
350 dummy=0;
351 for (i=0; i<len; i++)
353 if (ispunct(*(argval+i))) dummy++ ;
355 dummy = (dummy+1) / 4; // the number of IP addresses
357 // Specify: type code, length, pointer
358 if (getarg(tx.arg_string,"loose", argval)==1)
360 tx.ip_option[0] = 131; // loose source route
362 else
364 tx.ip_option[0] = 137; // strict source route
366 tx.ip_option[1] = 3+(dummy*4); // length
367 tx.ip_option[2] = 4; // Use first IP address as next hop
368 //tx.ip_option[2] = 4+4*dummy; // smallest pointer, points to first address, which is
369 // the 4th byte within this option
371 tx.ip_option_s = 3;
372 s = strtok(argval, ".+-:;/>");
375 len--;
376 tx.ip_option[tx.ip_option_s] = (u_int8_t) str2int(s);
377 tx.ip_option_s++;
378 } while ( (s=strtok(NULL, ".+-:;/>")) != NULL );
380 tx.ip_option_s++; // EOL
382 // add empty space for record route: //// NONSENSE? /////
384 for (i=0; i<(4*dummy); i++)
386 tx.ip_option[tx.ip_option_s] = 0x00;
387 tx.ip_option_s++;
394 // Allow any IP option specified as hex string
395 // An option can be a single byte or consist of multiple bytes in which case
396 // a length field is needed, see RFC 791.
397 if (getarg(tx.arg_string,"option", argval)==1)
399 // check if conflicting with argument "loose" or "strict"
400 if (tx.ip_option_s)
402 fprintf(stderr, " IP_Error: Another IP option already specified. Please check your arguments.\n");
403 exit(1);
406 tx.ip_option_s = str2hex (argval, tx.ip_option, 1023);
411 if (tx.ip_option_s)
413 t = libnet_build_ipv4_options (tx.ip_option,
414 tx.ip_option_s,
417 tx.ip_len += tx.ip_option_s;
421 ///////
422 // Did the user specify ANY payload? We require at least one byte!
424 if (!tx.ip_payload_s)
426 tx.ip_payload[0] = 0x42;
427 tx.ip_payload_s = 1;
431 t = libnet_build_ipv4 (tx.ip_len,
432 tx.ip_tos,
433 tx.ip_id,
434 tx.ip_frag,
435 tx.ip_ttl,
436 tx.ip_proto,
437 tx.ip_sum,
438 tx.ip_src, // init.c defaults this to own SA
439 tx.ip_dst, // init.c defaults this to 255.255.255.255
440 (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL, // if e.g. mode=UDP ignore payload argument
441 (mode==IP) ? tx.ip_payload_s : 0,
444 (mode==IP) ? tx.ip_payload : NULL, // if e.g. mode=UDP ignore payload argument
445 (mode==IP) ? tx.ip_payload_s : 0,
451 if (t == -1)
453 fprintf(stderr, " mz/create_ip_packet: Can't build IP header: %s\n", libnet_geterror(l));
454 exit (0);
458 return t;
462 //////////////////////////////////////////////////////////////////////////////
463 // Prepare IPv6 packet
464 libnet_ptag_t create_ip6_packet (libnet_t *l)
466 libnet_ptag_t t;
467 char argval[MAX_PAYLOAD_SIZE];
468 int i, T; // only an abbreviation for tx.packet_mode
470 // Default IP header fields
471 tx.ip_len = 0;
472 tx.ip_id = 0;
473 tx.ip6_segs = 0;
474 tx.ip6_rtype = 0;
475 tx.ip6_id = 0;
476 tx.ip_frag = 0; // Flags and Offset !!!
477 tx.ip_tos = 0;
478 tx.ip_ttl = 255;
480 // temporary variables
481 unsigned int dummy;
482 size_t len;
483 char *s;
485 T = tx.packet_mode; // >0 means automatic L2 creation
487 if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==IP) )
489 if (mz_port)
491 cli_print(gcli, "%s", MZ_IP6_HELP);
492 return -1;
494 else
496 fprintf(stderr,"\n"
497 MAUSEZAHN_VERSION
498 "\n%s", MZ_IP6_HELP);
500 exit(0);
504 // Check if hex_payload already specified (externally)
505 if (tx.hex_payload_s)
507 memcpy( (void*) tx.ip_payload, (void*) tx.hex_payload, tx.hex_payload_s);
508 tx.ip_payload_s = tx.hex_payload_s;
511 // Evaluate CLI parameters:
512 if ( (getarg(tx.arg_string,"payload", argval)==1) || (getarg(tx.arg_string,"p", argval)==1))
514 if (mode==IP)
515 tx.ip_payload_s = str2hex (argval, tx.ip_payload, MAX_PAYLOAD_SIZE);
517 // else payload has been specified as ASCII text via -P option
519 // NOTE: If 'mode' is NOT IP (e. g. UDP or TCP or something else)
520 // then the argument 'len' and 'sum' is NOT meant for the IP header!
521 // Instead the user can use 'iplen' and 'ipsum'.
522 if (mode==IP)
524 if (getarg(tx.arg_string,"len", argval)==1)
526 if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n");
527 tx.ip_len = (u_int16_t) str2int(argval);
529 else
531 tx.ip_len += tx.ip_payload_s;
534 else // mode is NOT IP
536 if (getarg(tx.arg_string,"iplen", argval)==1)
538 if (T) fprintf(stderr, " IP_Warning: 'len' cannot be set in this mode.\n");
539 tx.ip_len = (u_int16_t) str2int(argval);
541 else
543 tx.ip_len += tx.ip_payload_s;
548 if (getarg(tx.arg_string,"tos", argval)==1)
550 tx.ip_tos = (u_int8_t) strtol(argval,NULL,16);
551 dummy = (unsigned int) strtol(argval,NULL,16);
552 if (dummy > 255) fprintf(stderr, " IP_Warning: 'tos' too big, adjusted to LSBs\n");
555 if (getarg(tx.arg_string,"flow", argval)==1)
557 dummy = (unsigned int) strtol(argval,NULL,16);
558 if (dummy > 1048575)
560 fprintf(stderr, " IP_Warning: 'flow label' too big, adjusted to 0xfffff\n");
561 dummy = 0xfffff;
563 tx.ip_flow = dummy;
566 if (getarg(tx.arg_string,"dscp", argval)==1)
568 dummy = (unsigned int) str2int(argval);
569 if (dummy > 63)
571 fprintf(stderr, " IP_Warning: 'dscp' too big, adjusted to 63\n");
572 dummy = 63;
574 tx.ip_tos = (u_int8_t) dummy*4;
577 if (getarg(tx.arg_string,"id", argval)==1)
579 tx.ip6_id = str2int(argval);
582 if (getarg(tx.arg_string,"frag", argval)==1)
584 tx.ip_frag = ((u_int16_t) str2int(argval)) << 3;
587 if (getarg(tx.arg_string,"mf", NULL)==1)
589 tx.ip_frag |= 0x0001;
592 if (getarg(tx.arg_string,"frag_res1", NULL)==1)
594 tx.ip_frag |= 0x0002;
597 if (getarg(tx.arg_string,"frag_res2", NULL)==1)
599 tx.ip_frag |= 0x0004;
602 if (getarg(tx.arg_string,"hop", argval)==1)
604 tx.ip_ttl = (u_int8_t) str2int(argval);
607 if (getarg(tx.arg_string,"next", argval)==1)
609 tx.ip_proto = (u_int8_t) str2int(argval);
611 else if (mode==IP)
613 tx.ip_proto = 59; // No Next Header for IPv6
617 if ((tx.ascii)&&(mode==IP)) // ASCII PAYLOAD overrides hex payload
619 strncpy((char *)tx.ip_payload, (char *)tx.ascii_payload, MAX_PAYLOAD_SIZE);
620 tx.ip_payload_s = strlen((char *)tx.ascii_payload);
621 tx.ip_len += tx.ip_payload_s;
625 /////////
626 // Want some padding? The specified number of padding bytes are ADDED to the
627 // payload. Note that this is only evaluated if we are in IP mode because
628 // UDP and TCP already might have been padded and set the ip_payload_s.
629 // (Note the difference in send_eth() where you specified the total number
630 // of bytes in the frame)
632 if ((tx.padding)&&(mode==IP))
634 for (i=0; i<tx.padding; i++)
636 tx.ip_payload[tx.ip_payload_s+i] = 0x42; // pad with THE ANSWER (why random?)
638 tx.ip_payload_s += tx.padding;
639 tx.ip_len += tx.padding;
642 if (tx.ip6_id) {
643 t = libnet_build_ipv6_frag (tx.ip_proto,
645 htons(tx.ip_frag),
646 htonl(tx.ip6_id),
647 (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL,
648 (mode==IP) ? tx.ip_payload_s : 0,
651 tx.ip_len += LIBNET_IPV6_FRAG_H;
652 tx.ip_payload_s = 0;
653 tx.ip_proto = LIBNET_IPV6_NH_FRAGMENT;
656 // See RFC 2460 Routing Header
658 if ( (getarg(tx.arg_string,"segments", argval)==1) )
660 dummy = (unsigned int) str2int(argval);
661 if (dummy > 255) {
662 fprintf(stderr, " IP_Error: Maximal Routing Segments are 255!\n");
663 exit(1);
665 tx.ip6_segs = dummy;
668 if ( (getarg(tx.arg_string,"rtype", argval)==1) )
670 dummy = (unsigned int) str2int(argval);
671 if (dummy > 255) {
672 fprintf(stderr, " IP_Error: Maximum Routing Type is 255!\n");
673 exit(1);
675 tx.ip6_segs = dummy;
678 if ( (getarg(tx.arg_string,"loose", argval)==1) )
680 // Fill reserved
681 memset(tx.ip_option, 0, 4);
682 tx.ip_option_s=4;
684 len = strlen(argval);
685 s = strtok(argval, ".+-;/>");
688 len--;
689 *((struct libnet_in6_addr *) &tx.ip_option[tx.ip_option_s]) = libnet_name2addr6 (l, s, LIBNET_DONT_RESOLVE);
690 tx.ip_option_s += 16;
691 } while ( (s=strtok(NULL, ".+-;/>")) != NULL );
693 if (!tx.ip_option_s) {
694 fprintf(stderr, " IP_Error: No Routing Hops found!\n");
695 exit(1);
698 if (mode==IP && tx.ip_payload_s)
699 memmove(tx.ip_payload+tx.ip_option_s, tx.ip_payload, tx.ip_payload_s);
700 else
701 tx.ip_payload_s = 0;
703 memcpy(tx.ip_payload, tx.ip_option, tx.ip_option_s);
704 tx.ip_payload_s += tx.ip_option_s;
706 t = libnet_build_ipv6_routing(tx.ip_proto,
707 (tx.ip_option_s -4) / 8,
708 tx.ip6_rtype,
709 tx.ip6_segs,
710 tx.ip_payload,
711 tx.ip_payload_s,
714 tx.ip_len += LIBNET_IPV6_ROUTING_H + tx.ip_option_s;
715 tx.ip_payload_s = 0;
716 tx.ip_proto = LIBNET_IPV6_NH_ROUTING;
719 t = libnet_build_ipv6 (tx.ip_tos,
720 tx.ip_flow,
721 tx.ip_len,
722 tx.ip_proto,
723 tx.ip_ttl,
724 tx.ip6_src,
725 tx.ip6_dst,
726 (mode==IP) ? (tx.ip_payload_s) ? tx.ip_payload : NULL : NULL,
727 (mode==IP) ? tx.ip_payload_s : 0,
731 if (t == -1)
733 fprintf(stderr, " mz/create_ip_packet: Can't build IPv6 header: %s\n", libnet_geterror(l));
734 exit (0);
737 return t;