• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

schweikert / fping / 6729315960

02 Nov 2023 07:00AM UTC coverage: 81.581%. Remained the same
6729315960

push

github

schweikert
Change obsolete macro from AC_CANONICAL_SYSTEM to AC_CANONICAL_TARGET

1094 of 1341 relevant lines covered (81.58%)

120.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.52
/src/fping.c
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 "fping.h"
38
#include "config.h"
39
#include "options.h"
40
#include "optparse.h"
41

42
#include <errno.h>
43
#include <inttypes.h>
44
#include <signal.h>
45
#include <stdarg.h>
46
#include <stdint.h>
47
#include <stdio.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
/* CLOCK_MONTONIC starts under macOS, OpenBSD and FreeBSD with undefined positive point and can not be use
120
 * see github PR #217
121
 * The configure script detect the predefined operating systems an set CLOCK_REALTIME using over ONLY_CLOCK_REALTIME variable
122
 */
123
#if HAVE_SO_TIMESTAMPNS || ONLY_CLOCK_REALTIME
124
#define CLOCKID CLOCK_REALTIME
125
#endif
126

127
#if !defined(CLOCKID)
128
#if defined(CLOCK_MONOTONIC)
129
#define CLOCKID CLOCK_MONOTONIC
130
#else
131
#define CLOCKID CLOCK_REALTIME
132
#endif
133
#endif
134

135
/*** Ping packet defines ***/
136

137
#define MAX_IP_PACKET 65536 /* (theoretical) max IP packet size */
138
#define SIZE_IP_HDR 40
139
#define SIZE_ICMP_HDR 8 /* from ip_icmp.h */
140
#define MAX_PING_DATA (MAX_IP_PACKET - SIZE_IP_HDR - SIZE_ICMP_HDR)
141

142
#define MAX_GENERATE 100000 /* maximum number of hosts that -g can generate */
143

144
/* sized so as to be like traditional ping */
145
#define DEFAULT_PING_DATA_SIZE 56
146

147
/* maxima and minima */
148
#ifdef FPING_SAFE_LIMITS
149
#define MIN_INTERVAL 1 /* in millisec */
150
#define MIN_PERHOST_INTERVAL 10 /* in millisec */
151
#else
152
#define MIN_INTERVAL 0
153
#define MIN_PERHOST_INTERVAL 0
154
#endif
155

156
/* response time array flags */
157
#define RESP_WAITING -1
158
#define RESP_UNUSED -2
159
#define RESP_ERROR -3
160
#define RESP_TIMEOUT -4
161

162
/* debugging flags */
163
#if defined(DEBUG) || defined(_DEBUG)
164
#define DBG_TRACE 1
165
#define DBG_SENT_TIMES 2
166
#define DBG_RANDOM_LOSE_FEW 4
167
#define DBG_RANDOM_LOSE_MANY 8
168
#define DBG_PRINT_PER_SYSTEM 16
169
#define DBG_REPORT_ALL_RTTS 32
170
#endif /* DEBUG || _DEBUG */
171

172
/* Long names for ICMP packet types */
173
#define ICMP_TYPE_STR_MAX 18
174
char *icmp_type_str[19] = {
175
    "ICMP Echo Reply", /* 0 */
176
    "",
177
    "",
178
    "ICMP Unreachable", /* 3 */
179
    "ICMP Source Quench", /* 4 */
180
    "ICMP Redirect", /* 5 */
181
    "",
182
    "",
183
    "ICMP Echo", /* 8 */
184
    "",
185
    "",
186
    "ICMP Time Exceeded", /* 11 */
187
    "ICMP Parameter Problem", /* 12 */
188
    "ICMP Timestamp Request", /* 13 */
189
    "ICMP Timestamp Reply", /* 14 */
190
    "ICMP Information Request", /* 15 */
191
    "ICMP Information Reply", /* 16 */
192
    "ICMP Mask Request", /* 17 */
193
    "ICMP Mask Reply" /* 18 */
194
};
195

196
char *icmp_unreach_str[16] = {
197
    "ICMP Network Unreachable", /* 0 */
198
    "ICMP Host Unreachable", /* 1 */
199
    "ICMP Protocol Unreachable", /* 2 */
200
    "ICMP Port Unreachable", /* 3 */
201
    "ICMP Unreachable (Fragmentation Needed)", /* 4 */
202
    "ICMP Unreachable (Source Route Failed)", /* 5 */
203
    "ICMP Unreachable (Destination Network Unknown)", /* 6 */
204
    "ICMP Unreachable (Destination Host Unknown)", /* 7 */
205
    "ICMP Unreachable (Source Host Isolated)", /* 8 */
206
    "ICMP Unreachable (Communication with Network Prohibited)", /* 9 */
207
    "ICMP Unreachable (Communication with Host Prohibited)", /* 10 */
208
    "ICMP Unreachable (Network Unreachable For Type Of Service)", /* 11 */
209
    "ICMP Unreachable (Host Unreachable For Type Of Service)", /* 12 */
210
    "ICMP Unreachable (Communication Administratively Prohibited)", /* 13 */
211
    "ICMP Unreachable (Host Precedence Violation)", /* 14 */
212
    "ICMP Unreachable (Precedence cutoff in effect)" /* 15 */
213
};
214

215
#define ICMP_UNREACH_MAXTYPE 15
216

217
struct event;
218
typedef struct host_entry {
219
    int i; /* index into array */
220
    char *name; /* name as given by user */
221
    char *host; /* text description of host */
222
    struct sockaddr_storage saddr; /* internet address */
223
    socklen_t saddr_len;
224
    int64_t timeout; /* time to wait for response */
225
    int64_t last_send_time; /* time of last packet sent */
226
    int num_sent; /* number of ping packets sent (for statistics) */
227
    int num_recv; /* number of pings received (duplicates ignored) */
228
    int num_recv_total; /* number of pings received, including duplicates */
229
    int64_t max_reply; /* longest response time */
230
    int64_t min_reply; /* shortest response time */
231
    int64_t total_time; /* sum of response times */
232
    /* _i -> splits (reset on every report interval) */
233
    int num_sent_i; /* number of ping packets sent */
234
    int num_recv_i; /* number of pings received */
235
    int64_t max_reply_i; /* longest response time */
236
    int64_t min_reply_i; /* shortest response time */
237
    int64_t total_time_i; /* sum of response times */
238
    int64_t *resp_times; /* individual response times */
239

240
    /* to avoid allocating two struct events each time that we send a ping, we
241
     * preallocate here two struct events for each ping that we might send for
242
     * this host. */
243
    struct event *event_storage_ping;
244
    struct event *event_storage_timeout;
245
} HOST_ENTRY;
246

247
int event_storage_count; /* how many events can be stored in host_entry->event_storage_xxx */
248

249
/* basic algorithm to ensure that we have correct data at all times:
250
 *
251
 * 1. when a ping is sent:
252
 *    - two events get added into event_queue:
253
 *      - t+PERIOD: ping event
254
 *      - t+TIMEOUT: timeout event
255
 *
256
 * 2. when a ping is received:
257
 *    - record statistics (increase num_sent and num_received)
258
 *    - remove timeout event (we store the event in seqmap, so that we can retrieve it when the response is received)
259
 *
260
 * 3. when a timeout happens:
261
 *    - record statistics (increase num_sent only)
262
 */
263

264
#define EV_TYPE_PING 1
265
#define EV_TYPE_TIMEOUT 2
266

267
struct event {
268
    struct event *ev_prev;
269
    struct event *ev_next;
270
    int64_t ev_time;
271
    struct host_entry *host;
272
    int ping_index;
273
};
274

275
struct event_queue {
276
    struct event *first;
277
    struct event *last;
278
};
279

280
/*** globals ***/
281

282
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
283

284
/* we keep two separate queues: a ping queue, for when the next ping should be
285
 * sent, and a timeout queue. the reason for having two separate queues is that
286
 * the ping period and the timeout value are different, so if we put them in
287
 * the same event queue, we would need to scan many more entries when inserting
288
 * into the sorted list.
289
 */
290
struct event_queue event_queue_ping;
291
struct event_queue event_queue_timeout;
292

293
char *prog;
294
int ident4 = 0; /* our icmp identity field */
295
int ident6 = 0;
296
int socket4 = -1;
297
int socktype4 = -1;
298
int using_sock_dgram4 = 0;
299
#ifndef IPV6
300
int hints_ai_family = AF_INET;
301
#else
302
int socket6 = -1;
303
int socktype6 = -1;
304
int hints_ai_family = AF_UNSPEC;
305
#endif
306

307
volatile sig_atomic_t status_snapshot = 0;
308
volatile sig_atomic_t finish_requested = 0;
309

310
unsigned int debugging = 0;
311

312
/* all time-related values are int64_t nanoseconds */
313
unsigned int retry = DEFAULT_RETRY;
314
int64_t timeout = (int64_t)DEFAULT_TIMEOUT * 1000000;
315
int64_t interval = (int64_t)DEFAULT_INTERVAL * 1000000;
316
int64_t perhost_interval = (int64_t)DEFAULT_PERHOST_INTERVAL * 1000000;
317
float backoff = DEFAULT_BACKOFF_FACTOR;
318
unsigned int ping_data_size = DEFAULT_PING_DATA_SIZE;
319
unsigned int count = 1, min_reachable = 0;
320
unsigned int trials;
321
int64_t report_interval = 0;
322
unsigned int ttl = 0;
323
int src_addr_set = 0;
324
struct in_addr src_addr;
325
#ifdef IPV6
326
int src_addr6_set = 0;
327
struct in6_addr src_addr6;
328
#endif
329

330
/* global stats */
331
int64_t max_reply = 0;
332
int64_t min_reply = 0;
333
int64_t total_replies = 0;
334
int64_t sum_replies = 0;
335
int max_hostname_len = 0;
336
int num_hosts = 0; /* total number of hosts */
337
int num_alive = 0, /* total number alive */
338
    num_unreachable = 0, /* total number unreachable */
339
    num_noaddress = 0; /* total number of addresses not found */
340
int num_timeout = 0, /* number of times select timed out */
341
    num_pingsent = 0, /* total pings sent */
342
    num_pingreceived = 0, /* total pings received */
343
    num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
344

345
struct timespec current_time; /* current time (pseudo) */
346
int64_t current_time_ns;
347
int64_t start_time;
348
int64_t end_time;
349
int64_t last_send_time; /* time last ping was sent */
350
int64_t next_report_time; /* time next -Q report is expected */
351

352
/* switches */
353
int generate_flag = 0; /* flag for IP list generation */
354
int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
355
int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag;
356
int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
357
int multif_flag, timeout_flag, fast_reachable;
358
int outage_flag = 0;
359
int timestamp_flag = 0;
360
int random_data_flag = 0;
361
#if defined(DEBUG) || defined(_DEBUG)
362
int randomly_lose_flag, trace_flag, print_per_system_flag;
363
int lose_factor;
364
#endif /* DEBUG || _DEBUG */
365

366
char *filename = NULL; /* file containing hosts to ping */
367

368
/*** forward declarations ***/
369

370
void add_name(char *name);
371
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len);
372
char *na_cat(char *name, struct in_addr ipaddr);
373
void crash_and_burn(char *message);
374
void errno_crash_and_burn(char *message);
375
char *get_host_by_address(struct in_addr in);
376
int send_ping(HOST_ENTRY *h, int index);
377
void usage(int);
378
int wait_for_reply(int64_t);
379
void print_per_system_stats(void);
380
void print_per_system_splits(void);
381
void print_netdata(void);
382
void print_global_stats(void);
383
void main_loop();
384
void signal_handler(int);
385
void finish();
386
const char *sprint_tm(int64_t t);
387
void ev_enqueue(struct event_queue *queue, struct event *event);
388
struct event *ev_dequeue(struct event_queue *queue);
389
void ev_remove(struct event_queue *queue, struct event *event);
390
void add_cidr(char *);
391
void add_range(char *, char *);
392
void print_warning(char *fmt, ...);
393
int addr_cmp(struct sockaddr *a, struct sockaddr *b);
394
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
395
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
396
struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
397
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
398
void update_current_time();
399

