2 * Mausezahn - A fast versatile traffic generator
3 * Copyright (C) 2008-2010 Herbert Haas
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.
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
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
22 // ****************************************************************************
24 // 1. Initialize globals
25 // 2. Handle arguments in *argv[] and make a mode decision
26 // ****************************************************************************
32 // Purpose: reset globals, global structs, etc.
38 // Determine platform type sizes:
39 MZ_SIZE_LONG_INT
= sizeof(long int);
41 mz_default_config_path
[0] = 0x00;
42 mz_default_log_path
[0] = 0x00;
51 gind_max
= TIME_COUNT
;
58 for (i
=0;i
<TIME_COUNT_MAX
;i
++) jitter
[i
] = 0;
60 time0_flag
= 0; // If set then time0 has valid data
61 sqnr0_flag
= 0; // If set then sqnr_last and sqnr_next has valid data
63 mz_ssrc
[0]=0; mz_ssrc
[1]=0; mz_ssrc
[2]=0; mz_ssrc
[3]=0;
65 // Reset mgmt parameters of TX:
66 tx
.packet_mode
= 1; // assume we don't care about L2
68 tx
.delay
= DEFAULT_DELAY
;
69 tx
.arg_string
[0] = '\0';
71 // Reset Ethernet parameters of TX:
72 tx
.eth_params_already_set
= 0;
73 for (i
=0; i
<6; i
++) tx
.eth_dst
[i
] = 0xff;
74 for (i
=0; i
<6; i
++) tx
.eth_src
[i
] = 0; // TODO: Get own MAC !!!
75 tx
.eth_dst_txt
[0] = '\0';
76 tx
.eth_src_txt
[0] = '\0';
82 tx
.eth_payload
[0] = '\0';
86 // Reset CDP parameters for TX:
90 tx
.cdp_payload
[0] = '\0';
92 tx
.cdp_tlv_id
[0] = '\0';
93 tx
.cdp_tlv_id_len
= 0;
95 // Reset 802.1Q parameters of TX:
97 tx
.dot1Q_txt
[0] = '\0';
100 tx
.ascii
= 0; // 1 if specified
101 tx
.ascii_payload
[0]= '\0';
104 tx
.hex_payload_s
= 0;
106 // Reset MPLS parameters of TX:
108 tx
.mpls_txt
[0] = '\0';
113 tx
.mpls_verbose_string
[0] = '\0';
115 // Reset IP parameters of TX:
116 tx
.ip_src_txt
[0] = '\0';
118 tx
.ip_dst_txt
[0] = '\0';
119 tx
.ip_src_isrange
= 0;
125 tx
.ip_dst_isrange
= 0;
128 tx
.ip_payload
[0]= '\0';
130 tx
.ip_option
[0]= '\0';
133 // Reset ICMP parameters:
136 tx
.icmp_chksum
=0; // 0=autofill
141 // Reset general L4 parameters:
151 // Reset UDP parameters of TX:
153 tx
.udp_len
= 0; // If set to zero then create_udp_packet will calculate it
155 tx
.udp_payload
[0] = '\0';
156 tx
.udp_payload_s
= 0;
158 // Reset TCP parameters of TX:
161 tx
.tcp_seq_stop
= 42;
162 tx
.tcp_seq_delta
= 0; // also used as 'isrange' meaning
168 tx
.tcp_len
= 20; // Least size (TCP header only)
169 tx
.tcp_payload
[0] = '\0';
170 tx
.tcp_payload_s
= 0;
172 // Reset RTP parameters of TX:
176 // Initialize random generator
178 srand((unsigned int)t
);
181 for (i
=0; i
<MZ_MAX_DEVICES
; i
++) {
182 device_list
[i
].arprx_thread
= 0;
183 device_list
[i
].p_arp
= NULL
;
184 device_list
[i
].arp_table
= NULL
;
185 device_list
[i
].ps
=-1;
186 device_list
[i
].cli
=0;
187 device_list
[i
].mgmt_only
=0;
195 // Purpose: Properly handle arguments and configure global structs (tx)
196 int getopts (int argc
, char *argv
[])
198 int i
, c
, rargs
, RX
=0, count_set
=0, delay_set
=0;
199 unsigned int time_factor
;
200 char *packet_type
=NULL
, *mops_type
=NULL
;
202 unsigned char *dum1
, *dum2
;
205 char err_buf
[LIBNET_ERRBUF_SIZE
];
206 struct libnet_ether_addr
*mymac
;
209 char hexpld
[MAX_PAYLOAD_SIZE
*2];
210 int hexpld_specified
=0;
212 opterr
= 1; // let getopt print error message if necessary
215 while ((c
= getopt (argc
, argv
, "hqvVSxra:A:b:B:c:d:f:F:p:P:t:T:M:Q:X:")) != -1)
233 mz_port
= MZ_DEFAULT_PORT
;
236 strncpy (tx
.eth_src_txt
, optarg
, 32);
240 strncpy (tx
.ip_src_txt
, optarg
, 32);
243 strncpy (tx
.eth_dst_txt
, optarg
, 32);
247 strncpy (tx
.ip_dst_txt
, optarg
, 32);
251 tx
.count
= strtol(optarg
, (char **)NULL
, 10);
252 if ((errno
== ERANGE
&& (tx
.count
== LONG_MAX
|| tx
.count
== LONG_MIN
))
253 || (errno
!= 0 && tx
.count
== 0)) {
257 if (tx
.count
<0) tx
.count
=1; //TODO: Allow count=0 which means infinity (need to update all send_functions)
262 // determine whether seconds or msecs are used
263 // default is usec!!!
265 if (exists(optarg
,"s") || exists(optarg
,"sec")) time_factor
=1000000;
266 if (exists(optarg
,"m") || exists(optarg
,"msec")) time_factor
=1000;
267 dum
= strtok(optarg
,"ms");
268 tx
.delay
= strtol(dum
, (char **)NULL
, 10) * time_factor
;
269 if ((errno
== ERANGE
&& (tx
.delay
== LONG_MAX
|| tx
.delay
== LONG_MIN
))
270 || (errno
!= 0 && tx
.delay
== 0)) {
274 if (tx
.delay
<0) tx
.delay
=0; // no delay
279 tx
.padding
= strtol(optarg
, (char **)NULL
, 10);
280 if ((errno
== ERANGE
&& (tx
.padding
== LONG_MAX
|| tx
.padding
== LONG_MIN
))
281 || (errno
!= 0 && tx
.padding
== 0)) {
285 if (tx
.padding
>10000) {
286 fprintf(stderr
, " Warning: Padding must not exceed 10000!\n");
291 packet_type
= optarg
; // analyzed below
294 mops_type
= optarg
; // MOPS TRANSITION STRATEGY -- analyzed below
297 packet_type
= optarg
;
304 if (strncmp(optarg
,"help",4)==0) {
305 (void) get_mpls_params("help ");
308 strncpy (tx
.mpls_txt
, optarg
, 128);
309 tx
.eth_type
= ETHERTYPE_MPLS
;
314 case 'P': // ASCII payload
315 strncpy((char*)tx
.ascii_payload
, optarg
, MAX_PAYLOAD_SIZE
);
318 case 'f': // ASCII payload in FILE
319 afp
= fopen(optarg
, "r");
320 if (fgets((char*)tx
.ascii_payload
, MAX_PAYLOAD_SIZE
, afp
) == NULL
)
321 fprintf(stderr
, " mz/getopts: File empty?\n");
325 case 'F': // HEX payload in FILE
326 afp
= fopen(optarg
, "r");
328 while ( (hexpld
[i
]=fgetc(afp
))!=EOF
) {
329 if (isspace(hexpld
[i
])) {
338 case 'Q': // VLAN TAG
339 if (strncmp(optarg
,"help",4)==0) {
340 print_dot1Q_help(); // ugly but most simple and safe solution
343 strncpy (tx
.dot1Q_txt
, optarg
, 32);
345 // determine number of VLAN tags
346 for (i
=0; i
<strlen(tx
.dot1Q_txt
); i
++) {
347 if (tx
.dot1Q_txt
[i
]==',') tx
.dot1Q
++;
353 if ((optopt
== 'a') || (optopt
== 'b') || (optopt
= 'c') ||
354 (optopt
== 'd') || (optopt
== 'f') || (optopt
= 'p') ||
355 (optopt
== 't') || (optopt
== 'm'))
356 fprintf (stderr
, " mz/getopts: Option -%c requires an argument.\n", optopt
);
357 else if (isprint (optopt
))
358 fprintf (stderr
, " mz/getopts: Unknown option -%c'.\n", optopt
);
360 fprintf (stderr
, " mz/getopts: Unknown option character \\x%x'.\n", optopt
);
363 fprintf (stderr
," mz/getopts: Could not handle arguments properly!\n");
367 // ********************************************
368 // Handle additional arguments
369 // ********************************************
376 "Use at your own risk and responsibility!\n"
377 "-- Verbose mode --\n"
385 if ((rargs
=argc
-optind
)>2) { // number of remaining arguments
386 fprintf(stderr
," mz/getopts: Too many arguments!\n");
391 // There can be 0-2 additional arguments
394 if (lookupdev()) { // no device found
395 if (verbose
) fprintf(stderr
, " mz: no active interfaces found!\n");
396 strcpy(tx
.device
, "lo");
398 if (verbose
) // device found
399 fprintf(stderr
," mz: device not given, will use %s\n",tx
.device
);
401 case 1: // arg_string OR device given => find out!
402 if ( (strncmp(argv
[optind
],"eth",3)==0)
403 || (strncmp(argv
[optind
],"ath",3)==0)
404 || ((strncmp(argv
[optind
],"lo",2)==0)&&(strncmp(argv
[optind
],"log",3)!=0))
405 || (strncmp(argv
[optind
],"vmnet",5)==0)
406 || (strncmp(argv
[optind
],"wifi",4)==0) ) {
407 // device has been specified!
408 strncpy (tx
.device
, argv
[optind
], 16);
410 else { /// arg_string given => no device has been specified -- let's find one!
411 strncpy (tx
.arg_string
, argv
[optind
], MAX_PAYLOAD_SIZE
);
412 if (lookupdev()) { // no device found
413 if (verbose
) fprintf(stderr
, " mz: no active interfaces found!\n");
414 strcpy(tx
.device
, "lo");
417 fprintf(stderr
," mz: device not given, will use %s\n",tx
.device
);
420 case 2: // both device and arg_string given
421 strncpy (tx
.device
, argv
[optind
], 16);
422 strncpy (tx
.arg_string
, argv
[optind
+1], MAX_PAYLOAD_SIZE
);
425 fprintf(stderr
," mz/getopts: Unknown argument problem!\n");
429 if (hexpld_specified
) {
430 strcat(tx
.arg_string
, ",p=");
431 strcat(tx
.arg_string
, hexpld
);
435 //////////////////////////////////////////////////////////////////////////
437 // Initialize MAC and IP Addresses.
439 // - tx.eth_src = own interface MAC
440 // - tx.ip_src = own interface IP or user specified
441 // - tx.ip_dst = 255.255.255.255 or user specified (can be a range)
442 // - tx.ip_src_rand ... is set if needed.
445 // Get own device MAC address:
446 // Don't open context if only a help text is requested
447 if (getarg(tx
.arg_string
,"help", NULL
)!=1) {
448 l
= libnet_init (LIBNET_LINK_ADV
, tx
.device
, err_buf
);
450 fprintf(stderr
, " mz/getopts: libnet_init() failed (%s)", err_buf
);
453 mymac
= libnet_get_hwaddr(l
);
454 for (i
=0; i
<6; i
++) {
455 tx
.eth_src
[i
] = mymac
->ether_addr_octet
[i
];
456 tx
.eth_mac_own
[i
] = mymac
->ether_addr_octet
[i
];
459 // Set source IP address:
460 if (strlen(tx
.ip_src_txt
)) { // option -A has been specified
461 if (mz_strcmp(tx
.ip_src_txt
, "bcast", 2)==0) {
462 tx
.ip_src
= libnet_name2addr4 (l
, "255.255.255.255", LIBNET_DONT_RESOLVE
);
463 } else if (strcmp(tx
.ip_src_txt
, "rand") == 0) {
465 tx
.ip_src_h
= (u_int32_t
) ( ((float) rand()/RAND_MAX
)*0xE0000000); //this is 224.0.0.0
467 else if (get_ip_range_src(tx
.ip_src_txt
)) { // returns 1 when no range has been specified
468 // name2addr4 accepts a DOTTED DECIMAL ADDRESS or a FQDN:
469 tx
.ip_src
= libnet_name2addr4 (l
, tx
.ip_src_txt
, LIBNET_RESOLVE
);
472 else { // no source IP specified: by default use own IP address
473 tx
.ip_src
= libnet_get_ipaddr4(l
);
476 // Set destination IP address:
477 if (strlen(tx
.ip_dst_txt
)) { // option -B has been specified
478 if (mz_strcmp(tx
.ip_dst_txt
, "rand", 2)==0) {
479 fprintf(stderr
, "Option -B does not support random destination IP addresses currently.\n");
483 if (mz_strcmp(tx
.ip_dst_txt
, "bcast", 2)==0) {
484 tx
.ip_dst
= libnet_name2addr4 (l
, "255.255.255.255", LIBNET_DONT_RESOLVE
);
485 } else if (get_ip_range_dst(tx
.ip_dst_txt
)) { // returns 1 when no range has been specified
486 // name2addr4 accepts a DOTTED DECIMAL ADDRESS or a FQDN:
487 tx
.ip_dst
= libnet_name2addr4 (l
, tx
.ip_dst_txt
, LIBNET_RESOLVE
);
490 else { // no destination IP specified: by default use broadcast
491 tx
.ip_dst
= libnet_name2addr4 (l
, "255.255.255.255", LIBNET_DONT_RESOLVE
);
494 // Initialize tx.ip_src_h and tx.ip_dst_h which are used by 'print_frame_details()'
495 // in verbose mode. See 'modifications.c'.
497 if (tx
.ip_src_rand
) { // ip_src_h already given, convert to ip_src
498 dum1
= (unsigned char*) &tx
.ip_src_h
;
499 dum2
= (unsigned char*) &tx
.ip_src
;
501 else { // ip_src already given, convert to ip_src_h
502 dum1
= (unsigned char*) &tx
.ip_src
;
503 dum2
= (unsigned char*) &tx
.ip_src_h
;
514 dum1
= (unsigned char*) &tx
.ip_dst
;
515 dum2
= (unsigned char*) &tx
.ip_dst_h
;
529 // END OF ADDRESS INITIALIZATION
531 //////////////////////////////////////////////////////////////////////////
534 ////// retrieve interface parameters ///////
536 for (i
=0; i
<device_list_entries
; i
++) {
537 get_dev_params(device_list
[i
].dev
);
541 //////////////////////////////////////////////////////////////////////////
543 // Mausezahn CLI desired?
545 // has port number been specified?
546 if (strlen(tx
.arg_string
)) {
547 mz_port
= (int) str2int (tx
.arg_string
);
551 fprintf(stderr
, "Mausezahn accepts incoming Telnet connections on port %i.\n", mz_port
);
558 //////////////////////////////////////////////////////////////////////////
562 // Consider -t and -m option (used exclusively)
563 // -t => special packet types, stateless
565 // If -t not present then evaluate arg_string which must
566 // contain a byte-string in hexadecimal notation.
570 // ***** NEW: MOPS TRANSITION STRATEGY *****
571 if (mops_type
!= NULL
) {
573 if (mz_strcmp(mops_type
,"lldp",4)==0) {
574 mops_direct(tx
.device
, MOPS_LLDP
, tx
.arg_string
);
579 if (packet_type
== NULL
) { // raw hex string given
582 else if (strcmp(packet_type
,"arp")==0) {
585 else if (strcmp(packet_type
,"bpdu")==0) {
588 else if (strcmp(packet_type
,"ip")==0) {
591 else if (strcmp(packet_type
,"udp")==0) {
594 else if (strcmp(packet_type
,"icmp")==0) {
597 else if (strcmp(packet_type
,"tcp")==0) {
600 else if (strcmp(packet_type
,"dns")==0) {
603 else if (strcmp(packet_type
,"cdp")==0) {
606 else if (strcmp(packet_type
,"syslog")==0) {
609 else if (strcmp(packet_type
,"lldp")==0) {
611 tx
.packet_mode
=0; // create whole frame by ourself
613 else if (strcmp(packet_type
,"rtp")==0) {
619 if (!count_set
) tx
.count
= 0;
620 if (!delay_set
) tx
.delay
= 20000; // 20 msec inter-packet delay for RTP
623 else if (strcmp(packet_type
,"help")==0) {
627 "| The following packet types are currently implemented:\n"
629 "| arp ... sends ARP packets\n"
630 "| bpdu ... sends BPDU packets (STP or PVST+)\n"
631 "| cdp ... sends CDP messages\n"
632 "| ip ... sends IPv4 packets\n"
633 "| udp ... sends UDP datagrams\n"
634 "| tcp ... sends TCP segments\n"
635 "| icmp ... sends ICMP messages\n"
636 "| dns ... sends DNS messages\n"
637 "| rtp ... sends RTP datagrams\n"
638 "| syslog ... sends Syslog messages\n"
640 "| Of course you can build any other packet type 'manually' using the direct layer 2 mode.\n"
641 "| FYI: The interactive mode supports additional protocols. (Try mz -x <port>)\n"
647 fprintf(stderr
, " mz: you must specify a valid packet type!\n");
651 //////////////////////////////////////////////////////////////////////////
653 // TODO: Implement macro support
654 // Check macro types here