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
21 ////////////////////////////////////////////////////////////////////////////////////////////
23 // Contains various tools to ease development of new modules:
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
55 ////////////////////////////////////////////////////////////////////////////////////////////
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.
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
;
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
);
91 if ( (subtoken
= strtok_r(str2
, " =", &saveptr2
))!=NULL
)
93 if (strcasecmp(subtoken
,arg_name
)==0)
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);
103 strcpy(arg_value
,subtoken
);
115 // Convert str to (unsigned long) int
116 // Return value: the unsigned long int
117 unsigned long int str2int(char *str
)
123 i
= strtoul(str
, (char **)NULL
, 10);
125 if ((errno
== ERANGE
&& (i
== ULONG_MAX
))
126 || (errno
!= 0 && i
== 0))
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
;
144 i
= strtoull(str
, (char **)NULL
, 10);
146 if ((errno
== ERANGE
&& (i
== ULLONG_MAX
))
147 || (errno
!= 0 && i
== 0))
156 // Convert hex-str to (unsigned long) int
157 // Return value: the unsigned long int
158 unsigned long int xstr2int(char *str
)
164 i
= strtoul(str
, (char **)NULL
, 16);
166 if ((errno
== ERANGE
&& (i
== ULONG_MAX
))
167 || (errno
!= 0 && i
== 0))
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
;
185 i
= strtoull(str
, (char **)NULL
, 16);
187 if ((errno
== ERANGE
&& (i
== ULLONG_MAX
))
188 || (errno
!= 0 && i
== 0))
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
;
207 fprintf(stderr
, "Invalid IPv6 masklen: %u\n", masklen
);
210 masklen
= 128 - masklen
;
214 for (i
= 3; i
>= 0; i
--, masklen
-= 32) {
216 bcast
.__u6_addr
.__u6_addr32
[i
] = htonl(ntohl(bcast
.__u6_addr
.__u6_addr32
[i
]) | ((uint32_t) (~0) >> (32 - masklen
)));
219 bcast
.__u6_addr
.__u6_addr32
[i
] = (uint32_t) (~0);
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
;
236 for (i
= 0; i
< 4; i
++, p1
++, p2
++) {
237 val
= CMP_INT(ntohl(*p1
), ntohl(*p2
));
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;
256 addr
= dst
->__u6_addr
.__u6_addr8
;
258 addr
[i
- 1] += carry
;
259 if (addr
[i
- 1] > 0xff || !addr
[i
- 1]) {
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.
275 in6_range_too_big(struct libnet_in6_addr start
, struct libnet_in6_addr stop
)
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
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
)
310 found_slash
=0, found_dash
=0;
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
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 "-" !!!
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
)
346 tx
.ip_dst_isrange
= 1;
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
);
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;
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
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
)
408 found_slash
=0, found_dash
=0;
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
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 "-" !!!
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
)
445 tx
.ip_src_isrange
= 1;
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
);
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;
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
)
484 if (in6_range_too_big(start
, stop
)) {
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
) {
495 int get_ip6_range_src (char *arg
, libnet_t
*l
)
500 found_slash
=0, found_dash
=0;
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
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 "-" !!!
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)
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");
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");
569 return 1; // ERROR: The specified argument string is not a range!
572 int get_ip6_range_dst (char *arg
, libnet_t
*l
)
577 found_slash
=0, found_dash
=0;
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
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 "-" !!!
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)
614 tx
.ip_dst_isrange
= 1;
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;
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.
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
)
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
;
676 return 1; // wrong argument
680 // Did the user really specify a dst-address?
681 if (strnlen(eth_mac_txt
, 18)==0)
687 // Okay, lets check the commandline argument:
689 // Do you want a random MAC?
690 if (strncmp(eth_mac_txt
, "rand", 4)==0)
695 // Do you want your own interface MAC?
696 else if (strncmp(eth_mac_txt
, "own", 3)==0)
700 eth_mac
[i
] = tx
.eth_mac_own
[i
];
703 // Do you want a broadcast MAC?
704 else if (strncmp(eth_mac_txt
, "bc", 2)==0) // NOTE that this also fetches "bcast"
706 str2hex_mac("FF:FF:FF:FF:FF:FF", eth_mac
);
708 // Do you want the IEEE address 'all bridges' used for STP?
709 else if (strncmp(eth_mac_txt
, "stp", 3)==0) //
711 str2hex_mac("01:80:C2:00:00:00", eth_mac
); // IEEE for all bridges
713 // Do you want the Cisco address e. g. for CDP, VTP?
714 else if (strncmp(eth_mac_txt
, "cisco", 5)==0)
716 str2hex_mac("01:00:0C:CC:CC:CC", eth_mac
);
718 // Do you want the Cisco address e. g. for CDP, VTP?
719 else if (strncmp(eth_mac_txt
, "pvst", 5)==0)
721 str2hex_mac("01:00:0C:CC:CC:CD", eth_mac
);
723 // The string MUST contain a mac address
724 // TODO: CHECK whether the string has correct format for a mac address!
727 str2hex_mac(eth_mac_txt
, eth_mac
);
737 // Scans argument for a port number or range and sets
738 // the corresponding values in the tx struct:
740 // a) tx.sp_start, tx.sp_stop, tx.sp = tx.sp_start
744 // b) tx.dp_start, tx.dp_stop, tx.dp = tx.dp_start
748 // - 'sp_or_dp' is either SRC_PORT or DST_PORT
749 // - 'arg' contains the port range as string such as 1-1024
751 // Return value: 0 on success, 1 upon failure
753 int get_port_range (int sp_or_dp
, char *arg
)
756 int i
, len
, found_dash
=0;
758 u_int32_t tmp1
, tmp2
;
767 char *start_str
, *stop_str
;
770 // Check which port to manage
771 if (sp_or_dp
== DST_PORT
)
774 start
= &tx
.dp_start
;
776 isrange
= &tx
.dp_isrange
;
778 else if (sp_or_dp
== SRC_PORT
)
781 start
= &tx
.sp_start
;
783 isrange
= &tx
.sp_isrange
;
791 len
= strnlen(arg
,12);
792 if (len
==0) return 1; // error
795 for (i
=0; i
<len
; i
++)
797 if (arg
[i
]=='-') found_dash
=1;
800 if (found_dash
) // range specified
802 start_str
= strtok (arg
, "-");
803 stop_str
= strtok (NULL
, "-");
805 tmp1
= str2int (start_str
);
806 if ( (tmp1
<0)||(tmp1
>65535))
808 fprintf(stderr
," mz/get_port_range: Invalid port range!\n");
813 tmp2
= str2int (stop_str
);
814 if ( (tmp2
<0)||(tmp2
>65535))
816 fprintf(stderr
," mz/get_port_range: Invalid port range!\n");
821 if (tmp1
>tmp2
) // swap start/stop values!
832 else // single port number
834 tmp1
= str2int (arg
);
835 if ( (tmp1
<0)||(tmp1
>65535)) tmp1
=0;
847 // Scans argument for TCP flags and sets
848 // tx.tcp_control accordingly.
850 // Valid keywords are: fin, syn, rst, psh, ack, urg, ecn, cwr
851 // Valid delimiters are: | or + or -
852 // Return value: 0 on success, 1 upon failure
854 int get_tcp_flags (char* flags
)
858 // From LSB to MSB: fin, syn, reset, push, ack, urg, ecn, cwr
859 // ecn...ECN-Echo, cwr...Congestion Window Reduced
861 if (strnlen(flags
,40)==0) return 1; // error
864 f
= strtok (flags
, "|+-");
867 if (strncmp(f
,"fin",3)==0)
869 tx
.tcp_control
= tx
.tcp_control
| 1;
871 else if (strncmp(f
,"syn",3)==0)
873 tx
.tcp_control
= tx
.tcp_control
| 2;
875 else if (strncmp(f
,"rst",3)==0)
877 tx
.tcp_control
= tx
.tcp_control
| 4;
879 else if (strncmp(f
,"psh",3)==0)
881 tx
.tcp_control
= tx
.tcp_control
| 8;
883 else if (strncmp(f
,"ack",3)==0)
885 tx
.tcp_control
= tx
.tcp_control
| 16;
887 else if (strncmp(f
,"urg",3)==0)
889 tx
.tcp_control
= tx
.tcp_control
| 32;
891 else if (strncmp(f
,"ecn",3)==0)
893 tx
.tcp_control
= tx
.tcp_control
| 64;
895 else if (strncmp(f
,"cwr",3)==0)
897 tx
.tcp_control
= tx
.tcp_control
| 128;
900 } while ( (f
=strtok(NULL
, "|+-")) != NULL
);
907 // Scans string 'params' for MPLS parameters
908 // and sets tx.mpls_* accordingly.
910 // CLI Syntax Examples:
912 // -M help .... shows syntax
914 // -M 800 .... label=800
915 // -M 800:S .... label=800 and BOS flag set
916 // -M 800:S:64 .... label=800, BOS, TTL=64
917 // -M 800:64:S .... same
918 // -M 64:77 .... label=64, TTL=77
919 // -M 64:800 .... INVALID
920 // -M 800:64 .... label=800, TTL=64
921 // -M 800:3:S:64 .... additionally the experimental bits are set (all fields required!)
923 // Note: S = BOS(1), s = NOT-BOS(0)
925 // Valid delimiters: :-.,+
926 // Return value: 0 on success, 1 upon failure
927 int get_mpls_params(char *p
)
930 char *f1
, *f2
, *f3
, *f4
;
936 strncpy(params
, p
, 256);
939 if (strncmp(params
,"help",4)==0)
944 "| MPLS header Syntax: -M label[,label[,label[,...]]]\n"
945 "| where each header may consist of the following parameters:\n"
947 "| label ... the MPLS label (mandatory, 0..1048575)\n"
948 "| exp ..... experimental/CoS (default: 0, allowed values: 0..7)\n"
949 "| TTL ..... Time To Live (default: 255)\n"
950 "| BOS ..... marks bottom-of-stack; per default the last (most inner) header\n"
951 "| will have BOS=1. If desired you can set this flag for any header\n"
952 "| inbetween but this will lead to an invalid packet. Simply use\n"
953 "| 'S' to set BOS=1, or 's' to set BOS=0. Note that this flag MUST be\n"
954 "| the LAST argument.\n"
958 "| -M 800 .... label=800\n"
959 "| -M 800:6 .... label=800 and CoS=6\n"
960 "| -M 800:6:55 .... label=800, CoS=6, TTL=55\n"
961 "| -M 800:S .... label=800 and BOS=1\n"
962 "| -M 800:6:s .... label=800, CoS=6, and BOS=0\n"
964 "| multiple headers:\n"
966 "| -m 30,20:7,800:5:128 ... outer label=800 with CoS=5 and TTL=128,\n"
967 "| middle label=20 with CoS=7,\n"
968 "| inner label=30 (this one is closest to L3).\n"
970 "| Valid delimiters inside a header: : - . +\n"
978 if ( (f1
= strtok (params
, ":-.+")) == NULL
)
983 tx
.mpls_label
= (u_int32_t
) str2int (f1
);
984 if (tx
.mpls_label
>1048575)
986 tx
.mpls_label
= 1048575; // 2^20
987 fprintf(stderr
," Warning: MPLS label too big! Reduced to maximum allowed value.\n");
992 if ( (f2
= strtok (NULL
, ":-.+")) != NULL
) // 2nd param set
994 if (strncmp(f2
,"S",1)==0)
999 else if (strncmp(f2
,"s",1)==0)
1006 tx
.mpls_exp
= (u_int8_t
) str2int (f2
);
1007 if (tx
.mpls_exp
> 7)
1010 fprintf(stderr
," Warning: MPLS CoS too big! Reduced to maximum allowed value.\n");
1015 if ( (f3
= strtok (NULL
, ":-.+")) != NULL
) // 3rd param set
1017 if (strncmp(f3
,"S",1)==0)
1022 else if (strncmp(f3
,"s",1)==0)
1029 if ((u_int16_t
) str2int (f3
)>255)
1031 fprintf(stderr
," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n");
1036 tx
.mpls_ttl
= (u_int8_t
) str2int (f3
);
1040 if ( (f4
= strtok (NULL
, ":-.+")) != NULL
) // 4th param set
1043 if (strncmp(f3
,"S",1)==0)
1047 else if (strncmp(f3
,"s",1)==0)
1062 // Parses str for occurence of character or sequence ch.
1063 // Returns number of occurences
1064 int exists(char* str
, char* ch
)
1068 size_t len_str
, len_ch
;
1070 len_str
= strlen(str
);
1071 len_ch
= strlen(ch
);
1074 for (i
=0; i
<len_str
; i
++)
1076 if (strcmp(str
++,ch
)==0) match
++;
1084 // Checks if str consists only of 0 and 1
1088 // 0 if invalid chars found or str empty
1089 // n if str consists exactly of n binary digits
1090 int mz_strisbinary(char *str
)
1095 if (len
==0) return 0;
1097 for (i
=0; i
<len
; i
++) {
1098 if ((str
[i
]=='0') || (str
[i
]=='1')) {
1111 // Converts a string containing (max 8) binary digits into a number
1114 // Either the number on success
1115 // Or -1 upon failure
1117 int str2bin8 (char *str
)
1121 n
=mz_strisbinary(str
);
1123 if ((!n
) || (n
>8)) return -1;
1125 for (i
=0; i
<n
; i
++) if (str
[i
]=='1') ret
|= ( 0x01 << (n
-1-i
) );
1132 // Converts a string containing (max 16) binary digits into a number
1135 // Either the number on success
1136 // Or -1 upon failure
1138 long int str2bin16 (char *str
)
1143 n
=mz_strisbinary(str
);
1145 if ((!n
) || (n
>16)) return -1;
1147 for (i
=0; i
<n
; i
++) if (str
[i
]=='1') ret
|= ( 0x01 << (n
-1-i
) ); // C is great ;-)
1153 // Converts a char into a string containing ones and zeros
1157 // char c = 0x81; char str[16];
1158 // char2bits(c, str);
1159 // printf("%s\n",str); => "1 0 0 0 0 0 0 1"
1161 int char2bits (char c
, char *str
)
1164 char tmp
[]="0 0 0 0 0 0 0 0";
1168 if (c
&j
) tmp
[14-i
*2]='1';
1172 strncpy(str
, tmp
, 15);
1177 // Takes filename and prepends valid configuration directory
1179 // 1) prefer configurable mz_default_config_path[]
1180 // 2) otherwise use MZ_DEFAULT_CONFIG_PATH
1182 // NOTE: 'filename' finally holds the full path
1183 // and must therefore be big enough
1190 int getfullpath_cfg (char *filename
)
1195 lenf
= strnlen(filename
, 32);
1197 // filename not given?
1198 if ((lenf
==0) || (lenf
==32)) return 1;
1200 strncpy(tmp
, filename
, 32);
1202 // Prefer user-defined path if provided:
1203 lenp
= strnlen(mz_default_config_path
,255);
1206 if (strncmp(mz_default_config_path
+lenp
-1, "/",1))
1207 strncat(mz_default_config_path
, "/",1);
1208 snprintf(filename
, 255, "%s%s",mz_default_config_path
,tmp
);
1211 lenp
= strlen(MZ_DEFAULT_CONFIG_PATH
);
1212 snprintf(filename
, 255, "%s%s",MZ_DEFAULT_CONFIG_PATH
,tmp
);
1215 if ((lenf
+lenp
)>255) return 1;
1222 // Takes filename and prepends valid logging directory
1224 // 1) prefer configurable mz_default_log_path[]
1225 // 2) otherwise use MZ_DEFAULT_LOG_PATH
1227 // NOTE: filename is overwritten and must be big enough to hold full path!
1229 int getfullpath_log (char *filename
)
1234 lenf
= strnlen(filename
, 32);
1236 // filename not given?
1237 if ((lenf
==0) || (lenf
==32)) return 1;
1239 strncpy(tmp
, filename
, 32);
1241 // Prefer user-defined path if provided:
1242 lenp
= strnlen(mz_default_log_path
,255);
1244 if (strncmp(mz_default_log_path
+lenp
-1, "/",1))
1245 strncat(mz_default_log_path
, "/",1);
1246 snprintf(filename
, 255, "%s%s",mz_default_log_path
,tmp
);
1249 lenp
= strlen(MZ_DEFAULT_LOG_PATH
);
1250 snprintf(filename
, 255, "%s%s",MZ_DEFAULT_LOG_PATH
,tmp
);
1253 if ((lenf
+lenp
)>255) return 1;
1258 // Behaves much like strncpy but additionally ensures
1259 // that dest is always \0-terminated.
1261 // USAGE NOTE: If you know exactly the length n of your string,
1262 // then you must provide n+1 to support the termination character.
1264 // EXAMPLE: src="Hello", n=strlen(src)=5, and mz_strncpy(dest, src, n)
1265 // would result in dest={H,e,l,l,\0}.
1266 // Therefore the correct usage is:
1267 // mz_strncpy(dest, src, strlen(src)+1);
1270 // RETURN VALUE: pointer to dest
1271 char * mz_strncpy(char *dest
, const char *src
, size_t n
)
1274 tmp
= strncpy(dest
, src
, n
);
1282 // Helper function to count the number of arguments
1283 // in the Mausezahn argument string (comma separated args)
1285 // RETURN VALUE: Number of arguments
1287 // TODO: Improve it. Use strtok.
1289 int number_of_args (char *str
)
1291 int len
=0, i
=0, commas
=1;
1292 if ((len
=strnlen(str
,MAX_PAYLOAD_SIZE
))<2) return 0; // no valid argument
1293 for (i
=0; i
<len
; i
++) if (str
[i
]==',') commas
++;
1294 if (str
[len
-1]==',') commas
--; // comma at the end!
1300 // Checks if str consists only of digits 0..9
1304 // 0 if invalid chars found or str empty
1305 // n if str consists exactly of n digits
1306 int mz_strisnum(char *str
)
1311 if (len
==0) return 0;
1313 for (i
=0; i
<len
; i
++) {
1314 if (isdigit(str
[i
])) {
1324 // Checks if str consists only of hex digits 0..9 and a..f
1328 // 0 if invalid chars found or str empty
1329 // n if str consists exactly of n digits
1330 int mz_strishex(char *str
)
1335 if (len
==0) return 0;
1337 for (i
=0; i
<len
; i
++) {
1338 if (isxdigit(str
[i
])) {
1348 // Returns an 4-byte random number
1350 u_int32_t
mz_rand32 (void)
1352 static unsigned int r
=0;
1363 // Compares user-provided string with a specified string.
1367 // 0 if at least min characters match
1368 // 1 if at least one character of usr does NOT match the corresponding character in str.
1370 // Note: Case-insensitive!
1371 // Goal: Should be more practical and secure than strcmp (and related)
1372 int mz_strcmp(char* usr_orig
, char* str_orig
, int min
)
1374 int i
, same
=0, usrlen
, max
;
1375 char usr
[80], str
[80];
1377 usrlen
= strlen(usr_orig
);
1378 max
= strlen(str_orig
);
1380 strncpy(usr
, usr_orig
, 80);
1381 strncpy(str
, str_orig
, 80);
1383 // User provided not enough or too many chars
1384 if ((usrlen
<min
) || (usrlen
>max
)) return 1;
1386 // now check how many bytes really match
1387 for (i
=0; i
<usrlen
; i
++) {
1388 if (strncasecmp(&usr
[i
], &str
[i
], 1)==0) {
1393 if (same
<usrlen
) return 1;
1404 // Maps an arbitrary number of tokens from 'str' which are separated by
1405 // a character 'delim' into provided arguments.
1409 // char str[]="Am:Dam:Des";
1410 // char t1[64], t2[64], t3[64], t4[64];
1412 // mz_tok (str, ":", 4, t1, t2, t3, t4)
1414 // => t1="Am", t2="Dam", t3="Des", t4=NULL
1418 // 1. If the delimiter symbol occurs twice without gap, it is interpreted
1419 // as 'fill-up' command. To avoid ambiguities this may only occur once.
1420 // See the IPv6 address format shortcuts as similar example.
1422 // 2. If there are less tokens than allowed, the arguments are filled up
1423 // in order, while the remaining are casted to NULL:
1425 // 3. str must be smaller than 4096 bytes!
1427 // 4. To mitigate buffer overflow problems, the maximum token size is
1428 // currently limited to 64 bytes. Therefore it is recommended to
1429 // allocate 64 bytes for each argument.
1431 // RETURN VALUE: Number of returned tokens or -1 upon error
1433 int mz_tok(char * str
, char * delim
, int anz
, ...)
1437 int i
=0, n
=0, len
, llen
, rlen
, ltok
=0, rtok
=0;
1438 char *d
, *l
, *r
, *token
, *saveptr
, *arg
;
1439 char str2
[4096], delim2
[4]="", delim3
[4]="";;
1441 if (strlen(delim
)!=1) return -1; // delim must contain a single character!
1442 strncpy(str2
, str
, 4095); // protect the original str from strtok => operate on a copy only
1445 // Check if some tokens are omitted (::)
1446 strncpy(delim2
, delim
, 1); strncat(delim2
, delim
, 1); // create the double-delim
1447 strncpy(delim3
, delim2
, 2); strncat(delim3
, delim
, 1); // create the double-delim
1448 if (strstr(str2
, delim3
)!=NULL
) return -1; // Error: ':::' occured!
1450 if ( (d
=strstr(str2
, delim2
))!=NULL
) { // delim2 ('::') found
1451 // Check 3 cases: "::Sat:Sun", "Mon::Sat:Sun", and "Mon:Tue::"
1452 if (strlen(d
)>2) { // '::' is not at the end of str2
1453 r
=d
+2; // r points to beginning of right string
1454 if (strstr(r
, delim2
)!=NULL
) return -1; // Error: '::' occurs more than once!
1455 rtok
++; // there is at least one token in the right string
1457 for(i
=0;i
<rlen
;i
++) if (strncmp(r
++,delim
,1)==0) rtok
++;
1462 if (rlen
<(len
-2)) { // '::' is not at the beginning of str2
1463 l
=d
-1; // l points to end of left string
1465 llen
= len
- rlen
- 2;
1466 for(i
=0;i
<llen
;i
++) if (strncmp(l
--,delim
,1)==0) ltok
++;
1468 //printf("ltok=%i, rtok=%i\n",ltok,rtok);
1469 if ((ltok
+rtok
)>anz
) return -1; // More tokens than arguments ('::' here leads to ambiguous mapping)
1472 ltok
=len
+1; // makes subsequent algorithm to ignore exception handling
1479 token
= strtok_r(str2
, delim
, &saveptr
);
1480 if (token
==NULL
) { va_end(ap
); return n
; }
1482 for(i
=0; i
<anz
; i
++) {
1483 arg
= va_arg(ap
, char *);
1484 if ( (token
==NULL
) || // less tokens than arguments => assign NULL to the remaining arguments!
1485 ((i
>=ltok
) && (i
<rtok
))) {
1488 else { // we still have tokens...
1490 strncpy(arg
, token
, 64);
1491 token
= strtok_r(NULL
, delim
, &saveptr
);
1505 // PURPOSE: Simplify reading of user delay specifications.
1506 // Parse 'a' and 'b' and configure a struct timespec, i. e. seconds and nanoseconds.
1508 // Typically 'a' contains only the value and 'b' the unit.
1509 // But if b==NULL then 'a' may also contain the unit such as "100msec"
1511 // Allowed units are: nsec, usec, sec, min, hour
1513 // NOTE: If no unit is given then assume msec as default unit
1515 // RETURN VALUE: 0 upon success, 1 upon error (bad arguments)
1517 int delay_parse (struct timespec
*t
, char *a
, char *b
)
1520 unsigned int sfactor
=0, nfactor
=1000000; // assume msec as default unit
1521 unsigned long long delay
, sdelay
, ndelay
;
1523 if (b
==NULL
) { // only one argument, but may contain an unit (such as '314sec')
1524 if (strstr(a
, "msec")) {
1527 else if (strstr(a
, "usec")) {
1530 else if (strstr(a
, "nsec")) {
1533 else if (strstr(a
, "sec")) { // NOTE: This must be the last check since 'sec' is included in previous strings
1537 else if (strstr(a
, "min")) {
1541 else if (strstr(a
, "hour")) {
1545 else { // Unit not found; check for non-digits!
1546 // NOTE: we do not currently catch wrong strings that contain sec, usec, or msec.
1548 for (i
=0; i
<strlen(a
); i
++) {
1549 if (!isdigit(a
[i
])) return 1; // invalid unit
1551 nfactor
=1000000; // no unit given => assume msec
1553 } else { // caller specified two arguments
1554 if (mz_strcmp(b
,"nsec", 1)==0)
1556 else if (mz_strcmp(b
,"usec", 1)==0)
1558 else if (mz_strcmp(b
,"msec", 1)==0)
1560 else if (mz_strcmp(b
,"sec", 1)==0) {
1564 else if (mz_strcmp(b
,"min", 1)==0) {
1568 else if (mz_strcmp(b
,"hour", 1)==0) {
1572 else return 1; // Invalid unit
1575 // Get user-defined actual value:
1576 delay
= strtoull(a
, (char **)NULL
, 10);
1577 if ((errno
==ERANGE
) || (delay
>999999999L)) { // see man 2 nanosleep
1578 return 2; // Value too large! Supported range is from 0 to 999999999
1581 sdelay
= delay
* sfactor
;
1582 ndelay
= delay
* nfactor
;
1584 if (ndelay
>999999999L) {
1585 sdelay
= ndelay
/1000000000L;
1586 ndelay
= ndelay
- (sdelay
*1000000000L);
1590 t
->tv_nsec
= ndelay
;