400
/************************************************************
401

402
  Function: p_setsockopt
403

404
*************************************************************
405

406
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
407

408
  Description:
409

410
  Elevates privileges to p_uid when required, calls
411
  setsockopt, and drops privileges back.
412

413
************************************************************/
414

415
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
×
416
    const void *optval, socklen_t optlen)
417
{
418
    const uid_t saved_uid = geteuid();
×
419
    int res;
420

421
    if (p_uid != saved_uid && seteuid(p_uid)) {
×
422
        perror("cannot elevate privileges for setsockopt");
×
423
    }
424

425
    res = setsockopt(sockfd, level, optname, optval, optlen);
×
426

427
    if (p_uid != saved_uid && seteuid(saved_uid)) {
×
428
        perror("fatal error: could not drop privileges after setsockopt");
×
429
        /* continuing would be a security hole */
430
        exit(4);
×
431
    }
432

433
    return res;
×
434
}
435

436
/************************************************************
437

438
  Function: main
439

440
*************************************************************
441

442
  Inputs:  int argc, char** argv
443

444
  Description:
445

446
  Main program entry point
447

448
************************************************************/
449

450
int main(int argc, char **argv)
215✔
451
{
452
    int c;
453
    const uid_t suid = geteuid();
215✔
454
    int tos = 0;
215✔
455
    struct optparse optparse_state;
456
#ifdef USE_SIGACTION
457
    struct sigaction act;
458
#endif
459

460
    /* pre-parse -h/--help, so that we also can output help information
461
     * without trying to open the socket, which might fail */
462
    prog = argv[0];
215✔
463
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
215✔
464
        usage(0);
3✔
465
    }
1✔
466

467
    socket4 = open_ping_socket_ipv4(&socktype4);
213✔
468
#ifdef __linux__
469
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
470
     * structure is missing in the message.
471
     */
472
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
144✔
473
#endif
474

475
#ifdef IPV6
476
    socket6 = open_ping_socket_ipv6(&socktype6);
213✔
477
    /* if called (sym-linked) via 'fping6', imply '-6'
478
     * for backward compatibility */
479
    if (strstr(prog, "fping6")) {
213✔
480
        hints_ai_family = AF_INET6;
×
481
    }
482
#endif
483

484
    memset(&src_addr, 0, sizeof(src_addr));
213✔
485
#ifdef IPV6
486
    memset(&src_addr6, 0, sizeof(src_addr6));
213✔
487
#endif
488

489
    if (!suid && suid != getuid()) {
213✔
490
        /* *temporarily* drop privileges */
491
        if (seteuid(getuid()) == -1)
208✔
492
            perror("cannot setuid");
×
493
    }
64✔
494

495
    optparse_init(&optparse_state, argv);
213✔
496
    ident4 = ident6 = htons(getpid() & 0xFFFF);
213✔
497
    verbose_flag = 1;
213✔
498
    backoff_flag = 1;
213✔
499
    opterr = 1;
213✔
500

501
    /* get command line options */
502

503
    struct optparse_long longopts[] = {
213✔
504
        { "ipv4", '4', OPTPARSE_NONE },
505
        { "ipv6", '6', OPTPARSE_NONE },
506
        { "alive", 'a', OPTPARSE_NONE },
507
        { "addr", 'A', OPTPARSE_NONE },
508
        { "size", 'b', OPTPARSE_REQUIRED },
509
        { "backoff", 'B', OPTPARSE_REQUIRED },
510
        { "count", 'c', OPTPARSE_REQUIRED },
511
        { "vcount", 'C', OPTPARSE_REQUIRED },
512
        { "rdns", 'd', OPTPARSE_NONE },
513
        { "timestamp", 'D', OPTPARSE_NONE },
514
        { "elapsed", 'e', OPTPARSE_NONE },
515
        { "file", 'f', OPTPARSE_REQUIRED },
516
        { "generate", 'g', OPTPARSE_NONE },
517
        { "help", 'h', OPTPARSE_NONE },
518
        { "ttl", 'H', OPTPARSE_REQUIRED },
519
        { "interval", 'i', OPTPARSE_REQUIRED },
520
        { "iface", 'I', OPTPARSE_REQUIRED },
521
        { "loop", 'l', OPTPARSE_NONE },
522
        { "all", 'm', OPTPARSE_NONE },
523
        { "dontfrag", 'M', OPTPARSE_NONE },
524
        { "name", 'n', OPTPARSE_NONE },
525
        { "netdata", 'N', OPTPARSE_NONE },
526
        { "outage", 'o', OPTPARSE_NONE },
527
        { "tos", 'O', OPTPARSE_REQUIRED },
528
        { "period", 'p', OPTPARSE_REQUIRED },
529
        { "quiet", 'q', OPTPARSE_NONE },
530
        { "squiet", 'Q', OPTPARSE_REQUIRED },
531
        { "retry", 'r', OPTPARSE_REQUIRED },
532
        { "random", 'R', OPTPARSE_NONE },
533
        { "stats", 's', OPTPARSE_NONE },
534
        { "src", 'S', OPTPARSE_REQUIRED },
535
        { "timeout", 't', OPTPARSE_REQUIRED },
536
        { NULL, 'T', OPTPARSE_REQUIRED },
537
        { "unreach", 'u', OPTPARSE_NONE },
538
        { "version", 'v', OPTPARSE_NONE },
539
        { "reachable", 'x', OPTPARSE_REQUIRED },
540
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
541
#if defined(DEBUG) || defined(_DEBUG)
542
        { NULL, 'z', OPTPARSE_REQUIRED },
543
#endif
544
        { 0, 0, 0 }
545
    };
546

547
    float opt_value_float;
548
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
544✔
549
        switch (c) {
305✔
550
        case '4':
14✔
551
#ifdef IPV6
552
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
21✔
553
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
554
                exit(1);
3✔
555
            }
556
            hints_ai_family = AF_INET;
18✔
557
#endif
558
            break;
18✔
559
        case '6':
8✔
560
#ifdef IPV6
561
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
11✔
562
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
563
                exit(1);
3✔
564
            }
565
            hints_ai_family = AF_INET6;
8✔
566
#else
567
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
568
            exit(1);
569
#endif
570
            break;
8✔
571
        case 'M':
2✔
572
#ifdef IP_MTU_DISCOVER
573
            if (socket4 >= 0) {
2✔
574
                int val = IP_PMTUDISC_DO;
2✔
575
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
576
                    perror("setsockopt IP_MTU_DISCOVER");
577
                }
578
            }
579
#ifdef IPV6
580
            if (socket6 >= 0) {
2✔
581
                int val = IPV6_PMTUDISC_DO;
2✔
582
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
583
                    perror("setsockopt IPV6_MTU_DISCOVER");
584
                }
585
            }
586
#endif
587
#else
588
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
589
            exit(1);
590
#endif
591
            break;
2✔
592

593
        case 't':
10✔
594
            if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
12✔
595
                usage(1);
3✔
596
            if (opt_value_float < 0) {
10✔
597
                usage(1);
×
598
            }
599
            timeout = opt_value_float * 1000000;
10✔
600
            timeout_flag = 1;
10✔
601
            break;
10✔
602

603
        case 'r':
8✔
604
            if (!sscanf(optparse_state.optarg, "%u", &retry))
10✔
605
                usage(1);
3✔
606
            break;
8✔
607

608
        case 'i':
6✔
609
            if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
8✔
610
                usage(1);
3✔
611
            if (opt_value_float < 0) {
6✔
612
                usage(1);
×
613
            }
614
            interval = opt_value_float * 1000000;
6✔
615
            break;
6✔
616

617
        case 'p':
24✔
618
            if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
33✔
619
                usage(1);
3✔
620
            if (opt_value_float < 0) {
31✔
621
                usage(1);
×
622
            }
623
            perhost_interval = opt_value_float * 1000000;
31✔
624

625
            break;
31✔
626

627
        case 'c':
22✔
628
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
31✔
629
                usage(1);
3✔
630

631
            count_flag = 1;
29✔
632
            break;
29✔
633

634
        case 'C':
8✔
635
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
10✔
636
                usage(1);
3✔
637

638
            count_flag = 1;
8✔
639
            report_all_rtts_flag = 1;
8✔
640
            break;
8✔
641

642
        case 'b':
6✔
643
            if (!sscanf(optparse_state.optarg, "%u", &ping_data_size))
8✔
644
                usage(1);
3✔
645

646
            break;
6✔
647

648
        case 'h':
649
            usage(0);
×
650
            break;
×
651

652
        case 'q':
6✔
653
            verbose_flag = 0;
8✔
654
            quiet_flag = 1;
8✔
655
            break;
8✔
656

657
        case 'Q':
6✔
658
            verbose_flag = 0;
8✔
659
            quiet_flag = 1;
8✔
660
            if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
8✔
661
                usage(1);
3✔
662
            if (opt_value_float < 0) {
6✔
663
                usage(1);
×
664
            }
665
            report_interval = opt_value_float * 1e9;
6✔
666

667
            break;
6✔
668

669
        case 'e':
2✔
670
            elapsed_flag = 1;
3✔
671
            break;
3✔
672

673
        case 'm':
674
            multif_flag = 1;
×
675
            break;
×
676

677
        case 'N':
2✔
678
            netdata_flag = 1;
3✔
679
            break;
3✔
680

681
        case 'n':
4✔
682
            name_flag = 1;
6✔
683
            if (rdns_flag) {
6✔
684
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
685
                exit(1);
3✔
686
            }
687
            break;
3✔
688

689
        case 'd':
4✔
690
            rdns_flag = 1;
6✔
691
            if (name_flag) {
6✔
692
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
693
                exit(1);
3✔
694
            }
695
            break;
3✔
696

697
        case 'A':
2✔
698
            addr_flag = 1;
3✔
699
            break;
3✔
700

701
        case 'B':
8✔
702
            if (!(backoff = atof(optparse_state.optarg)))
10✔
703
                usage(1);
3✔
704

705
            break;
8✔
706

707
        case 's':
4✔
708
            stats_flag = 1;
6✔
709
            break;
6✔
710

711
        case 'D':
2✔
712
            timestamp_flag = 1;
3✔
713
            break;
3✔
714

715
        case 'R':
4✔
716
            random_data_flag = 1;
5✔
717
            break;
5✔
718

719
        case 'l':
2✔
720
            loop_flag = 1;
3✔
721
            backoff_flag = 0;
3✔
722
            break;
3✔
723

724
        case 'u':
4✔
725
            unreachable_flag = 1;
6✔
726
            break;
6✔
727

728
        case 'a':
6✔
729
            alive_flag = 1;
9✔
730
            break;
9✔
731

732
        case 'H':
6✔
733
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
8✔
734
                usage(1);
3✔
735
            break;
6✔
736

737
#if defined(DEBUG) || defined(_DEBUG)
738
        case 'z':
739
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
740
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
741
                    usage(1);
742

743
            break;
744
#endif /* DEBUG || _DEBUG */
745

746
        case 'v':
4✔
747
            printf("%s: Version %s\n", prog, VERSION);
6✔
748
            exit(0);
6✔
749

750
        case 'x':
4✔
751
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
6✔
752
                usage(1);
×
753
            break;
