From d0009856814c13d13770db5aadd7b2fabf947776 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 13 May 2013 13:53:27 +0200 Subject: staging: add mausezahn staging directory After some back and forth, we decided that it is easier to maintain mausezahn in a staging directory until it is fully reworked and cleaned up to be ready to be fully integrated. This way, it is better than having it in a separate branch, and we can also accept patches from outside more easily. Also, while at it, fix up some function mismatches with libcli. Signed-off-by: Daniel Borkmann Signed-off-by: Tobias Klauser --- staging/tools.c | 1344 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1344 insertions(+) create mode 100644 staging/tools.c (limited to 'staging/tools.c') diff --git a/staging/tools.c b/staging/tools.c new file mode 100644 index 0000000..96f0bb7 --- /dev/null +++ b/staging/tools.c @@ -0,0 +1,1344 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + + + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Contains various tools to ease development of new modules: +// +// getarg ............. scans string for arguments and returns assigned value +// str2int ............. Converts a string into unsigned long int in a safe way +// str2lint ............ Same as str2int but returns an unsigned long long int +// xstr2int ............ Same as str2int but expects hex digits +// xstr2lint ........... Same as above but returns an unsigned long long int +// get_ip_range_dst .... Parses string for an IP range and sets start/stop addresses +// get_ip_range_src .... Same for source addresses +// check_eth_mac_txt ... Scans tx.eth_dst|src_txt and sets tx.eth_dst|src appropriately +// get_port_range ...... Parses string for a dst|src-port range and sets start/stop values +// get_tcp_flags ....... Parses string for TCP arguments and sets tx.tcp_control +// get_mpls_params ..... Parses string for MPLS parameters (label, exp, BOS, TTL) +// exists .............. Parses a string for a single character and returns "1" if found +// mz_strisbinary ...... Checks whether string consists only of 0 and 1, returns how many digits total +// str2bin8 ............ Converts a string containing max 8 binary digits into a number +// str2bin16 ........... Converts a string containing max 16 binary digits into a number +// char2bits ........... Converts a char into a string containing ones and zeros +// getfullpath_cfg ..... Creates a full filename with path to the desired config directory +// getfullpath_log ..... Creates a full filename with path to the desired logging directory +// mz_strncpy .......... A safer implementation of strncpy +// number_of_args ...... Returns number of arguments of the Mausezahn argument string +// mz_strisnum ......... Returns 1 if string only consists of decimal digits +// mz_strishex ......... Returns 1 if string only consists of hexadecimal digits +// mz_strcmp ........... Matches a string or a prefix of it with given min-length +// Example usage: User CLI input +// mz_tok .............. Decomposes a string into tokens and maps them to args +// Example usage: IPv6-addresses, user input for MPLS-tags +// delay_parse ......... Parses one or two strings for a delay specification and sets a struct timespec +// +//////////////////////////////////////////////////////////////////////////////////////////// + +#include "mz.h" + + + +// Scan 'str' for an argument 'arg_name' and returns its value in arg_value +// Return value: number of occurences of arg_name +// Note that if arg_name occurs multiple times, the last found value is returned. +// If last argument (arg_value) is set to NULL it will be ignored. +// Example: +// int i; +// char ip[64]; +// i = getarg ("request, da=10.1.1.2, SYN", "da", ip); +// ...will assign "10.1.1.2" to ip and the occurence i is set to 1. +int getarg(char *str, char *arg_name, char *arg_value) +{ + char tmp[MAX_PAYLOAD_SIZE]; + char *str1, *str2, *token, *subtoken; + char *saveptr1, *saveptr2; + int j, occurence=0; + + strncpy(tmp,str,MAX_PAYLOAD_SIZE); // only operate on local strings + + for (j = 1, str1 = tmp; ; j++, str1 = NULL) + { + + token = strtok_r(str1, ",", &saveptr1); + if (token == NULL) + break; + + str2 = token; + if ( (subtoken = strtok_r(str2, " =", &saveptr2))!=NULL) + { + if (strcasecmp(subtoken,arg_name)==0) + { + occurence+=1; + //printf("found %s\n",arg_name); + if ( (subtoken = strtok_r(NULL, " =", &saveptr2))!=NULL) + { + // argument has a value! + //printf("%s has value: [%s]\n",arg_name, subtoken); + if (arg_value!=NULL) + { + strcpy(arg_value,subtoken); + } + } + } + } + else + break; + } + return occurence; +} + + +// Convert str to (unsigned long) int +// Return value: the unsigned long int +unsigned long int str2int(char *str) +{ + unsigned long int i; + + errno=0; + + i = strtoul(str, (char **)NULL, 10); + + if ((errno == ERANGE && (i == ULONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoul"); + } + + return i; +} + + + +// Convert str to (unsigned long long) int +// Return value: the unsigned long long int +unsigned long long int str2lint(char *str) +{ + unsigned long long int i; + + errno=0; + + i = strtoull(str, (char **)NULL, 10); + + if ((errno == ERANGE && (i == ULLONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoull"); + } + + return i; +} + + +// Convert hex-str to (unsigned long) int +// Return value: the unsigned long int +unsigned long int xstr2int(char *str) +{ + unsigned long int i; + + errno=0; + + i = strtoul(str, (char **)NULL, 16); + + if ((errno == ERANGE && (i == ULONG_MAX)) + || (errno != 0 && i == 0)) + { + + perror("strtoul"); + } + + return i; +} + + +// Convert hex-str to (unsigned long long) int +// Return value: the unsigned long long int +unsigned long long int xstr2lint(char *str) +{ + unsigned long long int i; + + errno=0; + + i = strtoull(str, (char **)NULL, 16); + + if ((errno == ERANGE && (i == ULLONG_MAX)) + || (errno != 0 && i == 0)) + { + perror("strtoull"); + } + + return i; +} + + + + +// Parses string 'arg' for an IP range and finds start and stop IP addresses. +// Return value: 0 upon success, 1 upon failure. +// +// NOTE: The results are written in the following variables: +// +// (u_int32_t) tx.ip_dst_start ... contains start value +// (u_int32_t) tx.ip_dst_stop ... contains stop value +// (u_int32_t) tx.ip_dst ... initialized with start value +// int tx.ip_dst_isrange ... set to 1 if above values valid +// +// Possible range specifications: +// +// 1) 192.168.0.0-192.168.0.12 +// 2) 10.2.11.0-10.55.13.2 +// 3) 172.18.96.0/19 +// +// That is: +// +// FIRST detect a range by scanning for the "-" OR "/" chars +// THEN determine start and stop value and store them as normal unsigned integers +// +int get_ip_range_dst (char *arg) +{ + + int + i, len, + found_slash=0, found_dash=0; + + unsigned int q; + u_int32_t mask, invmask; + + char *start_str, *stop_str; + + len = strnlen(arg, 32); + + if ( (len>31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars) + return 1; // ERROR: no range + + // Find "-" or "/" + for (i=0; i31) || (len<9) ) // 255.255.255.200-255.255.255.255 (31 chars) OR 1.0.0.0/8 (9 chars) + return 1; // ERROR: no range + + // Find "-" or "/" + for (i=0; i65535)) + { + fprintf(stderr," mz/get_port_range: Invalid port range!\n"); + exit (-1); + } + *start = tmp1; + + tmp2 = str2int (stop_str); + if ( (tmp2<0)||(tmp2>65535)) + { + fprintf(stderr," mz/get_port_range: Invalid port range!\n"); + exit (-1); + } + *stop = tmp2; + + if (tmp1>tmp2) // swap start/stop values! + { + *start = tmp2; + *stop = tmp1; + } + + *port = *start; + *isrange = 1; + + return 0; + } + else // single port number + { + tmp1 = str2int (arg); + if ( (tmp1<0)||(tmp1>65535)) tmp1=0; + *port = tmp1; + *isrange = 0; + return 0; + } + + return 1; // error +} + + + + +// Scans argument for TCP flags and sets +// tx.tcp_control accordingly. +// +// Valid keywords are: fin, syn, rst, psh, ack, urg, ecn, cwr +// Valid delimiters are: | or + or - +// Return value: 0 on success, 1 upon failure +// +int get_tcp_flags (char* flags) +{ + char *f; + + // From LSB to MSB: fin, syn, reset, push, ack, urg, ecn, cwr + // ecn...ECN-Echo, cwr...Congestion Window Reduced + + if (strnlen(flags,40)==0) return 1; // error + + + f = strtok (flags, "|+-"); + do + { + if (strncmp(f,"fin",3)==0) + { + tx.tcp_control = tx.tcp_control | 1; + } + else if (strncmp(f,"syn",3)==0) + { + tx.tcp_control = tx.tcp_control | 2; + } + else if (strncmp(f,"rst",3)==0) + { + tx.tcp_control = tx.tcp_control | 4; + } + else if (strncmp(f,"psh",3)==0) + { + tx.tcp_control = tx.tcp_control | 8; + } + else if (strncmp(f,"ack",3)==0) + { + tx.tcp_control = tx.tcp_control | 16; + } + else if (strncmp(f,"urg",3)==0) + { + tx.tcp_control = tx.tcp_control | 32; + } + else if (strncmp(f,"ecn",3)==0) + { + tx.tcp_control = tx.tcp_control | 64; + } + else if (strncmp(f,"cwr",3)==0) + { + tx.tcp_control = tx.tcp_control | 128; + } + + } while ( (f=strtok(NULL, "|+-")) != NULL); + + return 0; +} + + + +// Scans string 'params' for MPLS parameters +// and sets tx.mpls_* accordingly. +// +// CLI Syntax Examples: +// +// -M help .... shows syntax +// +// -M 800 .... label=800 +// -M 800:S .... label=800 and BOS flag set +// -M 800:S:64 .... label=800, BOS, TTL=64 +// -M 800:64:S .... same +// -M 64:77 .... label=64, TTL=77 +// -M 64:800 .... INVALID +// -M 800:64 .... label=800, TTL=64 +// -M 800:3:S:64 .... additionally the experimental bits are set (all fields required!) +// +// Note: S = BOS(1), s = NOT-BOS(0) +// +// Valid delimiters: :-.,+ +// Return value: 0 on success, 1 upon failure +int get_mpls_params(char *p) +{ + + char *f1, *f2, *f3, *f4; + char params[256]; + + tx.mpls_exp = 0; + tx.mpls_ttl = 255; + + strncpy(params, p, 256); + + if (strncmp(params,"help",4)==0) + { + fprintf(stderr,"\n" + MAUSEZAHN_VERSION + "\n" + "| MPLS header Syntax: -M label[,label[,label[,...]]]\n" + "| where each header may consist of the following parameters:\n" + "|\n" + "| label ... the MPLS label (mandatory, 0..1048575)\n" + "| exp ..... experimental/CoS (default: 0, allowed values: 0..7)\n" + "| TTL ..... Time To Live (default: 255)\n" + "| BOS ..... marks bottom-of-stack; per default the last (most inner) header\n" + "| will have BOS=1. If desired you can set this flag for any header\n" + "| inbetween but this will lead to an invalid packet. Simply use\n" + "| 'S' to set BOS=1, or 's' to set BOS=0. Note that this flag MUST be\n" + "| the LAST argument.\n" + "|\n" + "| Examples:\n" + "|\n" + "| -M 800 .... label=800\n" + "| -M 800:6 .... label=800 and CoS=6\n" + "| -M 800:6:55 .... label=800, CoS=6, TTL=55\n" + "| -M 800:S .... label=800 and BOS=1\n" + "| -M 800:6:s .... label=800, CoS=6, and BOS=0\n" + "|\n" + "| multiple headers:\n" + "|\n" + "| -m 30,20:7,800:5:128 ... outer label=800 with CoS=5 and TTL=128,\n" + "| middle label=20 with CoS=7,\n" + "| inner label=30 (this one is closest to L3).\n" + "|\n" + "| Valid delimiters inside a header: : - . +\n" + "|\n" + "\n"); + exit (0); + } + else + { + + if ( (f1 = strtok (params, ":-.+")) == NULL ) + { + return 1; // error! + } + + tx.mpls_label = (u_int32_t) str2int (f1); + if (tx.mpls_label>1048575) + { + tx.mpls_label = 1048575; // 2^20 + fprintf(stderr," Warning: MPLS label too big! Reduced to maximum allowed value.\n"); + } + } + + + if ( (f2 = strtok (NULL, ":-.+")) != NULL ) // 2nd param set + { + if (strncmp(f2,"S",1)==0) + { + tx.mpls_bos = 1; + return 0; + } + else if (strncmp(f2,"s",1)==0) + { + tx.mpls_bos = 0; + return 0; + } + else + { + tx.mpls_exp = (u_int8_t) str2int (f2); + if (tx.mpls_exp > 7) + { + tx.mpls_exp = 7; + fprintf(stderr," Warning: MPLS CoS too big! Reduced to maximum allowed value.\n"); + } + } + + + if ( (f3 = strtok (NULL, ":-.+")) != NULL ) // 3rd param set + { + if (strncmp(f3,"S",1)==0) + { + tx.mpls_bos = 1; + return 0; + } + else if (strncmp(f3,"s",1)==0) + { + tx.mpls_bos = 0; + return 0; + } + else + { + if ((u_int16_t) str2int (f3)>255) + { + fprintf(stderr," Warning: MPLS TTL too big! Reduced to maximum allowed value.\n"); + tx.mpls_ttl = 255; + } + else + { + tx.mpls_ttl = (u_int8_t) str2int (f3); + } + } + + if ( (f4 = strtok (NULL, ":-.+")) != NULL ) // 4th param set + { + + if (strncmp(f3,"S",1)==0) + { + tx.mpls_bos = 1; + } + else if (strncmp(f3,"s",1)==0) + { + tx.mpls_bos = 0; + } + + } + + } + } + + + return 0; +} + + +// Parses str for occurence of character or sequence ch. +// Returns number of occurences +int exists(char* str, char* ch) +{ + int i,match; + + size_t len_str, len_ch; + + len_str = strlen(str); + len_ch = strlen(ch); + match=0; + + for (i=0; i8)) return -1; + + for (i=0; i16)) return -1; + + for (i=0; i "1 0 0 0 0 0 0 1" +// +int char2bits (char c, char *str) +{ + int i,j=1; + char tmp[]="0 0 0 0 0 0 0 0"; + + for (i=0; i<8; i++) + { + if (c&j) tmp[14-i*2]='1'; + j=j*2; + } + + strncpy(str, tmp, 15); + return 0; +} + + +// Takes filename and prepends valid configuration directory +// +// 1) prefer configurable mz_default_config_path[] +// 2) otherwise use MZ_DEFAULT_CONFIG_PATH +// +// NOTE: 'filename' finally holds the full path +// and must therefore be big enough +// +// +// RETURN VALUE: +// 0 upon success +// 1 upon failure +// +int getfullpath_cfg (char *filename) +{ + int lenf, lenp; + char tmp[32]; + + lenf = strnlen(filename, 32); + + // filename not given? + if ((lenf==0) || (lenf==32)) return 1; + + strncpy(tmp, filename, 32); + + // Prefer user-defined path if provided: + lenp = strnlen(mz_default_config_path,255); + + if (lenp) { + if (strncmp(mz_default_config_path+lenp-1, "/",1)) + strncat(mz_default_config_path, "/",1); + snprintf(filename, 255, "%s%s",mz_default_config_path,tmp); + } + else { + lenp = strlen(MZ_DEFAULT_CONFIG_PATH); + snprintf(filename, 255, "%s%s",MZ_DEFAULT_CONFIG_PATH,tmp); + } + + if ((lenf+lenp)>255) return 1; + + return 0; +} + + + +// Takes filename and prepends valid logging directory +// +// 1) prefer configurable mz_default_log_path[] +// 2) otherwise use MZ_DEFAULT_LOG_PATH +// +// NOTE: filename is overwritten and must be big enough to hold full path! +// +int getfullpath_log (char *filename) +{ + int lenf, lenp; + char tmp[32]; + + lenf = strnlen(filename, 32); + + // filename not given? + if ((lenf==0) || (lenf==32)) return 1; + + strncpy(tmp, filename, 32); + + // Prefer user-defined path if provided: + lenp = strnlen(mz_default_log_path,255); + if (lenp) { + if (strncmp(mz_default_log_path+lenp-1, "/",1)) + strncat(mz_default_log_path, "/",1); + snprintf(filename, 255, "%s%s",mz_default_log_path,tmp); + } + else { + lenp = strlen(MZ_DEFAULT_LOG_PATH); + snprintf(filename, 255, "%s%s",MZ_DEFAULT_LOG_PATH,tmp); + } + + if ((lenf+lenp)>255) return 1; + + return 0; +} + +// Behaves much like strncpy but additionally ensures +// that dest is always \0-terminated. +// +// USAGE NOTE: If you know exactly the length n of your string, +// then you must provide n+1 to support the termination character. +// +// EXAMPLE: src="Hello", n=strlen(src)=5, and mz_strncpy(dest, src, n) +// would result in dest={H,e,l,l,\0}. +// Therefore the correct usage is: +// mz_strncpy(dest, src, strlen(src)+1); +// ============= +// +// RETURN VALUE: pointer to dest +char * mz_strncpy(char *dest, const char *src, size_t n) +{ + char *tmp; + tmp = strncpy(dest, src, n); + dest[n-1]='\0'; + return tmp; +} + + + + +// Helper function to count the number of arguments +// in the Mausezahn argument string (comma separated args) +// +// RETURN VALUE: Number of arguments +// +// TODO: Improve it. Use strtok. +// +int number_of_args (char *str) +{ + int len=0, i=0, commas=1; + if ((len=strnlen(str,MAX_PAYLOAD_SIZE))<2) return 0; // no valid argument + for (i=0; imax)) return 1; + + // now check how many bytes really match + for (i=0; i t1="Am", t2="Dam", t3="Des", t4=NULL +// +// NOTE: +// +// 1. If the delimiter symbol occurs twice without gap, it is interpreted +// as 'fill-up' command. To avoid ambiguities this may only occur once. +// See the IPv6 address format shortcuts as similar example. +// +// 2. If there are less tokens than allowed, the arguments are filled up +// in order, while the remaining are casted to NULL: +// +// 3. str must be smaller than 4096 bytes! +// +// 4. To mitigate buffer overflow problems, the maximum token size is +// currently limited to 64 bytes. Therefore it is recommended to +// allocate 64 bytes for each argument. +// +// RETURN VALUE: Number of returned tokens or -1 upon error + +int mz_tok(char * str, char * delim, int anz, ...) +{ + + va_list ap; + int i=0, n=0, len, llen, rlen, ltok=0, rtok=0; + char *d, *l, *r, *token, *saveptr, *arg; + char str2[4096], delim2[4]="", delim3[4]="";; + + if (strlen(delim)!=1) return -1; // delim must contain a single character! + strncpy(str2, str, 4095); // protect the original str from strtok => operate on a copy only + len = strlen(str2); + + // Check if some tokens are omitted (::) + strncpy(delim2, delim, 1); strncat(delim2, delim, 1); // create the double-delim + strncpy(delim3, delim2, 2); strncat(delim3, delim, 1); // create the double-delim + if (strstr(str2, delim3)!=NULL) return -1; // Error: ':::' occured! + + if ( (d=strstr(str2, delim2))!=NULL ) { // delim2 ('::') found + // Check 3 cases: "::Sat:Sun", "Mon::Sat:Sun", and "Mon:Tue::" + if (strlen(d)>2) { // '::' is not at the end of str2 + r=d+2; // r points to beginning of right string + if (strstr(r, delim2)!=NULL) return -1; // Error: '::' occurs more than once! + rtok++; // there is at least one token in the right string + rlen = strlen(r); + for(i=0;ianz) return -1; // More tokens than arguments ('::' here leads to ambiguous mapping) + } + else + ltok=len+1; // makes subsequent algorithm to ignore exception handling + + + + rtok=anz-rtok; + va_start(ap, anz); + + token = strtok_r(str2, delim, &saveptr); + if (token==NULL) { va_end(ap); return n; } + + for(i=0; i assign NULL to the remaining arguments! + ((i>=ltok) && (i assume msec + } + } else { // caller specified two arguments + if (mz_strcmp(b,"nsec", 1)==0) + nfactor=1; + else if (mz_strcmp(b,"usec", 1)==0) + nfactor=1000; + else if (mz_strcmp(b,"msec", 1)==0) + nfactor=1000000; + else if (mz_strcmp(b,"sec", 1)==0) { + sfactor=1; + nfactor=0; + } + else if (mz_strcmp(b,"min", 1)==0) { + sfactor=60; + nfactor=0; + } + else if (mz_strcmp(b,"hour", 1)==0) { + sfactor=3600; + nfactor=0; + } + else return 1; // Invalid unit + } + + // Get user-defined actual value: + delay = strtoull(a, (char **)NULL, 10); + if ((errno==ERANGE) || (delay>999999999L)) { // see man 2 nanosleep + return 2; // Value too large! Supported range is from 0 to 999999999 + } + + sdelay = delay * sfactor; + ndelay = delay * nfactor; + + if (ndelay>999999999L) { + sdelay = ndelay/1000000000L; + ndelay = ndelay - (sdelay*1000000000L); + } + + t->tv_sec = sdelay; + t->tv_nsec = ndelay; + return 0; +} + -- cgit v1.2.3-54-g00ecf