aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
authorDavid M. Syzdek <david@syzdek.net>2020-02-07 16:39:39 -0900
committerDavid M. Syzdek <david@syzdek.net>2020-02-07 16:39:39 -0900
commite5458fc2fa378a1b29cb4d6cdbf6727b4763b712 (patch)
treeddf72383b8236ed7a321ad4c89c243144b065d56 /src
parent1d9ed2af174b283c282e228433ecfc22bf989ccb (diff)
downloadakcom-udpecho-e5458fc2fa378a1b29cb4d6cdbf6727b4763b712.tar.gz
akcom-udpecho-e5458fc2fa378a1b29cb4d6cdbf6727b4763b712.tar.bz2
akcom-udpecho-e5458fc2fa378a1b29cb4d6cdbf6727b4763b712.tar.xz
akcom-udpecho-e5458fc2fa378a1b29cb4d6cdbf6727b4763b712.zip
akcom-udpecho: adding network code and refactoring options
Diffstat (limited to 'src')
-rw-r--r--src/akcom-udpecho.c350
1 files changed, 329 insertions, 21 deletions
diff --git a/src/akcom-udpecho.c b/src/akcom-udpecho.c
index 5b6d6e9..1dbfe1f 100644
--- a/src/akcom-udpecho.c
+++ b/src/akcom-udpecho.c
@@ -82,6 +82,8 @@
82#include <syslog.h> 82#include <syslog.h>
83#include <signal.h> 83#include <signal.h>
84#include <poll.h> 84#include <poll.h>
85#include <string.h>
86#include <strings.h>
85 87
86#include "akcom-udpecho.h" 88#include "akcom-udpecho.h"
87 89
@@ -111,6 +113,12 @@
111///////////////// 113/////////////////
112#pragma mark - Datatypes 114#pragma mark - Datatypes
113 115
116union udp_buffer
117{
118 uint8_t * data;
119 struct udp_echo_plus * echoplus;
120};
121
114 122
115///////////////// 123/////////////////
116// // 124// //
@@ -119,12 +127,17 @@
119///////////////// 127/////////////////
120#pragma mark - Variables 128#pragma mark - Variables
121 129
122static const char * prog_name = PROGRAM_NAME; 130static const char * prog_name = PROGRAM_NAME;
123static int cnf_echoplus = 0; 131static uint32_t cnf_count = 0;
124static int cnf_verbose = 0; 132static int cnf_echoplus = 0;
125static time_t cnf_timeout = 5; 133static int cnf_verbose = 0;
126static short cnf_port = 30006; 134static int cnf_silent = 0;
127static const char * cnf_host = NULL; 135static unsigned long cnf_timeout = 5;
136static const char * cnf_port = "30006";
137static const char * cnf_host = NULL;
138static unsigned long cnf_interval = 1;
139static size_t cnf_packetsize = sizeof(struct udp_echo_plus);
140static int should_stop = 0;
128 141
129 142
130////////////////// 143//////////////////
@@ -137,6 +150,9 @@ static const char * cnf_host = NULL;
137// main statement 150// main statement
138int main(int argc, char * argv[]); 151int main(int argc, char * argv[]);
139 152
153// signal system stop
154void my_stop(int signum);
155
140// display program usage 156// display program usage
141void my_usage(void); 157void my_usage(void);
142 158
@@ -155,16 +171,46 @@ void my_usage_error(const char * fmt, ...);
155int main(int argc, char * argv[]) 171int main(int argc, char * argv[])
156{ 172{
157 int c; 173 int c;
174 int s;
175 int fd;
176 int rc;
158 int opt_index; 177 int opt_index;
178 uint32_t count;
179 uint32_t rcvd;
180 uint64_t epoch;
181 uint64_t epoch_adj;
182 uint64_t epoch_max;
183 uint64_t delay;
184 uint64_t max;
185 uint64_t max_adj;
186 uint64_t min;
187 uint64_t min_adj;
188 uint64_t avg;
189 uint64_t avg_adj;
190 ssize_t size;
191 unsigned short port;
159 char * ptr; 192 char * ptr;
160 //union my_sa sa; 193 char addrstr[INET6_ADDRSTRLEN];
194 char logmsg[256];
195 union my_sa sa;
196 socklen_t socklen;
197 struct addrinfo * res;
198 struct addrinfo * info;
199 struct addrinfo hints;
200 struct timespec start;
201 struct timespec now;
202 struct pollfd fds[2];
203 union udp_buffer sndbuff;
204 union udp_buffer rcvbuff;
161 205
162 // getopt options 206 // getopt options
163 static char short_opt[] = "d:D:efg:hl:np:P:ru:vV"; 207 static char short_opt[] = "46c:ehi:qrs:t:vV";
164 static struct option long_opt[] = 208 static struct option long_opt[] =
165 { 209 {
166 {"echoplus", no_argument, 0, 'e'}, 210 {"echoplus", no_argument, 0, 'e'},
167 {"help", no_argument, 0, 'h'}, 211 {"help", no_argument, 0, 'h'},
212 {"quiet", no_argument, 0, 'q'},
213 {"silent", no_argument, 0, 'q'},
168 {"rfc", no_argument, 0, 'r'}, 214 {"rfc", no_argument, 0, 'r'},
169 {"verbose", no_argument, 0, 'v'}, 215 {"verbose", no_argument, 0, 'v'},
170 {"version", no_argument, 0, 'V'}, 216 {"version", no_argument, 0, 'V'},
@@ -176,6 +222,13 @@ int main(int argc, char * argv[])
176 if ((ptr = rindex(argv[0], '/')) != NULL) 222 if ((ptr = rindex(argv[0], '/')) != NULL)
177 prog_name = &ptr[1]; 223 prog_name = &ptr[1];
178 224
225 // initializes variables
226 bzero(&hints, sizeof(struct addrinfo));
227 hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED | AI_ALL;
228 hints.ai_family = PF_UNSPEC;
229 hints.ai_socktype = SOCK_DGRAM;
230 hints.ai_protocol = IPPROTO_UDP;
231
179 // process arguments 232 // process arguments
180 while((c = getopt_long(argc, argv, short_opt, long_opt, &opt_index)) != -1) 233 while((c = getopt_long(argc, argv, short_opt, long_opt, &opt_index)) != -1)
181 { 234 {
@@ -185,6 +238,18 @@ int main(int argc, char * argv[])
185 case 0: // long options toggles 238 case 0: // long options toggles
186 break; 239 break;
187 240
241 case '4':
242 hints.ai_family = PF_INET;
243 break;
244
245 case '6':
246 hints.ai_family = PF_INET6;
247 break;
248
249 case 'c':
250 cnf_count = (uint32_t)strtoul(optarg, NULL, 10);
251 break;
252
188 case 'e': 253 case 'e':
189 cnf_echoplus = 1; 254 cnf_echoplus = 1;
190 break; 255 break;
@@ -197,6 +262,22 @@ int main(int argc, char * argv[])
197 my_usage(); 262 my_usage();
198 return(0); 263 return(0);
199 264
265 case 'i':
266 cnf_interval = (unsigned)strtoul(optarg, NULL, 10);
267 break;
268
269 case 'q':
270 cnf_silent = 1;
271 break;
272
273 case 's':
274 cnf_packetsize = (size_t)strtoull(optarg, NULL, 10);
275 break;
276
277 case 't':
278 cnf_interval = (unsigned)strtoul(optarg, NULL, 10);
279 break;
280
200 case 'v': 281 case 'v':
201 cnf_verbose++; 282 cnf_verbose++;
202 break; 283 break;
@@ -222,12 +303,7 @@ int main(int argc, char * argv[])
222 cnf_host = argv[optind++]; 303 cnf_host = argv[optind++];
223 if (optind < argc) 304 if (optind < argc)
224 { 305 {
225 cnf_port = (short)strtoul(argv[optind++], &ptr, 10); 306 cnf_port = argv[optind++];
226 if ( (!(cnf_port)) || ((ptr[0])) )
227 {
228 my_usage_error("invalid port number");
229 return(1);
230 };
231 }; 307 };
232 if (optind < argc) 308 if (optind < argc)
233 { 309 {
@@ -235,23 +311,255 @@ int main(int argc, char * argv[])
235 return(1); 311 return(1);
236 }; 312 };
237 313
238printf("connect to %s:%hu\n", cnf_host, cnf_port); 314
315 // allocate buffer
316 if ((fd = open("/dev/urandom", O_RDONLY)) == -1)
317 {
318 fprintf(stderr, "%s: open(/dev/urandom): %s\n", prog_name, strerror(errno));
319 return(1);
320 };
321 if (cnf_packetsize < sizeof(struct udp_echo_plus))
322 cnf_packetsize = sizeof(struct udp_echo_plus);
323 if ((sndbuff.data = malloc(cnf_packetsize)) == NULL)
324 {
325 fprintf(stderr, "%s: out of virtual memory\n", prog_name);
326 close(fd);
327 return(1);
328 };
329 if ((rcvbuff.data = malloc(cnf_packetsize)) == NULL)
330 {
331 fprintf(stderr, "%s: out of virtual memory\n", prog_name);
332 close(fd);
333 return(1);
334 };
335 if ((size = read(fd, sndbuff.data, cnf_packetsize)) == -1)
336 {
337 fprintf(stderr, "%s: read(): %s\n", prog_name, strerror(errno));
338 close(fd);
339 free(sndbuff.data);
340 free(rcvbuff.data);
341 return(1);
342 };
343 close(fd);
344 bzero(sndbuff.data, sizeof(struct udp_echo_plus));
345
346
347 // resolve host
348 bzero(&sa, sizeof(sa));
349 bzero(addrstr, sizeof(addrstr));
350 socklen = 0;
351 if ((rc = getaddrinfo(cnf_host, cnf_port, &hints, &res)) != 0)
352 {
353 fprintf(stderr, "%s: getaddrinfo(): %s\n", prog_name, gai_strerror(rc));
354 free(sndbuff.data);
355 free(rcvbuff.data);
356 return(1);
357 };
358 for(info = res; (info != NULL); info = info->ai_next)
359 {
360 if (info->ai_addr->sa_family != AF_INET6)
361 continue;
362 socklen = info->ai_addrlen;
363 memcpy(&sa, info->ai_addr, socklen);
364 inet_ntop(AF_INET6, &sa.sin6.sin6_addr, addrstr, sizeof(addrstr));
365 port = ntohs(sa.sin6.sin6_port);
366 snprintf(logmsg, sizeof(logmsg), "UDPECHO %s:%hu ([%s]:%hu): %zu bytes\n", cnf_host, port, addrstr, port, cnf_packetsize);
367 }
368 if (sa.sa.sa_family != AF_INET6)
369 {
370 socklen = res->ai_addrlen;
371 memcpy(&sa.sa, res->ai_addr, socklen);
372 inet_ntop(AF_INET, &sa.sin.sin_addr, addrstr, sizeof(addrstr));
373 port = ntohs(sa.sin.sin_port);
374 snprintf(logmsg, sizeof(logmsg), "UDPECHO %s:%hu (%s:%hu): %zu bytes\n", cnf_host, port, addrstr, port, cnf_packetsize);
375 };
376 freeaddrinfo(res);
377 if (!(cnf_silent))
378 printf("%s", logmsg);
379
380
381 // open and connect socket
382 if ((s = socket(sa.sa.sa_family, SOCK_DGRAM, 0)) == -1)
383 {
384 fprintf(stderr, "%s: socket(): %s\n", prog_name, strerror(errno));
385 free(sndbuff.data);
386 free(rcvbuff.data);
387 return(1);
388 };
389 if ((rc = connect(s, &sa.sa, socklen)) == -1)
390 {
391 fprintf(stderr, "%s: connect(): %s\n", prog_name, strerror(errno));
392 free(sndbuff.data);
393 free(rcvbuff.data);
394 return(1);
395 };
396 fds[0].fd = s;
397 fds[0].events = POLLIN;
398 fds[0].revents = 0;
399 fds[1].fd = -1;
400 fds[1].events = 0;
401 fds[1].revents = 0;
402
403
404 // configure signals
405 signal(SIGPIPE, SIG_IGN);
406 signal(SIGINFO, SIG_IGN);
407 signal(SIGUSR1, SIG_IGN);
408 signal(SIGUSR2, SIG_IGN);
409 signal(SIGHUP, my_stop);
410 signal(SIGINT, my_stop);
411 signal(SIGQUIT, my_stop);
412 signal(SIGALRM, my_stop);
413 signal(SIGTERM, my_stop);
414
415
416 // initialize statistics
417 count = 0;
418 rcvd = 0;
419 min = 0;
420 min_adj = 0;
421 max = 0;
422 max_adj = 0;
423 avg = 0;
424 avg_adj = 0;
425
426
427 // start clock
428 clock_gettime(CLOCK_MONOTONIC_RAW, &start);
429 start.tv_sec -= cnf_interval;
430 epoch = 0;
431
432
433 // master loop
434 while (!(should_stop))
435 {
436 // calculate loop epoch
437 clock_gettime(CLOCK_MONOTONIC_RAW, &now);
438 epoch = ((uint64_t)now.tv_sec * 10000) + ((uint64_t)now.tv_nsec / 100000);
439 epoch -= ((uint64_t)start.tv_sec * 10000) + ((uint64_t)start.tv_nsec / 100000);
440
441 // trigger stop
442 if ( ((cnf_count)) && (count >= cnf_count) )
443 {
444 epoch_max = cnf_count * (uint64_t)cnf_interval * 10000;
445 epoch_max += (uint64_t)cnf_timeout * 10000;
446 if (( epoch >= epoch_max) || (rcvd >= count))
447 should_stop = 1;
448 };
449
450 // send UDP echo request
451 if ((count < (epoch/(10000*cnf_interval))) && ((count < cnf_count) || (!(cnf_count))) )
452 {
453 count++;
454 sndbuff.echoplus->req_sn = count;
455 send(s, sndbuff.data, cnf_packetsize, 0);
456 };
457
458 // receive UDP echo response
459 if ((rc = poll(fds, 1, 0)) > 0)
460 {
461 if ((size = recv(s, rcvbuff.data, cnf_packetsize, 0)) == (ssize_t)cnf_packetsize)
462 {
463 rcvd++;
464 delay = rcvbuff.echoplus->reply_time - rcvbuff.echoplus->recv_time;
465 delay /= 100000000;
466 epoch -= (rcvbuff.echoplus->req_sn * 10000);
467 epoch_adj = epoch - delay;
468 avg += epoch;
469 avg_adj += epoch_adj;
470 if ( (!(min)) || (epoch < min) )
471 min = epoch;
472 if ( (!(max)) || (epoch > max) )
473 max = epoch;
474 if ( (!(min_adj)) || (epoch_adj < min_adj) )
475 min_adj = epoch_adj;
476 if ( (!(max_adj)) || (epoch_adj > max_adj) )
477 max_adj = epoch_adj;
478 if ((cnf_echoplus))
479 {
480 printf("udpecho_seq=%u time=%llu.%llu ms delay=%llu.%llu ms adj_time=%llu.%llu ms\n",
481 rcvbuff.echoplus->req_sn,
482 epoch/10, epoch%10,
483 delay/10, delay%10,
484 epoch_adj/10, epoch_adj%10
485 );
486 } else
487 {
488 printf("udpecho_seq=%u time=%llu.%llu ms\n",
489 rcvbuff.echoplus->req_sn,
490 epoch/10, epoch%10
491 );
492 };
493 };
494 } else
495 {
496 usleep(100);
497 };
498 };
499
500
501 if (!(cnf_silent))
502 {
503 printf("\n");
504 printf("--- %s udpecho statistics ---\n", cnf_host);
505 printf("%i packets transmitted, %i packets received, %i.%i%% packet loss\n",
506 count,
507 rcvd,
508 ((count - rcvd) * 100) / count,
509 (((count - rcvd) * 1000) / count) % 10
510 );
511 printf("round-trip min/avg/max = %llu.%llu/%llu.%llu/%llu.%llu ms\n",
512 min/10, min%10,
513 (avg/rcvd)/10, (avg/rcvd)%10,
514 max/10, max%10
515 );
516 if ((cnf_echoplus))
517 {
518 printf("adjusted round-trip min/avg/max = %llu.%llu/%llu.%llu/%llu.%llu ms\n",
519 min_adj/10, min_adj%10,
520 (avg_adj/rcvd)/10, (avg_adj/rcvd)%10,
521 max_adj/10, max_adj%10
522 );
523 };
524 };
525
526
527 // free resources
528 free(sndbuff.data);
529 free(rcvbuff.data);
530 close(s);
531
239 532
240 return(0); 533 return(0);
241} 534}
242 535
243 536
537// signal system stop
538void my_stop(int signum)
539{
540 should_stop = 1;
541 signal(signum, my_stop);
542 return;
543}
544
545
244// display program usage 546// display program usage
245void my_usage(void) 547void my_usage(void)
246{ 548{
247 printf("Usage: %s [options] host [port]\n", prog_name); 549 printf("Usage: %s [options] host [port]\n", prog_name);
248 printf("OPTIONS:\n"); 550 printf("OPTIONS:\n");
249 printf(" -e, --echoplus enable echo plus, not RFC compliant%s\n", ((cnf_echoplus)) ? " (default)" : ""); 551 printf(" -4 connect via IPv4 only\n");
250 printf(" -h, --help print this help and exit\n"); 552 printf(" -6 connect via IPv6 only\n");
251 printf(" -r, --rfc RFC compliant echo protocol%s\n", (!(cnf_echoplus)) ? " (default)" : ""); 553 printf(" -c count stop after sending count packets\n");
252 printf(" -t sec response timeout (default: %lu s)\n", cnf_timeout); 554 printf(" -e, --echoplus expect echo plus response%s\n", ((cnf_echoplus)) ? " (default)" : "");
253 printf(" -v, --verbose enable verbose output\n"); 555 printf(" -h, --help print this help and exit\n");
254 printf(" -V, --version print version number and exit\n"); 556 printf(" -i interval interval between packet (default: %lu sec)\n", cnf_interval);
557 printf(" -r, --rfc expect RFC compliant echo response%s\n", (!(cnf_echoplus)) ? " (default)" : "");
558 printf(" -q, --quiet, --silent do not print messages\n");
559 printf(" -s packetsize size of data bytes to be sent. (default: %zu bytes)\n", cnf_packetsize);
560 printf(" -t sec response timeout (default: %lu sec)\n", cnf_timeout);
561 printf(" -v, --verbose enable verbose output\n");
562 printf(" -V, --version print version number and exit\n");
255 printf("\n"); 563 printf("\n");
256 return; 564 return;
257} 565}