6✔
754

755
        case 'X':
4✔
756
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
6✔
757
                usage(1);
×
758
            fast_reachable = 1;
6✔
759
            break;
6✔
760

761
        case 'f':
4✔
762
            filename = optparse_state.optarg;
6✔
763
            break;
6✔
764

765
        case 'g':
16✔
766
            /* use IP list generation */
767
            /* mutually exclusive with using file input or command line targets */
768
            generate_flag = 1;
24✔
769
            break;
24✔
770

771
        case 'S':
6✔
772
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
8✔
773
                src_addr_set = 1;
3✔
774
                break;
3✔
775
            }
776
#ifdef IPV6
777
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
5✔
778
                src_addr6_set = 1;
2✔
779
                break;
2✔
780
            }
781
#endif
782
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
783
            exit(1);
3✔
784

785
        case 'I':
786
#ifdef SO_BINDTODEVICE
787
            if (socket4 >= 0) {
788
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
789
                    perror("binding to specific interface (SO_BINTODEVICE)");
790
                    exit(1);
791
                }
792
            }
793
#ifdef IPV6
794
            if (socket6 >= 0) {
795
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
796
                    perror("binding to specific interface (SO_BINTODEVICE), IPV6");
797
                    exit(1);
798
                }
799
            }
800
#endif
801
#else
802
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
803
            exit(3);
804
            ;
805
#endif
806
            break;
807

808
        case 'T':
2✔
809
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
810
            break;
3✔
811

812
        case 'O':
4✔
813
            if (sscanf(optparse_state.optarg, "%i", &tos)) {
6✔
814
                if (socket4 >= 0) {
3✔
815
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
3✔
816
                        perror("setting type of service octet IP_TOS");
×
817
                    }
818
                }
1✔
819
#if defined(IPV6) && defined(IPV6_TCLASS)
820
                if (socket6 >= 0) {
3✔
821
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
3✔
822
                        perror("setting type of service octet IPV6_TCLASS");
×
823
                    }
824
                }
1✔
825
#endif
826
            }
1✔
827
            else {
828
                usage(1);
3✔
829
            }
830
            break;
4✔
831

832
        case 'o':
2✔
833
            outage_flag = 1;
3✔
834
            break;
3✔
835

836
        case '?':
2✔
837
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
3✔
838
            fprintf(stderr, "see 'fping -h' for usage information\n");
3✔
839
            exit(1);
3✔
840
            break;
841
        }
842
    }
162✔
843

844
    /* permanently drop privileges */
845
    if (suid != getuid() && setuid(getuid())) {
155✔
846
        perror("fatal: failed to permanently drop privileges");
×
847
        /* continuing would be a security hole */
848
        exit(4);
×
849
    }
850

851
    /* validate various option settings */
852

853
#ifndef IPV6
854
    if (socket4 < 0) {
855
        crash_and_burn("can't create socket (must run as root?)");
856
    }
857
#else
858
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
155✔
859
        crash_and_burn("can't create socket (must run as root?)");
×
860
    }
861
#endif
862

863
    if (ttl > 255) {
155✔
864
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
865
        exit(1);
3✔
866
    }
867

868
    if (unreachable_flag && alive_flag) {
152✔
869
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
870
        exit(1);
3✔
871
    }
872

873
    if (count_flag && loop_flag) {
149✔
874
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
875
        exit(1);
3✔
876
    }
877

878
#ifdef FPING_SAFE_LIMITS
879
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
146✔
880
        && getuid()) {
50✔
881
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
882
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
883
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
884
        exit(1);
6✔
885
    }
886
#endif
887

888
    if (ping_data_size > MAX_PING_DATA) {
140✔
889
        fprintf(stderr, "%s: data size %u not valid, must be lower than %u\n",
4✔
890
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
891
        exit(1);
3✔
892
    }
893

894
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
137✔
895
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
896
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
897
        exit(1);
6✔
898
    }
899

900
    if (alive_flag || unreachable_flag || min_reachable)
131✔
901
        verbose_flag = 0;
21✔
902

903
    if (count_flag) {
131✔
904
        if (verbose_flag)
31✔
905
            per_recv_flag = 1;
17✔
906

907
        alive_flag = unreachable_flag = verbose_flag = 0;
31✔
908
    }
9✔
909

910
    if (loop_flag) {
131✔
911
        if (!report_interval)
×
912
            per_recv_flag = 1;
×
913

914
        alive_flag = unreachable_flag = verbose_flag = 0;
×
915
    }
916

917
    trials = (count > retry + 1) ? count : retry + 1;
131✔
918

919
    /* auto-tune default timeout for count/loop modes
920
     * see also github #32 */
921
    if (loop_flag || count_flag) {
131✔
922
        if (!timeout_flag) {
31✔
923
            timeout = perhost_interval;
26✔
924
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
26✔
925
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
926
            }
927
        }
8✔
928
    }
9✔
929

930
#if defined(DEBUG) || defined(_DEBUG)
931
    if (debugging & DBG_TRACE)
932
        trace_flag = 1;
933

934
    if (debugging & DBG_RANDOM_LOSE_FEW) {
935
        randomly_lose_flag = 1;
936
        lose_factor = 1; /* ie, 1/4 */
937
    }
938

939
    if (debugging & DBG_RANDOM_LOSE_MANY) {
940
        randomly_lose_flag = 1;
941
        lose_factor = 5; /* ie, 3/4 */
942
    }
943

944
    if (debugging & DBG_PRINT_PER_SYSTEM)
945
        print_per_system_flag = 1;
946

947
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
948
        report_all_rtts_flag = 1;
949

950
    if (trace_flag) {
951
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
952
            prog, count, retry, interval / 1e6);
953
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
954
            perhost_interval / 1e6, timeout / 1e6);
955
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
956
            ping_data_size, trials);
957

958
        if (verbose_flag)
959
            fprintf(stderr, "  verbose_flag set\n");
960
        if (multif_flag)
961
            fprintf(stderr, "  multif_flag set\n");
962
        if (name_flag)
963
            fprintf(stderr, "  name_flag set\n");
964
        if (addr_flag)
965
            fprintf(stderr, "  addr_flag set\n");
966
        if (stats_flag)
967
            fprintf(stderr, "  stats_flag set\n");
968
        if (unreachable_flag)
969
            fprintf(stderr, "  unreachable_flag set\n");
970
        if (alive_flag)
971
            fprintf(stderr, "  alive_flag set\n");
972
        if (elapsed_flag)
973
            fprintf(stderr, "  elapsed_flag set\n");
974
        if (version_flag)
975
            fprintf(stderr, "  version_flag set\n");
976
        if (count_flag)
977
            fprintf(stderr, "  count_flag set\n");
978
        if (loop_flag)
979
            fprintf(stderr, "  loop_flag set\n");
980
        if (backoff_flag)
981
            fprintf(stderr, "  backoff_flag set\n");
982
        if (per_recv_flag)
983
            fprintf(stderr, "  per_recv_flag set\n");
984
        if (report_all_rtts_flag)
985
            fprintf(stderr, "  report_all_rtts_flag set\n");
986
        if (randomly_lose_flag)
987
            fprintf(stderr, "  randomly_lose_flag set\n");
988
        if (print_per_system_flag)
989
            fprintf(stderr, "  print_per_system_flag set\n");
990
        if (outage_flag)
991
            fprintf(stderr, "  outage_flag set\n");
992
        if (netdata_flag)
993
            fprintf(stderr, "  netdata_flag set\n");
994
    }
995
#endif /* DEBUG || _DEBUG */
996

997
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
998
    if (ttl > 0) {
131✔
999
        if (socket4 >= 0) {
3✔
1000
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1001
                perror("setting time to live");
×
1002
            }
1003
        }
1✔
1004
#ifdef IPV6
1005
        if (socket6 >= 0) {
3✔
1006
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1007
                perror("setting time to live");
×
1008
            }
1009
        }
1✔
1010
#endif
1011
    }
1✔
1012

1013
#if HAVE_SO_TIMESTAMPNS
1014
    {
1015
        int opt = 1;
90✔
1016
        if (socket4 >= 0) {
90✔
1017
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
90✔
1018
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1019
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1020
                }
1021
            }
1022
        }
1023
#ifdef IPV6
1024
        if (socket6 >= 0) {
90✔
1025
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
90✔
1026
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1027
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1028
                }
1029
            }
1030
        }
1031
#endif
1032
    }
1033
#endif
1034

1035
    update_current_time();
131✔
1036
    start_time = current_time_ns;
131✔
1037

1038
    /* handle host names supplied on command line or in a file */
1039
    /* if the generate_flag is on, then generate the IP list */
1040

1041
    argv = &argv[optparse_state.optind];
131✔
1042
    argc -= optparse_state.optind;
131✔
1043

1044
    /* calculate how many ping can be in-flight per host */
1045
    if (count_flag) {
131✔
1046
        event_storage_count = count;
35✔
1047
    }
13✔
1048
    else if (loop_flag) {
96✔
1049
        if (perhost_interval > timeout) {
×
1050
            event_storage_count = 1;
×
1051
        }
1052
        else {
1053
            event_storage_count = 1 + timeout / perhost_interval;
×
1054
        }
1055
    }
1056
    else {
1057
        event_storage_count = 1;
96✔
1058
    }
1059

1060
    /* file and generate are mutually exclusive */
1061
    /* file and command line are mutually exclusive */
1062
    /* generate requires command line parameters beyond the switches */
1063
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
131✔
1064
        usage(1);
4✔
1065

1066
    /* if no conditions are specified, then assume input from stdin */
1067
    if (!*argv && !filename && !generate_flag)
127✔
1068
        filename = "-";
3✔
1069

1070
    if (*argv && !generate_flag) {
127✔
1071
        while (*argv) {
215✔
1072
            add_name(*argv);
118✔
1073
            ++argv;
118✔
1074
        }
1075
    }
27✔
1076
    else if (filename) {
30✔
1077
        FILE *ping_file;
1078
        char line[132];
1079
        char host[132];
1080

1081
        if (strcmp(filename, "-") == 0)
9✔
1082
            ping_file = fdopen(0, "r");
6✔
1083
        else
1084
            ping_file = fopen(filename, "r");
3✔
1085

1086
        if (!ping_file)
9✔
1087
            errno_crash_and_burn("fopen");
×
1088

1089
        while (fgets(line, sizeof(line), ping_file)) {
27✔
1090
            if (sscanf(line, "%s", host) != 1)
18✔
1091
                continue;
×
1092

1093
            if ((!*host) || (host[0] == '#')) /* magic to avoid comments */
18✔
1094
                continue;
×
1095

1096
            add_name(host);
18✔
1097
        }
1098

1099
        fclose(ping_file);
9✔
1100
    }
3✔
1101
    else if (*argv && generate_flag) {
21✔
1102
        if (argc == 1) {
21✔
1103
            /* one target: we expect a cidr range (n.n.n.n/m) */
1104
            add_cidr(argv[0]);
18✔
1105
        }
6✔
1106
        else if (argc == 2) {
3✔
1107
            add_range(argv[0], argv[1]);
3✔
1108
        }
1✔
1109
        else {
1110
            usage(1);
×
1111
        }
1112
    }
7✔
1113
    else {
1114
        usage(1);
×
1115
    }
1116

1117
    if (!num_hosts) {
123✔
1118
        exit(num_noaddress ? 2 : 1);
9✔
1119
    }
1120

1121
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
114✔
1122
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
3✔
1123
    }
1✔
1124
#ifdef IPV6
1125
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
114✔
1126
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
2✔
1127
    }
1128
#endif
1129

1130
    /* allocate and initialize array to map host nr to host_entry */
