Outsource creation of random MAC address to a new function create_random_mac.
[mausezahn.git] / src / tools.c
blobc69a5fe73d607790d60ddc79008c9df4aa30d261
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 // 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
53 //
54 ////////////////////////////////////////////////////////////////////////////////////////////
56 #include "mz.h"
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.
64 // Example:
65 // int i;
66 // char ip[64];
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;
74 int j, occurence=0;
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);
82 if (token == NULL)
83 break;
85 str2 = token;
86 if ( (subtoken = strtok_r(str2, " =", &saveptr2))!=NULL)
88 if (strcasecmp(subtoken,arg_name)==0)
90 occurence+=1;
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);
96 if (arg_value!=NULL)
98 strcpy(arg_value,subtoken);
103 else
104 break;
106 return occurence;
110 // Convert str to (unsigned long) int
111 // Return value: the unsigned long int
112 unsigned long int str2int(char *str)
114 unsigned long int i;
116 errno=0;
118 i = strtoul(str, (char **)NULL, 10);
120 if ((errno == ERANGE && (i == ULONG_MAX))
121 || (errno != 0 && i == 0))
123 perror("strtoul");
126 return i;
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;
137 errno=0;
139 i = strtoull(str, (char **)NULL, 10);
141 if ((errno == ERANGE && (i == ULLONG_MAX))
142 || (errno != 0 && i == 0))
144 perror("strtoull");
147 return i;
151 // Convert hex-str to (unsigned long) int
152 // Return value: the unsigned long int
153 unsigned long int xstr2int(char *str)
155 unsigned long int i;
157 errno=0;
159 i = strtoul(str, (char **)NULL, 16);
161 if ((errno == ERANGE && (i == ULONG_MAX))
162 || (errno != 0 && i == 0))
165 perror("strtoul");
168 return i;
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;
178 errno=0;
180 i = strtoull(str, (char **)NULL, 16);
182 if ((errno == ERANGE && (i == ULLONG_MAX))
183 || (errno != 0 && i == 0))
185 perror("strtoull");
188 return i;
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
208 // 3) 172.18.96.0/19
210 // That is:
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)
218 int
219 i, len,
220 found_slash=0, found_dash=0;
222 unsigned int q;
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
232 // Find "-" or "/"
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 "-" !!!
242 if (found_dash)
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)
255 // Set range flag:
256 tx.ip_dst_isrange = 1;
257 return 0;
259 else
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);
272 mask = 0xffffffff;
273 mask <<= (32-q);
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;
281 return 0;
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
306 // 3) 172.18.96.0/19
308 // That is:
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)
316 int
317 i, len,
318 found_slash=0, found_dash=0;
320 unsigned int q;
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
331 // Find "-" or "/"
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 "-" !!!
341 if (found_dash)
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)
354 // Set range flag:
355 tx.ip_src_isrange = 1;
356 return 0;
358 else
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);
371 mask = 0xffffffff;
372 mask <<= (32-q);
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;
380 return 0;
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.
392 // Return value:
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)
403 char *eth_mac_txt;
404 u_int8_t *eth_mac;
405 int *eth_rand;
406 int i;
408 // Check argument
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;
421 else
423 return 1; // wrong argument
427 // Did the user really specify a dst-address?
428 if (strnlen(eth_mac_txt, 18)==0)
430 return 1; // No.
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)
440 for (i=0; i<6; i++)
442 eth_mac[0] = (u_int8_t) ( ((float) rand()/RAND_MAX)*256);
444 *eth_rand = 1;
446 // Do you want your own interface MAC?
447 else if (strncmp(eth_mac_txt, "own", 3)==0)
449 for (i=0; i<6; i++)
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!
476 else
478 str2hex_mac(eth_mac_txt, eth_mac);
481 return 0;
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
493 // ** OR **
495 // b) tx.dp_start, tx.dp_stop, tx.dp = tx.dp_start
497 // Arguments:
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;
511 u_int16_t
512 *port,
513 *start,
514 *stop;
515 int
516 *isrange;
518 char *start_str, *stop_str;
521 // Check which port to manage
522 if (sp_or_dp == DST_PORT)
524 port = &tx.dp;
525 start = &tx.dp_start;
526 stop = &tx.dp_stop;
527 isrange = &tx.dp_isrange;
529 else if (sp_or_dp == SRC_PORT)
531 port = &tx.sp;
532 start = &tx.sp_start;
533 stop = &tx.sp_stop;
534 isrange = &tx.sp_isrange;
536 else
538 return 1; // error
542 len = strnlen(arg,12);
543 if (len==0) return 1; // error
545 // Find "-"
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");
560 exit (-1);
562 *start = tmp1;
564 tmp2 = str2int (stop_str);
565 if ( (tmp2<0)||(tmp2>65535))
567 fprintf(stderr," mz/get_port_range: Invalid port range!\n");
568 exit (-1);
570 *stop = tmp2;
572 if (tmp1>tmp2) // swap start/stop values!
574 *start = tmp2;
575 *stop = tmp1;
578 *port = *start;
579 *isrange = 1;
581 return 0;
583 else // single port number
585 tmp1 = str2int (arg);
586 if ( (tmp1<0)||(tmp1>65535)) tmp1=0;
587 *port = tmp1;
588 *isrange = 0;
589 return 0;
592 return 1; // error
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)
607 char *f;
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);
653 return 0;
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;
682 char params[256];
684 tx.mpls_exp = 0;
685 tx.mpls_ttl = 255;
687 strncpy(params, p, 256);
689 if (strncmp(params,"help",4)==0)
691 fprintf(stderr,"\n"
692 MAUSEZAHN_VERSION
693 "\n"
694 "| MPLS header Syntax: -M label[,label[,label[,...]]]\n"
695 "| where each header may consist of the following parameters:\n"
696 "|\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"
705 "|\n"
706 "| Examples:\n"
707 "|\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"
713 "|\n"
714 "| multiple headers:\n"
715 "|\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"
719 "|\n"
720 "| Valid delimiters inside a header: : - . +\n"
721 "|\n"
722 "\n");
723 exit (0);
725 else
728 if ( (f1 = strtok (params, ":-.+")) == NULL )
730 return 1; // error!
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)
746 tx.mpls_bos = 1;
747 return 0;
749 else if (strncmp(f2,"s",1)==0)
751 tx.mpls_bos = 0;
752 return 0;
754 else
756 tx.mpls_exp = (u_int8_t) str2int (f2);
757 if (tx.mpls_exp > 7)
759 tx.mpls_exp = 7;
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)
769 tx.mpls_bos = 1;
770 return 0;
772 else if (strncmp(f3,"s",1)==0)
774 tx.mpls_bos = 0;
775 return 0;
777 else
779 if ((u_int16_t) str2int (f3)>255)
781 fprintf(stderr," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n");
782 tx.mpls_ttl = 255;
784 else
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)
795 tx.mpls_bos = 1;
797 else if (strncmp(f3,"s",1)==0)
799 tx.mpls_bos = 0;
808 return 0;
812 // Parses str for occurence of character or sequence ch.
813 // Returns number of occurences
814 int exists(char* str, char* ch)
816 int i,match;
818 size_t len_str, len_ch;
820 len_str = strlen(str);
821 len_ch = strlen(ch);
822 match=0;
824 for (i=0; i<len_str; i++)
826 if (strcmp(str++,ch)==0) match++;
829 return match;
834 // Checks if str consists only of 0 and 1
836 // RETURN VALUE:
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)
842 int i, len, ret=0;
844 len = strlen(str);
845 if (len==0) return 0;
847 for (i=0; i<len; i++) {
848 if ((str[i]=='0') || (str[i]=='1')) {
849 ret++;
850 } else {
851 return 0;
854 return ret;
861 // Converts a string containing (max 8) binary digits into a number
862 // RETURN VALUE:
864 // Either the number on success
865 // Or -1 upon failure
867 int str2bin8 (char *str)
869 int i, n, ret=0;
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) );
876 return ret;
882 // Converts a string containing (max 16) binary digits into a number
883 // RETURN VALUE:
885 // Either the number on success
886 // Or -1 upon failure
888 long int str2bin16 (char *str)
890 int i, n;
891 long int ret=0;
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 ;-)
898 return ret;
903 // Converts a char into a string containing ones and zeros
905 // EXAMPLE:
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)
913 int i,j=1;
914 char tmp[]="0 0 0 0 0 0 0 0";
916 for (i=0; i<8; i++)
918 if (c&j) tmp[14-i*2]='1';
919 j=j*2;
922 strncpy(str, tmp, 15);
923 return 0;
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
936 // RETURN VALUE:
937 // 0 upon success
938 // 1 upon failure
940 int getfullpath_cfg (char *filename)
942 int lenf, lenp;
943 char tmp[32];
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);
955 if (lenp) {
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);
960 else {
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;
967 return 0;
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)
981 int lenf, lenp;
982 char tmp[32];
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);
993 if (lenp) {
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);
998 else {
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;
1005 return 0;
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);
1018 // =============
1020 // RETURN VALUE: pointer to dest
1021 char * mz_strncpy(char *dest, const char *src, size_t n)
1023 char *tmp;
1024 tmp = strncpy(dest, src, n);
1025 dest[n-1]='\0';
1026 return tmp;
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!
1045 return commas;
1050 // Checks if str consists only of digits 0..9
1052 // RETURN VALUE:
1054 // 0 if invalid chars found or str empty
1055 // n if str consists exactly of n digits
1056 int mz_strisnum(char *str)
1058 int i, len, ret=0;
1060 len = strlen(str);
1061 if (len==0) return 0;
1063 for (i=0; i<len; i++) {
1064 if (isdigit(str[i])) {
1065 ret++;
1066 } else {
1067 return 0;
1070 return ret;
1074 // Checks if str consists only of hex digits 0..9 and a..f
1076 // RETURN VALUE:
1078 // 0 if invalid chars found or str empty
1079 // n if str consists exactly of n digits
1080 int mz_strishex(char *str)
1082 int i, len, ret=0;
1084 len = strlen(str);
1085 if (len==0) return 0;
1087 for (i=0; i<len; i++) {
1088 if (isxdigit(str[i])) {
1089 ret++;
1090 } else {
1091 return 0;
1094 return ret;
1098 // Returns an 4-byte random number
1100 u_int32_t mz_rand32 (void)
1102 static unsigned int r=0;
1103 srand(r);
1104 r=rand();
1105 return (r<<16 | r);
1113 // Compares user-provided string with a specified string.
1115 // Return value:
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) {
1139 same++;
1143 if (same<usrlen) return 1;
1145 return 0;
1152 // PURPOSE:
1154 // Maps an arbitrary number of tokens from 'str' which are separated by
1155 // a character 'delim' into provided arguments.
1157 // USAGE EXAMPLE:
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
1166 // NOTE:
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, ...)
1186 va_list ap;
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
1193 len = strlen(str2);
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
1206 rlen = strlen(r);
1207 for(i=0;i<rlen;i++) if (strncmp(r++,delim,1)==0) rtok++;
1209 else
1210 rlen = 0;
1212 if (rlen<(len-2)) { // '::' is not at the beginning of str2
1213 l=d-1; // l points to end of left string
1214 ltok++;
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)
1221 else
1222 ltok=len+1; // makes subsequent algorithm to ignore exception handling
1226 rtok=anz-rtok;
1227 va_start(ap, anz);
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))) {
1236 arg[0] = 0x00;
1238 else { // we still have tokens...
1239 n++;
1240 strncpy(arg, token, 64);
1241 token = strtok_r(NULL, delim, &saveptr);
1245 va_end(ap);
1246 return n;
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)
1269 int i;
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")) {
1275 nfactor=1000000;
1277 else if (strstr(a, "usec")) {
1278 nfactor=1000;
1280 else if (strstr(a, "nsec")) {
1281 nfactor=1;
1283 else if (strstr(a, "sec")) { // NOTE: This must be the last check since 'sec' is included in previous strings
1284 sfactor=1;
1285 nfactor=0;
1287 else if (strstr(a, "min")) {
1288 sfactor=60;
1289 nfactor=0;
1291 else if (strstr(a, "hour")) {
1292 sfactor=3600;
1293 nfactor=0;
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)
1305 nfactor=1;
1306 else if (mz_strcmp(b,"usec", 1)==0)
1307 nfactor=1000;
1308 else if (mz_strcmp(b,"msec", 1)==0)
1309 nfactor=1000000;
1310 else if (mz_strcmp(b,"sec", 1)==0) {
1311 sfactor=1;
1312 nfactor=0;
1314 else if (mz_strcmp(b,"min", 1)==0) {
1315 sfactor=60;
1316 nfactor=0;
1318 else if (mz_strcmp(b,"hour", 1)==0) {
1319 sfactor=3600;
1320 nfactor=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);
1339 t->tv_sec = sdelay;
1340 t->tv_nsec = ndelay;
1341 return 0;
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");
1358 int i;
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