"Fossies" - the Fresh Open Source Software Archive 
Member "fping-5.1/src/fping.c" (6 Feb 2022, 88372 Bytes) of package /linux/misc/fping-5.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /*
2 * fping: fast-ping, file-ping, favorite-ping, funky-ping
3 *
4 * Ping a list of target hosts in a round robin fashion.
5 * A better ping overall.
6 *
7 * fping website: http://www.fping.org
8 *
9 * Current maintainer of fping: David Schweikert
10 * Please send suggestions and patches to: david@schweikert.ch
11 *
12 *
13 * Original author: Roland Schemers <schemers@stanford.edu>
14 * IPv6 Support: Jeroen Massar <jeroen@unfix.org / jeroen@ipng.nl>
15 * Improved main loop: David Schweikert <david@schweikert.ch>
16 * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
17 * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
18 *
19 *
20 * Redistribution and use in source and binary forms are permitted
21 * provided that the above copyright notice and this paragraph are
22 * duplicated in all such forms and that any documentation,
23 * advertising materials, and other materials related to such
24 * distribution and use acknowledge that the software was developed
25 * by Stanford University. The name of the University may not be used
26 * to endorse or promote products derived from this software without
27 * specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31 */
32
33 #ifdef __cplusplus
34 extern "C" {
35 #endif /* __cplusplus */
36
37 #include "config.h"
38 #include "fping.h"
39 #include "options.h"
40 #include "optparse.h"
41
42 #include <inttypes.h>
43 #include <errno.h>
44 #include <signal.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdint.h>
48 #include <time.h>
49
50 #include "seqmap.h"
51
52 #ifdef HAVE_UNISTD_H
53 #include <unistd.h>
54 #endif /* HAVE_UNISTD_H */
55
56 #ifdef HAVE_STDLIB_H
57 #include <stdlib.h>
58 #endif /* HAVE_STDLIB_H */
59
60 #include <stddef.h>
61 #include <string.h>
62
63 #include <sys/socket.h>
64 #include <sys/time.h>
65 #include <sys/types.h>
66
67 #if HAVE_SYS_FILE_H
68 #include <sys/file.h>
69 #endif /* HAVE_SYS_FILE_H */
70
71 #ifdef IPV6
72 #include <netinet/icmp6.h>
73 #endif
74 #include <netinet/in_systm.h>
75
76 #include <netinet/ip.h>
77 #include <netinet/ip_icmp.h>
78
79 #include <arpa/inet.h>
80 #include <ctype.h>
81 #include <netdb.h>
82
83 #include <sys/select.h>
84
85 /*** compatibility ***/
86
87 /* Mac OS X's getaddrinfo() does not fail if we use an invalid combination,
88 * e.g. AF_INET6 with "127.0.0.1". If we pass AI_UNUSABLE to flags, it behaves
89 * like other platforms. But AI_UNUSABLE isn't available on other platforms,
90 * and we can safely use 0 for flags instead.
91 */
92 #ifndef AI_UNUSABLE
93 #define AI_UNUSABLE 0
94 #endif
95
96 /* MSG_TRUNC available on Linux kernel 2.2+, makes recvmsg return the full
97 * length of the raw packet received, even if the buffer is smaller */
98 #ifndef MSG_TRUNC
99 #define MSG_TRUNC 0
100 #define RECV_BUFSIZE 4096
101 #else
102 #define RECV_BUFSIZE 128
103 #endif
104
105 /*** externals ***/
106
107 extern char* optarg;
108 extern int optind, opterr;
109 #ifndef h_errno
110 extern int h_errno;
111 #endif
112
113 #ifdef __cplusplus
114 }
115 #endif /* __cplusplus */
116
117 /*** Constants ***/
118
119 #if HAVE_SO_TIMESTAMPNS
120 #define CLOCKID CLOCK_REALTIME
121 #endif
122
123 /* CLOCK_MONTONIC starts under macOS, OpenBSD and FreeBSD with undefined positive point and can not be use
124 * see github PR #217
125 */
126 #if !defined(CLOCKID)
127 #if defined(CLOCK_MONOTONIC) && !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
128 #define CLOCKID CLOCK_MONOTONIC
129 #else
130 #define CLOCKID CLOCK_REALTIME
131 #endif
132 #endif
133
134 /*** Ping packet defines ***/
135
136 #define MAX_IP_PACKET 65536 /* (theoretical) max IP packet size */
137 #define SIZE_IP_HDR 40
138 #define SIZE_ICMP_HDR 8 /* from ip_icmp.h */
139 #define MAX_PING_DATA (MAX_IP_PACKET - SIZE_IP_HDR - SIZE_ICMP_HDR)
140
141 #define MAX_GENERATE 100000 /* maximum number of hosts that -g can generate */
142
143 /* sized so as to be like traditional ping */
144 #define DEFAULT_PING_DATA_SIZE 56
145
146 /* maxima and minima */
147 #ifdef FPING_SAFE_LIMITS
148 #define MIN_INTERVAL 1 /* in millisec */
149 #define MIN_PERHOST_INTERVAL 10 /* in millisec */
150 #else
151 #define MIN_INTERVAL 0
152 #define MIN_PERHOST_INTERVAL 0
153 #endif
154
155 /* response time array flags */
156 #define RESP_WAITING -1
157 #define RESP_UNUSED -2
158 #define RESP_ERROR -3
159 #define RESP_TIMEOUT -4
160
161 /* debugging flags */
162 #if defined(DEBUG) || defined(_DEBUG)
163 #define DBG_TRACE 1
164 #define DBG_SENT_TIMES 2
165 #define DBG_RANDOM_LOSE_FEW 4
166 #define DBG_RANDOM_LOSE_MANY 8
167 #define DBG_PRINT_PER_SYSTEM 16
168 #define DBG_REPORT_ALL_RTTS 32
169 #endif /* DEBUG || _DEBUG */
170
171 /* Long names for ICMP packet types */
172 #define ICMP_TYPE_STR_MAX 18
173 char* icmp_type_str[19] = {
174 "ICMP Echo Reply", /* 0 */
175 "",
176 "",
177 "ICMP Unreachable", /* 3 */
178 "ICMP Source Quench", /* 4 */
179 "ICMP Redirect", /* 5 */
180 "",
181 "",
182 "ICMP Echo", /* 8 */
183 "",
184 "",
185 "ICMP Time Exceeded", /* 11 */
186 "ICMP Parameter Problem", /* 12 */
187 "ICMP Timestamp Request", /* 13 */
188 "ICMP Timestamp Reply", /* 14 */
189 "ICMP Information Request", /* 15 */
190 "ICMP Information Reply", /* 16 */
191 "ICMP Mask Request", /* 17 */
192 "ICMP Mask Reply" /* 18 */
193 };
194
195 char* icmp_unreach_str[16] = {
196 "ICMP Network Unreachable", /* 0 */
197 "ICMP Host Unreachable", /* 1 */
198 "ICMP Protocol Unreachable", /* 2 */
199 "ICMP Port Unreachable", /* 3 */
200 "ICMP Unreachable (Fragmentation Needed)", /* 4 */
201 "ICMP Unreachable (Source Route Failed)", /* 5 */
202 "ICMP Unreachable (Destination Network Unknown)", /* 6 */
203 "ICMP Unreachable (Destination Host Unknown)", /* 7 */
204 "ICMP Unreachable (Source Host Isolated)", /* 8 */
205 "ICMP Unreachable (Communication with Network Prohibited)", /* 9 */
206 "ICMP Unreachable (Communication with Host Prohibited)", /* 10 */
207 "ICMP Unreachable (Network Unreachable For Type Of Service)", /* 11 */
208 "ICMP Unreachable (Host Unreachable For Type Of Service)", /* 12 */
209 "ICMP Unreachable (Communication Administratively Prohibited)", /* 13 */
210 "ICMP Unreachable (Host Precedence Violation)", /* 14 */
211 "ICMP Unreachable (Precedence cutoff in effect)" /* 15 */
212 };
213
214 #define ICMP_UNREACH_MAXTYPE 15
215
216 struct event;
217 typedef struct host_entry {
218 int i; /* index into array */
219 char* name; /* name as given by user */
220 char* host; /* text description of host */
221 struct sockaddr_storage saddr; /* internet address */
222 socklen_t saddr_len;
223 int64_t timeout; /* time to wait for response */
224 int64_t last_send_time; /* time of last packet sent */
225 int num_sent; /* number of ping packets sent (for statistics) */
226 int num_recv; /* number of pings received (duplicates ignored) */
227 int num_recv_total; /* number of pings received, including duplicates */
228 int64_t max_reply; /* longest response time */
229 int64_t min_reply; /* shortest response time */
230 int64_t total_time; /* sum of response times */
231 /* _i -> splits (reset on every report interval) */
232 int num_sent_i; /* number of ping packets sent */
233 int num_recv_i; /* number of pings received */
234 int64_t max_reply_i; /* longest response time */
235 int64_t min_reply_i; /* shortest response time */
236 int64_t total_time_i; /* sum of response times */
237 int64_t* resp_times; /* individual response times */
238
239 /* to avoid allocating two struct events each time that we send a ping, we
240 * preallocate here two struct events for each ping that we might send for
241 * this host. */
242 struct event *event_storage_ping;
243 struct event *event_storage_timeout;
244 } HOST_ENTRY;
245
246 int event_storage_count; /* how many events can be stored in host_entry->event_storage_xxx */
247
248 /* basic algorithm to ensure that we have correct data at all times:
249 *
250 * 1. when a ping is sent:
251 * - two events get added into event_queue:
252 * - t+PERIOD: ping event
253 * - t+TIMEOUT: timeout event
254 *
255 * 2. when a ping is received:
256 * - record statistics (increase num_sent and num_received)
257 * - remove timeout event (we store the event in seqmap, so that we can retrieve it when the response is received)
258 *
259 * 3. when a timeout happens:
260 * - record statistics (increase num_sent only)
261 */
262
263 #define EV_TYPE_PING 1
264 #define EV_TYPE_TIMEOUT 2
265
266 struct event {
267 struct event *ev_prev;
268 struct event *ev_next;
269 int64_t ev_time;
270 struct host_entry *host;
271 int ping_index;
272 };
273
274 struct event_queue {
275 struct event *first;
276 struct event *last;
277 };
278
279 /*** globals ***/
280
281 HOST_ENTRY** table = NULL; /* array of pointers to items in the list */
282
283 /* we keep two separate queues: a ping queue, for when the next ping should be
284 * sent, and a timeout queue. the reason for having two separate queues is that
285 * the ping period and the timeout value are different, so if we put them in
286 * the same event queue, we would need to scan many more entries when inserting
287 * into the sorted list.
288 */
289 struct event_queue event_queue_ping;
290 struct event_queue event_queue_timeout;
291
292 char* prog;
293 int ident4 = 0; /* our icmp identity field */
294 int ident6 = 0;
295 int socket4 = -1;
296 int socktype4 = -1;
297 int using_sock_dgram4 = 0;
298 #ifndef IPV6
299 int hints_ai_family = AF_INET;
300 #else
301 int socket6 = -1;
302 int socktype6 = -1;
303 int hints_ai_family = AF_UNSPEC;
304 #endif
305
306 volatile sig_atomic_t status_snapshot = 0;
307 volatile sig_atomic_t finish_requested = 0;
308
309 unsigned int debugging = 0;
310
311 /* all time-related values are int64_t nanoseconds */
312 unsigned int retry = DEFAULT_RETRY;
313 int64_t timeout = (int64_t) DEFAULT_TIMEOUT * 1000000;
314 int64_t interval = (int64_t) DEFAULT_INTERVAL * 1000000;
315 int64_t perhost_interval = (int64_t) DEFAULT_PERHOST_INTERVAL * 1000000;
316 float backoff = DEFAULT_BACKOFF_FACTOR;
317 unsigned int ping_data_size = DEFAULT_PING_DATA_SIZE;
318 unsigned int count = 1, min_reachable = 0;
319 unsigned int trials;
320 int64_t report_interval = 0;
321 unsigned int ttl = 0;
322 int src_addr_set = 0;
323 struct in_addr src_addr;
324 #ifdef IPV6
325 int src_addr6_set = 0;
326 struct in6_addr src_addr6;
327 #endif
328
329 /* global stats */
330 int64_t max_reply = 0;
331 int64_t min_reply = 0;
332 int64_t total_replies = 0;
333 int64_t sum_replies = 0;
334 int max_hostname_len = 0;
335 int num_hosts = 0; /* total number of hosts */
336 int num_alive = 0, /* total number alive */
337 num_unreachable = 0, /* total number unreachable */
338 num_noaddress = 0; /* total number of addresses not found */
339 int num_timeout = 0, /* number of times select timed out */
340 num_pingsent = 0, /* total pings sent */
341 num_pingreceived = 0, /* total pings received */
342 num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
343
344 struct timespec current_time; /* current time (pseudo) */
345 int64_t current_time_ns;
346 int64_t start_time;
347 int64_t end_time;
348 int64_t last_send_time; /* time last ping was sent */
349 int64_t next_report_time; /* time next -Q report is expected */
350
351 /* switches */
352 int generate_flag = 0; /* flag for IP list generation */
353 int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
354 int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag;
355 int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
356 int multif_flag, timeout_flag;
357 int outage_flag = 0;
358 int timestamp_flag = 0;
359 int random_data_flag = 0;
360 #if defined(DEBUG) || defined(_DEBUG)
361 int randomly_lose_flag, trace_flag, print_per_system_flag;
362 int lose_factor;
363 #endif /* DEBUG || _DEBUG */
364
365 char* filename = NULL; /* file containing hosts to ping */
366
367 /*** forward declarations ***/
368
369 void add_name(char* name);
370 void add_addr(char* name, char* host, struct sockaddr* ipaddr, socklen_t ipaddr_len);
371 char* na_cat(char* name, struct in_addr ipaddr);
372 void crash_and_burn(char* message);
373 void errno_crash_and_burn(char* message);
374 char* get_host_by_address(struct in_addr in);
375 int send_ping(HOST_ENTRY* h, int index);
376 void usage(int);
377 int wait_for_reply(int64_t);
378 void print_per_system_stats(void);
379 void print_per_system_splits(void);
380 void print_netdata(void);
381 void print_global_stats(void);
382 void main_loop();
383 void signal_handler(int);
384 void finish();
385 const char* sprint_tm(int64_t t);
386 void ev_enqueue(struct event_queue *queue, struct event *event);
387 struct event *ev_dequeue(struct event_queue *queue);
388 void ev_remove(struct event_queue *queue, struct event *event);
389 void add_cidr(char*);
390 void add_range(char*, char*);
391 void print_warning(char* fmt, ...);
392 int addr_cmp(struct sockaddr* a, struct sockaddr* b);
393 void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
394 void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
395 struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
396 void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
397 void update_current_time();
398
399 /************************************************************
400
401 Function: p_setsockopt
402
403 *************************************************************
404
405 Inputs: p_uid: privileged uid. Others as per setsockopt(2)
406
407 Description:
408
409 Elevates privileges to p_uid when required, calls
410 setsockopt, and drops privileges back.
411
412 ************************************************************/
413
414 int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
415 const void *optval, socklen_t optlen)
416 {
417 const uid_t saved_uid = geteuid();
418 int res;
419
420 if (p_uid != saved_uid && seteuid(p_uid)) {
421 perror("cannot elevate privileges for setsockopt");
422 }
423
424 res = setsockopt(sockfd, level, optname, optval, optlen);
425
426 if (p_uid != saved_uid && seteuid(saved_uid)) {
427 perror("fatal error: could not drop privileges after setsockopt");
428 /* continuing would be a security hole */
429 exit(4);
430 }
431
432 return res;
433 }
434
435 /************************************************************
436
437 Function: main
438
439 *************************************************************
440
441 Inputs: int argc, char** argv
442
443 Description:
444
445 Main program entry point
446
447 ************************************************************/
448
449 int main(int argc, char** argv)
450 {
451 int c;
452 const uid_t suid = geteuid();
453 int tos = 0;
454 struct optparse optparse_state;
455 #ifdef USE_SIGACTION
456 struct sigaction act;
457 #endif
458
459 /* pre-parse -h/--help, so that we also can output help information
460 * without trying to open the socket, which might fail */
461 prog = argv[0];
462 if(argc == 2 && ( strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 )) {
463 usage(0);
464 }
465
466 socket4 = open_ping_socket_ipv4(&socktype4);
467 #ifdef __linux__
468 /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
469 * structure is missing in the message.
470 */
471 using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
472 #endif
473
474 #ifdef IPV6
475 socket6 = open_ping_socket_ipv6(&socktype6);
476 /* if called (sym-linked) via 'fping6', imply '-6'
477 * for backward compatibility */
478 if (strstr(prog, "fping6")) {
479 hints_ai_family = AF_INET6;
480 }
481 #endif
482
483 memset(&src_addr, 0, sizeof(src_addr));
484 #ifdef IPV6
485 memset(&src_addr6, 0, sizeof(src_addr6));
486 #endif
487
488 if (!suid && suid != getuid()) {
489 /* *temporarily* drop privileges */
490 if (seteuid(getuid()) == -1)
491 perror("cannot setuid");
492 }
493
494 optparse_init(&optparse_state, argv);
495 ident4 = ident6 = htons(getpid() & 0xFFFF);
496 verbose_flag = 1;
497 backoff_flag = 1;
498 opterr = 1;
499
500 /* get command line options */
501
502 struct optparse_long longopts[] = {
503 { "ipv4", '4', OPTPARSE_NONE },
504 { "ipv6", '6', OPTPARSE_NONE },
505 { "alive", 'a', OPTPARSE_NONE },
506 { "addr", 'A', OPTPARSE_NONE },
507 { "size", 'b', OPTPARSE_REQUIRED },
508 { "backoff", 'B', OPTPARSE_REQUIRED },
509 { "count", 'c', OPTPARSE_REQUIRED },
510 { "vcount", 'C', OPTPARSE_REQUIRED },
511 { "rdns", 'd', OPTPARSE_NONE },
512 { "timestamp", 'D', OPTPARSE_NONE },
513 { "elapsed", 'e', OPTPARSE_NONE },
514 { "file", 'f', OPTPARSE_REQUIRED },
515 { "generate", 'g', OPTPARSE_NONE },
516 { "help", 'h', OPTPARSE_NONE },
517 { "ttl", 'H', OPTPARSE_REQUIRED },
518 { "interval", 'i', OPTPARSE_REQUIRED },
519 { "iface", 'I', OPTPARSE_REQUIRED },
520 { "loop", 'l', OPTPARSE_NONE },
521 { "all", 'm', OPTPARSE_NONE },
522 { "dontfrag", 'M', OPTPARSE_NONE },
523 { "name", 'n', OPTPARSE_NONE },
524 { "netdata", 'N', OPTPARSE_NONE },
525 { "outage", 'o', OPTPARSE_NONE },
526 { "tos", 'O', OPTPARSE_REQUIRED },
527 { "period", 'p', OPTPARSE_REQUIRED },
528 { "quiet", 'q', OPTPARSE_NONE },
529 { "squiet", 'Q', OPTPARSE_REQUIRED },
530 { "retry", 'r', OPTPARSE_REQUIRED },
531 { "random", 'R', OPTPARSE_NONE },
532 { "stats", 's', OPTPARSE_NONE },
533 { "src", 'S', OPTPARSE_REQUIRED },
534 { "timeout", 't', OPTPARSE_REQUIRED },
535 { NULL, 'T', OPTPARSE_REQUIRED },
536 { "unreach", 'u', OPTPARSE_NONE },
537 { "version", 'v', OPTPARSE_NONE },
538 { "reachable", 'x', OPTPARSE_REQUIRED },
539 #if defined(DEBUG) || defined(_DEBUG)
540 { NULL, 'z', OPTPARSE_REQUIRED },
541 #endif
542 { 0, 0, 0 }
543 };
544
545 float opt_value_float;
546 while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
547 switch (c) {
548 case '4':
549 #ifdef IPV6
550 if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
551 fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
552 exit(1);
553 }
554 hints_ai_family = AF_INET;
555 #endif
556 break;
557 case '6':
558 #ifdef IPV6
559 if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
560 fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
561 exit(1);
562 }
563 hints_ai_family = AF_INET6;
564 #else
565 fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
566 exit(1);
567 #endif
568 break;
569 case 'M':
570 #ifdef IP_MTU_DISCOVER
571 if (socket4 >= 0) {
572 int val = IP_PMTUDISC_DO;
573 if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
574 perror("setsockopt IP_MTU_DISCOVER");
575 }
576 }
577 #ifdef IPV6
578 if (socket6 >= 0) {
579 int val = IPV6_PMTUDISC_DO;
580 if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
581 perror("setsockopt IPV6_MTU_DISCOVER");
582 }
583 }
584 #endif
585 #else
586 fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
587 exit(1);
588 #endif
589 break;
590
591 case 't':
592 if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
593 usage(1);
594 if (opt_value_float < 0) {
595 usage(1);
596 }
597 timeout = opt_value_float * 1000000;
598 timeout_flag = 1;
599 break;
600
601 case 'r':
602 if (!sscanf(optparse_state.optarg, "%u", &retry))
603 usage(1);
604 break;
605
606 case 'i':
607 if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
608 usage(1);
609 if (opt_value_float < 0) {
610 usage(1);
611 }
612 interval = opt_value_float * 1000000;
613 break;
614
615 case 'p':
616 if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
617 usage(1);
618 if (opt_value_float < 0) {
619 usage(1);
620 }
621 perhost_interval = opt_value_float * 1000000;
622
623 break;
624
625 case 'c':
626 if (!(count = (unsigned int)atoi(optparse_state.optarg)))
627 usage(1);
628
629 count_flag = 1;
630 break;
631
632 case 'C':
633 if (!(count = (unsigned int)atoi(optparse_state.optarg)))
634 usage(1);
635
636 count_flag = 1;
637 report_all_rtts_flag = 1;
638 break;
639
640 case 'b':
641 if (!sscanf(optparse_state.optarg, "%u", &ping_data_size))
642 usage(1);
643
644 break;
645
646 case 'h':
647 usage(0);
648 break;
649
650 case 'q':
651 verbose_flag = 0;
652 quiet_flag = 1;
653 break;
654
655 case 'Q':
656 verbose_flag = 0;
657 quiet_flag = 1;
658 if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
659 usage(1);
660 if (opt_value_float < 0) {
661 usage(1);
662 }
663 report_interval = opt_value_float * 1e9;
664
665 break;
666
667 case 'e':
668 elapsed_flag = 1;
669 break;
670
671 case 'm':
672 multif_flag = 1;
673 break;
674
675 case 'N':
676 netdata_flag = 1;
677 break;
678
679 case 'n':
680 name_flag = 1;
681 if (rdns_flag) {
682 fprintf(stderr, "%s: use either one of -d or -n\n", prog);
683 exit(1);
684 }
685 break;
686
687 case 'd':
688 rdns_flag = 1;
689 if (name_flag) {
690 fprintf(stderr, "%s: use either one of -d or -n\n", prog);
691 exit(1);
692 }
693 break;
694
695 case 'A':
696 addr_flag = 1;
697 break;
698
699 case 'B':
700 if (!(backoff = atof(optparse_state.optarg)))
701 usage(1);
702
703 break;
704
705 case 's':
706 stats_flag = 1;
707 break;
708
709 case 'D':
710 timestamp_flag = 1;
711 break;
712
713 case 'R':
714 random_data_flag = 1;
715 break;
716
717 case 'l':
718 loop_flag = 1;
719 backoff_flag = 0;
720 break;
721
722 case 'u':
723 unreachable_flag = 1;
724 break;
725
726 case 'a':
727 alive_flag = 1;
728 break;
729
730 case 'H':
731 if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
732 usage(1);
733 break;
734
735 #if defined(DEBUG) || defined(_DEBUG)
736 case 'z':
737 if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
738 if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
739 usage(1);
740
741 break;
742 #endif /* DEBUG || _DEBUG */
743
744 case 'v':
745 printf("%s: Version %s\n", prog, VERSION);
746 exit(0);
747
748 case 'x':
749 if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
750 usage(1);
751 break;
752
753 case 'f':
754 filename = optparse_state.optarg;
755 break;
756
757 case 'g':
758 /* use IP list generation */
759 /* mutually exclusive with using file input or command line targets */
760 generate_flag = 1;
761 break;
762
763 case 'S':
764 if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
765 src_addr_set = 1;
766 break;
767 }
768 #ifdef IPV6
769 if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
770 src_addr6_set = 1;
771 break;
772 }
773 #endif
774 fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
775 exit(1);
776
777 case 'I':
778 #ifdef SO_BINDTODEVICE
779 if (socket4 >= 0) {
780 if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
781 perror("binding to specific interface (SO_BINTODEVICE)");
782 exit(1);
783 }
784 }
785 #ifdef IPV6
786 if (socket6 >= 0) {
787 if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
788 perror("binding to specific interface (SO_BINTODEVICE), IPV6");
789 exit(1);
790 }
791 }
792 #endif
793 #else
794 printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
795 exit(3);
796 ;
797 #endif
798 break;
799
800 case 'T':
801 /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
802 break;
803
804 case 'O':
805 if (sscanf(optparse_state.optarg, "%i", &tos)) {
806 if (socket4 >= 0) {
807 if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
808 perror("setting type of service octet IP_TOS");
809 }
810 }
811 #if defined(IPV6) && defined(IPV6_TCLASS)
812 if (socket6 >= 0) {
813 if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
814 perror("setting type of service octet IPV6_TCLASS");
815 }
816 }
817 #endif
818 }
819 else {
820 usage(1);
821 }
822 break;
823
824 case 'o':
825 outage_flag = 1;
826 break;
827
828 case '?':
829 fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
830 fprintf(stderr, "see 'fping -h' for usage information\n");
831 exit(1);
832 break;
833 }
834 }
835
836 /* permanently drop privileges */
837 if (suid != getuid() && setuid(getuid())) {
838 perror("fatal: failed to permanently drop privileges");
839 /* continuing would be a security hole */
840 exit(4);
841 }
842
843 /* validate various option settings */
844
845 #ifndef IPV6
846 if (socket4 < 0) {
847 #else
848 if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
849 #endif
850 crash_and_burn("can't create socket (must run as root?)");
851 }
852
853 if (ttl > 255) {
854 fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
855 exit(1);
856 }
857
858 if (unreachable_flag && alive_flag) {
859 fprintf(stderr, "%s: specify only one of a, u\n", prog);
860 exit(1);
861 }
862
863 if (count_flag && loop_flag) {
864 fprintf(stderr, "%s: specify only one of c, l\n", prog);
865 exit(1);
866 }
867
868 #ifdef FPING_SAFE_LIMITS
869 if ((interval < (int64_t) MIN_INTERVAL * 1000000 || perhost_interval < (int64_t) MIN_PERHOST_INTERVAL * 1000000)
870 && getuid()) {
871 fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
872 fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
873 prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
874 exit(1);
875 }
876 #endif
877
878 if (ping_data_size > MAX_PING_DATA) {
879 fprintf(stderr, "%s: data size %u not valid, must be lower than %u\n",
880 prog, ping_data_size, (unsigned int)MAX_PING_DATA);
881 exit(1);
882 }
883
884 if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
885 fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
886 prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
887 exit(1);
888 }
889
890 if (alive_flag || unreachable_flag || min_reachable)
891 verbose_flag = 0;
892
893 if (count_flag) {
894 if (verbose_flag)
895 per_recv_flag = 1;
896
897 alive_flag = unreachable_flag = verbose_flag = 0;
898 }
899
900 if (loop_flag) {
901 if (!report_interval)
902 per_recv_flag = 1;
903
904 alive_flag = unreachable_flag = verbose_flag = 0;
905 }
906
907 trials = (count > retry + 1) ? count : retry + 1;
908
909 /* auto-tune default timeout for count/loop modes
910 * see also github #32 */
911 if (loop_flag || count_flag) {
912 if (!timeout_flag) {
913 timeout = perhost_interval;
914 if (timeout > (int64_t) AUTOTUNE_TIMEOUT_MAX * 1000000) {
915 timeout = (int64_t) AUTOTUNE_TIMEOUT_MAX * 1000000;
916 }
917 }
918 }
919
920 #if defined(DEBUG) || defined(_DEBUG)
921 if (debugging & DBG_TRACE)
922 trace_flag = 1;
923
924 if (debugging & DBG_RANDOM_LOSE_FEW) {
925 randomly_lose_flag = 1;
926 lose_factor = 1; /* ie, 1/4 */
927 }
928
929 if (debugging & DBG_RANDOM_LOSE_MANY) {
930 randomly_lose_flag = 1;
931 lose_factor = 5; /* ie, 3/4 */
932 }
933
934 if (debugging & DBG_PRINT_PER_SYSTEM)
935 print_per_system_flag = 1;
936
937 if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
938 report_all_rtts_flag = 1;
939
940 if (trace_flag) {
941 fprintf(stderr, "%s:\n count: %u, retry: %u, interval: %.0f ms\n",
942 prog, count, retry, interval / 1e6);
943 fprintf(stderr, " perhost_interval: %.0f ms, timeout: %.0f\n",
944 perhost_interval / 1e6, timeout / 1e6);
945 fprintf(stderr, " ping_data_size = %u, trials = %u\n",
946 ping_data_size, trials);
947
948 if (verbose_flag)
949 fprintf(stderr, " verbose_flag set\n");
950 if (multif_flag)
951 fprintf(stderr, " multif_flag set\n");
952 if (name_flag)
953 fprintf(stderr, " name_flag set\n");
954 if (addr_flag)
955 fprintf(stderr, " addr_flag set\n");
956 if (stats_flag)
957 fprintf(stderr, " stats_flag set\n");
958 if (unreachable_flag)
959 fprintf(stderr, " unreachable_flag set\n");
960 if (alive_flag)
961 fprintf(stderr, " alive_flag set\n");
962 if (elapsed_flag)
963 fprintf(stderr, " elapsed_flag set\n");
964 if (version_flag)
965 fprintf(stderr, " version_flag set\n");
966 if (count_flag)
967 fprintf(stderr, " count_flag set\n");
968 if (loop_flag)
969 fprintf(stderr, " loop_flag set\n");
970 if (backoff_flag)
971 fprintf(stderr, " backoff_flag set\n");
972 if (per_recv_flag)
973 fprintf(stderr, " per_recv_flag set\n");
974 if (report_all_rtts_flag)
975 fprintf(stderr, " report_all_rtts_flag set\n");
976 if (randomly_lose_flag)
977 fprintf(stderr, " randomly_lose_flag set\n");
978 if (print_per_system_flag)
979 fprintf(stderr, " print_per_system_flag set\n");
980 if (outage_flag)
981 fprintf(stderr, " outage_flag set\n");
982 if (netdata_flag)
983 fprintf(stderr, " netdata_flag set\n");
984 }
985 #endif /* DEBUG || _DEBUG */
986
987 /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
988 if (ttl > 0) {
989 if (socket4 >= 0) {
990 if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
991 perror("setting time to live");
992 }
993 }
994 #ifdef IPV6
995 if (socket6 >= 0) {
996 if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
997 perror("setting time to live");
998 }
999 }
1000 #endif
1001 }
1002
1003 #if HAVE_SO_TIMESTAMPNS
1004 {
1005 int opt = 1;
1006 if (socket4 >= 0) {
1007 if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
1008 perror("setting SO_TIMESTAMPNS option");
1009 }
1010 }
1011 #ifdef IPV6
1012 if (socket6 >= 0) {
1013 if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
1014 perror("setting SO_TIMESTAMPNS option (IPv6)");
1015 }
1016 }
1017 #endif
1018 }
1019 #endif
1020
1021 update_current_time();
1022 start_time = current_time_ns;
1023
1024 /* handle host names supplied on command line or in a file */
1025 /* if the generate_flag is on, then generate the IP list */
1026
1027 argv = &argv[optparse_state.optind];
1028 argc -= optparse_state.optind;
1029
1030 /* calculate how many ping can be in-flight per host */
1031 if(count_flag) {
1032 event_storage_count = count;
1033 }
1034 else if(loop_flag) {
1035 if(perhost_interval > timeout) {
1036 event_storage_count = 1;
1037 }
1038 else {
1039 event_storage_count = 1 + timeout / perhost_interval;
1040 }
1041 }
1042 else {
1043 event_storage_count = 1;
1044 }
1045
1046 /* file and generate are mutually exclusive */
1047 /* file and command line are mutually exclusive */
1048 /* generate requires command line parameters beyond the switches */
1049 if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
1050 usage(1);
1051
1052 /* if no conditions are specified, then assume input from stdin */
1053 if (!*argv && !filename && !generate_flag)
1054 filename = "-";
1055
1056 if (*argv && !generate_flag) {
1057 while (*argv) {
1058 add_name(*argv);
1059 ++argv;
1060 }
1061 }
1062 else if (filename) {
1063 FILE* ping_file;
1064 char line[132];
1065 char host[132];
1066
1067 if (strcmp(filename, "-") == 0)
1068 ping_file = fdopen(0, "r");
1069 else
1070 ping_file = fopen(filename, "r");
1071
1072 if (!ping_file)
1073 errno_crash_and_burn("fopen");
1074
1075 while (fgets(line, sizeof(line), ping_file)) {
1076 if (sscanf(line, "%s", host) != 1)
1077 continue;
1078
1079 if ((!*host) || (host[0] == '#')) /* magic to avoid comments */
1080 continue;
1081
1082 add_name(host);
1083 }
1084
1085 fclose(ping_file);
1086 }
1087 else if (*argv && generate_flag) {
1088 if (argc == 1) {
1089 /* one target: we expect a cidr range (n.n.n.n/m) */
1090 add_cidr(argv[0]);
1091 }
1092 else if (argc == 2) {
1093 add_range(argv[0], argv[1]);
1094 }
1095 else {
1096 usage(1);
1097 }
1098 }
1099 else {
1100 usage(1);
1101 }
1102
1103 if (!num_hosts) {
1104 exit(num_noaddress ? 2 : 1);
1105 }
1106
1107 if (src_addr_set && socket4 >= 0) {
1108 socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
1109 }
1110 #ifdef IPV6
1111 if (src_addr6_set && socket6 >= 0) {
1112 socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
1113 }
1114 #endif
1115
1116 /* allocate and initialize array to map host nr to host_entry */
1117 {
1118 struct event *cursor = event_queue_ping.first;
1119 int i= 0;
1120 table = (HOST_ENTRY**)calloc(num_hosts, sizeof(HOST_ENTRY *));
1121 if (!table)
1122 crash_and_burn("Can't malloc array of hosts");
1123 /* initialize table of hosts. we know that we have ping events scheduled
1124 * for each of them */
1125 for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
1126 table[i] = cursor->host;
1127 cursor->host->i = i;
1128 i++;
1129 }
1130 }
1131
1132 init_ping_buffer_ipv4(ping_data_size);
1133 #ifdef IPV6
1134 init_ping_buffer_ipv6(ping_data_size);
1135 #endif
1136
1137 #ifdef USE_SIGACTION
1138 memset(&act, 0, sizeof(act));
1139 act.sa_handler = signal_handler;
1140 sigemptyset(&act.sa_mask);
1141 sigaddset(&act.sa_mask, SIGINT);
1142 sigaddset(&act.sa_mask, SIGQUIT);
1143 act.sa_flags = SA_RESTART;
1144 if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
1145 crash_and_burn("failure to set signal handler");
1146 }
1147 #else
1148 signal(SIGINT, signal_handler);
1149 signal(SIGQUIT, signal_handler);
1150 #endif
1151 setlinebuf(stdout);
1152
1153 if (report_interval) {
1154 next_report_time = current_time_ns + report_interval;
1155 }
1156
1157 last_send_time = 0;
1158
1159 seqmap_init();
1160
1161 /* main loop */
1162 main_loop();
1163
1164 finish();
1165
1166 return 0;
1167 }
1168
1169 static inline int64_t timespec_ns(struct timespec* a)
1170 {
1171 return ((int64_t) a->tv_sec * 1000000000) + a->tv_nsec;
1172 }
1173
1174 void add_cidr(char* addr)
1175 {
1176 char* addr_end;
1177 char* mask_str;
1178 unsigned long mask;
1179 unsigned long bitmask;
1180 int ret;
1181 struct addrinfo addr_hints;
1182 struct addrinfo* addr_res;
1183 unsigned long net_addr;
1184 unsigned long net_last;
1185
1186 /* Split address from mask */
1187 addr_end = strchr(addr, '/');
1188 if (addr_end == NULL) {
1189 usage(1);
1190 }
1191 *addr_end = '\0';
1192 mask_str = addr_end + 1;
1193 mask = atoi(mask_str);
1194
1195 /* parse address (IPv4 only) */
1196 memset(&addr_hints, 0, sizeof(struct addrinfo));
1197 addr_hints.ai_family = AF_UNSPEC;
1198 addr_hints.ai_flags = AI_NUMERICHOST;
1199 ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
1200 if (ret) {
1201 fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
1202 exit(1);
1203 }
1204 if (addr_res->ai_family != AF_INET) {
1205 fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
1206 exit(1);
1207 }
1208 net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
1209
1210 /* check mask */
1211 if (mask < 1 || mask > 32) {
1212 fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %s)\n", prog, mask_str);
1213 exit(1);
1214 }
1215
1216 /* convert mask integer from 1 to 32 to a bitmask */
1217 bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
1218
1219 /* calculate network range */
1220 net_addr &= bitmask;
1221 net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
1222
1223 /* exclude network and broadcast address for regular prefixes */
1224 if (mask < 31) {
1225 net_last--;
1226 net_addr++;
1227 }
1228
1229 /* add all hosts in that network (net_addr and net_last inclusive) */
1230 for (; net_addr <= net_last; net_addr++) {
1231 struct in_addr in_addr_tmp;
1232 char buffer[20];
1233 in_addr_tmp.s_addr = htonl(net_addr);
1234 inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
1235 add_name(buffer);
1236 }
1237
1238 freeaddrinfo(addr_res);
1239 }
1240
1241 void add_range(char* start, char* end)
1242 {
1243 struct addrinfo addr_hints;
1244 struct addrinfo* addr_res;
1245 unsigned long start_long;
1246 unsigned long end_long;
1247 int ret;
1248
1249 /* parse start address (IPv4 only) */
1250 memset(&addr_hints, 0, sizeof(struct addrinfo));
1251 addr_hints.ai_family = AF_UNSPEC;
1252 addr_hints.ai_flags = AI_NUMERICHOST;
1253 ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
1254 if (ret) {
1255 fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
1256 exit(1);
1257 }
1258 if (addr_res->ai_family != AF_INET) {
1259 freeaddrinfo(addr_res);
1260 fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
1261 exit(1);
1262 }
1263 start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
1264
1265 /* parse end address (IPv4 only) */
1266 memset(&addr_hints, 0, sizeof(struct addrinfo));
1267 addr_hints.ai_family = AF_UNSPEC;
1268 addr_hints.ai_flags = AI_NUMERICHOST;
1269 ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
1270 if (ret) {
1271 fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
1272 exit(1);
1273 }
1274 if (addr_res->ai_family != AF_INET) {
1275 freeaddrinfo(addr_res);
1276 fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
1277 exit(1);
1278 }
1279 end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
1280 freeaddrinfo(addr_res);
1281
1282 if (end_long > start_long + MAX_GENERATE) {
1283 fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
1284 exit(1);
1285 }
1286
1287 /* generate */
1288 for (; start_long <= end_long; start_long++) {
1289 struct in_addr in_addr_tmp;
1290 char buffer[20];
1291 in_addr_tmp.s_addr = htonl(start_long);
1292 inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
1293 add_name(buffer);
1294 }
1295 }
1296
1297 void main_loop()
1298 {
1299 int64_t lt;
1300 int64_t wait_time_ns;
1301 struct event *event;
1302 struct host_entry *h;
1303
1304 while (event_queue_ping.first || event_queue_timeout.first) {
1305 dbg_printf("%s", "# main_loop\n");
1306
1307 /* timeout event ? */
1308 if (event_queue_timeout.first &&
1309 event_queue_timeout.first->ev_time - current_time_ns <= 0)
1310 {
1311 event = ev_dequeue(&event_queue_timeout);
1312 h = event->host;
1313
1314 dbg_printf("%s [%d]: timeout event\n", h->host, event->ping_index);
1315
1316 stats_add(h, event->ping_index, 0, -1);
1317
1318 if (per_recv_flag) {
1319 if (timestamp_flag) {
1320 printf("[%.5f] ", (double)current_time_ns / 1e9);
1321 }
1322 printf("%-*s : [%d], timed out",
1323 max_hostname_len, h->host, event->ping_index);
1324 if(h->num_recv > 0) {
1325 printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
1326 }
1327 else {
1328 printf(" (NaN avg, ");
1329 }
1330 if (h->num_recv <= h->num_sent) {
1331 printf("%d%% loss)",
1332 ((h->num_sent - h->num_recv) * 100) / h->num_sent);
1333 }
1334 else {
1335 printf("%d%% return)",
1336 (h->num_recv_total * 100) / h->num_sent);
1337 }
1338 printf("\n");
1339 }
1340
1341 /* do we need to send a retry? */
1342 if (!loop_flag && !count_flag) {
1343 if (h->num_sent < retry + 1) {
1344 if (backoff_flag) {
1345 h->timeout *= backoff;
1346 }
1347 send_ping(h, event->ping_index);
1348 }
1349 }
1350
1351 /* note: we process first timeout events, because we might need to
1352 * wait to process ping events, while we for sure never need to
1353 * wait for timeout events.
1354 */
1355 continue;
1356 }
1357
1358 /* ping event ? */
1359 if (event_queue_ping.first &&
1360 event_queue_ping.first->ev_time - current_time_ns <= 0)
1361 {
1362 /* Make sure that we don't ping more than once every "interval" */
1363 lt = current_time_ns - last_send_time;
1364 if (lt < interval)
1365 goto wait_for_reply;
1366
1367 /* Dequeue the event */
1368 event = ev_dequeue(&event_queue_ping);
1369 h = event->host;
1370
1371 dbg_printf("%s [%d]: ping event\n", h->host, event->ping_index);
1372
1373 /* Send the ping */
1374 send_ping(h, event->ping_index);
1375
1376 /* Loop and count mode: schedule next ping */
1377 if (loop_flag || (count_flag && event->ping_index+1 < count)) {
1378 host_add_ping_event(h, event->ping_index+1, event->ev_time + perhost_interval);
1379 }
1380 }
1381
1382 wait_for_reply:
1383
1384 /* When is the next ping next event? */
1385 wait_time_ns = -1;
1386 if (event_queue_ping.first) {
1387 wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1388 if (wait_time_ns < 0)
1389 wait_time_ns = 0;
1390 /* make sure that we wait enough, so that the inter-ping delay is
1391 * bigger than 'interval' */
1392 if (wait_time_ns < interval) {
1393 lt = current_time_ns - last_send_time;
1394 if (lt < interval) {
1395 wait_time_ns = interval - lt;
1396 }
1397 }
1398
1399 dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1400 }
1401
1402 /* When is the next timeout event? */
1403 if (event_queue_timeout.first) {
1404 int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
1405 if(wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
1406 wait_time_ns = wait_time_timeout;
1407 if (wait_time_ns < 0) {
1408 wait_time_ns = 0;
1409 }
1410 }
1411
1412 dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1413 }
1414
1415 /* When is the next report due? */
1416 if (report_interval && (loop_flag || count_flag)) {
1417 int64_t wait_time_next_report = next_report_time - current_time_ns;
1418 if (wait_time_next_report < wait_time_ns) {
1419 wait_time_ns = wait_time_next_report;
1420 if (wait_time_ns < 0) {
1421 wait_time_ns = 0;
1422 }
1423 }
1424
1425 dbg_printf("next report event in %0.f ms\n", wait_time_next_report / 1e6);
1426 }
1427
1428 /* if wait_time is still -1, it means that we are waiting for nothing... */
1429 if(wait_time_ns == -1) {
1430 break;
1431 }
1432
1433 /* end of loop was requested by interrupt signal handler */
1434 if (finish_requested) {
1435 break;
1436 }
1437
1438 /* Receive replies */
1439 /* (this is what sleeps during each loop iteration) */
1440 dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1441 if (wait_for_reply(wait_time_ns)) {
1442 while (wait_for_reply(0))
1443 ; /* process other replies in the queue */
1444 }
1445
1446 update_current_time();
1447
1448 if (status_snapshot) {
1449 status_snapshot = 0;
1450 print_per_system_splits();
1451 }
1452
1453 /* Print report */
1454 if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1455 if (netdata_flag)
1456 print_netdata();
1457 else
1458 print_per_system_splits();
1459
1460 while (current_time_ns >= next_report_time) {
1461 next_report_time += report_interval;
1462 }
1463 }
1464 }
1465 }
1466
1467 /************************************************************
1468
1469 Function: signal_handler
1470
1471 *************************************************************
1472
1473 Inputs: int signum
1474
1475 Description:
1476
1477 SIGQUIT signal handler - set flag and return
1478 SIGINT signal handler - set flag and return
1479
1480 ************************************************************/
1481
1482 void signal_handler(int signum)
1483 {
1484 switch (signum) {
1485 case SIGINT:
1486 finish_requested = 1;
1487 break;
1488
1489 case SIGQUIT:
1490 status_snapshot = 1;
1491 break;
1492 }
1493 }
1494
1495 /************************************************************
1496
1497 Function: update_current_time
1498
1499 *************************************************************/
1500
1501 void update_current_time()
1502 {
1503 clock_gettime(CLOCKID, ¤t_time);
1504 current_time_ns = timespec_ns(¤t_time);
1505 }
1506
1507
1508 /************************************************************
1509
1510 Function: finish
1511
1512 *************************************************************
1513
1514 Inputs: void (none)
1515
1516 Description:
1517
1518 Main program clean up and exit point
1519
1520 ************************************************************/
1521
1522 void finish()
1523 {
1524 int i;
1525 HOST_ENTRY* h;
1526
1527 update_current_time();
1528 end_time = current_time_ns;
1529
1530 /* tot up unreachables */
1531 for (i = 0; i < num_hosts; i++) {
1532 h = table[i];
1533
1534 if (!h->num_recv) {
1535 num_unreachable++;
1536
1537 if (verbose_flag || unreachable_flag) {
1538 printf("%s", h->host);
1539
1540 if (verbose_flag)
1541 printf(" is unreachable");
1542
1543 printf("\n");
1544 }
1545 }
1546 }
1547
1548 if (count_flag || loop_flag)
1549 print_per_system_stats();
1550 #if defined(DEBUG) || defined(_DEBUG)
1551 else if (print_per_system_flag)
1552 print_per_system_stats();
1553 #endif /* DEBUG || _DEBUG */
1554
1555 if (stats_flag)
1556 print_global_stats();
1557
1558 if (min_reachable) {
1559 if ((num_hosts-num_unreachable) >= min_reachable) {
1560 printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts-num_unreachable);
1561 exit(0);
1562 } else {
1563 printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts-num_unreachable);
1564 exit(1);
1565 }
1566 }
1567
1568 if (num_noaddress)
1569 exit(2);
1570 else if (num_alive != num_hosts)
1571 exit(1);
1572
1573 exit(0);
1574 }
1575
1576 /************************************************************
1577
1578 Function: print_per_system_stats
1579
1580 *************************************************************
1581
1582 Inputs: void (none)
1583
1584 Description:
1585
1586
1587 ************************************************************/
1588
1589 void print_per_system_stats(void)
1590 {
1591 int i, j, avg, outage_ms;
1592 HOST_ENTRY* h;
1593 int64_t resp;
1594
1595 if (verbose_flag || per_recv_flag)
1596 fprintf(stderr, "\n");
1597
1598 for (i = 0; i < num_hosts; i++) {
1599 h = table[i];
1600 fprintf(stderr, "%-*s :", max_hostname_len, h->host);
1601
1602 if (report_all_rtts_flag) {
1603 for (j = 0; j < h->num_sent; j++) {
1604 if ((resp = h->resp_times[j]) >= 0)
1605 fprintf(stderr, " %s", sprint_tm(resp));
1606 else
1607 fprintf(stderr, " -");
1608 }
1609
1610 fprintf(stderr, "\n");
1611 }
1612 else {
1613 if (h->num_recv <= h->num_sent) {
1614 fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
1615 h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
1616
1617 if (outage_flag) {
1618 /* Time outage total */
1619 outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
1620 fprintf(stderr, ", outage(ms) = %d", outage_ms);
1621 }
1622 }
1623 else {
1624 fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
1625 h->num_sent, h->num_recv,
1626 ((h->num_recv * 100) / h->num_sent));
1627 }
1628
1629 if (h->num_recv) {
1630 avg = h->total_time / h->num_recv;
1631 fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
1632 fprintf(stderr, "/%s", sprint_tm(avg));
1633 fprintf(stderr, "/%s", sprint_tm(h->max_reply));
1634 }
1635
1636 fprintf(stderr, "\n");
1637 }
1638 }
1639 }
1640
1641 /************************************************************
1642
1643 Function: print_netdata
1644
1645 *************************************************************
1646
1647 Inputs: void (none)
1648
1649 Description:
1650
1651
1652 ************************************************************/
1653
1654 void print_netdata(void)
1655 {
1656 static int sent_charts = 0;
1657
1658 int i;
1659 int64_t avg;
1660 HOST_ENTRY* h;
1661
1662 for (i = 0; i < num_hosts; i++) {
1663 h = table[i];
1664
1665 if (!sent_charts) {
1666 printf("CHART fping.%s_packets '' 'FPing Packets for host %s' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, h->host, report_interval / 1e9);
1667 printf("DIMENSION xmt sent absolute 1 1\n");
1668 printf("DIMENSION rcv received absolute 1 1\n");
1669 }
1670
1671 printf("BEGIN fping.%s_packets\n", h->name);
1672 printf("SET xmt = %d\n", h->num_sent_i);
1673 printf("SET rcv = %d\n", h->num_recv_i);
1674 printf("END\n");
1675
1676 if (!sent_charts) {
1677 printf("CHART fping.%s_quality '' 'FPing Quality for host %s' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, h->host, report_interval / 1e9);
1678 printf("DIMENSION returned '' absolute 1 1\n");
1679 /* printf("DIMENSION lost '' absolute 1 1\n"); */
1680 }
1681
1682 printf("BEGIN fping.%s_quality\n", h->name);
1683 /*
1684 if( h->num_recv_i <= h->num_sent_i )
1685 printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
1686 else
1687 printf("SET lost = 0\n");
1688 */
1689
1690 printf("SET returned = %d\n", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
1691 printf("END\n");
1692
1693 if (!sent_charts) {
1694 printf("CHART fping.%s_latency '' 'FPing Latency for host %s' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, h->host, report_interval / 1e9);
1695 printf("DIMENSION min minimum absolute 1 1000000\n");
1696 printf("DIMENSION max maximum absolute 1 1000000\n");
1697 printf("DIMENSION avg average absolute 1 1000000\n");
1698 }
1699
1700 printf("BEGIN fping.%s_latency\n", h->name);
1701 if (h->num_recv_i) {
1702 avg = h->total_time_i / h->num_recv_i;
1703 printf("SET min = %" PRId64 "\n", h->min_reply_i);
1704 printf("SET avg = %" PRId64 "\n", avg);
1705 printf("SET max = %" PRId64 "\n", h->max_reply_i);
1706 }
1707 printf("END\n");
1708
1709 h->num_sent_i = h->num_recv_i = h->max_reply_i = h->min_reply_i = h->total_time_i = 0;
1710 }
1711
1712 sent_charts = 1;
1713 }
1714
1715 /************************************************************
1716
1717 Function: print_per_system_splits
1718
1719 *************************************************************
1720
1721 Inputs: void (none)
1722
1723 Description:
1724
1725
1726 ************************************************************/
1727
1728 void print_per_system_splits(void)
1729 {
1730 int i, avg, outage_ms_i;
1731 HOST_ENTRY* h;
1732 struct tm* curr_tm;
1733
1734 if (verbose_flag || per_recv_flag)
1735 fprintf(stderr, "\n");
1736
1737 update_current_time();
1738 curr_tm = localtime((time_t*)¤t_time.tv_sec);
1739 fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
1740 curr_tm->tm_min, curr_tm->tm_sec);
1741
1742 for (i = 0; i < num_hosts; i++) {
1743 h = table[i];
1744 fprintf(stderr, "%-*s :", max_hostname_len, h->host);
1745
1746 if (h->num_recv_i <= h->num_sent_i) {
1747 fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
1748 h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
1749
1750 if (outage_flag) {
1751 /* Time outage */
1752 outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
1753 fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
1754 }
1755 }
1756 else {
1757 fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
1758 h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
1759 }
1760
1761 if (h->num_recv_i) {
1762 avg = h->total_time_i / h->num_recv_i;
1763 fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
1764 fprintf(stderr, "/%s", sprint_tm(avg));
1765 fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
1766 }
1767
1768 fprintf(stderr, "\n");
1769 h->num_sent_i = h->num_recv_i = h->max_reply_i = h->min_reply_i = h->total_time_i = 0;
1770 }
1771 }
1772
1773 /************************************************************
1774
1775 Function: print_global_stats
1776
1777 *************************************************************
1778
1779 Inputs: void (none)
1780
1781 Description:
1782
1783
1784 ************************************************************/
1785
1786 void print_global_stats(void)
1787 {
1788 fprintf(stderr, "\n");
1789 fprintf(stderr, " %7d targets\n", num_hosts);
1790 fprintf(stderr, " %7d alive\n", num_alive);
1791 fprintf(stderr, " %7d unreachable\n", num_unreachable);
1792 fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
1793 fprintf(stderr, "\n");
1794 fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
1795 fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
1796 fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
1797 fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
1798 fprintf(stderr, "\n");
1799
1800 if (total_replies == 0) {
1801 min_reply = 0;
1802 max_reply = 0;
1803 total_replies = 1;
1804 sum_replies = 0;
1805 }
1806
1807 fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
1808 fprintf(stderr, " %s ms (avg round trip time)\n",
1809 sprint_tm(sum_replies / total_replies));
1810 fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
1811 fprintf(stderr, " %12.3f sec (elapsed real time)\n",
1812 (end_time - start_time) / 1e9);
1813 fprintf(stderr, "\n");
1814 }
1815
1816 /************************************************************
1817
1818 Function: send_ping
1819
1820 *************************************************************
1821
1822 Inputs: int s, HOST_ENTRY *h
1823
1824 Description:
1825
1826 Compose and transmit an ICMP_ECHO REQUEST packet. The IP packet
1827 will be added on by the kernel. The ID field is our UNIX process ID,
1828 and the sequence number is an index into an array of outstanding
1829 ping requests. The sequence number will later be used to quickly
1830 figure out who the ping reply came from.
1831
1832 ************************************************************/
1833
1834 int send_ping(HOST_ENTRY* h, int index)
1835 {
1836 int n;
1837 int myseq;
1838 int ret = 1;
1839
1840 update_current_time();
1841 h->last_send_time = current_time_ns;
1842 myseq = seqmap_add(h->i, index, current_time_ns);
1843
1844 dbg_printf("%s [%d]: send ping\n", h->host, index);
1845
1846 if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
1847 n = socket_sendto_ping_ipv4(socket4, (struct sockaddr*)&h->saddr, h->saddr_len, myseq, ident4);
1848 }
1849 #ifdef IPV6
1850 else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
1851 n = socket_sendto_ping_ipv6(socket6, (struct sockaddr*)&h->saddr, h->saddr_len, myseq, ident6);
1852 }
1853 #endif
1854 else {
1855 return 0;
1856 }
1857
1858 /* error sending? */
1859 if (
1860 (n < 0)
1861 #if defined(EHOSTDOWN)
1862 && errno != EHOSTDOWN
1863 #endif
1864 ) {
1865 if (verbose_flag) {
1866 print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
1867 }
1868 else {
1869 dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
1870 }
1871
1872 h->num_sent++;
1873 h->num_sent_i++;
1874 if (!loop_flag)
1875 h->resp_times[index] = RESP_ERROR;
1876
1877 ret = 0;
1878 }
1879 else {
1880 /* schedule timeout */
1881 host_add_timeout_event(h, index, current_time_ns + h->timeout);
1882
1883 /* mark this trial as outstanding */
1884 if (!loop_flag) {
1885 h->resp_times[index] = RESP_WAITING;
1886 }
1887 }
1888
1889 num_pingsent++;
1890 last_send_time = h->last_send_time;
1891
1892 return (ret);
1893 }
1894
1895 int socket_can_read(struct timeval* timeout)
1896 {
1897 int nfound;
1898 fd_set readset;
1899 int socketmax;
1900
1901 #ifndef IPV6
1902 socketmax = socket4;
1903 #else
1904 socketmax = socket4 > socket6 ? socket4 : socket6;
1905 #endif
1906
1907 select_again:
1908 FD_ZERO(&readset);
1909 if(socket4 >= 0) FD_SET(socket4, &readset);
1910 #ifdef IPV6
1911 if(socket6 >= 0) FD_SET(socket6, &readset);
1912 #endif
1913
1914 nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
1915 if (nfound < 0) {
1916 if (errno == EINTR) {
1917 /* interrupted system call: redo the select */
1918 goto select_again;
1919 }
1920 else {
1921 perror("select");
1922 }
1923 }
1924
1925 if (nfound > 0) {
1926 if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1927 return socket4;
1928 }
1929 #ifdef IPV6
1930 if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
1931 return socket6;
1932 }
1933 #endif
1934 }
1935
1936 return -1;
1937 }
1938
1939 int receive_packet(int64_t wait_time,
1940 int64_t* reply_timestamp,
1941 struct sockaddr* reply_src_addr,
1942 size_t reply_src_addr_len,
1943 char* reply_buf,
1944 size_t reply_buf_len)
1945 {
1946 struct timeval to;
1947 int s = 0;
1948 int recv_len;
1949 static unsigned char msg_control[40];
1950 struct iovec msg_iov = {
1951 reply_buf,
1952 reply_buf_len
1953 };
1954 struct msghdr recv_msghdr = {
1955 reply_src_addr,
1956 reply_src_addr_len,
1957 &msg_iov,
1958 1,
1959 &msg_control,
1960 sizeof(msg_control),
1961 0
1962 };
1963 #if HAVE_SO_TIMESTAMPNS
1964 struct cmsghdr* cmsg;
1965 #endif
1966
1967 /* Wait for a socket to become ready */
1968 if (wait_time) {
1969 to.tv_sec = wait_time / UINT64_C(1000000000);
1970 to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1971 }
1972 else {
1973 to.tv_sec = 0;
1974 to.tv_usec = 0;
1975 }
1976 s = socket_can_read(&to);
1977 if (s == -1) {
1978 return 0; /* timeout */
1979 }
1980
1981 recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1982 if (recv_len <= 0) {
1983 return 0;
1984 }
1985
1986 #if HAVE_SO_TIMESTAMPNS
1987 /* ancilliary data */
1988 {
1989 struct timespec reply_timestamp_ts;
1990 for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1991 cmsg != NULL;
1992 cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg))
1993 {
1994 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1995 memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1996 *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1997 }
1998 }
1999 }
2000 #endif
2001
2002 #if defined(DEBUG) || defined(_DEBUG)
2003 if (randomly_lose_flag) {
2004 if ((random() & 0x07) <= lose_factor)
2005 return 0;
2006 }
2007 #endif
2008
2009 return recv_len;
2010 }
2011
2012 /* stats_add: update host statistics for a single packet that was received (or timed out)
2013 * h: host entry to update
2014 * index: if in count mode: index number for this ping packet (-1 otherwise)
2015 * success: 1 if response received, 0 otherwise
2016 * latency: response time, in ns
2017 */
2018 void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
2019 {
2020 /* sent count - we update only on receive/timeout, so that we don't get
2021 * weird loss percentage, just because a packet was note recived yet.
2022 */
2023 h->num_sent++;
2024 h->num_sent_i++;
2025
2026 if(!success) {
2027 if(!loop_flag && index>=0) {
2028 h->resp_times[index] = RESP_TIMEOUT;
2029 }
2030 num_timeout++;
2031 return;
2032 }
2033
2034 /* received count */
2035 h->num_recv++;
2036 h->num_recv_i++;
2037
2038 /* maximum */
2039 if (!h->max_reply || latency > h->max_reply) {
2040 h->max_reply = latency;
2041 }
2042 if (!h->max_reply_i || latency > h->max_reply_i) {
2043 h->max_reply_i = latency;
2044 }
2045
2046 /* minimum */
2047 if (!h->min_reply || latency < h->min_reply) {
2048 h->min_reply = latency;
2049 }
2050 if (!h->min_reply_i || latency < h->min_reply_i) {
2051 h->min_reply_i = latency;
2052 }
2053
2054 /* total time (for average) */
2055 h->total_time += latency;
2056 h->total_time_i += latency;
2057
2058 /* response time per-packet (count mode) */
2059 if(!loop_flag && index>=0) {
2060 h->resp_times[index] = latency;
2061 }
2062 }
2063
2064 /* stats_reset_interval: reset interval statistics
2065 * h: host entry to update
2066 */
2067 void stats_reset_interval(HOST_ENTRY *h)
2068 {
2069 h->num_sent_i = 0;
2070 h->num_recv_i = 0;
2071 h->max_reply_i = 0;
2072 h->min_reply_i = 0;
2073 h->total_time_i = 0;
2074 }
2075
2076 int decode_icmp_ipv4(
2077 struct sockaddr* response_addr,
2078 size_t response_addr_len,
2079 char* reply_buf,
2080 size_t reply_buf_len,
2081 unsigned short* id,
2082 unsigned short* seq)
2083 {
2084 struct icmp* icp;
2085 int hlen = 0;
2086
2087 if (!using_sock_dgram4) {
2088 struct ip* ip = (struct ip*)reply_buf;
2089
2090 #if defined(__alpha__) && __STDC__ && !defined(__GLIBC__)
2091 /* The alpha headers are decidedly broken.
2092 * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2093 * ip_v. So, to get ip_hl, we mask off the bottom four bits.
2094 */
2095 hlen = (ip->ip_vhl & 0x0F) << 2;
2096 #else
2097 hlen = ip->ip_hl << 2;
2098 #endif
2099 }
2100
2101
2102 if (reply_buf_len < hlen + ICMP_MINLEN) {
2103 /* too short */
2104 if (verbose_flag) {
2105 char buf[INET6_ADDRSTRLEN];
2106 getnameinfo( response_addr, sizeof( struct sockaddr_in ), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
2107 printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
2108 }
2109 return -1;
2110 }
2111
2112 icp = (struct icmp*)(reply_buf + hlen);
2113
2114 if (icp->icmp_type != ICMP_ECHOREPLY) {
2115 /* Handle other ICMP packets */
2116 struct icmp* sent_icmp;
2117 SEQMAP_VALUE* seqmap_value;
2118 char addr_ascii[INET6_ADDRSTRLEN];
2119 HOST_ENTRY* h;
2120
2121 /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2122 if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
2123 /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2124 return -1;
2125 }
2126
2127 sent_icmp = (struct icmp*)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
2128
2129 if (sent_icmp->icmp_type != ICMP_ECHO || sent_icmp->icmp_id != ident4) {
2130 /* not caused by us */
2131 return -1;
2132 }
2133
2134 seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
2135 if (seqmap_value == NULL) {
2136 return -1;
2137 }
2138
2139 getnameinfo(response_addr, sizeof( struct sockaddr_in ), addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
2140
2141 switch (icp->icmp_type) {
2142 case ICMP_UNREACH:
2143 h = table[seqmap_value->host_nr];
2144 if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
2145 print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
2146 addr_ascii, h->host);
2147 }
2148 else {
2149 print_warning("%s from %s for ICMP Echo sent to %s",
2150 icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
2151 }
2152
2153 print_warning("\n");
2154 num_othericmprcvd++;
2155 break;
2156
2157 case ICMP_SOURCEQUENCH:
2158 case ICMP_REDIRECT:
2159 case ICMP_TIMXCEED:
2160 case ICMP_PARAMPROB:
2161 h = table[seqmap_value->host_nr];
2162 if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
2163 print_warning("%s from %s for ICMP Echo sent to %s",
2164 icmp_type_str[icp->icmp_type], addr_ascii, h->host);
2165 }
2166 else {
2167 print_warning("ICMP %d from %s for ICMP Echo sent to %s",
2168 icp->icmp_type, addr_ascii, h->host);
2169 }
2170 print_warning("\n");
2171 num_othericmprcvd++;
2172 break;
2173 }
2174
2175 return -1;
2176 }
2177
2178 *id = icp->icmp_id;
2179 *seq = ntohs(icp->icmp_seq);
2180
2181 return hlen;
2182 }
2183
2184 #ifdef IPV6
2185 int decode_icmp_ipv6(
2186 struct sockaddr* response_addr,
2187 size_t response_addr_len,
2188 char* reply_buf,
2189 size_t reply_buf_len,
2190 unsigned short* id,
2191 unsigned short* seq)
2192 {
2193 struct icmp6_hdr* icp;
2194
2195 if (reply_buf_len < sizeof(struct icmp6_hdr)) {
2196 if (verbose_flag) {
2197 char buf[INET6_ADDRSTRLEN];
2198 getnameinfo((struct sockaddr*)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
2199 printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
2200 }
2201 return 0; /* too short */
2202 }
2203
2204 icp = (struct icmp6_hdr*)reply_buf;
2205
2206 if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
2207 /* Handle other ICMP packets */
2208 struct icmp6_hdr* sent_icmp;
2209 SEQMAP_VALUE* seqmap_value;
2210 char addr_ascii[INET6_ADDRSTRLEN];
2211 HOST_ENTRY* h;
2212
2213 /* reply icmp packet (ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2214 if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
2215 /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2216 return 0;
2217 }
2218
2219 sent_icmp = (struct icmp6_hdr*)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
2220
2221 if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
2222 /* not caused by us */
2223 return 0;
2224 }
2225
2226 seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
2227 if (seqmap_value == NULL) {
2228 return 0;
2229 }
2230
2231 getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
2232
2233 switch (icp->icmp6_type) {
2234 case ICMP_UNREACH:
2235 h = table[seqmap_value->host_nr];
2236 if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
2237 print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
2238 addr_ascii, h->host);
2239 }
2240 else {
2241 print_warning("%s from %s for ICMP Echo sent to %s",
2242 icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
2243 }
2244
2245 print_warning("\n");
2246 num_othericmprcvd++;
2247 break;
2248
2249 case ICMP_SOURCEQUENCH:
2250 case ICMP_REDIRECT:
2251 case ICMP_TIMXCEED:
2252 case ICMP_PARAMPROB:
2253 h = table[seqmap_value->host_nr];
2254 if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
2255 print_warning("%s from %s for ICMP Echo sent to %s",
2256 icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
2257 }
2258 else {
2259 print_warning("ICMP %d from %s for ICMP Echo sent to %s",
2260 icp->icmp6_type, addr_ascii, h->host);
2261 }
2262 print_warning("\n");
2263 num_othericmprcvd++;
2264 break;
2265 }
2266
2267 return 0;
2268 }
2269
2270 *id = icp->icmp6_id;
2271 *seq = ntohs(icp->icmp6_seq);
2272
2273 return 1;
2274 }
2275 #endif
2276
2277 int wait_for_reply(int64_t wait_time)
2278 {
2279 int result;
2280 static char buffer[RECV_BUFSIZE];
2281 struct sockaddr_storage response_addr;
2282 int n, avg;
2283 HOST_ENTRY* h;
2284 int64_t this_reply;
2285 int this_count;
2286 int64_t recv_time=0;
2287 SEQMAP_VALUE* seqmap_value;
2288 unsigned short id;
2289 unsigned short seq;
2290
2291 /* Receive packet */
2292 result = receive_packet(wait_time, /* max. wait time, in ns */
2293 &recv_time, /* reply_timestamp */
2294 (struct sockaddr*)&response_addr, /* reply_src_addr */
2295 sizeof(response_addr), /* reply_src_addr_len */
2296 buffer, /* reply_buf */
2297 sizeof(buffer) /* reply_buf_len */
2298 );
2299
2300 if (result <= 0) {
2301 return 0;
2302 }
2303
2304 update_current_time();
2305 if(recv_time==0) recv_time = current_time_ns;
2306
2307 /* Process ICMP packet and retrieve id/seq */
2308 if (response_addr.ss_family == AF_INET) {
2309 int ip_hlen = decode_icmp_ipv4(
2310 (struct sockaddr*)&response_addr,
2311 sizeof(response_addr),
2312 buffer,
2313 sizeof(buffer),
2314 &id,
2315 &seq);
2316 if (ip_hlen < 0) {
2317 return 1;
2318 }
2319 if (id != ident4) {
2320 return 1; /* packet received, but not the one we are looking for! */
2321 }
2322 if (!using_sock_dgram4) {
2323 /* do not include IP header in returned size, to be consistent with ping(8) and also
2324 * with fping with IPv6 hosts */
2325 result -= ip_hlen;
2326 }
2327 }
2328 #ifdef IPV6
2329 else if (response_addr.ss_family == AF_INET6) {
2330 if (!decode_icmp_ipv6(
2331 (struct sockaddr*)&response_addr,
2332 sizeof(response_addr),
2333 buffer,
2334 sizeof(buffer),
2335 &id,
2336 &seq)) {
2337 return 1;
2338 }
2339 if (id != ident6) {
2340 return 1; /* packet received, but not the one we are looking for! */
2341 }
2342 }
2343 #endif
2344 else {
2345 return 1;
2346 }
2347
2348 seqmap_value = seqmap_fetch(seq, current_time_ns);
2349 if (seqmap_value == NULL) {
2350 return 1;
2351 }
2352
2353 /* find corresponding host_entry */
2354 n = seqmap_value->host_nr;
2355 h = table[n];
2356 this_count = seqmap_value->ping_count;
2357 this_reply = recv_time - seqmap_value->ping_ts;
2358
2359 /* update stats that include invalid replies */
2360 h->num_recv_total++;
2361 num_pingreceived++;
2362
2363 dbg_printf("received [%d] from %s\n", this_count, h->host);
2364
2365 /* discard duplicates */
2366 if (!loop_flag && h->resp_times[this_count] >= 0) {
2367 if (!per_recv_flag) {
2368 fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
2369 h->host, this_count, result, sprint_tm(this_reply));
2370
2371 if (addr_cmp((struct sockaddr*)&response_addr, (struct sockaddr*)&h->saddr)) {
2372 char buf[INET6_ADDRSTRLEN];
2373 getnameinfo((struct sockaddr*)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
2374 fprintf(stderr, " [<- %s]", buf);
2375 }
2376 fprintf(stderr, "\n");
2377 }
2378 return 1;
2379 }
2380
2381 /* discard reply if delay is larger than timeout
2382 * (see also: github #32) */
2383 if (this_reply > h->timeout) {
2384 return 1;
2385 }
2386
2387 /* update stats */
2388 stats_add(h, this_count, 1, this_reply);
2389 // TODO: move to stats_add?
2390 if (!max_reply || this_reply > max_reply)
2391 max_reply = this_reply;
2392 if (!min_reply || this_reply < min_reply)
2393 min_reply = this_reply;
2394 sum_replies += this_reply;
2395 total_replies++;
2396
2397 /* initialize timeout to initial timeout (without backoff) */
2398 h->timeout = timeout;
2399
2400 /* remove timeout event */
2401 struct event *timeout_event = host_get_timeout_event(h, this_count);
2402 if(timeout_event) {
2403 ev_remove(&event_queue_timeout, timeout_event);
2404 }
2405
2406 /* print "is alive" */
2407 if (h->num_recv == 1) {
2408 num_alive++;
2409 if (verbose_flag || alive_flag) {
2410 printf("%s", h->host);
2411
2412 if (verbose_flag)
2413 printf(" is alive");
2414
2415 if (elapsed_flag)
2416 printf(" (%s ms)", sprint_tm(this_reply));
2417
2418 if (addr_cmp((struct sockaddr*)&response_addr, (struct sockaddr*)&h->saddr)) {
2419 char buf[INET6_ADDRSTRLEN];
2420 getnameinfo((struct sockaddr*)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
2421 fprintf(stderr, " [<- %s]", buf);
2422 }
2423
2424 printf("\n");
2425 }
2426 }
2427
2428 /* print received ping (unless --quiet) */
2429 if (per_recv_flag) {
2430 if (timestamp_flag) {
2431 printf("[%.5f] ", (double)recv_time / 1e9);
2432 }
2433 avg = h->total_time / h->num_recv;
2434 printf("%-*s : [%d], %d bytes, %s ms",
2435 max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
2436 printf(" (%s avg, ", sprint_tm(avg));
2437
2438 if (h->num_recv <= h->num_sent) {
2439 printf("%d%% loss)",
2440 ((h->num_sent - h->num_recv) * 100) / h->num_sent);
2441 }
2442 else {
2443 printf("%d%% return)",
2444 (h->num_recv_total * 100) / h->num_sent);
2445 }
2446
2447 if (addr_cmp((struct sockaddr*)&response_addr, (struct sockaddr*)&h->saddr)) {
2448 char buf[INET6_ADDRSTRLEN];
2449 getnameinfo((struct sockaddr*)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
2450 fprintf(stderr, " [<- %s]", buf);
2451 }
2452
2453 printf("\n");
2454 }
2455
2456 return 1;
2457 }
2458
2459 /************************************************************
2460
2461 Function: add_name
2462
2463 *************************************************************
2464
2465 Inputs: char* name
2466
2467 Description:
2468
2469 process input name for addition to target list
2470 name can turn into multiple targets via multiple interfaces (-m)
2471 or via NIS groups
2472
2473 ************************************************************/
2474
2475 void add_name(char* name)
2476 {
2477 struct addrinfo *res0, *res, hints;
2478 int ret_ga;
2479 char* printname;
2480 char namebuf[256];
2481 char addrbuf[256];
2482
2483 /* getaddrinfo */
2484 memset(&hints, 0, sizeof(struct addrinfo));
2485 hints.ai_flags = AI_UNUSABLE;
2486 hints.ai_socktype = SOCK_RAW;
2487 hints.ai_family = hints_ai_family;
2488 if (hints_ai_family == AF_INET) {
2489 hints.ai_protocol = IPPROTO_ICMP;
2490 }
2491 #ifdef IPV6
2492 else if (hints_ai_family == AF_INET6) {
2493 hints.ai_protocol = IPPROTO_ICMPV6;
2494 }
2495 #endif
2496 else {
2497 hints.ai_socktype = SOCK_STREAM;
2498 hints.ai_protocol = 0;
2499 }
2500 ret_ga = getaddrinfo(name, NULL, &hints, &res0);
2501 if (ret_ga) {
2502 if (!quiet_flag)
2503 print_warning("%s: %s\n", name, gai_strerror(ret_ga));
2504 num_noaddress++;
2505 return;
2506 }
2507
2508 /* NOTE: we could/should loop with res on all addresses like this:
2509 * for (res = res0; res; res = res->ai_next) {
2510 * We don't do it yet, however, because is is an incompatible change
2511 * (need to implement a separate option for this)
2512 */
2513 for (res = res0; res; res = res->ai_next) {
2514 /* name_flag: addr -> name lookup requested) */
2515 if (name_flag || rdns_flag) {
2516 int do_rdns = rdns_flag ? 1 : 0;
2517 if (name_flag) {
2518 /* Was it a numerical address? Only then do a rdns-query */
2519 struct addrinfo* nres;
2520 hints.ai_flags = AI_NUMERICHOST;
2521 if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
2522 do_rdns = 1;
2523 freeaddrinfo(nres);
2524 }
2525 }
2526
2527 if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
2528 printname = namebuf;
2529 }
2530 else {
2531 printname = name;
2532 }
2533 }
2534 else {
2535 printname = name;
2536 }
2537
2538 /* addr_flag: name -> addr lookup requested */
2539 if (addr_flag) {
2540 int ret;
2541 ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
2542 sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2543 if (ret) {
2544 if (!quiet_flag) {
2545 print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
2546 }
2547 continue;
2548 }
2549
2550 if (name_flag || rdns_flag) {
2551 char nameaddrbuf[512+3];
2552 snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
2553 add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
2554 }
2555 else {
2556 add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
2557 }
2558 }
2559 else {
2560 add_addr(name, printname, res->ai_addr, res->ai_addrlen);
2561 }
2562
2563 if (!multif_flag) {
2564 break;
2565 }
2566 }
2567
2568 freeaddrinfo(res0);
2569 }
2570
2571 /************************************************************
2572
2573 Function: add_addr
2574
2575 *************************************************************
2576
2577 Description:
2578
2579 add single address to list of hosts to be pinged
2580
2581 ************************************************************/
2582
2583 void add_addr(char* name, char* host, struct sockaddr* ipaddr, socklen_t ipaddr_len)
2584 {
2585 HOST_ENTRY* p;
2586 int n;
2587 int64_t *i;
2588
2589 p = (HOST_ENTRY*)calloc(1, sizeof(HOST_ENTRY));
2590 if (!p)
2591 crash_and_burn("can't allocate HOST_ENTRY");
2592
2593 p->name = strdup(name);
2594 p->host = strdup(host);
2595 memcpy(&p->saddr, ipaddr, ipaddr_len);
2596 p->saddr_len = ipaddr_len;
2597 p->timeout = timeout;
2598 p->min_reply = 0;
2599
2600 if (netdata_flag) {
2601 char* s = p->name;
2602 while (*s) {
2603 if (!isalnum(*s))
2604 *s = '_';
2605 s++;
2606 }
2607 }
2608
2609 if (strlen(p->host) > max_hostname_len)
2610 max_hostname_len = strlen(p->host);
2611
2612 /* array for response time results */
2613 if (!loop_flag) {
2614 i = (int64_t*)malloc(trials * sizeof(int64_t));
2615 if (!i)
2616 crash_and_burn("can't allocate resp_times array");
2617
2618 for (n = 1; n < trials; n++)
2619 i[n] = RESP_UNUSED;
2620
2621 p->resp_times = i;
2622 }
2623
2624 /* allocate event storage */
2625 p->event_storage_ping = (struct event *) calloc(event_storage_count, sizeof(struct event));
2626 p->event_storage_timeout = (struct event *) calloc(event_storage_count, sizeof(struct event));
2627
2628 /* schedule first ping */
2629 host_add_ping_event(p, 0, current_time_ns);
2630
2631 num_hosts++;
2632 }
2633
2634 /************************************************************
2635
2636 Function: crash_and_burn
2637
2638 *************************************************************
2639
2640 Inputs: char* message
2641
2642 Description:
2643
2644 ************************************************************/
2645
2646 void crash_and_burn(char* message)
2647 {
2648 if (verbose_flag)
2649 fprintf(stderr, "%s: %s\n", prog, message);
2650
2651 exit(4);
2652 }
2653
2654 /************************************************************
2655
2656 Function: errno_crash_and_burn
2657
2658 *************************************************************
2659
2660 Inputs: char* message
2661
2662 Description:
2663
2664 ************************************************************/
2665
2666 void errno_crash_and_burn(char* message)
2667 {
2668 fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
2669 exit(4);
2670 }
2671
2672 /************************************************************
2673
2674 Function: print_warning
2675
2676 Description: fprintf(stderr, ...), unless running with -q
2677
2678 *************************************************************/
2679
2680 void print_warning(char* format, ...)
2681 {
2682 va_list args;
2683 if (!quiet_flag) {
2684 va_start(args, format);
2685 vfprintf(stderr, format, args);
2686 va_end(args);
2687 }
2688 }
2689
2690 /************************************************************
2691
2692 Function: sprint_tm
2693
2694 *************************************************************
2695
2696 render nanosecond int64_t value into milliseconds string with three digits of
2697 precision.
2698
2699 ************************************************************/
2700
2701 const char* sprint_tm(int64_t ns)
2702 {
2703 static char buf[10];
2704 double t = (double)ns / 1e6;
2705
2706 if (t < 0.0) {
2707 /* negative (unexpected) */
2708 sprintf(buf, "%.2g", t);
2709 }
2710 else if (t < 1.0) {
2711 /* <= 0.99 ms */
2712 sprintf(buf, "%.3f", t);
2713 }
2714 else if (t < 10.0) {
2715 /* 1.00 - 9.99 ms */
2716 sprintf(buf, "%.2f", t);
2717 }
2718 else if (t < 100.0) {
2719 /* 10.0 - 99.9 ms */
2720 sprintf(buf, "%.1f", t);
2721 }
2722 else if (t < 1000000.0) {
2723 /* 100 - 1'000'000 ms */
2724 sprintf(buf, "%.0f", t);
2725 }
2726 else {
2727 sprintf(buf, "%.3e", t);
2728 }
2729
2730 return (buf);
2731 }
2732
2733 /************************************************************
2734
2735 Function: addr_cmp
2736
2737 *************************************************************/
2738 int addr_cmp(struct sockaddr* a, struct sockaddr* b)
2739 {
2740 if (a->sa_family != b->sa_family) {
2741 return a->sa_family - b->sa_family;
2742 }
2743 else {
2744 if (a->sa_family == AF_INET) {
2745 return ((struct sockaddr_in*)a)->sin_addr.s_addr - ((struct sockaddr_in*)b)->sin_addr.s_addr;
2746 }
2747 else if (a->sa_family == AF_INET6) {
2748 return memcmp(&((struct sockaddr_in6*)a)->sin6_addr,
2749 &((struct sockaddr_in6*)b)->sin6_addr,
2750 sizeof(((struct sockaddr_in6*)a)->sin6_addr));
2751 }
2752 }
2753
2754 return 0;
2755 }
2756
2757 void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
2758 {
2759 struct event *event = &h->event_storage_ping[index % event_storage_count];
2760 event->host = h;
2761 event->ping_index = index;
2762 event->ev_time = ev_time;
2763 ev_enqueue(&event_queue_ping, event);
2764
2765 dbg_printf("%s [%d]: add ping event in %.0f ms\n",
2766 event->host->host, index, (ev_time - current_time_ns) / 1e6);
2767 }
2768
2769 void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
2770 {
2771 struct event *event = &h->event_storage_timeout[index % event_storage_count];
2772 event->host = h;
2773 event->ping_index = index;
2774 event->ev_time = ev_time;
2775 ev_enqueue(&event_queue_timeout, event);
2776
2777 dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
2778 event->host->host, index, (ev_time - current_time_ns) / 1e6);
2779 }
2780
2781 struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
2782 {
2783 return &h->event_storage_timeout[index % event_storage_count];
2784 }
2785
2786
2787 /************************************************************
2788
2789 Function: ev_enqueue
2790
2791 Enqueue an event
2792
2793 The queue is sorted by event->ev_time, so that queue->first always points to
2794 the earliest event.
2795
2796 We start scanning the queue from the tail, because we assume
2797 that new events mostly get inserted with a event time higher
2798 than the others.
2799
2800 *************************************************************/
2801 void ev_enqueue(struct event_queue *queue, struct event* event)
2802 {
2803 struct event* i;
2804 struct event* i_prev;
2805
2806 /* Empty list */
2807 if (queue->last == NULL) {
2808 event->ev_next = NULL;
2809 event->ev_prev = NULL;
2810 queue->first = event;
2811 queue->last = event;
2812 return;
2813 }
2814
2815 /* Insert on tail? */
2816 if (event->ev_time - queue->last->ev_time >= 0) {
2817 event->ev_next = NULL;
2818 event->ev_prev = queue->last;
2819 queue->last->ev_next = event;
2820 queue->last = event;
2821 return;
2822 }
2823
2824 /* Find insertion point */
2825 i = queue->last;
2826 while (1) {
2827 i_prev = i->ev_prev;
2828 if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
2829 event->ev_prev = i_prev;
2830 event->ev_next = i;
2831 i->ev_prev = event;
2832 if (i_prev != NULL) {
2833 i_prev->ev_next = event;
2834 }
2835 else {
2836 queue->first = event;
2837 }
2838 return;
2839 }
2840 i = i_prev;
2841 }
2842 }
2843
2844 /************************************************************
2845
2846 Function: ev_dequeue
2847
2848 *************************************************************/
2849 struct event *ev_dequeue(struct event_queue *queue)
2850 {
2851 struct event *dequeued;
2852
2853 if (queue->first == NULL) {
2854 return NULL;
2855 }
2856 dequeued = queue->first;
2857 ev_remove(queue, dequeued);
2858
2859 return dequeued;
2860 }
2861
2862 /************************************************************
2863
2864 Function: ev_remove
2865
2866 *************************************************************/
2867 void ev_remove(struct event_queue *queue, struct event *event)
2868 {
2869 if (queue->first == event) {
2870 queue->first = event->ev_next;
2871 }
2872 if (queue->last == event) {
2873 queue->last = event->ev_prev;
2874 }
2875 if (event->ev_prev) {
2876 event->ev_prev->ev_next = event->ev_next;
2877 }
2878 if (event->ev_next) {
2879 event->ev_next->ev_prev = event->ev_prev;
2880 }
2881 event->ev_prev = NULL;
2882 event->ev_next = NULL;
2883 }
2884
2885 /************************************************************
2886
2887 Function: usage
2888
2889 *************************************************************
2890
2891 Inputs: int: 0 if output on request, 1 if output because of wrong argument
2892
2893 Description:
2894
2895 ************************************************************/
2896
2897 void usage(int is_error)
2898 {
2899 FILE* out = is_error ? stderr : stdout;
2900 fprintf(out, "Usage: %s [options] [targets...]\n", prog);
2901 fprintf(out, "\n");
2902 fprintf(out, "Probing options:\n");
2903 fprintf(out, " -4, --ipv4 only ping IPv4 addresses\n");
2904 fprintf(out, " -6, --ipv6 only ping IPv6 addresses\n");
2905 fprintf(out, " -b, --size=BYTES amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
2906 fprintf(out, " -B, --backoff=N set exponential backoff factor to N (default: 1.5)\n");
2907 fprintf(out, " -c, --count=N count mode: send N pings to each target\n");
2908 fprintf(out, " -f, --file=FILE read list of targets from a file ( - means stdin)\n");
2909 fprintf(out, " -g, --generate generate target list (only if no -f specified)\n");
2910 fprintf(out, " (give start and end IP in the target list, or a CIDR address)\n");
2911 fprintf(out, " (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
2912 fprintf(out, " -H, --ttl=N set the IP TTL value (Time To Live hops)\n");
2913 #ifdef SO_BINDTODEVICE
2914 fprintf(out, " -I, --iface=IFACE bind to a particular interface\n");
2915 #endif
2916 fprintf(out, " -l, --loop loop mode: send pings forever\n");
2917 fprintf(out, " -m, --all use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
2918 fprintf(out, " -M, --dontfrag set the Don't Fragment flag\n");
2919 fprintf(out, " -O, --tos=N set the type of service (tos) flag on the ICMP packets\n");
2920 fprintf(out, " -p, --period=MSEC interval between ping packets to one target (in ms)\n");
2921 fprintf(out, " (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
2922 fprintf(out, " -r, --retry=N number of retries (default: %d)\n", DEFAULT_RETRY);
2923 fprintf(out, " -R, --random random packet data (to foil link data compression)\n");
2924 fprintf(out, " -S, --src=IP set source address\n");
2925 fprintf(out, " -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
2926 fprintf(out, " except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
2927 fprintf(out, "\n");
2928 fprintf(out, "Output options:\n");
2929 fprintf(out, " -a, --alive show targets that are alive\n");
2930 fprintf(out, " -A, --addr show targets by address\n");
2931 fprintf(out, " -C, --vcount=N same as -c, report results in verbose format\n");
2932 fprintf(out, " -d, --rdns show targets by name (force reverse-DNS lookup)\n");
2933 fprintf(out, " -D, --timestamp print timestamp before each output line\n");
2934 fprintf(out, " -e, --elapsed show elapsed time on return packets\n");
2935 fprintf(out, " -i, --interval=MSEC interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
2936 fprintf(out, " -n, --name show targets by name (reverse-DNS lookup for target IPs)\n");
2937 fprintf(out, " -N, --netdata output compatible for netdata (-l -Q are required)\n");
2938 fprintf(out, " -o, --outage show the accumulated outage time (lost packets * packet interval)\n");
2939 fprintf(out, " -q, --quiet quiet (don't show per-target/per-ping results)\n");
2940 fprintf(out, " -Q, --squiet=SECS same as -q, but add interval summary every SECS seconds\n");
2941 fprintf(out, " -s, --stats print final stats\n");
2942 fprintf(out, " -u, --unreach show targets that are unreachable\n");
2943 fprintf(out, " -v, --version show version\n");
2944 fprintf(out, " -x, --reachable=N shows if >=N hosts are reachable or not\n");
2945 exit(is_error);
2946 }