1131
    {
1132
        struct event *cursor = event_queue_ping.first;
114✔
1133
        int i = 0;
114✔
1134
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
114✔
1135
        if (!table)
114✔
1136
            crash_and_burn("Can't malloc array of hosts");
×
1137
        /* initialize table of hosts. we know that we have ping events scheduled
1138
         * for each of them */
1139
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
304✔
1140
            table[i] = cursor->host;
190✔
1141
            cursor->host->i = i;
190✔
1142
            i++;
190✔
1143
        }
58✔
1144
    }
1145

1146
    init_ping_buffer_ipv4(ping_data_size);
114✔
1147
#ifdef IPV6
1148
    init_ping_buffer_ipv6(ping_data_size);
114✔
1149
#endif
1150

1151
#ifdef USE_SIGACTION
1152
    memset(&act, 0, sizeof(act));
114✔
1153
    act.sa_handler = signal_handler;
114✔
1154
    sigemptyset(&act.sa_mask);
114✔
1155
    sigaddset(&act.sa_mask, SIGINT);
114✔
1156
    sigaddset(&act.sa_mask, SIGQUIT);
114✔
1157
    act.sa_flags = SA_RESTART;
114✔
1158
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
114✔
1159
        crash_and_burn("failure to set signal handler");
4✔
1160
    }
4✔
1161
#else
1162
    signal(SIGINT, signal_handler);
1163
    signal(SIGQUIT, signal_handler);
1164
#endif
1165
    setlinebuf(stdout);
110✔
1166

1167
    if (report_interval) {
110✔
1168
        next_report_time = current_time_ns + report_interval;
6✔
1169
    }
2✔
1170

1171
    last_send_time = 0;
110✔
1172

1173
    seqmap_init();
110✔
1174

1175
    /* main loop */
1176
    main_loop();
110✔
1177

1178
    finish();
110✔
1179

1180
    return 0;
30✔
1181
}
1182

1183
static inline int64_t timespec_ns(struct timespec *a)
1,563✔
1184
{
1185
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
1,563✔
1186
}
1187

1188
void add_cidr(char *addr)
18✔
1189
{
1190
    char *addr_end;
1191
    char *mask_str;
1192
    unsigned long mask;
1193
    unsigned long bitmask;
1194
    int ret;
1195
    struct addrinfo addr_hints;
1196
    struct addrinfo *addr_res;
1197
    unsigned long net_addr;
1198
    unsigned long net_last;
1199

1200
    /* Split address from mask */
1201
    addr_end = strchr(addr, '/');
18✔
1202
    if (addr_end == NULL) {
18✔
1203
        usage(1);
×
1204
    }
1205
    *addr_end = '\0';
18✔
1206
    mask_str = addr_end + 1;
18✔
1207
    mask = atoi(mask_str);
18✔
1208

1209
    /* parse address (IPv4 only) */
1210
    memset(&addr_hints, 0, sizeof(struct addrinfo));
18✔
1211
    addr_hints.ai_family = AF_UNSPEC;
18✔
1212
    addr_hints.ai_flags = AI_NUMERICHOST;
18✔
1213
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
18✔
1214
    if (ret) {
18✔
1215
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
×
1216
        exit(1);
×
1217
    }
1218
    if (addr_res->ai_family != AF_INET) {
18✔
1219
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
3✔
1220
        exit(1);
3✔
1221
    }
1222
    net_addr = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
15✔
1223

1224
    /* check mask */
1225
    if (mask < 1 || mask > 32) {
15✔
1226
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %s)\n", prog, mask_str);
3✔
1227
        exit(1);
3✔
1228
    }
1229

1230
    /* convert mask integer from 1 to 32 to a bitmask */
1231
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
12✔
1232

1233
    /* calculate network range */
1234
    net_addr &= bitmask;
12✔
1235
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
12✔
1236

1237
    /* exclude network and broadcast address for regular prefixes */
1238
    if (mask < 31) {
12✔
1239
        net_last--;
9✔
1240
        net_addr++;
9✔
1241
    }
3✔
1242

1243
    /* add all hosts in that network (net_addr and net_last inclusive) */
1244
    for (; net_addr <= net_last; net_addr++) {
60✔
1245
        struct in_addr in_addr_tmp;
1246
        char buffer[20];
1247
        in_addr_tmp.s_addr = htonl(net_addr);
48✔
1248
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
48✔
1249
        add_name(buffer);
48✔
1250
    }
16✔
1251

1252
    freeaddrinfo(addr_res);
12✔
1253
}
12✔
1254

1255
void add_range(char *start, char *end)
3✔
1256
{
1257
    struct addrinfo addr_hints;
1258
    struct addrinfo *addr_res;
1259
    unsigned long start_long;
1260
    unsigned long end_long;
1261
    int ret;
1262

1263
    /* parse start address (IPv4 only) */
1264
    memset(&addr_hints, 0, sizeof(struct addrinfo));
3✔
1265
    addr_hints.ai_family = AF_UNSPEC;
3✔
1266
    addr_hints.ai_flags = AI_NUMERICHOST;
3✔
1267
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
3✔
1268
    if (ret) {
3✔
1269
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
×
1270
        exit(1);
×
1271
    }
1272
    if (addr_res->ai_family != AF_INET) {
3✔
1273
        freeaddrinfo(addr_res);
×
1274
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
×
1275
        exit(1);
×
1276
    }
1277
    start_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
3✔
1278

1279
    /* parse end address (IPv4 only) */
1280
    memset(&addr_hints, 0, sizeof(struct addrinfo));
3✔
1281
    addr_hints.ai_family = AF_UNSPEC;
3✔
1282
    addr_hints.ai_flags = AI_NUMERICHOST;
3✔
1283
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
3✔
1284
    if (ret) {
3✔
1285
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
×
1286
        exit(1);
×
1287
    }
1288
    if (addr_res->ai_family != AF_INET) {
3✔
1289
        freeaddrinfo(addr_res);
×
1290
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
×
1291
        exit(1);
×
1292
    }
1293
    end_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
3✔
1294
    freeaddrinfo(addr_res);
3✔
1295

1296
    if (end_long > start_long + MAX_GENERATE) {
3✔
1297
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
×
1298
        exit(1);
×
1299
    }
1300

1301
    /* generate */
1302
    for (; start_long <= end_long; start_long++) {
18✔
1303
        struct in_addr in_addr_tmp;
1304
        char buffer[20];
1305
        in_addr_tmp.s_addr = htonl(start_long);
15✔
1306
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
15✔
1307
        add_name(buffer);
15✔
1308
    }
5✔
1309
}
3✔
1310

1311
void main_loop()
112✔
1312
{
1313
    int64_t lt;
1314
    int64_t wait_time_ns;
1315
    struct event *event;
1316
    struct host_entry *h;
1317

1318
    while (event_queue_ping.first || event_queue_timeout.first) {
663✔
1319
        dbg_printf("%s", "# main_loop\n");
1320

1321
        /* timeout event ? */
1322
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
556✔
1323
            event = ev_dequeue(&event_queue_timeout);
125✔
1324
            h = event->host;
125✔
1325

1326
            dbg_printf("%s [%d]: timeout event\n", h->host, event->ping_index);
1327

1328
            stats_add(h, event->ping_index, 0, -1);
125✔
1329

1330
            if (per_recv_flag) {
125✔
1331
                if (timestamp_flag) {
15✔
1332
                    printf("[%.5f] ", (double)current_time_ns / 1e9);
×
1333
                }
1334
                printf("%-*s : [%d], timed out",
15✔
1335
                    max_hostname_len, h->host, event->ping_index);
5✔
1336
                if (h->num_recv > 0) {
15✔
1337
                    printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1338
                }
1339
                else {
1340
                    printf(" (NaN avg, ");
15✔
1341
                }
1342
                if (h->num_recv <= h->num_sent) {
15✔
1343
                    printf("%d%% loss)",
15✔
1344
                        ((h->num_sent - h->num_recv) * 100) / h->num_sent);
15✔
1345
                }
5✔
1346
                else {
1347
                    printf("%d%% return)",
×
1348
                        (h->num_recv_total * 100) / h->num_sent);
×
1349
                }
1350
                printf("\n");
15✔
1351
            }
5✔
1352

1353
            /* do we need to send a retry? */
1354
            if (!loop_flag && !count_flag) {
125✔
1355
                if (h->num_sent < retry + 1) {
110✔
1356
                    if (backoff_flag) {
78✔
1357
                        h->timeout *= backoff;
78✔
1358
                    }
24✔
1359
                    send_ping(h, event->ping_index);
78✔
1360
                }
24✔
1361
            }
34✔
1362

1363
            /* note: we process first timeout events, because we might need to
1364
             * wait to process ping events, while we for sure never need to
1365
             * wait for timeout events.
1366
             */
1367
            continue;
125✔
1368
        }
1369

1370
        /* ping event ? */
1371
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
503✔
1372
            /* Make sure that we don't ping more than once every "interval" */
1373
            lt = current_time_ns - last_send_time;
289✔
1374
            if (lt < interval)
289✔
1375
                goto wait_for_reply;
49✔
1376

1377
            /* Dequeue the event */
1378
            event = ev_dequeue(&event_queue_ping);
240✔
1379
            h = event->host;
240✔
1380

1381
            dbg_printf("%s [%d]: ping event\n", h->host, event->ping_index);
1382

1383
            /* Send the ping */
1384
            send_ping(h, event->ping_index);
240✔
1385

1386
            /* Loop and count mode: schedule next ping */
1387
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
240✔
1388
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
65✔
1389
            }
19✔
1390
        }
72✔
1391

1392
    wait_for_reply:
230✔
1393

1394
        /* When is the next ping next event? */
1395
        wait_time_ns = -1;
431✔
1396
        if (event_queue_ping.first) {
431✔
1397
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
229✔
1398
            if (wait_time_ns < 0)
229✔
1399
                wait_time_ns = 0;
122✔
1400
            /* make sure that we wait enough, so that the inter-ping delay is
1401
             * bigger than 'interval' */
1402
            if (wait_time_ns < interval) {
229✔
1403
                lt = current_time_ns - last_send_time;
123✔
1404
                if (lt < interval) {
123✔
1405
                    wait_time_ns = interval - lt;
123✔
1406
                }
43✔
1407
            }
43✔
1408

1409
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1410
        }
71✔
1411

1412
        /* When is the next timeout event? */
1413
        if (event_queue_timeout.first) {
431✔
1414
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
331✔
1415
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
331✔
1416
                wait_time_ns = wait_time_timeout;
205✔
1417
                if (wait_time_ns < 0) {
205✔
1418
                    wait_time_ns = 0;
×
1419
                }
1420
            }
53✔
1421

1422
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1423
        }
93✔
1424

1425
        /* When is the next report due? */
1426
        if (report_interval && (loop_flag || count_flag)) {
431✔
1427
            int64_t wait_time_next_report = next_report_time - current_time_ns;
33✔
1428
            if (wait_time_next_report < wait_time_ns) {
33✔
1429
                wait_time_ns = wait_time_next_report;
9✔
1430
                if (wait_time_ns < 0) {
9✔
1431
                    wait_time_ns = 0;
×
1432
                }
1433
            }
3✔
1434

1435
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1436
        }
11✔
1437

1438
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1439
        if (wait_time_ns == -1) {
431✔
1440
            break;
2✔
1441
        }
1442

1443
        /* end of loop was requested by interrupt signal handler */
1444
        if (finish_requested) {
429✔
1445
            break;
3✔
1446
        }
1447

1448
        /* Receive replies */
1449
        /* (this is what sleeps during each loop iteration) */
