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 // check_eth_mac_txt ... Scans tx.eth_dst|src_txt and sets tx.eth_dst|src appropriately
33 // get_port_range ...... Parses string for a dst|src-port range and sets start/stop values
34 // get_tcp_flags ....... Parses string for TCP arguments and sets tx.tcp_control
35 // get_mpls_params ..... Parses string for MPLS parameters (label, exp, BOS, TTL)
36 // exists .............. Parses a string for a single character and returns "1" if found
37 // mz_strisbinary ...... Checks whether string consists only of 0 and 1, returns how many digits total
38 // str2bin8 ............ Converts a string containing max 8 binary digits into a number
39 // str2bin16 ........... Converts a string containing max 16 binary digits into a number
40 // char2bits ........... Converts a char into a string containing ones and zeros
41 // getfullpath_cfg ..... Creates a full filename with path to the desired config directory
42 // getfullpath_log ..... Creates a full filename with path to the desired logging directory
43 // mz_strncpy .......... A safer implementation of strncpy
44 // number_of_args ...... Returns number of arguments of the Mausezahn argument string
45 // mz_strisnum ......... Returns 1 if string only consists of decimal digits
46 // mz_strishex ......... Returns 1 if string only consists of hexadecimal digits
47 // mz_strcmp ........... Matches a string or a prefix of it with given min-length
48 // Example usage: User CLI input
49 // mz_tok .............. Decomposes a string into tokens and maps them to args
50 // Example usage: IPv6-addresses, user input for MPLS-tags
51 // delay_parse ......... Parses one or two strings for a delay specification and sets a struct timespec
52 // create_random_mac ... Creates a random unicast Ethernet MAC address
54 ////////////////////////////////////////////////////////////////////////////////////////////
60 // Scan 'str' for an argument 'arg_name' and returns its value in arg_value
61 // Return value: number of occurences of arg_name
62 // Note that if arg_name occurs multiple times, the last found value is returned.
63 // If last argument (arg_value) is set to NULL it will be ignored.
67 // i = getarg ("request, da=10.1.1.2, SYN", "da", ip);
68 // ...will assign "10.1.1.2" to ip and the occurence i is set to 1.
69 int getarg(char *str
, char *arg_name
, char *arg_value
)
71 char tmp
[MAX_PAYLOAD_SIZE
];
72 char *str1
, *str2
, *token
, *subtoken
;
73 char *saveptr1
, *saveptr2
;
76 strncpy(tmp
,str
,MAX_PAYLOAD_SIZE
); // only operate on local strings
78 for (j
= 1, str1
= tmp
; ; j
++, str1
= NULL
)
81 token
= strtok_r(str1
, ",", &saveptr1
);
86 if ( (subtoken
= strtok_r(str2
, " =", &saveptr2
))!=NULL
)
88 if (strcasecmp(subtoken
,arg_name
)==0)
91 //printf("found %s\n",arg_name);
92 if ( (subtoken
= strtok_r(NULL
, " =", &saveptr2
))!=NULL
)
94 // argument has a value!
95 //printf("%s has value: [%s]\n",arg_name, subtoken);
98 strcpy(arg_value
,subtoken
);
110 // Convert str to (unsigned long) int
111 // Return value: the unsigned long int
112 unsigned long int str2int(char *str
)
118 i
= strtoul(str
, (char **)NULL
, 10);
120 if ((errno
== ERANGE
&& (i
== ULONG_MAX
))
121 || (errno
!= 0 && i
== 0))
131 // Convert str to (unsigned long long) int
132 // Return value: the unsigned long long int
133 unsigned long long int str2lint(char *str
)
135 unsigned long long int i
;
139 i
= strtoull(str
, (char **)NULL
, 10);
141 if ((errno
== ERANGE
&& (i
== ULLONG_MAX
))
142 || (errno
!= 0 && i
== 0))
151 // Convert hex-str to (unsigned long) int
152 // Return value: the unsigned long int
153 unsigned long int xstr2int(char *str
)
159 i
= strtoul(str
, (char **)NULL
, 16);
161 if ((errno
== ERANGE
&& (i
== ULONG_MAX
))
162 || (errno
!= 0 && i
== 0))
172 // Convert hex-str to (unsigned long long) int
173 // Return value: the unsigned long long int
174 unsigned long long int xstr2lint(char *str
)
176 unsigned long long int i
;
180 i
= strtoull(str
, (char **)NULL
, 16);
182 if ((errno
== ERANGE
&& (i
== ULLONG_MAX
))
183 || (errno
!= 0 && i
== 0))
194 // Parses string 'arg' for an IP range and finds start and stop IP addresses.
195 // Return value: 0 upon success, 1 upon failure.
197 // NOTE: The results are written in the following variables:
199 // (u_int32_t) tx.ip_dst_start ... contains start value
200 // (u_int32_t) tx.ip_dst_stop ... contains stop value
201 // (u_int32_t) tx.ip_dst ... initialized with start value
202 // int tx.ip_dst_isrange ... set to 1 if above values valid
204 // Possible range specifications:
206 // 1) 192.168.0.0-192.168.0.12
207 // 2) 10.2.11.0-10.55.13.2
212 // FIRST detect a range by scanning for the "-" OR "/" chars
213 // THEN determine start and stop value and store them as normal unsigned integers
215 int get_ip_range_dst (char *arg
)
220 found_slash
=0, found_dash
=0;
223 u_int32_t mask
, invmask
;
225 char *start_str
, *stop_str
;
227 len
= strnlen(arg
, 32);
229 if ( (len
>31) || (len
<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars)
230 return 1; // ERROR: no range
233 for (i
=0; i
<len
; i
++)
235 if (arg
[i
]=='/') found_slash
=1;
236 if (arg
[i
]=='-') found_dash
=1;
239 if ((found_slash
) && (found_dash
))
240 exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!!
244 start_str
= strtok (arg
, "-");
245 stop_str
= strtok (NULL
, "-");
247 // These are the start and stop IP addresses of the range:
248 tx
.ip_dst_start
= str2ip32 (start_str
);
249 tx
.ip_dst_stop
= str2ip32 (stop_str
);
250 tx
.ip_dst_h
= tx
.ip_dst_start
;
251 tx
.ip_dst
= str2ip32_rev (start_str
);
253 if (tx
.ip_dst_start
< tx
.ip_dst_stop
)
256 tx
.ip_dst_isrange
= 1;
261 tx
.ip_dst_isrange
= 0;
262 return 1; // ERROR: stop value must be greater than start value !!!
265 else if (found_slash
)
267 start_str
= strtok (arg
, "/");
268 stop_str
= strtok (NULL
, "/"); // Actually contains the prefix length, e. g. "24"
270 q
= (unsigned int) str2int (stop_str
);
274 invmask
= 0xffffffff - mask
;
276 tx
.ip_dst_start
= (str2ip32 (start_str
) & mask
) +1; // the '+1' is to ensure that we do not start with the net-id
277 tx
.ip_dst_stop
= tx
.ip_dst_start
| invmask
;
278 tx
.ip_dst_h
= tx
.ip_dst_start
;
279 tx
.ip_dst
= str2ip32_rev (start_str
) | 0x01000000; // the '0x01000000' is to ensure that we do not start with the net-id
280 tx
.ip_dst_isrange
= 1;
285 return 1; // ERROR: The specified argument string is not a range!
292 // Parses string 'arg' for an IP range and finds start and stop IP addresses.
293 // Return value: 0 upon success, 1 upon failure.
295 // NOTE: The results are written in the following variables:
297 // (u_int32_t) tx.ip_src_start ... contains start value
298 // (u_int32_t) tx.ip_src_stop ... contains stop value
299 // (u_int32_t) tx.ip_src ... initialized with start value
300 // int tx.ip_src_isrange ... set to 1 if above values valid
302 // Possible range specifications:
304 // 1) 192.168.0.0-192.168.0.12
305 // 2) 10.2.11.0-10.55.13.2
310 // FIRST detect a range by scanning for the "-" OR "/" chars
311 // THEN determine start and stop value and store them as normal unsigned integers
313 int get_ip_range_src (char *arg
)
318 found_slash
=0, found_dash
=0;
321 u_int32_t mask
, invmask
;
323 char *start_str
, *stop_str
;
326 len
= strnlen(arg
,32);
328 if ( (len
>31) || (len
<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars)
329 return 1; // ERROR: no range
332 for (i
=0; i
<len
; i
++)
334 if (arg
[i
]=='/') found_slash
=1;
335 if (arg
[i
]=='-') found_dash
=1;
338 if ((found_slash
) && (found_dash
))
339 exit (1); // ERROR: Wrong range string syntax (cannot use both "/" and "-" !!!
343 start_str
= strtok (arg
, "-");
344 stop_str
= strtok (NULL
, "-");
346 // These are the start and stop IP addresses of the range:
347 tx
.ip_src_start
= str2ip32 (start_str
);
348 tx
.ip_src_stop
= str2ip32 (stop_str
);
349 tx
.ip_src_h
= tx
.ip_src_start
;
350 tx
.ip_src
= str2ip32_rev (start_str
);
352 if (tx
.ip_src_start
< tx
.ip_src_stop
)
355 tx
.ip_src_isrange
= 1;
360 tx
.ip_src_isrange
= 0;
361 return 1; // ERROR: stop value must be greater than start value !!!
364 else if (found_slash
)
366 start_str
= strtok (arg
, "/");
367 stop_str
= strtok (NULL
, "/"); // Actually contains the prefix length, e. g. "24"
369 q
= (unsigned int) str2int (stop_str
);
373 invmask
= 0xffffffff - mask
;
375 tx
.ip_src_start
= (str2ip32 (start_str
) & mask
) +1; // the '+1' is to ensure that we do not start with the net-id
376 tx
.ip_src_stop
= tx
.ip_src_start
| invmask
;
377 tx
.ip_src_h
= tx
.ip_src_start
;
378 tx
.ip_src
= str2ip32_rev (start_str
) | 0x01000000; // the '0x01000000' is to ensure that we do not start with the net-id
379 tx
.ip_src_isrange
= 1;
383 return 1; // ERROR: The specified argument string is not a range!
388 // Scans tx.eth_dst_txt or tx.eth_src_txt and sets the corresponding
389 // MAC addresses (tx.eth_dst or tx.eth_src) accordingly.
390 // Argument: What string should be checked, ETH_SRC or ETH_DST.
393 // 0 when a MAC address has been set or
394 // 1 when not set (or wrongly set)
396 // Currently eth_src|dst_txt can be:
397 // 'rand', 'own', 'bc'|'bcast', 'stp', 'pvst', 'cisco',
398 // or a real mac address.
400 // TODO: implement other important MAC addresses
401 int check_eth_mac_txt(int src_or_dst
)
409 if (src_or_dst
== ETH_SRC
)
411 eth_mac_txt
= tx
.eth_src_txt
;
412 eth_mac
= tx
.eth_src
;
413 eth_rand
= &tx
.eth_src_rand
;
415 else if (src_or_dst
== ETH_DST
)
417 eth_mac_txt
= tx
.eth_dst_txt
;
418 eth_mac
= tx
.eth_dst
;
419 eth_rand
= &tx
.eth_dst_rand
;
423 return 1; // wrong argument
427 // Did the user really specify a dst-address?
428 if (strnlen(eth_mac_txt
, 18)==0)
434 // Okay, lets check the commandline argument:
436 // Do you want a random MAC?
437 // TODO: Consider enforcement of unicast addresses
438 if (strncmp(eth_mac_txt
, "rand", 4)==0)
442 eth_mac
[0] = (u_int8_t
) ( ((float) rand()/RAND_MAX
)*256);
446 // Do you want your own interface MAC?
447 else if (strncmp(eth_mac_txt
, "own", 3)==0)
451 eth_mac
[i
] = tx
.eth_mac_own
[i
];
454 // Do you want a broadcast MAC?
455 else if (strncmp(eth_mac_txt
, "bc", 2)==0) // NOTE that this also fetches "bcast"
457 str2hex_mac("FF:FF:FF:FF:FF:FF", eth_mac
);
459 // Do you want the IEEE address 'all bridges' used for STP?
460 else if (strncmp(eth_mac_txt
, "stp", 3)==0) //
462 str2hex_mac("01:80:C2:00:00:00", eth_mac
); // IEEE for all bridges
464 // Do you want the Cisco address e. g. for CDP, VTP?
465 else if (strncmp(eth_mac_txt
, "cisco", 5)==0)
467 str2hex_mac("01:00:0C:CC:CC:CC", eth_mac
);
469 // Do you want the Cisco address e. g. for CDP, VTP?
470 else if (strncmp(eth_mac_txt
, "pvst", 5)==0)
472 str2hex_mac("01:00:0C:CC:CC:CD", eth_mac
);
474 // The string MUST contain a mac address
475 // TODO: CHECK whether the string has correct format for a mac address!
478 str2hex_mac(eth_mac_txt
, eth_mac
);
488 // Scans argument for a port number or range and sets
489 // the corresponding values in the tx struct:
491 // a) tx.sp_start, tx.sp_stop, tx.sp = tx.sp_start
495 // b) tx.dp_start, tx.dp_stop, tx.dp = tx.dp_start
499 // - 'sp_or_dp' is either SRC_PORT or DST_PORT
500 // - 'arg' contains the port range as string such as 1-1024
502 // Return value: 0 on success, 1 upon failure
504 int get_port_range (int sp_or_dp
, char *arg
)
507 int i
, len
, found_dash
=0;
509 u_int32_t tmp1
, tmp2
;
518 char *start_str
, *stop_str
;
521 // Check which port to manage
522 if (sp_or_dp
== DST_PORT
)
525 start
= &tx
.dp_start
;
527 isrange
= &tx
.dp_isrange
;
529 else if (sp_or_dp
== SRC_PORT
)
532 start
= &tx
.sp_start
;
534 isrange
= &tx
.sp_isrange
;
542 len
= strnlen(arg
,12);
543 if (len
==0) return 1; // error
546 for (i
=0; i
<len
; i
++)
548 if (arg
[i
]=='-') found_dash
=1;
551 if (found_dash
) // range specified
553 start_str
= strtok (arg
, "-");
554 stop_str
= strtok (NULL
, "-");
556 tmp1
= str2int (start_str
);
557 if ( (tmp1
<0)||(tmp1
>65535))
559 fprintf(stderr
," mz/get_port_range: Invalid port range!\n");
564 tmp2
= str2int (stop_str
);
565 if ( (tmp2
<0)||(tmp2
>65535))
567 fprintf(stderr
," mz/get_port_range: Invalid port range!\n");
572 if (tmp1
>tmp2
) // swap start/stop values!
583 else // single port number
585 tmp1
= str2int (arg
);
586 if ( (tmp1
<0)||(tmp1
>65535)) tmp1
=0;
598 // Scans argument for TCP flags and sets
599 // tx.tcp_control accordingly.
601 // Valid keywords are: fin, syn, rst, psh, ack, urg, ecn, cwr
602 // Valid delimiters are: | or + or -
603 // Return value: 0 on success, 1 upon failure
605 int get_tcp_flags (char* flags
)
609 // From LSB to MSB: fin, syn, reset, push, ack, urg, ecn, cwr
610 // ecn...ECN-Echo, cwr...Congestion Window Reduced
612 if (strnlen(flags
,40)==0) return 1; // error
615 f
= strtok (flags
, "|+-");
618 if (strncmp(f
,"fin",3)==0)
620 tx
.tcp_control
= tx
.tcp_control
| 1;
622 else if (strncmp(f
,"syn",3)==0)
624 tx
.tcp_control
= tx
.tcp_control
| 2;
626 else if (strncmp(f
,"rst",3)==0)
628 tx
.tcp_control
= tx
.tcp_control
| 4;
630 else if (strncmp(f
,"psh",3)==0)
632 tx
.tcp_control
= tx
.tcp_control
| 8;
634 else if (strncmp(f
,"ack",3)==0)
636 tx
.tcp_control
= tx
.tcp_control
| 16;
638 else if (strncmp(f
,"urg",3)==0)
640 tx
.tcp_control
= tx
.tcp_control
| 32;
642 else if (strncmp(f
,"ecn",3)==0)
644 tx
.tcp_control
= tx
.tcp_control
| 64;
646 else if (strncmp(f
,"cwr",3)==0)
648 tx
.tcp_control
= tx
.tcp_control
| 128;
651 } while ( (f
=strtok(NULL
, "|+-")) != NULL
);
658 // Scans string 'params' for MPLS parameters
659 // and sets tx.mpls_* accordingly.
661 // CLI Syntax Examples:
663 // -M help .... shows syntax
665 // -M 800 .... label=800
666 // -M 800:S .... label=800 and BOS flag set
667 // -M 800:S:64 .... label=800, BOS, TTL=64
668 // -M 800:64:S .... same
669 // -M 64:77 .... label=64, TTL=77
670 // -M 64:800 .... INVALID
671 // -M 800:64 .... label=800, TTL=64
672 // -M 800:3:S:64 .... additionally the experimental bits are set (all fields required!)
674 // Note: S = BOS(1), s = NOT-BOS(0)
676 // Valid delimiters: :-.,+
677 // Return value: 0 on success, 1 upon failure
678 int get_mpls_params(char *p
)
681 char *f1
, *f2
, *f3
, *f4
;
687 strncpy(params
, p
, 256);
689 if (strncmp(params
,"help",4)==0)
694 "| MPLS header Syntax: -M label[,label[,label[,...]]]\n"
695 "| where each header may consist of the following parameters:\n"
697 "| label ... the MPLS label (mandatory, 0..1048575)\n"
698 "| exp ..... experimental/CoS (default: 0, allowed values: 0..7)\n"
699 "| TTL ..... Time To Live (default: 255)\n"
700 "| BOS ..... marks bottom-of-stack; per default the last (most inner) header\n"
701 "| will have BOS=1. If desired you can set this flag for any header\n"
702 "| inbetween but this will lead to an invalid packet. Simply use\n"
703 "| 'S' to set BOS=1, or 's' to set BOS=0. Note that this flag MUST be\n"
704 "| the LAST argument.\n"
708 "| -M 800 .... label=800\n"
709 "| -M 800:6 .... label=800 and CoS=6\n"
710 "| -M 800:6:55 .... label=800, CoS=6, TTL=55\n"
711 "| -M 800:S .... label=800 and BOS=1\n"
712 "| -M 800:6:s .... label=800, CoS=6, and BOS=0\n"
714 "| multiple headers:\n"
716 "| -m 30,20:7,800:5:128 ... outer label=800 with CoS=5 and TTL=128,\n"
717 "| middle label=20 with CoS=7,\n"
718 "| inner label=30 (this one is closest to L3).\n"
720 "| Valid delimiters inside a header: : - . +\n"
728 if ( (f1
= strtok (params
, ":-.+")) == NULL
)
733 tx
.mpls_label
= (u_int32_t
) str2int (f1
);
734 if (tx
.mpls_label
>1048575)
736 tx
.mpls_label
= 1048575; // 2^20
737 fprintf(stderr
," Warning: MPLS label too big! Reduced to maximum allowed value.\n");
742 if ( (f2
= strtok (NULL
, ":-.+")) != NULL
) // 2nd param set
744 if (strncmp(f2
,"S",1)==0)
749 else if (strncmp(f2
,"s",1)==0)
756 tx
.mpls_exp
= (u_int8_t
) str2int (f2
);
760 fprintf(stderr
," Warning: MPLS CoS too big! Reduced to maximum allowed value.\n");
765 if ( (f3
= strtok (NULL
, ":-.+")) != NULL
) // 3rd param set
767 if (strncmp(f3
,"S",1)==0)
772 else if (strncmp(f3
,"s",1)==0)
779 if ((u_int16_t
) str2int (f3
)>255)
781 fprintf(stderr
," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n");
786 tx
.mpls_ttl
= (u_int8_t
) str2int (f3
);
790 if ( (f4
= strtok (NULL
, ":-.+")) != NULL
) // 4th param set
793 if (strncmp(f3
,"S",1)==0)
797 else if (strncmp(f3
,"s",1)==0)
812 // Parses str for occurence of character or sequence ch.
813 // Returns number of occurences
814 int exists(char* str
, char* ch
)
818 size_t len_str
, len_ch
;
820 len_str
= strlen(str
);
824 for (i
=0; i
<len_str
; i
++)
826 if (strcmp(str
++,ch
)==0) match
++;
834 // Checks if str consists only of 0 and 1
838 // 0 if invalid chars found or str empty
839 // n if str consists exactly of n binary digits
840 int mz_strisbinary(char *str
)
845 if (len
==0) return 0;
847 for (i
=0; i
<len
; i
++) {
848 if ((str
[i
]=='0') || (str
[i
]=='1')) {
861 // Converts a string containing (max 8) binary digits into a number
864 // Either the number on success
865 // Or -1 upon failure
867 int str2bin8 (char *str
)
871 n
=mz_strisbinary(str
);
873 if ((!n
) || (n
>8)) return -1;
875 for (i
=0; i
<n
; i
++) if (str
[i
]=='1') ret
|= ( 0x01 << (n
-1-i
) );
882 // Converts a string containing (max 16) binary digits into a number
885 // Either the number on success
886 // Or -1 upon failure
888 long int str2bin16 (char *str
)
893 n
=mz_strisbinary(str
);
895 if ((!n
) || (n
>16)) return -1;
897 for (i
=0; i
<n
; i
++) if (str
[i
]=='1') ret
|= ( 0x01 << (n
-1-i
) ); // C is great ;-)
903 // Converts a char into a string containing ones and zeros
907 // char c = 0x81; char str[16];
908 // char2bits(c, str);
909 // printf("%s\n",str); => "1 0 0 0 0 0 0 1"
911 int char2bits (char c
, char *str
)
914 char tmp
[]="0 0 0 0 0 0 0 0";
918 if (c
&j
) tmp
[14-i
*2]='1';
922 strncpy(str
, tmp
, 15);
927 // Takes filename and prepends valid configuration directory
929 // 1) prefer configurable mz_default_config_path[]
930 // 2) otherwise use MZ_DEFAULT_CONFIG_PATH
932 // NOTE: 'filename' finally holds the full path
933 // and must therefore be big enough
940 int getfullpath_cfg (char *filename
)
945 lenf
= strnlen(filename
, 32);
947 // filename not given?
948 if ((lenf
==0) || (lenf
==32)) return 1;
950 strncpy(tmp
, filename
, 32);
952 // Prefer user-defined path if provided:
953 lenp
= strnlen(mz_default_config_path
,255);
956 if (strncmp(mz_default_config_path
+lenp
-1, "/",1))
957 strncat(mz_default_config_path
, "/",1);
958 snprintf(filename
, 255, "%s%s",mz_default_config_path
,tmp
);
961 lenp
= strlen(MZ_DEFAULT_CONFIG_PATH
);
962 snprintf(filename
, 255, "%s%s",MZ_DEFAULT_CONFIG_PATH
,tmp
);
965 if ((lenf
+lenp
)>255) return 1;
972 // Takes filename and prepends valid logging directory
974 // 1) prefer configurable mz_default_log_path[]
975 // 2) otherwise use MZ_DEFAULT_LOG_PATH
977 // NOTE: filename is overwritten and must be big enough to hold full path!
979 int getfullpath_log (char *filename
)
984 lenf
= strnlen(filename
, 32);
986 // filename not given?
987 if ((lenf
==0) || (lenf
==32)) return 1;
989 strncpy(tmp
, filename
, 32);
991 // Prefer user-defined path if provided:
992 lenp
= strnlen(mz_default_log_path
,255);
994 if (strncmp(mz_default_log_path
+lenp
-1, "/",1))
995 strncat(mz_default_log_path
, "/",1);
996 snprintf(filename
, 255, "%s%s",mz_default_log_path
,tmp
);
999 lenp
= strlen(MZ_DEFAULT_LOG_PATH
);
1000 snprintf(filename
, 255, "%s%s",MZ_DEFAULT_LOG_PATH
,tmp
);
1003 if ((lenf
+lenp
)>255) return 1;
1008 // Behaves much like strncpy but additionally ensures
1009 // that dest is always \0-terminated.
1011 // USAGE NOTE: If you know exactly the length n of your string,
1012 // then you must provide n+1 to support the termination character.
1014 // EXAMPLE: src="Hello", n=strlen(src)=5, and mz_strncpy(dest, src, n)
1015 // would result in dest={H,e,l,l,\0}.
1016 // Therefore the correct usage is:
1017 // mz_strncpy(dest, src, strlen(src)+1);
1020 // RETURN VALUE: pointer to dest
1021 char * mz_strncpy(char *dest
, const char *src
, size_t n
)
1024 tmp
= strncpy(dest
, src
, n
);
1032 // Helper function to count the number of arguments
1033 // in the Mausezahn argument string (comma separated args)
1035 // RETURN VALUE: Number of arguments
1037 // TODO: Improve it. Use strtok.
1039 int number_of_args (char *str
)
1041 int len
=0, i
=0, commas
=1;
1042 if ((len
=strnlen(str
,MAX_PAYLOAD_SIZE
))<2) return 0; // no valid argument
1043 for (i
=0; i
<len
; i
++) if (str
[i
]==',') commas
++;
1044 if (str
[len
-1]==',') commas
--; // comma at the end!
1050 // Checks if str consists only of digits 0..9
1054 // 0 if invalid chars found or str empty
1055 // n if str consists exactly of n digits
1056 int mz_strisnum(char *str
)
1061 if (len
==0) return 0;
1063 for (i
=0; i
<len
; i
++) {
1064 if (isdigit(str
[i
])) {
1074 // Checks if str consists only of hex digits 0..9 and a..f
1078 // 0 if invalid chars found or str empty
1079 // n if str consists exactly of n digits
1080 int mz_strishex(char *str
)
1085 if (len
==0) return 0;
1087 for (i
=0; i
<len
; i
++) {
1088 if (isxdigit(str
[i
])) {
1098 // Returns an 4-byte random number
1100 u_int32_t
mz_rand32 (void)
1102 static unsigned int r
=0;
1113 // Compares user-provided string with a specified string.
1117 // 0 if at least min characters match
1118 // 1 if at least one character of usr does NOT match the corresponding character in str.
1120 // Note: Case-insensitive!
1121 // Goal: Should be more practical and secure than strcmp (and related)
1122 int mz_strcmp(char* usr_orig
, char* str_orig
, int min
)
1124 int i
, same
=0, usrlen
, max
;
1125 char usr
[80], str
[80];
1127 usrlen
= strlen(usr_orig
);
1128 max
= strlen(str_orig
);
1130 strncpy(usr
, usr_orig
, 80);
1131 strncpy(str
, str_orig
, 80);
1133 // User provided not enough or too many chars
1134 if ((usrlen
<min
) || (usrlen
>max
)) return 1;
1136 // now check how many bytes really match
1137 for (i
=0; i
<usrlen
; i
++) {
1138 if (strncasecmp(&usr
[i
], &str
[i
], 1)==0) {
1143 if (same
<usrlen
) return 1;
1154 // Maps an arbitrary number of tokens from 'str' which are separated by
1155 // a character 'delim' into provided arguments.
1159 // char str[]="Am:Dam:Des";
1160 // char t1[64], t2[64], t3[64], t4[64];
1162 // mz_tok (str, ":", 4, t1, t2, t3, t4)
1164 // => t1="Am", t2="Dam", t3="Des", t4=NULL
1168 // 1. If the delimiter symbol occurs twice without gap, it is interpreted
1169 // as 'fill-up' command. To avoid ambiguities this may only occur once.
1170 // See the IPv6 address format shortcuts as similar example.
1172 // 2. If there are less tokens than allowed, the arguments are filled up
1173 // in order, while the remaining are casted to NULL:
1175 // 3. str must be smaller than 4096 bytes!
1177 // 4. To mitigate buffer overflow problems, the maximum token size is
1178 // currently limited to 64 bytes. Therefore it is recommended to
1179 // allocate 64 bytes for each argument.
1181 // RETURN VALUE: Number of returned tokens or -1 upon error
1183 int mz_tok(char * str
, char * delim
, int anz
, ...)
1187 int i
=0, n
=0, len
, llen
, rlen
, ltok
=0, rtok
=0;
1188 char *d
, *l
, *r
, *token
, *saveptr
, *arg
;
1189 char str2
[4096], delim2
[4]="", delim3
[4]="";;
1191 if (strlen(delim
)!=1) return -1; // delim must contain a single character!
1192 strncpy(str2
, str
, 4095); // protect the original str from strtok => operate on a copy only
1195 // Check if some tokens are omitted (::)
1196 strncpy(delim2
, delim
, 1); strncat(delim2
, delim
, 1); // create the double-delim
1197 strncpy(delim3
, delim2
, 2); strncat(delim3
, delim
, 1); // create the double-delim
1198 if (strstr(str2
, delim3
)!=NULL
) return -1; // Error: ':::' occured!
1200 if ( (d
=strstr(str2
, delim2
))!=NULL
) { // delim2 ('::') found
1201 // Check 3 cases: "::Sat:Sun", "Mon::Sat:Sun", and "Mon:Tue::"
1202 if (strlen(d
)>2) { // '::' is not at the end of str2
1203 r
=d
+2; // r points to beginning of right string
1204 if (strstr(r
, delim2
)!=NULL
) return -1; // Error: '::' occurs more than once!
1205 rtok
++; // there is at least one token in the right string
1207 for(i
=0;i
<rlen
;i
++) if (strncmp(r
++,delim
,1)==0) rtok
++;
1212 if (rlen
<(len
-2)) { // '::' is not at the beginning of str2
1213 l
=d
-1; // l points to end of left string
1215 llen
= len
- rlen
- 2;
1216 for(i
=0;i
<llen
;i
++) if (strncmp(l
--,delim
,1)==0) ltok
++;
1218 //printf("ltok=%i, rtok=%i\n",ltok,rtok);
1219 if ((ltok
+rtok
)>anz
) return -1; // More tokens than arguments ('::' here leads to ambiguous mapping)
1222 ltok
=len
+1; // makes subsequent algorithm to ignore exception handling
1229 token
= strtok_r(str2
, delim
, &saveptr
);
1230 if (token
==NULL
) { va_end(ap
); return n
; }
1232 for(i
=0; i
<anz
; i
++) {
1233 arg
= va_arg(ap
, char *);
1234 if ( (token
==NULL
) || // less tokens than arguments => assign NULL to the remaining arguments!
1235 ((i
>=ltok
) && (i
<rtok
))) {
1238 else { // we still have tokens...
1240 strncpy(arg
, token
, 64);
1241 token
= strtok_r(NULL
, delim
, &saveptr
);
1255 // PURPOSE: Simplify reading of user delay specifications.
1256 // Parse 'a' and 'b' and configure a struct timespec, i. e. seconds and nanoseconds.
1258 // Typically 'a' contains only the value and 'b' the unit.
1259 // But if b==NULL then 'a' may also contain the unit such as "100msec"
1261 // Allowed units are: nsec, usec, sec, min, hour
1263 // NOTE: If no unit is given then assume msec as default unit
1265 // RETURN VALUE: 0 upon success, 1 upon error (bad arguments)
1267 int delay_parse (struct timespec
*t
, char *a
, char *b
)
1270 unsigned int sfactor
=0, nfactor
=1000000; // assume msec as default unit
1271 unsigned long long delay
, sdelay
, ndelay
;
1273 if (b
==NULL
) { // only one argument, but may contain an unit (such as '314sec')
1274 if (strstr(a
, "msec")) {
1277 else if (strstr(a
, "usec")) {
1280 else if (strstr(a
, "nsec")) {
1283 else if (strstr(a
, "sec")) { // NOTE: This must be the last check since 'sec' is included in previous strings
1287 else if (strstr(a
, "min")) {
1291 else if (strstr(a
, "hour")) {
1295 else { // Unit not found; check for non-digits!
1296 // NOTE: we do not currently catch wrong strings that contain sec, usec, or msec.
1298 for (i
=0; i
<strlen(a
); i
++) {
1299 if (!isdigit(a
[i
])) return 1; // invalid unit
1301 nfactor
=1000000; // no unit given => assume msec
1303 } else { // caller specified two arguments
1304 if (mz_strcmp(b
,"nsec", 1)==0)
1306 else if (mz_strcmp(b
,"usec", 1)==0)
1308 else if (mz_strcmp(b
,"msec", 1)==0)
1310 else if (mz_strcmp(b
,"sec", 1)==0) {
1314 else if (mz_strcmp(b
,"min", 1)==0) {
1318 else if (mz_strcmp(b
,"hour", 1)==0) {
1322 else return 1; // Invalid unit
1325 // Get user-defined actual value:
1326 delay
= strtoull(a
, (char **)NULL
, 10);
1327 if ((errno
==ERANGE
) || (delay
>999999999L)) { // see man 2 nanosleep
1328 return 2; // Value too large! Supported range is from 0 to 999999999
1331 sdelay
= delay
* sfactor
;
1332 ndelay
= delay
* nfactor
;
1334 if (ndelay
>999999999L) {
1335 sdelay
= ndelay
/1000000000L;
1336 ndelay
= ndelay
- (sdelay
*1000000000L);
1340 t
->tv_nsec
= ndelay
;
1344 // PURPOSE: Create random MAC address.
1345 // Create a random MAC address and store it in the array
1346 // passed in the function argument.
1348 // NOTE: The MAC address returned is a unicast address.
1349 // Unicast addresses have the LSB always set to zero.
1350 // At least that's what thay say on the Internet.. ;-)
1352 // RETURN VALUE: none
1353 // ToDo: This function should have a return value.
1355 void create_random_mac(u_int8_t eth_mac
[6])
1357 printf("I'm right here!\n");
1359 for (i
= 0; i
< 6; i
++)
1361 tx
.eth_src
[i
] = (u_int8_t
) ( ((float) rand()/RAND_MAX
)*256);
1363 tx
.eth_src
[0] &= 0xFE; // keeps b-cast bit zero