/* pinger: A programm for high resolution pinging, with averaging of the return value probabilities and the RTT. Before evaluation the RTTs are filtered by a cutoff, to eleminate outliners. Compile e. g. with gcc -D_REENTRANT -Wall -O3 -lm -pthread -o pinger pinger.c && strip pinger First parameter: IP (or name) of the target computer Second parameter: Cutoff RTT [ns, integer], for ignoring outliners. Third and optional parameter: Ping net size [Byte, integer], should be greater or equal to 16. The gross size is +28. Example: pinger 192.168.1.1 400000 750 This sends a 750 +28 Byte big ping packet to 192.168.1.1 every second, with a cutoff RTT of 4e5 ns = 0.4 ms The output files are text files, with suffix txt, e. g. rtt_10s_750B_192.168.1.1.txt - the RTT values [ms] with 10 s average, 750 Byte net size, destination 192.168.1.1 retval1_10s_750B_192.168.1.1.txt - ping return value 1, packet loss [%], with 10 s average, 750 Byte net size, destination 192.168.1.1. Pinger is a program for long-term monitoring the quality of a data connection, with one unfragmented ping per second. It also creates statistics on the return values of ping. Purpose is to create statistics for a) RTT (round trip time), exit code 0 from ping b) Packet Loss, exit code 1 from ping c) errors, not a simple timeout but duplicate packets, bit rot/silent data corruption etc., exit code 2 from ping So if the exit code/return value from ping is never two there should be no bit rot/silent data corruption, but if some pings return the value two that can have many reasons, e. g. no route to the destination (connect: Network is unreachable) or unknown host. Structure: A coordinating and evaluating main thread and MAX_THREADS, usually 10, threads for pinging. The values are calculated as exponential moving average with alpha = 0.1, 0.001 and 1/86.400, for 10 s, 1000 s and 1 day average values, to see short-term variations, e. g. the (short) connection interruption during the change of the public IP, medium-term variations like crosstalk or EMI, and long-term variations like a slightly higher RTT caused by an added switch or IMSI-catcher. The values are also good for comparing internet connections, routers, switches etc.. For a detailed monitoring you should use a) am small ping size, b) a medium ping size and c) the maximum ping size. For example 16, 722 and 1472 (net size). Less than 16 should not be used because the iputils ping does not show the RTT for such small pings. The traffic caused by the example is 44 +750 +1500 B/s = 2294 B/s upstream and downstream, about 6GB/month. The first version from 2014-01 works under Ubuntu and with the ping from the iputils package, but it can be easily modified to use other pings, also non-ICMP "pings" like: * arping for ARP pings * hping for TCP, UDP, ICMP and RAW-IP pings * httping for HTTP pings * ipmiping or rmcpping for IMPI pings * Echo (RFC 862) * small downloads via wget * time for establishing and terminating a telnet connection ... For monitoring the quantity (bandwitdth) of a data connection you need other methods than single pings, e. g. flooding ping, ethtool status, iwconfig status, router status or test downloads/uploads. The program was developed with the Iputils Ping under Bash (GNU bash, Version 4.2.25) and Debian/Ubuntu and may not work properly with other Shells, Pings or operating systems. Copyright Dr. Rolf Freitag (rolf dot freitag at email dot de), 2013 - ... * ---------------------------------------------------------------------------- * "THE BEERWARE LICENSE" (Revision 44): * Dr. Rolf Freitag (rolf dot freitag at email dot de) wrote this file. * As long as you retain this notice you can do whatever * the GPL (GNU Public License version 3) allows with this stuff. * If you think this stuff is worth it, you can send me money via * paypal, and get a contribution receipt if you wish, or if we met some day * you can buy me a beer in return. * ---------------------------------------------------------------------------- */ #define _GNU_SOURCE #define __STDC_FORMAT_MACROS #include #include #include #include // strndup, #include // necessary for unlink, gtopt, link, fcmp. #include // signals #include #include #include #include #include #include // for and, bitand, or, bitor ... #include // UCHAR_MAX #include #include #include #include #include #include #include #include // mlockall // simple version (date) #define SOFTWARE_VERSION "2014-05-13" #define mc_MIN(a, b) ((a) < (b) ? (a) : (b)) #define mc_MAX(a, b) ((a) > (b) ? (a) : (b)) // Macro for low priority and usually ignored errors like return code (arg) not equal zero from printf. #define mc_ERRORREPORT(arg) if (arg) { struct tm *tm; time_t t1= time (NULL); tm = localtime (&t1); (void)fprintf(stderr, \ "Low priority Error %d at %d-%d-%d, %d:%d:%d, \"%s\", \"%s\", line %d, errno: %d\n", \ arg, tm->tm_year +1900, tm->tm_mon +1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, __FILE__, __FUNCTION__, __LINE__, errno);} // For registering the signal handler void sig_handler (int sig). This macro saves 32 bytes ;-) #define mc_register_sig_handler \ {\ int i;\ for(i=0; i<=UCHAR_MAX; i++)\ signal (i, sig_handler);\ } // Initialize an array, struct or union variable via memset with value uc. #define mc_INIT_AR(varptr, uc) memset((void *)(varptr), (unsigned char)(uc), sizeof((varptr))) // maximum number of threads; 10 should be enough #define MAX_THREADS 10 #define MAX_LINE_LENGTH 1234 #define PINGSIZE 56 // ping net size: Must be > 16 to get the RTT, default 56 static int gi_pingsize = PINGSIZE; // net ping size //char gac_tmpdir[MAX_LINE_LENGTH + 1] = "\0"; // tmporary working directory, will be set in main //static int fd; // file descriptors static long long int g_t; // global time = start time static char *g_s; // global string static int i_exit_flag; // flag for exiting soon (not immediately) and controlled after a signal to terminate static pthread_t th[MAX_THREADS]; // ping threads static pthread_rwlock_t rwlock[MAX_THREADS]; // Read-Write-Locks static struct { int i_retval; // last return value of the ping from thread [index] float f_rtt; // last rtt value of the ping from thread [index] } s_value[MAX_THREADS]; // struct of the ping values: At index X from the thread with ID X static float af_rtt_min[3]; // minimum rtt value for 10 s, 1000 s, 1 d static float af_rtt_max[3]; // maximum rtt value for 10 s, 1000 s, 1 d static float f_rtt_limit = 1; // Cutoff rtt value [ms], default 1 s static uint64_t u64_cutoffcounter; // counter for the cutoffs void sig_handler (const int sig) // signal handler { if ((SIGINT == sig) or (SIGILL == sig) or (SIGKILL == sig) or (SIGSEGV == sig) or (SIGTERM == sig)) { i_exit_flag = 1; // if ((SIGILL == sig) or (SIGKILL == sig) or (SIGSEGV == sig)) // { // (void) printf ("\a\a\a\a\a\a\a Signal %d, program exiting... \r\n\a\a\a", sig); // exit (0); // } } return; } // time in microseconds inline signed long long int get_time () { static struct timeval ti; static struct timezone tzp; gettimeofday (&ti, &tzp); return (ti.tv_usec + 1000000 * ((long long int) ti.tv_sec)); } // ping thread: Ping at (second % MAX_THREADS) == ID, store the RTT and return value for later evaluation void * func_ping (void *threadid) { FILE *fp = NULL; int i_ret = 0, i_id = *(int *) threadid, i_sendflag = 0, i_tmp = 0; long long int lli_i = 0; char ac_line[MAX_LINE_LENGTH + 1] = // command line { '\0' }; float f_rtt = 0, f_0 = 0, f_1 = 0, f_2 = 0, f_3 = 0; for (; i_exit_flag < 1;) // (nearly) endless loop { lli_i = get_time () / 1000000; // time (NULL); if (i_id == (lli_i - g_t) % MAX_THREADS) // if the time (second) is right { if (0 == i_sendflag) // if the pinging was not done yet { //printf ("id %d\n", i_id); mc_INIT_AR (ac_line, 0); //snprintf (ac_line, MAX_LINE_LENGTH, "ping -q -c 1 -W 1 -M do -s %d -p 0f1e2d3c4b5a6978 %s | grep rtt | cut -d\"=\" -f2 | cut -d\" \" -f2 | cut -d\"/\" -f1 > %s%d.txt", gi_pingsize, g_s, gac_tmpdir, i_id); //printf ("%s\n", ac_line); //i_ret = system (ac_line); // command line: Ping quiet, one time, with 5 s timeout, unfragmented, net sizte gi_pingsize, with random 128 bit pattern (8x16 bit), to target g_s i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "ping -q -c 1 -W 5 -M do -s %d -p $(printf \"%%04x\" $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM $RANDOM) %s", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = popen (ac_line, "r"); // send the ping (command line ac_line), set the file pointer (fp) to the output of ping if (NULL == fp) { (void) fprintf (stderr, "Could not open Pipe, ignoring.\n"); //err_sys("popen error"); //return (-1); } else // pipe could be opened { f_rtt = -1; // init with sentinel -1 while (fgets (ac_line, sizeof (ac_line) - 1, fp)) // read the complete output from ping, line by line, till EOF (termination of ping) { //(void)printf ("%s", ac_line); i_ret = sscanf (ac_line, "rtt min/avg/max/mdev = %f/%f/%f/%f ms", &f_0, &f_1, &f_2, &f_3); // ignore EOF and not successfull scans, store the RTT in f_rtt if ((EOF != i_ret) and (4 == i_ret)) f_rtt = f_0; } i_ret = pclose (fp); // The returnvalue of the child process is in the top 16 bits; in the first 8. i_tmp = i_ret >> 8; // now we have the RTT and return value //(void) printf ("rtt: %f, return value: %d, i_tmp: %d, eq: %d\n", f_rtt, i_ret, i_tmp, (-1 == f_rtt) ? 1 : 0); i_ret = pthread_rwlock_wrlock (&rwlock[i_id]); mc_ERRORREPORT (i_ret); s_value[i_id].i_retval = i_tmp; s_value[i_id].f_rtt = f_rtt; i_ret = pthread_rwlock_unlock (&rwlock[i_id]); i_sendflag = 1; // sending done } } // if sendflag } // if the time is right else { i_sendflag = 0; // not the right time; the sent flag can be cleared } (void) usleep (400000); // wait 400 ms } pthread_exit (NULL); } // func_ping // 10 s evaluation function: Each start adds values. // When the rtt is zero the value is invalid, from an empty ping output, because even (ICMP) pinging to localhost shows a rtt of some µs. void add_val10s (int i_retval, float f_rtt) { static uint64_t u64_counter; // counter of function calls static long double ald_avg[5]; // averages: At index 0 return code 0, ... at index 2 return code 2, at 3 all other, at 4 RTT int i = 0, i_ret = 0; float f = 0; static int i_validcounter = 0; FILE *fp; struct tm *ptrtm_now; time_t tactualtime = time (NULL); char ac_line[MAX_LINE_LENGTH + 1] = // command line { '\0' }; //(void) printf ("counter: %" PRIu64 ", retval: %d, rtt: %f\n", u64_counter, i_retval, f_rtt); // exponential moving average or warming up. if (u64_counter < 10) // warming up: Add the first 10 values { switch (i_retval) { case 0: ald_avg[0]++; break; case 1: ald_avg[1]++; break; case 2: ald_avg[2]++; break; default: ald_avg[3]++; break; } if (f_rtt > 0.0) { ald_avg[4] += (long double) f_rtt; i_validcounter++; } if (9 == u64_counter) // End of warming up: Divide by 10 { for (i = 0; i <= 3; i++) ald_avg[i] /= (long double) 10.0; if (i_validcounter) // if there was at least one valid value { ald_avg[4] /= (long double) i_validcounter; f = (float) ald_avg[4]; af_rtt_min[0] = f; af_rtt_max[0] = f; } } } else // moving average { switch (i_retval) { case 0: ald_avg[0] += 0.1; break; case 1: ald_avg[1] += 0.1; break; case 2: ald_avg[2] += 0.1; break; default: ald_avg[3] += 0.1; break; } for (i = 0; i <= 3; i++) ald_avg[i] /= (long double) 1.1; if (f_rtt > 0.0) // if the rtt is valid { ald_avg[4] += (long double) f_rtt / (long double) 10.0; ald_avg[4] /= (long double) 1.1; f = (float) ald_avg[4]; af_rtt_min[0] = mc_MIN (f, af_rtt_min[0]); af_rtt_max[0] = mc_MAX (f, af_rtt_max[0]); } //else printf ("invalid rtt\n"); } // now the output, at counter equal 9, 19, ...: The return codes in %, the rtt in ms if (0 == (u64_counter + 1ull) % 10) { ptrtm_now = localtime (&tactualtime); //for (i = 0; i <= 4; i++) (void) printf ("%" PRIu64 " ald_avg[i]: %Lf\n", u64_counter, ald_avg[i]); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retval0_10s_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[0] * (long double) 100.0); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retval1_10s_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[1] * (long double) 100.0); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retval2_10s_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[2] * (long double) 100.0); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); /* i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retval3_10s_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[3] * (long double) 100.0); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); */ i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "rtt_10s_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[4]); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); } u64_counter++; // increase the call counter before the exit return; } // add_val10s // 1000 s evaluation function: Each start adds values. void add_val1000s (int i_retval, float f_rtt) { static uint64_t u64_counter; // counter of function calls static long double ald_avg[5]; // averages: At index 0 return code 0, ... at index 2 return code 2, at 3 all other, at 4 RTT int i = 0, i_ret = 0; float f = 0; static int i_validcounter = 0; FILE *fp; struct tm *ptrtm_now; time_t tactualtime = time (NULL); char ac_line[MAX_LINE_LENGTH + 1] = // command line { '\0' }; //(void) printf ("counter: %" PRIu64 ", retval: %d, rtt: %f\n", u64_counter, i_retval, f_rtt); // exponential moving average or warming up. if (u64_counter < 1000) // warming up: Add the first 1000 values { switch (i_retval) { case 0: ald_avg[0]++; break; case 1: ald_avg[1]++; break; case 2: ald_avg[2]++; break; default: ald_avg[3]++; break; } if (f_rtt > 0.0) { ald_avg[4] += (long double) f_rtt; i_validcounter++; } if (999 == u64_counter) // End of warming up: Divide by 1000 { for (i = 0; i <= 3; i++) ald_avg[i] /= (long double) 1000.0; if (i_validcounter) // if there was at least one valid value { ald_avg[4] /= (long double) i_validcounter; f = (float) ald_avg[4]; af_rtt_min[1] = f; af_rtt_max[1] = f; } } } else // moving average { switch (i_retval) { case 0: ald_avg[0] += 0.001; break; case 1: ald_avg[1] += 0.001; break; case 2: ald_avg[2] += 0.001; break; default: ald_avg[3] += 0.001; break; } for (i = 0; i <= 3; i++) ald_avg[i] /= (long double) 1.001; if (f_rtt > 0.0) // if the rtt is valid { ald_avg[4] += (long double) f_rtt / (long double) 1000.0; ald_avg[4] /= (long double) 1.001; f = (float) ald_avg[4]; af_rtt_min[1] = mc_MIN (f, af_rtt_min[1]); af_rtt_max[1] = mc_MAX (f, af_rtt_max[1]); } //else printf ("invalid rtt\n"); } // now the output, at counter equal 9, 19, ...: The return codes in %, the rtt in ms if (0 == (u64_counter + 1ull) % 1000) { ptrtm_now = localtime (&tactualtime); //for (i = 0; i <= 4; i++) (void) printf ("%" PRIu64 " ald_avg[i]: %Lf\n", u64_counter, ald_avg[i]); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retval0_1000s_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[0] * (long double) 100.0); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retval1_1000s_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[1] * (long double) 100.0); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retval2_1000s_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[2] * (long double) 100.0); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "rtt_1000s_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[4]); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); } u64_counter++; // increase the call counter before the exit return; } // add_val1000s // 1 d evaluation function: Each start adds values. void add_val1d (int i_retval, float f_rtt) { static uint64_t u64_counter; // counter of function calls static long double ald_avg[5]; // averages: At index 0 return code 0, ... at index 2 return code 2, at 3 all other, at 4 RTT int i = 0, i_ret = 0; float f = 0; static int i_validcounter = 0; FILE *fp; struct tm *ptrtm_now; time_t tactualtime = time (NULL); char ac_line[MAX_LINE_LENGTH + 1] = // command line { '\0' }; //(void) printf ("counter: %" PRIu64 ", retval: %d, rtt: %f\n", u64_counter, i_retval, f_rtt); // exponential moving average or warming up. if (u64_counter < 86400) // warming up: Add the first 86400 values { switch (i_retval) { case 0: ald_avg[0]++; break; case 1: ald_avg[1]++; break; case 2: ald_avg[2]++; break; default: ald_avg[3]++; break; } if (f_rtt > 0.0) { ald_avg[4] += (long double) f_rtt; i_validcounter++; } if (86399 == u64_counter) // End of warming up: Divide by 86400 { for (i = 0; i <= 3; i++) ald_avg[i] /= (long double) 86400.0; if (i_validcounter) // if there was at least one valid value { ald_avg[4] /= (long double) i_validcounter; f = (float) ald_avg[4]; af_rtt_min[2] = f; af_rtt_max[2] = f; } } } else // moving average { switch (i_retval) { case 0: ald_avg[0] += (long double) 1.0 / (long double) 86400.0; break; case 1: ald_avg[1] += (long double) 1.0 / (long double) 86400.0; break; case 2: ald_avg[2] += (long double) 1.0 / (long double) 86400.0; break; default: ald_avg[3] += (long double) 1.0 / (long double) 86400.0; break; } for (i = 0; i <= 3; i++) ald_avg[i] /= (long double) 86401.0 / (long double) 86400.0; if (f_rtt > 0.0) // if the rtt is valid { ald_avg[4] += (long double) f_rtt / (long double) 86400.0; ald_avg[4] /= (long double) 86401.0 / (long double) 86400.0; f = (float) ald_avg[4]; af_rtt_min[2] = mc_MIN (f, af_rtt_min[2]); af_rtt_max[2] = mc_MAX (f, af_rtt_max[2]); } //else printf ("invalid rtt\n"); } // now the output, at counter equal 9, 19, ...: The return codes in %, the rtt in ms if (0 == (u64_counter + 1ull) % 86400) { ptrtm_now = localtime (&tactualtime); //for (i = 0; i <= 4; i++) (void) printf ("%" PRIu64 " ald_avg[i]: %Lf\n", u64_counter, ald_avg[i]); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retval0_1d_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[0] * (long double) 100.0); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retval1_1d_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[1] * (long double) 100.0); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retval2_1d_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[2] * (long double) 100.0); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "rtt_1d_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "a"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d %-14.10Lf\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, ald_avg[4]); mc_ERRORREPORT (!i_ret); i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); } u64_counter++; // increase the call counter before the exit return; } // add_val1d // Simple return values evaluation function, should update the statistics output file each second void add_retval (int i_retval, float f_rtt) { static uint64_t au64_retval[258]; // counter for the return values: Index 0 for value 0 etc., 256 for all values > 255, 257 for all values < 0 static uint64_t u64_functioncallcounter, u64_validrttcounter; static float f_rtt_min, f_rtt_max, f_rtt_max_cutoffed; static int i_retval_min, i_retval_max; int i_ret = 0; FILE *fp; struct tm *ptrtm_now; time_t tactualtime = time (NULL); char ac_line[MAX_LINE_LENGTH + 1] = // command line { '\0' }; if (u64_functioncallcounter++) // after the first run of this function { // store minimal and maximal value i_retval_min = mc_MIN (i_retval, i_retval_min); i_retval_max = mc_MAX (i_retval, i_retval_max); } else // first run { i_retval_min = i_retval; i_retval_max = i_retval; } if (f_rtt > 0) // if the rtt is valid { u64_validrttcounter++; if (0 == f_rtt_min) // if the minimum has the initial value (0) f_rtt_min = f_rtt; else f_rtt_min = mc_MIN (f_rtt, f_rtt_min); f_rtt_max = mc_MAX (f_rtt, f_rtt_max); if (f_rtt_limit >= f_rtt) // if the rtt is not above the cutoff limit f_rtt_max_cutoffed = mc_MAX (f_rtt, f_rtt_max_cutoffed); } // limitations before storing in the array if (i_retval > 255) i_retval = 256; if (i_retval < 0) i_retval = 257; // logging (storing) au64_retval[i_retval]++; // output ptrtm_now = localtime (&tactualtime); i_ret = snprintf (ac_line, MAX_LINE_LENGTH, "retvals_%dB_%s.txt", gi_pingsize, g_s); mc_ERRORREPORT (!i_ret); fp = fopen (ac_line, "w"); if (fp) { i_ret = fprintf (fp, "%04d-%02d-%02d %02d:%02d:%02d, Cutoff at %f ms, Cutoffs: %" PRIu64 " = %f %%\n", ptrtm_now->tm_year + 1900, ptrtm_now->tm_mon + 1, ptrtm_now->tm_mday, ptrtm_now->tm_hour, ptrtm_now->tm_min, ptrtm_now->tm_sec, f_rtt_limit, u64_cutoffcounter, 100 * (float) u64_cutoffcounter / (float) u64_validrttcounter); mc_ERRORREPORT (!i_ret); i_ret = fprintf (fp, "Min rtt value [ms]: %f, Max: %f, Max with cutoff: %f\n", f_rtt_min, f_rtt_max, f_rtt_max_cutoffed); mc_ERRORREPORT (!i_ret); if (af_rtt_min[0] or af_rtt_max[0]) { i_ret = fprintf (fp, "Min rtt value (10 s avg): %f, Max: %f\n", af_rtt_min[0], af_rtt_max[0]); mc_ERRORREPORT (!i_ret); } if (af_rtt_min[1] or af_rtt_max[1]) { i_ret = fprintf (fp, "Min rtt value (1000 s avg): %f, Max: %f\n", af_rtt_min[1], af_rtt_max[1]); mc_ERRORREPORT (!i_ret); } if (af_rtt_min[2] or af_rtt_max[2]) { i_ret = fprintf (fp, "Min rtt value (1 d avg): %f, Max: %f\n", af_rtt_min[2], af_rtt_max[2]); mc_ERRORREPORT (!i_ret); } i_ret = fprintf (fp, "Minimum ping return value: %d, Max: %d\n", i_retval_min, i_retval_max); mc_ERRORREPORT (!i_ret); i_ret = fprintf (fp, "Count of ping returns with value 0: %" PRIu64 ", 1: %" PRIu64 ", 2: %" PRIu64 "\n", au64_retval[0], au64_retval[1], au64_retval[2]); mc_ERRORREPORT (!i_ret); /* fprintf (fp, " Ping return value counts, empty when zero and starting with the counter for 0: \n "); for (i = 0; i < 258; i++) { if (au64_retval[i]) fprintf (fp, " % " PRIu64 " \ n ", au64_retval[i]); else fprintf (fp, " \ n "); } */ i_ret = fclose (fp); mc_ERRORREPORT (i_ret); } else (void) fprintf (stderr, "Error: Could not open output file %s.\n", ac_line); return; } // add_retval /*--------------------------------------- Main -------------------------------------------*/ int main (int argc, char **argv) { signed int i = 0, a_i[MAX_THREADS]; long long int t = 0; int i_ret = 0, i_retval = 0; float f_rtt = 0; struct sched_param param; param.sched_priority = 98; // second highest RT priority pthread_t this_thread = pthread_self(); // the currently running thread if ((argc > 1) and not strncmp (argv[1], "-V", 123)) { (void) fprintf (stderr, "%s version %s\n", argv[0], SOFTWARE_VERSION); exit ((2 == argc) ? 0 : -1); } if ((argc < 3) or (argc > 4)) { (void) fprintf (stderr, " Usage: %s < Target IP > < RTT cutoff [ns]> [ping net size [Bytes]]\n", argv[0]); //(void)fprintf (stderr, "[] = Optional <> = Parameter Requirement.1 parameter required. \n"); exit (-1); } g_s = argv[1]; // make the first argument (IP) global f_rtt_limit = 1e-6 * strtol (argv[2], (char **) NULL, 0); // set the RTT cutoff in ms if (4 == argc) // set the ping net size gi_pingsize = strtol (argv[3], (char **) NULL, 0); mc_register_sig_handler; // register signal handler before critical sections if (getuid ()and geteuid ()) { (void) fprintf (stderr, "Warning: The program was not started from root and can therefore not set a high scheduling priority.\n"); } // set a high priority to minmize delays by local processes i_ret = -20 - nice (-40); // set a nice level of -20, independent of the current one, return the difference between -20 and the new nice level mc_ERRORREPORT (i_ret); i_ret = system ("ionice -c 1 -n 0 -p $PPID"); // realtime sheduling class for I/O mc_ERRORREPORT (i_ret); #ifndef __CYGWIN__ i_ret = mlockall (MCL_CURRENT | MCL_FUTURE); // lock memory, be cached mc_ERRORREPORT (i_ret); i_ret = sched_setscheduler (0, SCHED_FIFO, ¶m); // set RT priority, not hard real time but better than nothing mc_ERRORREPORT (i_ret); param.sched_priority = sched_get_priority_max (SCHED_FIFO); i_ret = pthread_setschedparam (this_thread, SCHED_FIFO, ¶m); // set thread priority mc_ERRORREPORT (i_ret); #endif t = time (NULL); // store the start time g_t = t; // make the start time global // create threads, Initialise thread variables for (i = 0; i < MAX_THREADS; i++) { a_i[i] = i; // Initialize thread ids i_ret = pthread_rwlock_init (&rwlock[i], NULL); // Initialize locks mc_ERRORREPORT (i_ret); s_value[i].i_retval = -1; // sentinel -1 s_value[i].f_rtt = -1; // sentinel -1 // create ping thread with i as argument i_ret = pthread_create (&th[i], NULL, func_ping, (void *) &a_i[i]); //(void *(*)(void *)) if (i_ret) { (void) fprintf (stderr, "ERROR at id %d; return code from pthread_create() is %d\n", i, i_ret); exit (-1); } } for (; i_exit_flag < 1;) { // Check the ping results for (i = 0; i < MAX_THREADS; i++) { i_ret = pthread_rwlock_rdlock (&rwlock[i]); mc_ERRORREPORT (i_ret); if (s_value[i].i_retval not_eq - 1) // if new value { // get the new values i_retval = s_value[i].i_retval; f_rtt = s_value[i].f_rtt; // finish: New lock, Set sentinel values i_ret = pthread_rwlock_unlock (&rwlock[i]); mc_ERRORREPORT (i_ret); i_ret = pthread_rwlock_wrlock (&rwlock[i]); mc_ERRORREPORT (i_ret); s_value[i].i_retval = -1; s_value[i].f_rtt = -1; // now the evaluation add_retval (i_retval, f_rtt); // filter: Set all rtts above the limit to zero = invalid, before calculating the average values if (f_rtt > f_rtt_limit) { f_rtt = 0; u64_cutoffcounter++; // new cutoff } add_val10s (i_retval, f_rtt); add_val1000s (i_retval, f_rtt); add_val1d (i_retval, f_rtt); } i_ret = pthread_rwlock_unlock (&rwlock[i]); mc_ERRORREPORT (i_ret); } (void) usleep (500000); // wait 0.5 s } // wait for the termination of the threads for (i = 0; i < MAX_THREADS; i++) { i_ret = pthread_join (th[i], NULL); mc_ERRORREPORT (i_ret); } i_ret = fcloseall (); mc_ERRORREPORT (i_ret); pthread_exit (NULL); // exit (0); } // main