1450
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1451
        if (wait_for_reply(wait_time_ns)) {
426✔
1452
            while (wait_for_reply(0))
317✔
1453
                ; /* process other replies in the queue */
1454
        }
57✔
1455

1456
        update_current_time();
426✔
1457

1458
        if (status_snapshot) {
426✔
1459
            status_snapshot = 0;
×
1460
            print_per_system_splits();
×
1461
        }
1462

1463
        /* Print report */
1464
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
426✔
1465
            if (netdata_flag)
6✔
1466
                print_netdata();
3✔
1467
            else
1468
                print_per_system_splits();
3✔
1469

1470
            while (current_time_ns >= next_report_time) {
12✔
1471
                next_report_time += report_interval;
6✔
1472
            }
1473
        }
2✔
1474
    }
1475
}
112✔
1476

1477
/************************************************************
1478

1479
  Function: signal_handler
1480

1481
*************************************************************
1482

1483
  Inputs:  int signum
1484

1485
  Description:
1486

1487
  SIGQUIT signal handler - set flag and return
1488
  SIGINT signal handler - set flag and return
1489

1490
************************************************************/
1491

1492
void signal_handler(int signum)
×
1493
{
1494
    switch (signum) {
×
1495
    case SIGINT:
1496
        finish_requested = 1;
×
1497
        break;
×
1498

1499
    case SIGQUIT:
1500
        status_snapshot = 1;
×
1501
        break;
×
1502
    }
1503
}
×
1504

1505
/************************************************************
1506

1507
  Function: update_current_time
1508

1509
*************************************************************/
1510

1511
void update_current_time()
1,303✔
1512
{
1513
    clock_gettime(CLOCKID, &current_time);
1,303✔
1514
    current_time_ns = timespec_ns(&current_time);
1,303✔
1515
}
1,303✔
1516

1517
/************************************************************
1518

1519
  Function: finish
1520

1521
*************************************************************
1522

1523
  Inputs:  void (none)
1524

1525
  Description:
1526

1527
  Main program clean up and exit point
1528

1529
************************************************************/
1530

1531
void finish()
112✔
1532
{
1533
    int i;
1534
    HOST_ENTRY *h;
1535

1536
    update_current_time();
112✔
1537
    end_time = current_time_ns;
112✔
1538

1539
    /* tot up unreachables */
1540
    for (i = 0; i < num_hosts; i++) {
302✔
1541
        h = table[i];
190✔
1542

1543
        if (!h->num_recv) {
190✔
1544
            num_unreachable++;
52✔
1545

1546
            if (verbose_flag || unreachable_flag) {
52✔
1547
                printf("%s", h->host);
8✔
1548

1549
                if (verbose_flag)
8✔
1550
                    printf(" is unreachable");
5✔
1551

1552
                printf("\n");
8✔
1553
            }
2✔
1554
        }
16✔
1555
    }
58✔
1556

1557
    if (count_flag || loop_flag)
112✔
1558
        print_per_system_stats();
31✔
1559
#if defined(DEBUG) || defined(_DEBUG)
1560
    else if (print_per_system_flag)
1561
        print_per_system_stats();
1562
#endif /* DEBUG || _DEBUG */
1563

1564
    if (stats_flag)
112✔
1565
        print_global_stats();
6✔
1566

1567
    if (min_reachable) {
112✔
1568
        if ((num_hosts - num_unreachable) >= min_reachable) {
12✔
1569
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1570
            exit(0);
6✔
1571
        }
1572
        else {
1573
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1574
            exit(1);
6✔
1575
        }
1576
    }
1577

1578
    if (num_noaddress)
100✔
1579
        exit(2);
×
1580
    else if (num_alive != num_hosts)
100✔
1581
        exit(1);
13✔
1582

1583
    exit(0);
87✔
1584
}
1585

1586
/************************************************************
1587

1588
  Function: print_per_system_stats
1589

1590
*************************************************************
1591

1592
  Inputs:  void (none)
1593

1594
  Description:
1595

1596

1597
************************************************************/
1598

1599
void print_per_system_stats(void)
31✔
1600
{
1601
    int i, j, avg, outage_ms;
1602
    HOST_ENTRY *h;
1603
    int64_t resp;
1604

1605
    if (verbose_flag || per_recv_flag)
31✔
1606
        fprintf(stderr, "\n");
17✔
1607

1608
    for (i = 0; i < num_hosts; i++) {
68✔
1609
        h = table[i];
37✔
1610
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
37✔
1611

1612
        if (report_all_rtts_flag) {
37✔
1613
            for (j = 0; j < h->num_sent; j++) {
38✔
1614
                if ((resp = h->resp_times[j]) >= 0)
27✔
1615
                    fprintf(stderr, " %s", sprint_tm(resp));
21✔
1616
                else
1617
                    fprintf(stderr, " -");
6✔
1618
            }
7✔
1619

1620
            fprintf(stderr, "\n");
11✔
1621
        }
3✔
1622
        else {
1623
            if (h->num_recv <= h->num_sent) {
26✔
1624
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
34✔
1625
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
26✔
1626

1627
                if (outage_flag) {
26✔
1628
                    /* Time outage total */
1629
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
3✔
1630
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
3✔
1631
                }
1✔
1632
            }
8✔
1633
            else {
1634
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
1635
                    h->num_sent, h->num_recv,
1636
                    ((h->num_recv * 100) / h->num_sent));
×
1637
            }
1638

1639
            if (h->num_recv) {
26✔
1640
                avg = h->total_time / h->num_recv;
23✔
1641
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
23✔
1642
                fprintf(stderr, "/%s", sprint_tm(avg));
23✔
1643
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
23✔
1644
            }
7✔
1645

1646
            fprintf(stderr, "\n");
26✔
1647
        }
1648
    }
11✔
1649
}
31✔
1650

1651
/************************************************************
1652

1653
  Function: print_netdata
1654

1655
*************************************************************
1656

1657
  Inputs:  void (none)
1658

1659
  Description:
1660

1661

1662
************************************************************/
1663

1664
void print_netdata(void)
3✔
1665
{
1666
    static int sent_charts = 0;
1667

1668
    int i;
1669
    int64_t avg;
1670
    HOST_ENTRY *h;
1671

1672
    for (i = 0; i < num_hosts; i++) {
6✔
1673
        h = table[i];
3✔
1674

1675
        if (!sent_charts) {
3✔
1676
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1677
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
1678
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
1679
        }
1✔
1680

1681
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
1682
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
1683
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
1684
        printf("END\n");
3✔
1685

1686
        if (!sent_charts) {
3✔
1687
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1688
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
1689
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
1690
        }
1✔
1691

1692
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
1693
        /*
1694
        if( h->num_recv_i <= h->num_sent_i )
1695
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
1696
        else
1697
            printf("SET lost = 0\n");
1698
*/
1699

1700
        printf("SET returned = %d\n", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
3✔
1701
        printf("END\n");
3✔
1702

1703
        if (!sent_charts) {
3✔
1704
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1705
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
1706
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
1707
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
1708
        }
1✔
1709

1710
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
1711
        if (h->num_recv_i) {
3✔
1712
            avg = h->total_time_i / h->num_recv_i;
3✔
1713
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
1714
            printf("SET avg = %" PRId64 "\n", avg);
3✔
1715
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
1716
        }
1✔
1717
        printf("END\n");
3✔
1718

1719
        h->num_sent_i = h->num_recv_i = h->max_reply_i = h->min_reply_i = h->total_time_i = 0;
3✔
1720
    }
1✔
1721

1722
    sent_charts = 1;
3✔
1723
}
3✔
1724

1725
/************************************************************
1726

1727
  Function: print_per_system_splits
1728

1729
*************************************************************
1730

1731
  Inputs:  void (none)
1732

1733
  Description:
1734

1735

1736
************************************************************/
1737

1738
void print_per_system_splits(void)
3✔
1739
{
1740
    int i, avg, outage_ms_i;
1741
    HOST_ENTRY *h;
1742
    struct tm *curr_tm;
1743

1744
    if (verbose_flag || per_recv_flag)
3✔
1745
        fprintf(stderr, "\n");
×
1746

1747
    update_current_time();
3✔
1748
    curr_tm = localtime((time_t *)&current_time.tv_sec);
3✔
1749
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
4✔
1750
        curr_tm->tm_min, curr_tm->tm_sec);
1✔
1751

1752
    for (i = 0; i < num_hosts; i++) {
6✔
1753
        h = table[i];
3✔
1754
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
3✔
1755

1756
        if (h->num_recv_i <= h->num_sent_i) {
3✔
1757
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
4✔
1758
                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);
3✔
1759

1760
            if (outage_flag) {
3✔
1761
                /* Time outage  */
1762
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
1763
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
×
1764
            }
1765
        }
1✔
1766
        else {
1767
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
1768
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
1769
        }
1770

1771
        if (h->num_recv_i) {
3✔
1772
            avg = h->total_time_i / h->num_recv_i;
3✔
1773
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
3✔
1774
            fprintf(stderr, "/%s", sprint_tm(avg));
3✔
1775
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
3✔
1776
        }
1✔
1777

1778
        fprintf(stderr, "\n");
3✔
1779
        h->num_sent_i = h->num_recv_i = h->max_reply_i = h->min_reply_i = h->total_time_i = 0;
3✔
1780
    }
1✔
1781
}
3✔
1782

1783
/************************************************************
1784

1785
  Function: print_global_stats
1786

1787
*************************************************************
1788

1789
  Inputs:  void (none)
1790

1791
  Description:
1792

1793

1794
************************************************************/
1795

1796
void print_global_stats(void)
6✔
1797
{
1798
    fprintf(stderr, "\n");
6✔
1799
    fprintf(stderr, " %7d targets\n", num_hosts);
6✔
1800
    fprintf(stderr, " %7d alive\n", num_alive);
6✔
1801
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
6✔
1802
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
6✔
1803
    fprintf(stderr, "\n");
6✔
1804
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
6✔
1805
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
6✔
1806
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
6✔
1807
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
6✔
1808
    fprintf(stderr, "\n");
6✔
1809

1810
    if (total_replies == 0) {
6✔
1811
        min_reply = 0;
3✔
1812
        max_reply = 0;
3✔
1813
        total_replies = 1;
3✔
1814
        sum_replies = 0;
3✔
1815
    }
1✔
1816

1817
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
6✔
1818
    fprintf(stderr, " %s ms (avg round trip time)\n",
8✔
1819
        sprint_tm(sum_replies / total_replies));
2✔
1820
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
6✔
1821
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
8✔
1822
        (end_time - start_time) / 1e9);
6✔
1823
    fprintf(stderr, "\n");
6✔
1824
}
6✔
1825

1826
/************************************************************
1827

1828
  Function: send_ping
1829

1830
*************************************************************
1831

1832
  Inputs:  int s, HOST_ENTRY *h
1833

1834
  Description:
1835

1836
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
1837
  will be added on by the kernel.  The ID field is our UNIX process ID,
1838
  and the sequence number is an index into an array of outstanding
1839
  ping requests. The sequence number will later be used to quickly
1840
  figure out who the ping reply came from.
1841

1842
************************************************************/
1843

1844
int send_ping(HOST_ENTRY *h, int index)
318✔
1845
{
1846
    int n;
1847
    int myseq;
1848
    int ret = 1;
318✔
1849

1850
    update_current_time();
318✔
1851
    h->last_send_time = current_time_ns;
318✔
1852
    myseq = seqmap_add(h->i, index, current_time_ns);
318✔
1853

1854
    dbg_printf("%s [%d]: send ping\n", h->host, index);
1855

1856
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
318✔
1857
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4);
306✔
1858
    }
96✔
1859
#ifdef IPV6
1860
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
12✔
1861
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
12✔
1862
    }
