ifpps: use uint32_t instead of u32
[netsniff-ng.git] / staging / tools.c
blob9d2d1bec494bf6ca79a3102fe5be806602d050ca
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 ////////////////////////////////////////////////////////////////////////////////////////////
23 // Contains various tools to ease development of new modules:
24 //
25 // getarg ............. scans string for arguments and returns assigned value
26 // str2int ............. Converts a string into unsigned long int in a safe way
27 // str2lint ............ Same as str2int but returns an unsigned long long int
28 // xstr2int ............ Same as str2int but expects hex digits
29 // xstr2lint ........... Same as above but returns an unsigned long long int
30 // get_ip_range_dst .... Parses string for an IP range and sets start/stop addresses
31 // get_ip_range_src .... Same for source addresses
32 // get_ip6_range_dst ... Parses string for an IPv6 range and sets start/stop addresses
33 // get_ip6_range_src ... Same for source addresses
34 // check_eth_mac_txt ... Scans tx.eth_dst|src_txt and sets tx.eth_dst|src appropriately
35 // get_port_range ...... Parses string for a dst|src-port range and sets start/stop values
36 // get_tcp_flags ....... Parses string for TCP arguments and sets tx.tcp_control
37 // get_mpls_params ..... Parses string for MPLS parameters (label, exp, BOS, TTL)
38 // exists .............. Parses a string for a single character and returns "1" if found
39 // mz_strisbinary ...... Checks whether string consists only of 0 and 1, returns how many digits total
40 // str2bin8 ............ Converts a string containing max 8 binary digits into a number
41 // str2bin16 ........... Converts a string containing max 16 binary digits into a number
42 // char2bits ........... Converts a char into a string containing ones and zeros
43 // getfullpath_cfg ..... Creates a full filename with path to the desired config directory
44 // getfullpath_log ..... Creates a full filename with path to the desired logging directory
45 // mz_strncpy .......... A safer implementation of strncpy
46 // number_of_args ...... Returns number of arguments of the Mausezahn argument string
47 // mz_strisnum ......... Returns 1 if string only consists of decimal digits
48 // mz_strishex ......... Returns 1 if string only consists of hexadecimal digits
49 // mz_strcmp ........... Matches a string or a prefix of it with given min-length
50 // Example usage: User CLI input
51 // mz_tok .............. Decomposes a string into tokens and maps them to args
52 // Example usage: IPv6-addresses, user input for MPLS-tags
53 // delay_parse ......... Parses one or two strings for a delay specification and sets a struct timespec
54 //
55 ////////////////////////////////////////////////////////////////////////////////////////////
57 #include "mz.h"
59 #define CMP_INT(a, b) ((a) < (b) ? -1 : (a) > (b))
60 #define IPV6_MAX_RANGE_LEN strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128")
61 #define IPV6_MIN_RANGE_LEN strlen("::/0")
63 static int in6_range_too_big(struct libnet_in6_addr start, struct libnet_in6_addr stop);
65 // Scan 'str' for an argument 'arg_name' and returns its value in arg_value
66 // Return value: number of occurences of arg_name
67 // Note that if arg_name occurs multiple times, the last found value is returned.
68 // If last argument (arg_value) is set to NULL it will be ignored.
69 // Example:
70 // int i;
71 // char ip[64];
72 // i = getarg ("request, da=10.1.1.2, SYN", "da", ip);
73 // ...will assign "10.1.1.2" to ip and the occurence i is set to 1.
74 int getarg(char *str, char *arg_name, char *arg_value)
76 char tmp[MAX_PAYLOAD_SIZE];
77 char *str1, *str2, *token, *subtoken;
78 char *saveptr1, *saveptr2;
79 int j, occurence=0;
81 strncpy(tmp,str,MAX_PAYLOAD_SIZE); // only operate on local strings
83 for (j = 1, str1 = tmp; ; j++, str1 = NULL)
86 token = strtok_r(str1, ",", &saveptr1);
87 if (token == NULL)
88 break;
90 str2 = token;
91 if ( (subtoken = strtok_r(str2, " =", &saveptr2))!=NULL)
93 if (strcasecmp(subtoken,arg_name)==0)
95 occurence+=1;
96 //printf("found %s\n",arg_name);
97 if ( (subtoken = strtok_r(NULL, " =", &saveptr2))!=NULL)
99 // argument has a value!
100 //printf("%s has value: [%s]\n",arg_name, subtoken);
101 if (arg_value!=NULL)
103 strcpy(arg_value,subtoken);
108 else
109 break;
111 return occurence;
115 // Convert str to (unsigned long) int
116 // Return value: the unsigned long int
117 unsigned long int str2int(char *str)
119 unsigned long int i;
121 errno=0;
123 i = strtoul(str, (char **)NULL, 10);
125 if ((errno == ERANGE && (i == ULONG_MAX))
126 || (errno != 0 && i == 0))
128 perror("strtoul");
131 return i;
136 // Convert str to (unsigned long long) int
137 // Return value: the unsigned long long int
138 unsigned long long int str2lint(char *str)
140 unsigned long long int i;
142 errno=0;
144 i = strtoull(str, (char **)NULL, 10);
146 if ((errno == ERANGE && (i == ULLONG_MAX))
147 || (errno != 0 && i == 0))
149 perror("strtoull");
152 return i;
156 // Convert hex-str to (unsigned long) int
157 // Return value: the unsigned long int
158 unsigned long int xstr2int(char *str)
160 unsigned long int i;
162 errno=0;
164 i = strtoul(str, (char **)NULL, 16);
166 if ((errno == ERANGE && (i == ULONG_MAX))
167 || (errno != 0 && i == 0))
170 perror("strtoul");
173 return i;
177 // Convert hex-str to (unsigned long long) int
178 // Return value: the unsigned long long int
179 unsigned long long int xstr2lint(char *str)
181 unsigned long long int i;
183 errno=0;
185 i = strtoull(str, (char **)NULL, 16);
187 if ((errno == ERANGE && (i == ULLONG_MAX))
188 || (errno != 0 && i == 0))
190 perror("strtoull");
193 return i;
198 * Return the IPv6 broadcast address for the given network/mask.
200 struct libnet_in6_addr
201 in6_addr_bcast(struct libnet_in6_addr addr, unsigned int masklen)
203 struct libnet_in6_addr bcast;
204 uint32_t mask = 0;
205 int i = 3;
206 if (masklen > 128) {
207 fprintf(stderr, "Invalid IPv6 masklen: %u\n", masklen);
208 exit(1);
210 masklen = 128 - masklen;
212 bcast = addr;
214 for (i = 3; i >= 0; i--, masklen -= 32) {
215 if (masklen <= 32) {
216 bcast.__u6_addr.__u6_addr32[i] = htonl(ntohl(bcast.__u6_addr.__u6_addr32[i]) | ((uint32_t) (~0) >> (32 - masklen)));
217 break;
219 bcast.__u6_addr.__u6_addr32[i] = (uint32_t) (~0);
221 return bcast;
225 * Returns 0 if the given IPv6 addresses are equal,
226 * -1 if addr1 is lower than addr2,
227 * 1 if addr2 is lower than addr1.
229 int in6_addr_cmp(struct libnet_in6_addr addr1,
230 struct libnet_in6_addr addr2)
232 uint32_t *p1 = addr1.__u6_addr.__u6_addr32,
233 *p2 = addr2.__u6_addr.__u6_addr32;
234 int i, val = 0;
236 for (i = 0; i < 4; i++) {
237 val = CMP_INT(ntohl(*p1++), ntohl(*p2++));
238 if (val) {
239 break;
242 return val;
246 * Calculate the address that comes immediately after the one given.
247 * Store the result in *dst.
248 * Returns 1 if an overflow occurred. Otherwise, returns 0.
251 incr_in6_addr(struct libnet_in6_addr src, struct libnet_in6_addr *dst)
253 uint32_t i = 16, carry = 1;
254 uint8_t *addr;
255 *dst = src;
256 addr = dst->__u6_addr.__u6_addr8;
257 while (carry && i) {
258 addr[i - 1] += carry;
259 if (addr[i - 1] > 0xff || !addr[i - 1]) {
260 addr[i - 1] &= 0xff;
261 } else {
262 carry = 0;
264 i--;
266 return (int)carry;
271 * Return 1 if the number of addresses that are in the range from start to stop
272 * is greater than what can be stored in a uint64_t. Otherwise, return 0.
274 static int
275 in6_range_too_big(struct libnet_in6_addr start, struct libnet_in6_addr stop)
277 return (
278 (start.__u6_addr.__u6_addr32[0] != stop.__u6_addr.__u6_addr32[0])
279 || (start.__u6_addr.__u6_addr32[1] != stop.__u6_addr.__u6_addr32[1])
284 // Parses string 'arg' for an IP range and finds start and stop IP addresses.
285 // Return value: 0 upon success, 1 upon failure.
287 // NOTE: The results are written in the following variables:
289 // (u_int32_t) tx.ip_dst_start ... contains start value
290 // (u_int32_t) tx.ip_dst_stop ... contains stop value
291 // (u_int32_t) tx.ip_dst ... initialized with start value
292 // int tx.ip_dst_isrange ... set to 1 if above values valid
294 // Possible range specifications:
296 // 1) 192.168.0.0-192.168.0.12
297 // 2) 10.2.11.0-10.55.13.2
298 // 3) 172.18.96.0/19
300 // That is:
302 // FIRST detect a range by scanning for the "-" OR "/" chars
303 // THEN determine start and stop value and store them as normal unsigned integers
305 int get_ip_range_dst (char *arg)
308 int
309 i, len,
310 found_slash=0, found_dash=0;
312 unsigned int q;
313 u_int32_t mask, invmask;
315 char *start_str, *stop_str;
317 len = strnlen(arg, 32);
319 if ( (len>31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars)
320 return 1; // ERROR: no range
322 // Find "-" or "/"
323 for (i=0; i<len; i++)
325 if (arg[i]=='/') found_slash=1;
326 if (arg[i]=='-') found_dash=1;
329 if ((found_slash) && (found_dash))
330 exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!!
332 if (found_dash)
334 start_str = strtok (arg, "-");
335 stop_str = strtok (NULL, "-");
337 // These are the start and stop IP addresses of the range:
338 tx.ip_dst_start = str2ip32 (start_str);
339 tx.ip_dst_stop = str2ip32 (stop_str);
340 tx.ip_dst_h = tx.ip_dst_start;
341 tx.ip_dst = str2ip32_rev (start_str);
343 if (tx.ip_dst_start < tx.ip_dst_stop)
345 // Set range flag:
346 tx.ip_dst_isrange = 1;
347 return 0;
349 else
351 tx.ip_dst_isrange = 0;
352 return 1; // ERROR: stop value must be greater than start value !!!
355 else if (found_slash)
357 start_str = strtok (arg, "/");
358 stop_str = strtok (NULL, "/"); // Actually contains the prefix length, e. g. "24"
360 q = (unsigned int) str2int (stop_str);
362 mask = 0xffffffff;
363 mask <<= (32-q);
364 invmask = 0xffffffff - mask;
366 tx.ip_dst_start = (str2ip32 (start_str) & mask) +1; // the '+1' is to ensure that we do not start with the net-id
367 tx.ip_dst_stop = tx.ip_dst_start | invmask;
368 tx.ip_dst_h = tx.ip_dst_start;
369 tx.ip_dst = str2ip32_rev (start_str) | 0x01000000; // the '0x01000000' is to ensure that we do not start with the net-id
370 tx.ip_dst_isrange = 1;
371 return 0;
375 return 1; // ERROR: The specified argument string is not a range!
382 // Parses string 'arg' for an IP range and finds start and stop IP addresses.
383 // Return value: 0 upon success, 1 upon failure.
385 // NOTE: The results are written in the following variables:
387 // (u_int32_t) tx.ip_src_start ... contains start value
388 // (u_int32_t) tx.ip_src_stop ... contains stop value
389 // (u_int32_t) tx.ip_src ... initialized with start value
390 // int tx.ip_src_isrange ... set to 1 if above values valid
392 // Possible range specifications:
394 // 1) 192.168.0.0-192.168.0.12
395 // 2) 10.2.11.0-10.55.13.2
396 // 3) 172.18.96.0/19
398 // That is:
400 // FIRST detect a range by scanning for the "-" OR "/" chars
401 // THEN determine start and stop value and store them as normal unsigned integers
403 int get_ip_range_src (char *arg)
406 int
407 i, len,
408 found_slash=0, found_dash=0;
410 unsigned int q;
411 u_int32_t mask, invmask;
413 char *start_str, *stop_str;
416 len = strnlen(arg,32);
418 if ( (len>31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars)
419 return 1; // ERROR: no range
421 // Find "-" or "/"
422 for (i=0; i<len; i++)
424 if (arg[i]=='/') found_slash=1;
425 if (arg[i]=='-') found_dash=1;
428 if ((found_slash) && (found_dash))
429 exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!!
431 if (found_dash)
433 start_str = strtok (arg, "-");
434 stop_str = strtok (NULL, "-");
436 // These are the start and stop IP addresses of the range:
437 tx.ip_src_start = str2ip32 (start_str);
438 tx.ip_src_stop = str2ip32 (stop_str);
439 tx.ip_src_h = tx.ip_src_start;
440 tx.ip_src = str2ip32_rev (start_str);
442 if (tx.ip_src_start < tx.ip_src_stop)
444 // Set range flag:
445 tx.ip_src_isrange = 1;
446 return 0;
448 else
450 tx.ip_src_isrange = 0;
451 return 1; // ERROR: stop value must be greater than start value !!!
454 else if (found_slash)
456 start_str = strtok (arg, "/");
457 stop_str = strtok (NULL, "/"); // Actually contains the prefix length, e. g. "24"
459 q = (unsigned int) str2int (stop_str);
461 mask = 0xffffffff;
462 mask <<= (32-q);
463 invmask = 0xffffffff - mask;
465 tx.ip_src_start = (str2ip32 (start_str) & mask) +1; // the '+1' is to ensure that we do not start with the net-id
466 tx.ip_src_stop = tx.ip_src_start | invmask;
467 tx.ip_src_h = tx.ip_src_start;
468 tx.ip_src = str2ip32_rev (start_str) | 0x01000000; // the '0x01000000' is to ensure that we do not start with the net-id
469 tx.ip_src_isrange = 1;
470 return 0;
473 return 1; // ERROR: The specified argument string is not a range!
478 * Return the number of IPv6 addresses in the range from start to stop or
479 * UINT64_MAX, whichever is smaller.
481 uint64_t get_ip6_range_count(struct libnet_in6_addr start, struct libnet_in6_addr stop)
483 uint64_t retval = 0;
484 if (in6_range_too_big(start, stop)) {
485 return UINT64_MAX;
487 retval = ((uint64_t)(ntohl(stop.__u6_addr.__u6_addr32[2]) - ntohl(start.__u6_addr.__u6_addr32[2])) << 32)
488 + (ntohl(stop.__u6_addr.__u6_addr32[3]) - ntohl(start.__u6_addr.__u6_addr32[3]));
489 if (retval < UINT64_MAX) {
490 retval++;
492 return retval;
495 int get_ip6_range_src (char *arg, libnet_t *l)
499 i, len,
500 found_slash=0, found_dash=0;
502 unsigned int q;
503 struct libnet_in6_addr tmp_in6_addr;
504 uint32_t mask, invmask;
505 char *start_str, *stop_str;
507 len = strnlen(arg, IPV6_MAX_RANGE_LEN);
509 if ( (len > IPV6_MAX_RANGE_LEN) || (len < IPV6_MIN_RANGE_LEN) )
510 return 1; // ERROR: no range
512 // Find "-" or "/"
513 for (i=0; i<len; i++)
515 if (arg[i]=='/') found_slash=1;
516 if (arg[i]=='-') found_dash=1;
519 if ((found_slash) && (found_dash))
520 exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!!
522 if (found_dash)
524 start_str = strtok (arg, "-");
525 stop_str = strtok (NULL, "-");
527 // These are the start and stop IP addresses of the range:
528 tx.ip6_src_start = libnet_name2addr6 (l, start_str, LIBNET_DONT_RESOLVE);
529 tx.ip6_src_stop = libnet_name2addr6 (l, stop_str, LIBNET_DONT_RESOLVE);
530 //XXX Not sure if this needs to be modified
531 //tx.ip_src_h = tx.ip_src_start;
532 //tx.ip_src = str2ip32_rev (start_str);
534 if (in6_addr_cmp(tx.ip6_src_start, tx.ip6_src_stop) < 0)
536 // Set range flag:
537 tx.ip_src_isrange = 1;
538 if (in6_range_too_big(tx.ip6_src_start, tx.ip6_src_stop)) {
539 fprintf(stderr, "The IPv6 range is too big. It must be smaller than a /64.\n");
540 exit (1);
542 return 0;
544 else
546 tx.ip_src_isrange = 0;
547 return 1; // ERROR: stop value must be greater than start value !!!
550 else if (found_slash)
552 start_str = strtok (arg, "/");
553 stop_str = strtok (NULL, "/"); // Actually contains the prefix length, e. g. "24"
555 q = (unsigned int) str2int (stop_str);
557 tmp_in6_addr = libnet_name2addr6 (l, start_str, LIBNET_DONT_RESOLVE);
558 tx.ip6_src_stop = in6_addr_bcast(tmp_in6_addr, q);
559 incr_in6_addr(tmp_in6_addr, &tx.ip6_src_start), // ensure that we do not start with the net-id
560 // TODO decrement the bcast address so it's not included in the range.
561 tx.ip_src_isrange = 1;
562 if (in6_range_too_big(tx.ip6_src_start, tx.ip6_src_stop)) {
563 fprintf(stderr, "The IPv6 range is too big. It must be smaller than a /64.\n");
564 exit (1);
566 return 0;
569 return 1; // ERROR: The specified argument string is not a range!
572 int get_ip6_range_dst (char *arg, libnet_t *l)
576 i, len,
577 found_slash=0, found_dash=0;
579 unsigned int q;
580 uint32_t mask, invmask;
581 struct libnet_in6_addr tmp_in6_addr;
582 char *start_str, *stop_str;
584 len = strnlen(arg,IPV6_MAX_RANGE_LEN);
586 if ( (len > IPV6_MAX_RANGE_LEN) || (len < IPV6_MIN_RANGE_LEN) )
587 return 1; // ERROR: no range
589 // Find "-" or "/"
590 for (i=0; i<len; i++)
592 if (arg[i]=='/') found_slash=1;
593 if (arg[i]=='-') found_dash=1;
596 if ((found_slash) && (found_dash))
597 exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!!
599 if (found_dash)
601 start_str = strtok (arg, "-");
602 stop_str = strtok (NULL, "-");
604 // These are the start and stop IP addresses of the range:
605 tx.ip6_dst_start = libnet_name2addr6 (l, start_str, LIBNET_DONT_RESOLVE);
606 tx.ip6_dst_stop = libnet_name2addr6 (l, stop_str, LIBNET_DONT_RESOLVE);
607 //XXX Not sure if this needs to be modified
608 //tx.ip_dst_h = tx.ip_dst_start;
609 //tx.ip_dst = str2ip32_rev (start_str);
611 if (in6_addr_cmp(tx.ip6_dst_start, tx.ip6_dst_stop) < 0)
613 // Set range flag:
614 tx.ip_dst_isrange = 1;
615 return 0;
617 else
619 tx.ip_dst_isrange = 0;
620 return 1; // ERROR: stop value must be greater than start value !!!
623 else if (found_slash)
625 start_str = strtok (arg, "/");
626 stop_str = strtok (NULL, "/"); // Actually contains the prefix length, e. g. "24"
628 q = (unsigned int) str2int (stop_str);
630 tmp_in6_addr = libnet_name2addr6 (l, start_str, LIBNET_DONT_RESOLVE);
631 tx.ip6_dst_stop = in6_addr_bcast(tmp_in6_addr, q);
632 incr_in6_addr(tmp_in6_addr, &tx.ip6_dst_start), // ensure that we do not start with the net-id
633 // TODO decrement the bcast address so it's not included in the range.
634 tx.ip_dst_isrange = 1;
635 return 0;
638 return 1; // ERROR: The specified argument string is not a range!
641 // Scans tx.eth_dst_txt or tx.eth_src_txt and sets the corresponding
642 // MAC addresses (tx.eth_dst or tx.eth_src) accordingly.
643 // Argument: What string should be checked, ETH_SRC or ETH_DST.
645 // Return value:
646 // 0 when a MAC address has been set or
647 // 1 when not set (or wrongly set)
649 // Currently eth_src|dst_txt can be:
650 // 'rand', 'own', 'bc'|'bcast', 'stp', 'pvst', 'cisco',
651 // or a real mac address.
653 // TODO: implement other important MAC addresses
654 int check_eth_mac_txt(int src_or_dst)
656 char *eth_mac_txt;
657 u_int8_t *eth_mac;
658 int *eth_rand;
659 int i;
661 // Check argument
662 if (src_or_dst == ETH_SRC)
664 eth_mac_txt = tx.eth_src_txt;
665 eth_mac = tx.eth_src;
666 eth_rand = &tx.eth_src_rand;
668 else if (src_or_dst == ETH_DST)
670 eth_mac_txt = tx.eth_dst_txt;
671 eth_mac = tx.eth_dst;
672 eth_rand = &tx.eth_dst_rand;
674 else
676 return 1; // wrong argument
680 // Did the user really specify a dst-address?
681 if (strnlen(eth_mac_txt, 18)==0)
683 return 1; // No.
687 // Okay, lets check the commandline argument:
689 // Do you want a random MAC?
690 // TODO: Consider enforcement of unicast addresses
691 if (strncmp(eth_mac_txt, "rand", 4)==0)
693 eth_mac[0] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
694 eth_mac[1] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
695 eth_mac[2] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
696 eth_mac[3] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
697 eth_mac[4] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
698 eth_mac[5] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
699 *eth_rand = 1;
701 // Do you want your own interface MAC?
702 else if (strncmp(eth_mac_txt, "own", 3)==0)
704 for (i=0; i<6; i++)
706 eth_mac[i] = tx.eth_mac_own[i];
709 // Do you want a broadcast MAC?
710 else if (strncmp(eth_mac_txt, "bc", 2)==0) // NOTE that this also fetches "bcast"
712 str2hex_mac("FF:FF:FF:FF:FF:FF", eth_mac);
714 // Do you want the IEEE address 'all bridges' used for STP?
715 else if (strncmp(eth_mac_txt, "stp", 3)==0) //
717 str2hex_mac("01:80:C2:00:00:00", eth_mac); // IEEE for all bridges
719 // Do you want the Cisco address e. g. for CDP, VTP?
720 else if (strncmp(eth_mac_txt, "cisco", 5)==0)
722 str2hex_mac("01:00:0C:CC:CC:CC", eth_mac);
724 // Do you want the Cisco address e. g. for CDP, VTP?
725 else if (strncmp(eth_mac_txt, "pvst", 5)==0)
727 str2hex_mac("01:00:0C:CC:CC:CD", eth_mac);
729 // The string MUST contain a mac address
730 // TODO: CHECK whether the string has correct format for a mac address!
731 else
733 str2hex_mac(eth_mac_txt, eth_mac);
736 return 0;
743 // Scans argument for a port number or range and sets
744 // the corresponding values in the tx struct:
746 // a) tx.sp_start, tx.sp_stop, tx.sp = tx.sp_start
748 // ** OR **
750 // b) tx.dp_start, tx.dp_stop, tx.dp = tx.dp_start
752 // Arguments:
754 // - 'sp_or_dp' is either SRC_PORT or DST_PORT
755 // - 'arg' contains the port range as string such as 1-1024
757 // Return value: 0 on success, 1 upon failure
759 int get_port_range (int sp_or_dp, char *arg)
762 int i, len, found_dash=0;
764 u_int32_t tmp1, tmp2;
766 u_int16_t
767 *port,
768 *start,
769 *stop;
770 int
771 *isrange;
773 char *start_str, *stop_str;
776 // Check which port to manage
777 if (sp_or_dp == DST_PORT)
779 port = &tx.dp;
780 start = &tx.dp_start;
781 stop = &tx.dp_stop;
782 isrange = &tx.dp_isrange;
784 else if (sp_or_dp == SRC_PORT)
786 port = &tx.sp;
787 start = &tx.sp_start;
788 stop = &tx.sp_stop;
789 isrange = &tx.sp_isrange;
791 else
793 return 1; // error
797 len = strnlen(arg,12);
798 if (len==0) return 1; // error
800 // Find "-"
801 for (i=0; i<len; i++)
803 if (arg[i]=='-') found_dash=1;
806 if (found_dash) // range specified
808 start_str = strtok (arg, "-");
809 stop_str = strtok (NULL, "-");
811 tmp1 = str2int (start_str);
812 if ( (tmp1<0)||(tmp1>65535))
814 fprintf(stderr," mz/get_port_range: Invalid port range!\n");
815 exit (-1);
817 *start = tmp1;
819 tmp2 = str2int (stop_str);
820 if ( (tmp2<0)||(tmp2>65535))
822 fprintf(stderr," mz/get_port_range: Invalid port range!\n");
823 exit (-1);
825 *stop = tmp2;
827 if (tmp1>tmp2) // swap start/stop values!
829 *start = tmp2;
830 *stop = tmp1;
833 *port = *start;
834 *isrange = 1;
836 return 0;
838 else // single port number
840 tmp1 = str2int (arg);
841 if ( (tmp1<0)||(tmp1>65535)) tmp1=0;
842 *port = tmp1;
843 *isrange = 0;
844 return 0;
847 return 1; // error
853 // Scans argument for TCP flags and sets
854 // tx.tcp_control accordingly.
856 // Valid keywords are: fin, syn, rst, psh, ack, urg, ecn, cwr
857 // Valid delimiters are: | or + or -
858 // Return value: 0 on success, 1 upon failure
860 int get_tcp_flags (char* flags)
862 char *f;
864 // From LSB to MSB: fin, syn, reset, push, ack, urg, ecn, cwr
865 // ecn...ECN-Echo, cwr...Congestion Window Reduced
867 if (strnlen(flags,40)==0) return 1; // error
870 f = strtok (flags, "|+-");
873 if (strncmp(f,"fin",3)==0)
875 tx.tcp_control = tx.tcp_control | 1;
877 else if (strncmp(f,"syn",3)==0)
879 tx.tcp_control = tx.tcp_control | 2;
881 else if (strncmp(f,"rst",3)==0)
883 tx.tcp_control = tx.tcp_control | 4;
885 else if (strncmp(f,"psh",3)==0)
887 tx.tcp_control = tx.tcp_control | 8;
889 else if (strncmp(f,"ack",3)==0)
891 tx.tcp_control = tx.tcp_control | 16;
893 else if (strncmp(f,"urg",3)==0)
895 tx.tcp_control = tx.tcp_control | 32;
897 else if (strncmp(f,"ecn",3)==0)
899 tx.tcp_control = tx.tcp_control | 64;
901 else if (strncmp(f,"cwr",3)==0)
903 tx.tcp_control = tx.tcp_control | 128;
906 } while ( (f=strtok(NULL, "|+-")) != NULL);
908 return 0;
913 // Scans string 'params' for MPLS parameters
914 // and sets tx.mpls_* accordingly.
916 // CLI Syntax Examples:
918 // -M help .... shows syntax
920 // -M 800 .... label=800
921 // -M 800:S .... label=800 and BOS flag set
922 // -M 800:S:64 .... label=800, BOS, TTL=64
923 // -M 800:64:S .... same
924 // -M 64:77 .... label=64, TTL=77
925 // -M 64:800 .... INVALID
926 // -M 800:64 .... label=800, TTL=64
927 // -M 800:3:S:64 .... additionally the experimental bits are set (all fields required!)
929 // Note: S = BOS(1), s = NOT-BOS(0)
931 // Valid delimiters: :-.,+
932 // Return value: 0 on success, 1 upon failure
933 int get_mpls_params(char *p)
936 char *f1, *f2, *f3, *f4;
937 char params[256];
939 tx.mpls_exp = 0;
940 tx.mpls_ttl = 255;
942 strncpy(params, p, 256);
943 params[255] = '\0';
945 if (strncmp(params,"help",4)==0)
947 fprintf(stderr,"\n"
948 MAUSEZAHN_VERSION
949 "\n"
950 "| MPLS header Syntax: -M label[,label[,label[,...]]]\n"
951 "| where each header may consist of the following parameters:\n"
952 "|\n"
953 "| label ... the MPLS label (mandatory, 0..1048575)\n"
954 "| exp ..... experimental/CoS (default: 0, allowed values: 0..7)\n"
955 "| TTL ..... Time To Live (default: 255)\n"
956 "| BOS ..... marks bottom-of-stack; per default the last (most inner) header\n"
957 "| will have BOS=1. If desired you can set this flag for any header\n"
958 "| inbetween but this will lead to an invalid packet. Simply use\n"
959 "| 'S' to set BOS=1, or 's' to set BOS=0. Note that this flag MUST be\n"
960 "| the LAST argument.\n"
961 "|\n"
962 "| Examples:\n"
963 "|\n"
964 "| -M 800 .... label=800\n"
965 "| -M 800:6 .... label=800 and CoS=6\n"
966 "| -M 800:6:55 .... label=800, CoS=6, TTL=55\n"
967 "| -M 800:S .... label=800 and BOS=1\n"
968 "| -M 800:6:s .... label=800, CoS=6, and BOS=0\n"
969 "|\n"
970 "| multiple headers:\n"
971 "|\n"
972 "| -m 30,20:7,800:5:128 ... outer label=800 with CoS=5 and TTL=128,\n"
973 "| middle label=20 with CoS=7,\n"
974 "| inner label=30 (this one is closest to L3).\n"
975 "|\n"
976 "| Valid delimiters inside a header: : - . +\n"
977 "|\n"
978 "\n");
979 exit (0);
981 else
984 if ( (f1 = strtok (params, ":-.+")) == NULL )
986 return 1; // error!
989 tx.mpls_label = (u_int32_t) str2int (f1);
990 if (tx.mpls_label>1048575)
992 tx.mpls_label = 1048575; // 2^20
993 fprintf(stderr," Warning: MPLS label too big! Reduced to maximum allowed value.\n");
998 if ( (f2 = strtok (NULL, ":-.+")) != NULL ) // 2nd param set
1000 if (strncmp(f2,"S",1)==0)
1002 tx.mpls_bos = 1;
1003 return 0;
1005 else if (strncmp(f2,"s",1)==0)
1007 tx.mpls_bos = 0;
1008 return 0;
1010 else
1012 tx.mpls_exp = (u_int8_t) str2int (f2);
1013 if (tx.mpls_exp > 7)
1015 tx.mpls_exp = 7;
1016 fprintf(stderr," Warning: MPLS CoS too big! Reduced to maximum allowed value.\n");
1021 if ( (f3 = strtok (NULL, ":-.+")) != NULL ) // 3rd param set
1023 if (strncmp(f3,"S",1)==0)
1025 tx.mpls_bos = 1;
1026 return 0;
1028 else if (strncmp(f3,"s",1)==0)
1030 tx.mpls_bos = 0;
1031 return 0;
1033 else
1035 if ((u_int16_t) str2int (f3)>255)
1037 fprintf(stderr," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n");
1038 tx.mpls_ttl = 255;
1040 else
1042 tx.mpls_ttl = (u_int8_t) str2int (f3);
1046 if ( (f4 = strtok (NULL, ":-.+")) != NULL ) // 4th param set
1049 if (strncmp(f3,"S",1)==0)
1051 tx.mpls_bos = 1;
1053 else if (strncmp(f3,"s",1)==0)
1055 tx.mpls_bos = 0;
1064 return 0;
1068 // Parses str for occurence of character or sequence ch.
1069 // Returns number of occurences
1070 int exists(char* str, char* ch)
1072 int i,match;
1074 size_t len_str, len_ch;
1076 len_str = strlen(str);
1077 len_ch = strlen(ch);
1078 match=0;
1080 for (i=0; i<len_str; i++)
1082 if (strcmp(str++,ch)==0) match++;
1085 return match;
1090 // Checks if str consists only of 0 and 1
1092 // RETURN VALUE:
1094 // 0 if invalid chars found or str empty
1095 // n if str consists exactly of n binary digits
1096 int mz_strisbinary(char *str)
1098 int i, len, ret=0;
1100 len = strlen(str);
1101 if (len==0) return 0;
1103 for (i=0; i<len; i++) {
1104 if ((str[i]=='0') || (str[i]=='1')) {
1105 ret++;
1106 } else {
1107 return 0;
1110 return ret;
1117 // Converts a string containing (max 8) binary digits into a number
1118 // RETURN VALUE:
1120 // Either the number on success
1121 // Or -1 upon failure
1123 int str2bin8 (char *str)
1125 int i, n, ret=0;
1127 n=mz_strisbinary(str);
1129 if ((!n) || (n>8)) return -1;
1131 for (i=0; i<n; i++) if (str[i]=='1') ret |= ( 0x01 << (n-1-i) );
1132 return ret;
1138 // Converts a string containing (max 16) binary digits into a number
1139 // RETURN VALUE:
1141 // Either the number on success
1142 // Or -1 upon failure
1144 long int str2bin16 (char *str)
1146 int i, n;
1147 long int ret=0;
1149 n=mz_strisbinary(str);
1151 if ((!n) || (n>16)) return -1;
1153 for (i=0; i<n; i++) if (str[i]=='1') ret |= ( 0x01 << (n-1-i) ); // C is great ;-)
1154 return ret;
1159 // Converts a char into a string containing ones and zeros
1161 // EXAMPLE:
1163 // char c = 0x81; char str[16];
1164 // char2bits(c, str);
1165 // printf("%s\n",str); => "1 0 0 0 0 0 0 1"
1167 int char2bits (char c, char *str)
1169 int i,j=1;
1170 char tmp[]="0 0 0 0 0 0 0 0";
1172 for (i=0; i<8; i++)
1174 if (c&j) tmp[14-i*2]='1';
1175 j=j*2;
1178 strncpy(str, tmp, 15);
1179 return 0;
1183 // Takes filename and prepends valid configuration directory
1185 // 1) prefer configurable mz_default_config_path[]
1186 // 2) otherwise use MZ_DEFAULT_CONFIG_PATH
1188 // NOTE: 'filename' finally holds the full path
1189 // and must therefore be big enough
1192 // RETURN VALUE:
1193 // 0 upon success
1194 // 1 upon failure
1196 int getfullpath_cfg (char *filename)
1198 int lenf, lenp;
1199 char tmp[32];
1201 lenf = strnlen(filename, 32);
1203 // filename not given?
1204 if ((lenf==0) || (lenf==32)) return 1;
1206 strncpy(tmp, filename, 32);
1208 // Prefer user-defined path if provided:
1209 lenp = strnlen(mz_default_config_path,255);
1211 if (lenp) {
1212 if (strncmp(mz_default_config_path+lenp-1, "/",1))
1213 strncat(mz_default_config_path, "/",1);
1214 snprintf(filename, 255, "%s%s",mz_default_config_path,tmp);
1216 else {
1217 lenp = strlen(MZ_DEFAULT_CONFIG_PATH);
1218 snprintf(filename, 255, "%s%s",MZ_DEFAULT_CONFIG_PATH,tmp);
1221 if ((lenf+lenp)>255) return 1;
1223 return 0;
1228 // Takes filename and prepends valid logging directory
1230 // 1) prefer configurable mz_default_log_path[]
1231 // 2) otherwise use MZ_DEFAULT_LOG_PATH
1233 // NOTE: filename is overwritten and must be big enough to hold full path!
1235 int getfullpath_log (char *filename)
1237 int lenf, lenp;
1238 char tmp[32];
1240 lenf = strnlen(filename, 32);
1242 // filename not given?
1243 if ((lenf==0) || (lenf==32)) return 1;
1245 strncpy(tmp, filename, 32);
1247 // Prefer user-defined path if provided:
1248 lenp = strnlen(mz_default_log_path,255);
1249 if (lenp) {
1250 if (strncmp(mz_default_log_path+lenp-1, "/",1))
1251 strncat(mz_default_log_path, "/",1);
1252 snprintf(filename, 255, "%s%s",mz_default_log_path,tmp);
1254 else {
1255 lenp = strlen(MZ_DEFAULT_LOG_PATH);
1256 snprintf(filename, 255, "%s%s",MZ_DEFAULT_LOG_PATH,tmp);
1259 if ((lenf+lenp)>255) return 1;
1261 return 0;
1264 // Behaves much like strncpy but additionally ensures
1265 // that dest is always \0-terminated.
1267 // USAGE NOTE: If you know exactly the length n of your string,
1268 // then you must provide n+1 to support the termination character.
1270 // EXAMPLE: src="Hello", n=strlen(src)=5, and mz_strncpy(dest, src, n)
1271 // would result in dest={H,e,l,l,\0}.
1272 // Therefore the correct usage is:
1273 // mz_strncpy(dest, src, strlen(src)+1);
1274 // =============
1276 // RETURN VALUE: pointer to dest
1277 char * mz_strncpy(char *dest, const char *src, size_t n)
1279 char *tmp;
1280 tmp = strncpy(dest, src, n);
1281 dest[n-1]='\0';
1282 return tmp;
1288 // Helper function to count the number of arguments
1289 // in the Mausezahn argument string (comma separated args)
1291 // RETURN VALUE: Number of arguments
1293 // TODO: Improve it. Use strtok.
1295 int number_of_args (char *str)
1297 int len=0, i=0, commas=1;
1298 if ((len=strnlen(str,MAX_PAYLOAD_SIZE))<2) return 0; // no valid argument
1299 for (i=0; i<len; i++) if (str[i]==',') commas++;
1300 if (str[len-1]==',') commas--; // comma at the end!
1301 return commas;
1306 // Checks if str consists only of digits 0..9
1308 // RETURN VALUE:
1310 // 0 if invalid chars found or str empty
1311 // n if str consists exactly of n digits
1312 int mz_strisnum(char *str)
1314 int i, len, ret=0;
1316 len = strlen(str);
1317 if (len==0) return 0;
1319 for (i=0; i<len; i++) {
1320 if (isdigit(str[i])) {
1321 ret++;
1322 } else {
1323 return 0;
1326 return ret;
1330 // Checks if str consists only of hex digits 0..9 and a..f
1332 // RETURN VALUE:
1334 // 0 if invalid chars found or str empty
1335 // n if str consists exactly of n digits
1336 int mz_strishex(char *str)
1338 int i, len, ret=0;
1340 len = strlen(str);
1341 if (len==0) return 0;
1343 for (i=0; i<len; i++) {
1344 if (isxdigit(str[i])) {
1345 ret++;
1346 } else {
1347 return 0;
1350 return ret;
1354 // Returns an 4-byte random number
1356 u_int32_t mz_rand32 (void)
1358 static unsigned int r=0;
1359 srand(r);
1360 r=rand();
1361 return (r<<16 | r);
1369 // Compares user-provided string with a specified string.
1371 // Return value:
1373 // 0 if at least min characters match
1374 // 1 if at least one character of usr does NOT match the corresponding character in str.
1376 // Note: Case-insensitive!
1377 // Goal: Should be more practical and secure than strcmp (and related)
1378 int mz_strcmp(char* usr_orig, char* str_orig, int min)
1380 int i, same=0, usrlen, max;
1381 char usr[80], str[80];
1383 usrlen = strlen(usr_orig);
1384 max = strlen(str_orig);
1386 strncpy(usr, usr_orig, 80);
1387 strncpy(str, str_orig, 80);
1389 // User provided not enough or too many chars
1390 if ((usrlen<min) || (usrlen>max)) return 1;
1392 // now check how many bytes really match
1393 for (i=0; i<usrlen; i++) {
1394 if (strncasecmp(&usr[i], &str[i], 1)==0) {
1395 same++;
1399 if (same<usrlen) return 1;
1401 return 0;
1408 // PURPOSE:
1410 // Maps an arbitrary number of tokens from 'str' which are separated by
1411 // a character 'delim' into provided arguments.
1413 // USAGE EXAMPLE:
1415 // char str[]="Am:Dam:Des";
1416 // char t1[64], t2[64], t3[64], t4[64];
1418 // mz_tok (str, ":", 4, t1, t2, t3, t4)
1420 // => t1="Am", t2="Dam", t3="Des", t4=NULL
1422 // NOTE:
1424 // 1. If the delimiter symbol occurs twice without gap, it is interpreted
1425 // as 'fill-up' command. To avoid ambiguities this may only occur once.
1426 // See the IPv6 address format shortcuts as similar example.
1428 // 2. If there are less tokens than allowed, the arguments are filled up
1429 // in order, while the remaining are casted to NULL:
1431 // 3. str must be smaller than 4096 bytes!
1433 // 4. To mitigate buffer overflow problems, the maximum token size is
1434 // currently limited to 64 bytes. Therefore it is recommended to
1435 // allocate 64 bytes for each argument.
1437 // RETURN VALUE: Number of returned tokens or -1 upon error
1439 int mz_tok(char * str, char * delim, int anz, ...)
1442 va_list ap;
1443 int i=0, n=0, len, llen, rlen, ltok=0, rtok=0;
1444 char *d, *l, *r, *token, *saveptr, *arg;
1445 char str2[4096], delim2[4]="", delim3[4]="";;
1447 if (strlen(delim)!=1) return -1; // delim must contain a single character!
1448 strncpy(str2, str, 4095); // protect the original str from strtok => operate on a copy only
1449 len = strlen(str2);
1451 // Check if some tokens are omitted (::)
1452 strncpy(delim2, delim, 1); strncat(delim2, delim, 1); // create the double-delim
1453 strncpy(delim3, delim2, 2); strncat(delim3, delim, 1); // create the double-delim
1454 if (strstr(str2, delim3)!=NULL) return -1; // Error: ':::' occured!
1456 if ( (d=strstr(str2, delim2))!=NULL ) { // delim2 ('::') found
1457 // Check 3 cases: "::Sat:Sun", "Mon::Sat:Sun", and "Mon:Tue::"
1458 if (strlen(d)>2) { // '::' is not at the end of str2
1459 r=d+2; // r points to beginning of right string
1460 if (strstr(r, delim2)!=NULL) return -1; // Error: '::' occurs more than once!
1461 rtok++; // there is at least one token in the right string
1462 rlen = strlen(r);
1463 for(i=0;i<rlen;i++) if (strncmp(r++,delim,1)==0) rtok++;
1465 else
1466 rlen = 0;
1468 if (rlen<(len-2)) { // '::' is not at the beginning of str2
1469 l=d-1; // l points to end of left string
1470 ltok++;
1471 llen = len - rlen - 2;
1472 for(i=0;i<llen;i++) if (strncmp(l--,delim,1)==0) ltok++;
1474 //printf("ltok=%i, rtok=%i\n",ltok,rtok);
1475 if ((ltok+rtok)>anz) return -1; // More tokens than arguments ('::' here leads to ambiguous mapping)
1477 else
1478 ltok=len+1; // makes subsequent algorithm to ignore exception handling
1482 rtok=anz-rtok;
1483 va_start(ap, anz);
1485 token = strtok_r(str2, delim, &saveptr);
1486 if (token==NULL) { va_end(ap); return n; }
1488 for(i=0; i<anz; i++) {
1489 arg = va_arg(ap, char *);
1490 if ( (token==NULL) || // less tokens than arguments => assign NULL to the remaining arguments!
1491 ((i>=ltok) && (i<rtok))) {
1492 arg[0] = 0x00;
1494 else { // we still have tokens...
1495 n++;
1496 strncpy(arg, token, 64);
1497 token = strtok_r(NULL, delim, &saveptr);
1501 va_end(ap);
1502 return n;
1511 // PURPOSE: Simplify reading of user delay specifications.
1512 // Parse 'a' and 'b' and configure a struct timespec, i. e. seconds and nanoseconds.
1514 // Typically 'a' contains only the value and 'b' the unit.
1515 // But if b==NULL then 'a' may also contain the unit such as "100msec"
1517 // Allowed units are: nsec, usec, sec, min, hour
1519 // NOTE: If no unit is given then assume msec as default unit
1521 // RETURN VALUE: 0 upon success, 1 upon error (bad arguments)
1523 int delay_parse (struct timespec *t, char *a, char *b)
1525 int i;
1526 unsigned int sfactor=0, nfactor=1000000; // assume msec as default unit
1527 unsigned long long delay, sdelay, ndelay;
1529 if (b==NULL) { // only one argument, but may contain an unit (such as '314sec')
1530 if (strstr(a, "msec")) {
1531 nfactor=1000000;
1533 else if (strstr(a, "usec")) {
1534 nfactor=1000;
1536 else if (strstr(a, "nsec")) {
1537 nfactor=1;
1539 else if (strstr(a, "sec")) { // NOTE: This must be the last check since 'sec' is included in previous strings
1540 sfactor=1;
1541 nfactor=0;
1543 else if (strstr(a, "min")) {
1544 sfactor=60;
1545 nfactor=0;
1547 else if (strstr(a, "hour")) {
1548 sfactor=3600;
1549 nfactor=0;
1551 else { // Unit not found; check for non-digits!
1552 // NOTE: we do not currently catch wrong strings that contain sec, usec, or msec.
1554 for (i=0; i<strlen(a); i++) {
1555 if (!isdigit(a[i])) return 1; // invalid unit
1557 nfactor=1000000; // no unit given => assume msec
1559 } else { // caller specified two arguments
1560 if (mz_strcmp(b,"nsec", 1)==0)
1561 nfactor=1;
1562 else if (mz_strcmp(b,"usec", 1)==0)
1563 nfactor=1000;
1564 else if (mz_strcmp(b,"msec", 1)==0)
1565 nfactor=1000000;
1566 else if (mz_strcmp(b,"sec", 1)==0) {
1567 sfactor=1;
1568 nfactor=0;
1570 else if (mz_strcmp(b,"min", 1)==0) {
1571 sfactor=60;
1572 nfactor=0;
1574 else if (mz_strcmp(b,"hour", 1)==0) {
1575 sfactor=3600;
1576 nfactor=0;
1578 else return 1; // Invalid unit
1581 // Get user-defined actual value:
1582 delay = strtoull(a, (char **)NULL, 10);
1583 if ((errno==ERANGE) || (delay>999999999L)) { // see man 2 nanosleep
1584 return 2; // Value too large! Supported range is from 0 to 999999999
1587 sdelay = delay * sfactor;
1588 ndelay = delay * nfactor;
1590 if (ndelay>999999999L) {
1591 sdelay = ndelay/1000000000L;
1592 ndelay = ndelay - (sdelay*1000000000L);
1595 t->tv_sec = sdelay;
1596 t->tv_nsec = ndelay;
1597 return 0;