1863
#endif
1864
    else {
1865
        return 0;
×
1866
    }
1867

1868
    /* error sending? */
1869
    if (
222✔
1870
        (n < 0)
96✔
1871
#if defined(EHOSTDOWN)
1872
        && errno != EHOSTDOWN
102✔
1873
#endif
1874
    ) {
1875
        if (verbose_flag) {
6✔
1876
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
×
1877
        }
1878
        else {
1879
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
1880
        }
1881

1882
        h->num_sent++;
6✔
1883
        h->num_sent_i++;
6✔
1884
        if (!loop_flag)
6✔
1885
            h->resp_times[index] = RESP_ERROR;
6✔
1886

1887
        ret = 0;
6✔
1888
    }
1889
    else {
1890
        /* schedule timeout */
1891
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
312✔
1892

1893
        /* mark this trial as outstanding */
1894
        if (!loop_flag) {
312✔
1895
            h->resp_times[index] = RESP_WAITING;
312✔
1896
        }
96✔
1897
    }
1898

1899
    num_pingsent++;
318✔
1900
    last_send_time = h->last_send_time;
318✔
1901

1902
    return (ret);
318✔
1903
}
96✔
1904

1905
int socket_can_read(struct timeval *timeout)
743✔
1906
{
1907
    int nfound;
1908
    fd_set readset;
1909
    int socketmax;
1910

1911
#ifndef IPV6
1912
    socketmax = socket4;
1913
#else
1914
    socketmax = socket4 > socket6 ? socket4 : socket6;
743✔
1915
#endif
1916

1917
select_again:
564✔
1918
    FD_ZERO(&readset);
5,255✔
1919
    if (socket4 >= 0)
743✔
1920
        FD_SET(socket4, &readset);
743✔
1921
#ifdef IPV6
1922
    if (socket6 >= 0)
743✔
1923
        FD_SET(socket6, &readset);
743✔
1924
#endif
1925

1926
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
743✔
1927
    if (nfound < 0) {
743✔
1928
        if (errno == EINTR) {
×
1929
            /* interrupted system call: redo the select */
1930
            goto select_again;
×
1931
        }
1932
        else {
1933
            perror("select");
×
1934
        }
1935
    }
1936

1937
    if (nfound > 0) {
743✔
1938
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
317✔
1939
            return socket4;
293✔
1940
        }
1941
#ifdef IPV6
1942
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
24✔
1943
            return socket6;
24✔
1944
        }
1945
#endif
1946
    }
1947

1948
    return -1;
426✔
1949
}
179✔
1950

1951
int receive_packet(int64_t wait_time,
743✔
1952
    int64_t *reply_timestamp,
1953
    struct sockaddr *reply_src_addr,
1954
    size_t reply_src_addr_len,
1955
    char *reply_buf,
1956
    size_t reply_buf_len)
1957
{
1958
    struct timeval to;
1959
    int s = 0;
743✔
1960
    int recv_len;
1961
    static unsigned char msg_control[40];
1962
    struct iovec msg_iov = {
1,101✔
1963
        reply_buf,
179✔
1964
        reply_buf_len
179✔
1965
    };
1966
    struct msghdr recv_msghdr = {0};
743✔
1967
    recv_msghdr.msg_name = reply_src_addr;
743✔
1968
    recv_msghdr.msg_namelen = reply_src_addr_len;
743✔
1969
    recv_msghdr.msg_iov = &msg_iov;
743✔
1970
    recv_msghdr.msg_iovlen = 1;
743✔
1971
    recv_msghdr.msg_control = &msg_control;
743✔
1972
    recv_msghdr.msg_controllen = sizeof(msg_control);
743✔
1973
#if HAVE_SO_TIMESTAMPNS
1974
    struct cmsghdr *cmsg;
1975
#endif
1976

1977
    /* Wait for a socket to become ready */
1978
    if (wait_time) {
743✔
1979
        to.tv_sec = wait_time / UINT64_C(1000000000);
426✔
1980
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
426✔
1981
    }
122✔
1982
    else {
1983
        to.tv_sec = 0;
317✔
1984
        to.tv_usec = 0;
317✔
1985
    }
1986
    s = socket_can_read(&to);
743✔
1987
    if (s == -1) {
743✔
1988
        return 0; /* timeout */
426✔
1989
    }
1990

1991
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
317✔
1992
    if (recv_len <= 0) {
317✔
1993
        return 0;
×
1994
    }
1995

1996
#if HAVE_SO_TIMESTAMPNS
1997
    /* ancilliary data */
1998
    {
1999
        struct timespec reply_timestamp_ts;
2000
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
390✔
2001
             cmsg != NULL;
260✔
2002
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
260✔
2003
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
260✔
2004
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
260✔
2005
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
260✔
2006
            }
2007
        }
2008
    }
2009
#endif
2010

2011
#if defined(DEBUG) || defined(_DEBUG)
2012
    if (randomly_lose_flag) {
2013
        if ((random() & 0x07) <= lose_factor)
2014
            return 0;
2015
    }
2016
#endif
2017

2018
    return recv_len;
317✔
2019
}
179✔
2020

2021
/* stats_add: update host statistics for a single packet that was received (or timed out)
2022
 * h: host entry to update
2023
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2024
 * success: 1 if response received, 0 otherwise
2025
 * latency: response time, in ns
2026
 */
2027
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
312✔
2028
{
2029
    /* sent count - we update only on receive/timeout, so that we don't get
2030
     * weird loss percentage, just because a packet was note recived yet.
2031
     */
2032
    h->num_sent++;
312✔
2033
    h->num_sent_i++;
312✔
2034

2035
    if (!success) {
312✔
2036
        if (!loop_flag && index >= 0) {
125✔
2037
            h->resp_times[index] = RESP_TIMEOUT;
125✔
2038
        }
39✔
2039
        num_timeout++;
125✔
2040
        return;
125✔
2041
    }
2042

2043
    /* received count */
2044
    h->num_recv++;
187✔
2045
    h->num_recv_i++;
187✔
2046

2047
    /* maximum */
2048
    if (!h->max_reply || latency > h->max_reply) {
187✔
2049
        h->max_reply = latency;
167✔
2050
    }
51✔
2051
    if (!h->max_reply_i || latency > h->max_reply_i) {
187✔
2052
        h->max_reply_i = latency;
171✔
2053
    }
53✔
2054

2055
    /* minimum */
2056
    if (!h->min_reply || latency < h->min_reply) {
187✔
2057
        h->min_reply = latency;
154✔
2058
    }
46✔
2059
    if (!h->min_reply_i || latency < h->min_reply_i) {
187✔
2060
        h->min_reply_i = latency;
157✔
2061
    }
46✔
2062

2063
    /* total time (for average) */
2064
    h->total_time += latency;
187✔
2065
    h->total_time_i += latency;
187✔
2066

2067
    /* response time per-packet (count mode) */
2068
    if (!loop_flag && index >= 0) {
187✔
2069
        h->resp_times[index] = latency;
187✔
2070
    }
57✔
2071
}
96✔
2072

2073
/* stats_reset_interval: reset interval statistics
2074
 * h: host entry to update
2075
 */
2076
void stats_reset_interval(HOST_ENTRY *h)
×
2077
{
2078
    h->num_sent_i = 0;
×
2079
    h->num_recv_i = 0;
×
2080
    h->max_reply_i = 0;
×
2081
    h->min_reply_i = 0;
×
2082
    h->total_time_i = 0;
×
2083
}
×
2084

2085
int decode_icmp_ipv4(
293✔
2086
    struct sockaddr *response_addr,
2087
    size_t response_addr_len,
2088
    char *reply_buf,
2089
    size_t reply_buf_len,
2090
    unsigned short *id,
2091
    unsigned short *seq)
2092
{
2093
    struct icmp *icp;
2094
    int hlen = 0;
293✔
2095

2096
    if (!using_sock_dgram4) {
293✔
2097
        struct ip *ip = (struct ip *)reply_buf;
293✔
2098

2099
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2100
        /* The alpha headers are decidedly broken.
2101
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2102
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2103
         */
2104
        hlen = (ip->ip_vhl & 0x0F) << 2;
2105
#else
2106
        hlen = ip->ip_hl << 2;
293✔
2107
#endif
2108
    }
57✔
2109

2110
    if (reply_buf_len < hlen + ICMP_MINLEN) {
293✔
2111
        /* too short */
2112
        if (verbose_flag) {
×
2113
            char buf[INET6_ADDRSTRLEN];
2114
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2115
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2116
        }
2117
        return -1;
×
2118
    }
2119

2120
    icp = (struct icmp *)(reply_buf + hlen);
293✔
2121

2122
    if (icp->icmp_type != ICMP_ECHOREPLY) {
293✔
2123
        /* Handle other ICMP packets */
2124
        struct icmp *sent_icmp;
2125
        SEQMAP_VALUE *seqmap_value;
2126
        char addr_ascii[INET6_ADDRSTRLEN];
2127
        HOST_ENTRY *h;
2128

2129
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2130
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
118✔
2131
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2132
            return -1;
×
2133
        }
2134

2135
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
118✔
2136

2137
        if (sent_icmp->icmp_type != ICMP_ECHO || sent_icmp->icmp_id != ident4) {
118✔
2138
            /* not caused by us */
2139
            return -1;
118✔
2140
        }
2141

2142
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2143
        if (seqmap_value == NULL) {
×
2144
            return -1;
×
2145
        }
2146

2147
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2148

2149
        switch (icp->icmp_type) {
×
2150
        case ICMP_UNREACH:
2151
            h = table[seqmap_value->host_nr];
×
2152
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2153
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2154
                    addr_ascii, h->host);
2155
            }
2156
            else {
2157
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2158
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2159
            }
2160

2161
            print_warning("\n");
×
2162
            num_othericmprcvd++;
×
2163
            break;
×
2164

2165
        case ICMP_SOURCEQUENCH:
2166
        case ICMP_REDIRECT:
2167
        case ICMP_TIMXCEED:
2168
        case ICMP_PARAMPROB:
2169
            h = table[seqmap_value->host_nr];
×
2170
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2171
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2172
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2173
            }
2174
            else {
2175
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2176
                    icp->icmp_type, addr_ascii, h->host);
×
2177
            }
2178
            print_warning("\n");
×
2179
            num_othericmprcvd++;
×
2180
            break;
×
2181
        }
2182

2183
        return -1;
×
2184
    }
2185

2186
    *id = icp->icmp_id;
175✔
2187
    *seq = ntohs(icp->icmp_seq);
175✔
2188

2189
    return hlen;
175✔
2190
}
57✔
2191

2192
#ifdef IPV6
2193
int decode_icmp_ipv6(
24✔
2194
    struct sockaddr *response_addr,
2195
    size_t response_addr_len,
2196
    char *reply_buf,
2197
    size_t reply_buf_len,
2198
    unsigned short *id,
2199
    unsigned short *seq)
2200
{
2201
    struct icmp6_hdr *icp;
2202

2203
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
24✔
2204
        if (verbose_flag) {
×
2205
            char buf[INET6_ADDRSTRLEN];
2206
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2207
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2208
        }
2209
        return 0; /* too short */
×
2210
    }
2211

2212
    icp = (struct icmp6_hdr *)reply_buf;
24✔
2213

2214
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
24✔
2215
        /* Handle other ICMP packets */
2216
        struct icmp6_hdr *sent_icmp;
2217
        SEQMAP_VALUE *seqmap_value;
2218
        char addr_ascii[INET6_ADDRSTRLEN];
2219
        HOST_ENTRY *h;
2220

2221
        /* reply icmp packet (ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2222
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
12✔
2223
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2224
            return 0;
×
2225
        }
2226

2227
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
12✔
2228

2229
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
12✔
2230
            /* not caused by us */
2231
            return 0;
12✔
2232
        }
2233

2234
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2235
        if (seqmap_value == NULL) {
×
2236
            return 0;
×
2237
        }
2238

2239
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2240

2241
        switch (icp->icmp6_type) {
×
2242
        case ICMP_UNREACH:
2243
            h = table[seqmap_value->host_nr];
×
2244
            if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
×
2245
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2246
                    addr_ascii, h->host);
2247
            }
2248
            else {
2249
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2250
                    icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2251
            }
2252

2253
            print_warning("\n");
×
2254
            num_othericmprcvd++;
×
2255
            break;
×
2256

2257
        case ICMP_SOURCEQUENCH:
2258
        case ICMP_REDIRECT:
2259
        case ICMP_TIMXCEED:
2260
        case ICMP_PARAMPROB:
2261
            h = table[seqmap_value->host_nr];
×
2262
            if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
×
2263
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2264
                    icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
×
2265
            }
2266
            else {
2267
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2268
                    icp->icmp6_type, addr_ascii, h->host);
×
2269
            }
2270
            print_warning("\n");
×
2271
            num_othericmprcvd++;
×
2272
            break;
×
2273
        }
2274

2275
        return 0;
×
2276
    }
2277

2278
    *id = icp->icmp6_id;
12✔
2279
    *seq = ntohs(icp->icmp6_seq);
12✔
2280

2281
    return 1;
12✔
2282
}
2283
#endif
2284

2285
int wait_for_reply(int64_t wait_time)
743✔
2286
{
2287
    int result;
2288
    static char buffer[RECV_BUFSIZE];
2289
    struct sockaddr_storage response_addr;
2290
    int n, avg;
2291
    HOST_ENTRY *h;
2292
    int64_t this_reply;
2293
    int this_count;
2294
    int64_t recv_time = 0;
743✔
2295
    SEQMAP_VALUE *seqmap_value;
2296
    unsigned short id;
2297
    unsigned short seq;
2298

2299
    /* Receive packet */
2300
    result = receive_packet(wait_time, /* max. wait time, in ns */
922✔
2301
        &recv_time, /* reply_timestamp */
2302
        (struct sockaddr *)&response_addr, /* reply_src_addr */
179✔
2303
        sizeof(response_addr), /* reply_src_addr_len */
2304
        buffer, /* reply_buf */
2305
        sizeof(buffer) /* reply_buf_len */
2306
    );
2307

2308
    if (result <= 0) {
743✔
2309
        return 0;
426✔
2310
    }
2311

2312
    update_current_time();
317✔
2313
    if (recv_time == 0)
317✔
2314
        recv_time = current_time_ns;
57✔
2315

2316
    /* Process ICMP packet and retrieve id/seq */
2317
    if (response_addr.ss_family == AF_INET) {
317✔
2318
        int ip_hlen = decode_icmp_ipv4(
293✔
2319
            (struct sockaddr *)&response_addr,
57✔
2320
            sizeof(response_addr),
2321
            buffer,
2322
            sizeof(buffer),
2323
            &id,
2324
            &seq);
2325
        if (ip_hlen < 0) {
293✔
2326
            return 1;
118✔
2327
        }
2328
        if (id != ident4) {
175✔
2329
            return 1; /* packet received, but not the one we are looking for! */
×
2330
        }
2331
        if (!using_sock_dgram4) {
175✔
2332
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2333
             * with fping with IPv6 hosts */
2334
            result -= ip_hlen;
175✔
2335
        }
57✔
2336
    }
57✔
2337
#ifdef IPV6
2338
    else if (response_addr.ss_family == AF_INET6) {
24✔
2339
        if (!decode_icmp_ipv6(
24✔
2340
                (struct sockaddr *)&response_addr,
2341
                sizeof(response_addr),
2342
                buffer,
2343
                sizeof(buffer),
2344
                &id,
2345
                &seq)) {
2346
            return 1;
12✔
2347
        }
2348
        if (id != ident6) {
12✔
2349
            return 1; /* packet received, but not the one we are looking for! */
×
2350
        }
2351
    }
2352
#endif
2353
    else {
2354
        return 1;
×
2355
    }
2356

2357
    seqmap_value = seqmap_fetch(seq, current_time_ns);
187✔
2358
    if (seqmap_value == NULL) {
187✔
2359
        return 1;
×
2360
    }
2361

2362
    /* find corresponding host_entry */
2363
    n = seqmap_value->host_nr;
187✔
2364
    h = table[n];
187✔
2365
    this_count = seqmap_value->ping_count;
187✔
2366
    this_reply = recv_time - seqmap_value->ping_ts;
187✔
2367

2368
    /* update stats that include invalid replies */
2369
    h->num_recv_total++;
187✔
2370
    num_pingreceived++;
187✔
2371

2372
    dbg_printf("received [%d] from %s\n", this_count, h->host);
2373

2374
    /* discard duplicates */
2375
    if (!loop_flag && h->resp_times[this_count] >= 0) {
187✔
2376
        if (!per_recv_flag) {
×
2377
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2378
                h->host, this_count, result, sprint_tm(this_reply));
2379

2380
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2381
                char buf[INET6_ADDRSTRLEN];
2382
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2383
                fprintf(stderr, " [<- %s]", buf);
×
2384
            }
2385
            fprintf(stderr, "\n");
×
2386
        }
2387
        return 1;
×
2388
    }
2389

2390
    /* discard reply if delay is larger than timeout
2391
     * (see also: github #32) */
2392
    if (this_reply > h->timeout) {
187✔
2393
        return 1;
×
2394
    }
2395

2396
    /* update stats */
2397
    stats_add(h, this_count, 1, this_reply);
187✔
2398
    // TODO: move to stats_add?
2399
    if (!max_reply || this_reply > max_reply)
187✔
2400
        max_reply = this_reply;
142✔
2401
    if (!min_reply || this_reply < min_reply)
187✔
2402
        min_reply = this_reply;
132✔
2403
    sum_replies += this_reply;
187✔
2404
    total_replies++;
187✔
2405

2406
    /* initialize timeout to initial timeout (without backoff) */
2407
    h->timeout = timeout;
187✔
2408

2409
    /* remove timeout event */
2410
    struct event *timeout_event = host_get_timeout_event(h, this_count);
187✔
2411
    if (timeout_event) {
187✔
2412
        ev_remove(&event_queue_timeout, timeout_event);
187✔
2413
    }
57✔
2414

2415
    /* print "is alive" */
2416
    if (h->num_recv == 1) {
187✔
2417
        num_alive++;
138✔
2418
        if (fast_reachable && num_alive >= min_reachable)
138✔
2419
                finish_requested = 1;
3✔
2420

2421
        if (verbose_flag || alive_flag) {
138✔
2422
            printf("%s", h->host);
94✔
2423

2424
            if (verbose_flag)
94✔
2425
                printf(" is alive");
88✔
2426

2427
            if (elapsed_flag)
94✔
2428
                printf(" (%s ms)", sprint_tm(this_reply));
3✔
2429

2430
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
94✔
2431
                char buf[INET6_ADDRSTRLEN];
2432
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2433
                fprintf(stderr, " [<- %s]", buf);
×
2434
            }
2435

2436
            printf("\n");
94✔
2437
        }
28✔
2438
    }
42✔
2439

2440
    /* print received ping (unless --quiet) */
2441
    if (per_recv_flag) {
187✔
2442
        if (timestamp_flag) {
39✔
2443
            printf("[%.5f] ", (double)recv_time / 1e9);
6✔
2444
        }
2✔
2445
        avg = h->total_time / h->num_recv;
39✔
2446
        printf("%-*s : [%d], %d bytes, %s ms",
39✔
2447
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
13✔
2448
        printf(" (%s avg, ", sprint_tm(avg));
39✔
2449

2450
        if (h->num_recv <= h->num_sent) {
39✔
2451
            printf("%d%% loss)",
39✔
2452
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
39✔
2453
        }
13✔
2454
        else {
2455
            printf("%d%% return)",
×
2456
                (h->num_recv_total * 100) / h->num_sent);
×
2457
        }
2458

2459
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
39✔
2460
            char buf[INET6_ADDRSTRLEN];
2461
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2462
            fprintf(stderr, " [<- %s]", buf);
×
2463
        }
2464

2465
        printf("\n");
39✔
2466
    }
13✔
2467

2468
    return 1;
187✔
2469
}
179✔
2470

2471
/************************************************************
2472

2473
  Function: add_name
2474

2475
*************************************************************
2476

2477
  Inputs:  char* name
2478

2479
  Description:
2480

2481
  process input name for addition to target list
2482
  name can turn into multiple targets via multiple interfaces (-m)
2483
  or via NIS groups
2484

2485
************************************************************/
2486

2487
void add_name(char *name)
199✔
2488
{
2489
    struct addrinfo *res0, *res, hints;
2490
    int ret_ga;
2491
    char *printname;
2492
    char namebuf[256];
2493
    char addrbuf[256];
2494

2495
    /* getaddrinfo */
2496
    memset(&hints, 0, sizeof(struct addrinfo));
199✔
2497
    hints.ai_flags = AI_UNUSABLE;
199✔
2498
    hints.ai_socktype = SOCK_RAW;
199✔
2499
    hints.ai_family = hints_ai_family;
199✔
2500
    if (hints_ai_family == AF_INET) {
199✔
2501
        hints.ai_protocol = IPPROTO_ICMP;
21✔
2502
    }
7✔
2503
#ifdef IPV6
2504
    else if (hints_ai_family == AF_INET6) {
178✔
2505
        hints.ai_protocol = IPPROTO_ICMPV6;
5✔
2506
    }
1✔
2507
#endif
2508
    else {
2509
        hints.ai_socktype = SOCK_STREAM;
173✔
2510
        hints.ai_protocol = 0;
173✔
2511
    }
2512
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
199✔
2513
    if (ret_ga) {
199✔
2514
        if (!quiet_flag)
9✔
2515
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
9✔
2516
        num_noaddress++;
9✔
2517
        return;
9✔
2518
    }
2519

2520
    /* NOTE: we could/should loop with res on all addresses like this:
2521
     * for (res = res0; res; res = res->ai_next) {
2522
     * We don't do it yet, however, because is is an incompatible change
2523
     * (need to implement a separate option for this)
2524
     */
2525
    for (res = res0; res; res = res->ai_next) {
190✔
2526
        /* name_flag: addr -> name lookup requested) */
2527
        if (name_flag || rdns_flag) {
190✔
2528
            int do_rdns = rdns_flag ? 1 : 0;
×
2529
            if (name_flag) {
×
2530
                /* Was it a numerical address? Only then do a rdns-query */
2531
                struct addrinfo *nres;
2532
                hints.ai_flags = AI_NUMERICHOST;
×
2533
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
2534
                    do_rdns = 1;
×
2535
                    freeaddrinfo(nres);
×
2536
                }
2537
            }
2538

2539
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
×
2540
                printname = namebuf;
×
2541
            }
2542
            else {
2543
                printname = name;
×
2544
            }
2545
        }
2546
        else {
2547
            printname = name;
190✔
2548
        }
2549

2550
        /* addr_flag: name -> addr lookup requested */
2551
        if (addr_flag) {
190✔
2552
            int ret;
2553
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2554
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2555
            if (ret) {
3✔
2556
                if (!quiet_flag) {
×
2557
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2558
                }
2559
                continue;
×
2560
            }
2561

2562
            if (name_flag || rdns_flag) {
3✔
2563
                char nameaddrbuf[512 + 3];
2564
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
2565
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2566
            }
2567
            else {
2568
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2569
            }
2570
        }
1✔
2571
        else {
2572
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
187✔
2573
        }
2574

2575
        if (!multif_flag) {
190✔
2576
            break;
190✔
2577
        }
2578
    }
2579

2580
    freeaddrinfo(res0);
190✔
2581
}
61✔
2582

2583
/************************************************************
2584

2585
  Function: add_addr
2586

2587
*************************************************************
2588

2589
  Description:
2590

2591
  add single address to list of hosts to be pinged
2592

2593
************************************************************/
2594

2595
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
190✔
2596
{
2597
    HOST_ENTRY *p;
2598
    int n;
2599
    int64_t *i;
2600

2601
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
190✔
2602
    if (!p)
190✔
2603
        crash_and_burn("can't allocate HOST_ENTRY");
×
2604

2605
    p->name = strdup(name);
190✔
2606
    p->host = strdup(host);
190✔
2607
    memcpy(&p->saddr, ipaddr, ipaddr_len);
190✔
2608
    p->saddr_len = ipaddr_len;
190✔
2609
    p->timeout = timeout;
190✔
2610
    p->min_reply = 0;
190✔
2611

2612
    if (netdata_flag) {
190✔
2613
        char *s = p->name;
3✔
2614
        while (*s) {
30✔
2615
            if (!isalnum(*s))
27✔
2616
                *s = '_';
9✔
2617
            s++;
27✔
2618
        }
2619
    }
1✔
2620

2621
    if (strlen(p->host) > max_hostname_len)
190✔
2622
        max_hostname_len = strlen(p->host);
121✔
2623

2624
    /* array for response time results */
2625
    if (!loop_flag) {
190✔
2626
        i = (int64_t *)malloc(trials * sizeof(int64_t));
190✔
2627
        if (!i)
190✔
2628
            crash_and_burn("can't allocate resp_times array");
×
2629

2630
        for (n = 1; n < trials; n++)
736✔
2631
            i[n] = RESP_UNUSED;
546✔
2632

2633
        p->resp_times = i;
190✔
2634
    }
58✔
2635

2636
    /* allocate event storage */
2637
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
190✔
2638
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
190✔
2639

2640
    /* schedule first ping */
2641
    host_add_ping_event(p, 0, current_time_ns);
190✔
2642

2643
    num_hosts++;
190✔
2644
}
190✔
2645

2646
/************************************************************
2647

2648
  Function: crash_and_burn
2649

2650
*************************************************************
2651

2652
  Inputs:  char* message
2653

2654
  Description:
2655

2656
************************************************************/
2657

2658
void crash_and_burn(char *message)
×
2659
{
2660
    if (verbose_flag)
×
2661
        fprintf(stderr, "%s: %s\n", prog, message);
×
2662

2663
    exit(4);
×
2664
}
2665

2666
/************************************************************
2667

2668
  Function: errno_crash_and_burn
2669

2670
*************************************************************
2671

2672
  Inputs:  char* message
2673

2674
  Description:
2675

2676
************************************************************/
2677

2678
void errno_crash_and_burn(char *message)
×
2679
{
2680
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
×
2681
    exit(4);
×
2682
}
2683

2684
/************************************************************
2685

2686
  Function: print_warning
2687

2688
  Description: fprintf(stderr, ...), unless running with -q
2689

2690
*************************************************************/
2691

2692
void print_warning(char *format, ...)
9✔
2693
{
2694
    va_list args;
2695
    if (!quiet_flag) {
9✔
2696
        va_start(args, format);
9✔
2697
        vfprintf(stderr, format, args);
9✔
2698
        va_end(args);
9✔
2699
    }
3✔
2700
}
9✔
2701

2702
/************************************************************
2703

2704
  Function: sprint_tm
2705

2706
*************************************************************
2707

2708
  render nanosecond int64_t value into milliseconds string with three digits of
2709
  precision.
2710

2711
************************************************************/
2712

2713
const char *sprint_tm(int64_t ns)
198✔
2714
{
2715
    static char buf[10];
2716
    double t = (double)ns / 1e6;
198✔
2717

2718
    if (t < 0.0) {
198✔
2719
        /* negative (unexpected) */
2720
        sprintf(buf, "%.2g", t);
×
2721
    }
2722
    else if (t < 1.0) {
198✔
2723
        /* <= 0.99 ms */
2724
        sprintf(buf, "%.3f", t);
198✔
2725
    }
64✔
2726
    else if (t < 10.0) {
×
2727
        /* 1.00 - 9.99 ms */
2728
        sprintf(buf, "%.2f", t);
×
2729
    }
2730
    else if (t < 100.0) {
×
2731
        /* 10.0 - 99.9 ms */
2732
        sprintf(buf, "%.1f", t);
×
2733
    }
2734
    else if (t < 1000000.0) {
×
2735
        /* 100 - 1'000'000 ms */
2736
        sprintf(buf, "%.0f", t);
×
2737
    }
2738
    else {
2739
        sprintf(buf, "%.3e", t);
×
2740
    }
2741

2742
    return (buf);
198✔
2743
}
2744

2745
/************************************************************
2746

2747
  Function: addr_cmp
2748

2749
*************************************************************/
2750
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
133✔
2751
{
2752
    if (a->sa_family != b->sa_family) {
133✔
2753
        return a->sa_family - b->sa_family;
×
2754
    }
2755
    else {
2756
        if (a->sa_family == AF_INET) {
133✔
2757
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
127✔
2758
        }
2759
        else if (a->sa_family == AF_INET6) {
6✔
2760
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
6✔
2761
                &((struct sockaddr_in6 *)b)->sin6_addr,
6✔
2762
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
2763
        }
2764
    }
2765

2766
    return 0;
×
2767
}
41✔
2768

2769
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
255✔
2770
{
2771
    struct event *event = &h->event_storage_ping[index % event_storage_count];
255✔
2772
    event->host = h;
255✔
2773
    event->ping_index = index;
255✔
2774
    event->ev_time = ev_time;
255✔
2775
    ev_enqueue(&event_queue_ping, event);
255✔
2776

2777
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
2778
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2779
}
255✔
2780

2781
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
312✔
2782
{
2783
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
312✔
2784
    event->host = h;
312✔
2785
    event->ping_index = index;
312✔
2786
    event->ev_time = ev_time;
312✔
2787
    ev_enqueue(&event_queue_timeout, event);
312✔
2788

2789
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
2790
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2791
}
312✔
2792

2793
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
187✔
2794
{
2795
    return &h->event_storage_timeout[index % event_storage_count];
187✔
2796
}
2797

2798
/************************************************************
2799

2800
  Function: ev_enqueue
2801

2802
  Enqueue an event
2803

2804
  The queue is sorted by event->ev_time, so that queue->first always points to
2805
  the earliest event.
2806

2807
  We start scanning the queue from the tail, because we assume
2808
  that new events mostly get inserted with a event time higher
2809
  than the others.
2810

2811
*************************************************************/
2812
void ev_enqueue(struct event_queue *queue, struct event *event)
567✔
2813
{
2814
    struct event *i;
2815
    struct event *i_prev;
2816

2817
    /* Empty list */
2818
    if (queue->last == NULL) {
567✔
2819
        event->ev_next = NULL;
392✔
2820
        event->ev_prev = NULL;
392✔
2821
        queue->first = event;
392✔
2822
        queue->last = event;
392✔
2823
        return;
392✔
2824
    }
2825

2826
    /* Insert on tail? */
2827
    if (event->ev_time - queue->last->ev_time >= 0) {
175✔
2828
        event->ev_next = NULL;
175✔
2829
        event->ev_prev = queue->last;
175✔
2830
        queue->last->ev_next = event;
175✔
2831
        queue->last = event;
175✔
2832
        return;
175✔
2833
    }
2834

2835
    /* Find insertion point */
2836
    i = queue->last;
×
2837
    while (1) {
2838
        i_prev = i->ev_prev;
×
2839
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
2840
            event->ev_prev = i_prev;
×
2841
            event->ev_next = i;
×
2842
            i->ev_prev = event;
×
2843
            if (i_prev != NULL) {
×
2844
                i_prev->ev_next = event;
×
2845
            }
2846
            else {
2847
                queue->first = event;
×
2848
            }
2849
            return;
×
2850
        }
2851
        i = i_prev;
×
2852
    }
2853
}
173✔
2854

2855
/************************************************************
2856

2857
  Function: ev_dequeue
2858

2859
*************************************************************/
2860
struct event *ev_dequeue(struct event_queue *queue)
365✔
2861
{
2862
    struct event *dequeued;
2863

2864
    if (queue->first == NULL) {
365✔
2865
        return NULL;
×
2866
    }
2867
    dequeued = queue->first;
365✔
2868
    ev_remove(queue, dequeued);
365✔
2869

2870
    return dequeued;
365✔
2871
}
111✔
2872

2873
/************************************************************
2874

2875
  Function: ev_remove
2876

2877
*************************************************************/
2878
void ev_remove(struct event_queue *queue, struct event *event)
552✔
2879
{
2880
    if (queue->first == event) {
552✔
2881
        queue->first = event->ev_next;
543✔
2882
    }
165✔
2883
    if (queue->last == event) {
552✔
2884
        queue->last = event->ev_prev;
398✔
2885
    }
118✔
2886
    if (event->ev_prev) {
552✔
2887
        event->ev_prev->ev_next = event->ev_next;
9✔
2888
    }
3✔
2889
    if (event->ev_next) {
552✔
2890
        event->ev_next->ev_prev = event->ev_prev;
154✔
2891
    }
50✔
2892
    event->ev_prev = NULL;
552✔
2893
    event->ev_next = NULL;
552✔
2894
}
552✔
2895

2896
/************************************************************
2897

2898
  Function: usage
2899

2900
*************************************************************
2901

2902
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
2903

2904
  Description:
2905

2906
************************************************************/
2907

2908
void usage(int is_error)
36✔
2909
{
2910
    FILE *out = is_error ? stderr : stdout;
36✔
2911
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
36✔
2912
    fprintf(out, "\n");
36✔
2913
    fprintf(out, "Probing options:\n");
36✔
2914
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
36✔
2915
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
36✔
2916
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
36✔
2917
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
36✔
2918
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target\n");
36✔
2919
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
36✔
2920
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified)\n");
36✔
2921
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
36✔
2922
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
36✔
2923
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
36✔
2924
#ifdef SO_BINDTODEVICE
2925
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
24✔
2926
#endif
2927
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
36✔
2928
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
36✔
2929
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
36✔
2930
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
36✔
2931
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
36✔
2932
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
36✔
2933
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
36✔
2934
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
36✔
2935
    fprintf(out, "   -S, --src=IP       set source address\n");
36✔
2936
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
36✔
2937
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
36✔
2938
    fprintf(out, "\n");
36✔
2939
    fprintf(out, "Output options:\n");
36✔
2940
    fprintf(out, "   -a, --alive        show targets that are alive\n");
36✔
2941
    fprintf(out, "   -A, --addr         show targets by address\n");
36✔
2942
    fprintf(out, "   -C, --vcount=N     same as -c, report results in verbose format\n");
36✔
2943
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
36✔
2944
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
36✔
2945
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
36✔
2946
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
36✔
2947
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
36✔
2948
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
36✔
2949
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
36✔
2950
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
36✔
2951
    fprintf(out, "   -Q, --squiet=SECS  same as -q, but add interval summary every SECS seconds\n");
36✔
2952
    fprintf(out, "   -s, --stats        print final stats\n");
36✔
2953
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
36✔
2954
    fprintf(out, "   -v, --version      show version\n");
36✔
2955
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
36✔
2956
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
36✔
2957
    exit(is_error);
36✔
2958
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc