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

schweikert / fping / 13751806735

09 Mar 2025 07:31PM UTC coverage: 87.625% (-0.2%) from 87.833%
13751806735

Pull #384

github

web-flow
Merge cc75deb8c into a237394ca
Pull Request #384: print TOS & TTL also for IPv6 and w/o privileges

18 of 22 new or added lines in 1 file covered. (81.82%)

2 existing lines in 1 file now uncovered.

1402 of 1600 relevant lines covered (87.63%)

322.0 hits per line

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

87.81
/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 65535 /* (theoretical) max IPv4 packet size */
138
#define SIZE_IP_HDR 20 /* min IPv4 header size */
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 131072 /* maximum number of hosts that -g can generate */
143
#define MAX_TARGET_NAME_LEN 255 /* maximum target name length read from file */
144

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

148
/* ICMP Timestamp has a fixed payload size of 12 bytes */
149
#define ICMP_TIMESTAMP_DATA_SIZE 12
150

151
/* maxima and minima */
152
#ifdef FPING_SAFE_LIMITS
153
#define MIN_INTERVAL 1 /* in millisec */
154
#define MIN_PERHOST_INTERVAL 10 /* in millisec */
155
#else
156
#define MIN_INTERVAL 0
157
#define MIN_PERHOST_INTERVAL 0
158
#endif
159

160
/* response time array flags */
161
#define RESP_WAITING -1
162
#define RESP_UNUSED -2
163
#define RESP_ERROR -3
164
#define RESP_TIMEOUT -4
165

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

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

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

219
#define ICMP_UNREACH_MAXTYPE 15
220

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

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

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

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

268
#define EV_TYPE_PING 1
269
#define EV_TYPE_TIMEOUT 2
270

271
struct event {
272
    struct event *ev_prev;
273
    struct event *ev_next;
274
    int64_t ev_time;
275
    struct host_entry *host;
276
    int ping_index;
277
};
278

279
struct event_queue {
280
    struct event *first;
281
    struct event *last;
282
};
283

284
/*** globals ***/
285

286
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
287

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

297
char *prog;
298
int ident4 = 0; /* our icmp identity field */
299
int ident6 = 0;
300
const int sock_opt_on = 1; /* to activate a socket option */
301
int socket4 = -1;
302
int socktype4 = -1;
303
int using_sock_dgram4 = 0;
304
#ifndef IPV6
305
int hints_ai_family = AF_INET;
306
#else
307
int socket6 = -1;
308
int socktype6 = -1;
309
int hints_ai_family = AF_UNSPEC;
310
#endif
311

312
volatile sig_atomic_t status_snapshot = 0;
313
volatile sig_atomic_t finish_requested = 0;
314

315
unsigned int debugging = 0;
316

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

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

350
struct timespec current_time; /* current time (pseudo) */
351
int64_t current_time_ns;
352
int64_t start_time;
353
int64_t end_time;
354
int64_t last_send_time; /* time last ping was sent */
355
int64_t next_report_time; /* time next -Q report is expected */
356

357
/* switches */
358
int generate_flag = 0; /* flag for IP list generation */
359
int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
360
int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag;
361
int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
362
int multif_flag, timeout_flag, fast_reachable;
363
int outage_flag = 0;
364
int timestamp_flag = 0;
365
int timestamp_format_flag = 0;
366
int random_data_flag = 0;
367
int cumulative_stats_flag = 0;
368
int check_source_flag = 0;
369
int icmp_request_typ = 0;
370
int print_tos_flag = 0;
371
int print_ttl_flag = 0;
372
int size_flag = 0;
373
#if defined(DEBUG) || defined(_DEBUG)
374
int randomly_lose_flag, trace_flag, print_per_system_flag;
375
int lose_factor;
376
#endif /* DEBUG || _DEBUG */
377

378
unsigned int fwmark = 0;
379

380
char *filename = NULL; /* file containing hosts to ping */
381

382
/*** forward declarations ***/
383

384
void add_name(char *name);
385
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len);
386
char *na_cat(char *name, struct in_addr ipaddr);
387
void crash_and_burn(char *message);
388
void errno_crash_and_burn(char *message);
389
char *get_host_by_address(struct in_addr in);
390
int send_ping(HOST_ENTRY *h, int index);
391
void usage(int);
392
int wait_for_reply(int64_t);
393
void print_per_system_stats(void);
394
void print_per_system_splits(void);
395
void stats_reset_interval(HOST_ENTRY *h);
396
void print_netdata(void);
397
void print_global_stats(void);
398
void main_loop();
399
void signal_handler(int);
400
void finish();
401
const char *sprint_tm(int64_t t);
402
void ev_enqueue(struct event_queue *queue, struct event *event);
403
struct event *ev_dequeue(struct event_queue *queue);
404
void ev_remove(struct event_queue *queue, struct event *event);
405
void add_cidr(char *);
406
void add_cidr_ipv4(unsigned long, unsigned long);
407
void add_range(char *, char *);
408
void add_addr_range_ipv4(unsigned long, unsigned long);
409
#ifdef IPV6
410
uint64_t be_octets_to_uint64(uint8_t*);
411
void uint64_to_be_octets(uint64_t, uint8_t*);
412
void add_cidr_ipv6(uint64_t, uint64_t, unsigned long, const char *);
413
void add_addr_range_ipv6(uint64_t, uint64_t, uint64_t, uint64_t, const char *);
414
#endif
415
void print_warning(char *fmt, ...);
416
int addr_cmp(struct sockaddr *a, struct sockaddr *b);
417
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
418
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
419
struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
420
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
421
void update_current_time();
422
void print_timestamp_format(int64_t current_time_ns, int timestamp_format);
423
static uint32_t ms_since_midnight_utc(int64_t time_val);
424

425
/************************************************************
426

427
  Function: p_setsockopt
428

429
*************************************************************
430

431
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
432

433
  Description:
434

435
  Elevates privileges to p_uid when required, calls
436
  setsockopt, and drops privileges back.
437

438
************************************************************/
439

440
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
12✔
441
    const void *optval, socklen_t optlen)
442
{
443
    const uid_t saved_uid = geteuid();
12✔
444
    int res;
445

446
    if (p_uid != saved_uid && seteuid(p_uid)) {
12✔
447
        perror("cannot elevate privileges for setsockopt");
×
448
    }
449

450
    res = setsockopt(sockfd, level, optname, optval, optlen);
12✔
451

452
    if (p_uid != saved_uid && seteuid(saved_uid)) {
12✔
453
        perror("fatal error: could not drop privileges after setsockopt");
×
454
        /* continuing would be a security hole */
455
        exit(4);
×
456
    }
457

458
    return res;
12✔
459
}
460

461
/************************************************************
462

463
  Function: main
464

465
*************************************************************
466

467
  Inputs:  int argc, char** argv
468

469
  Description:
470

471
  Main program entry point
472

473
************************************************************/
474

475
int main(int argc, char **argv)
632✔
476
{
477
/* Debug: CPU Performance */
478
#if defined(DEBUG) || defined(_DEBUG)
479
    clock_t perf_cpu_start, perf_cpu_end;
480
    double perf_cpu_time_used;
481
    perf_cpu_start = clock();
482
#endif /* DEBUG || _DEBUG */
483

484
    int c;
485
    const uid_t suid = geteuid();
632✔
486
    int tos = 0;
632✔
487
    struct optparse optparse_state;
488
#ifdef USE_SIGACTION
489
    struct sigaction act;
490
#endif
491

492
    /* pre-parse -h/--help, so that we also can output help information
493
     * without trying to open the socket, which might fail */
494
    prog = argv[0];
632✔
495
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
632✔
496
        usage(0);
3✔
497
    }
1✔
498

499
    socket4 = open_ping_socket_ipv4(&socktype4);
630✔
500
#ifdef __linux__
501
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
502
     * structure is missing in the message.
503
     */
504
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
444✔
505
#endif
506

507
#ifdef IPV6
508
    socket6 = open_ping_socket_ipv6(&socktype6);
630✔
509
    /* if called (sym-linked) via 'fping6', imply '-6'
510
     * for backward compatibility */
511
    if (strstr(prog, "fping6")) {
630✔
512
        hints_ai_family = AF_INET6;
×
513
    }
514
#endif
515

516
    memset(&src_addr, 0, sizeof(src_addr));
630✔
517
#ifdef IPV6
518
    memset(&src_addr6, 0, sizeof(src_addr6));
630✔
519
#endif
520

521
    if (!suid && suid != getuid()) {
630✔
522
        /* *temporarily* drop privileges */
523
        if (seteuid(getuid()) == -1)
594✔
524
            perror("cannot setuid");
×
525
    }
160✔
526

527
    optparse_init(&optparse_state, argv);
630✔
528
    ident4 = ident6 = htons(getpid() & 0xFFFF);
630✔
529
    verbose_flag = 1;
630✔
530
    backoff_flag = 1;
630✔
531
    opterr = 1;
630✔
532

533
    /* get command line options */
534

535
    struct optparse_long longopts[] = {
630✔
536
        { "ipv4", '4', OPTPARSE_NONE },
537
        { "ipv6", '6', OPTPARSE_NONE },
538
        { "alive", 'a', OPTPARSE_NONE },
539
        { "addr", 'A', OPTPARSE_NONE },
540
        { "size", 'b', OPTPARSE_REQUIRED },
541
        { "backoff", 'B', OPTPARSE_REQUIRED },
542
        { "count", 'c', OPTPARSE_REQUIRED },
543
        { "vcount", 'C', OPTPARSE_REQUIRED },
544
        { "rdns", 'd', OPTPARSE_NONE },
545
        { "timestamp", 'D', OPTPARSE_NONE },
546
        { "timestamp-format", '0', OPTPARSE_REQUIRED },
547
        { "elapsed", 'e', OPTPARSE_NONE },
548
        { "file", 'f', OPTPARSE_REQUIRED },
549
        { "generate", 'g', OPTPARSE_NONE },
550
        { "help", 'h', OPTPARSE_NONE },
551
        { "ttl", 'H', OPTPARSE_REQUIRED },
552
        { "interval", 'i', OPTPARSE_REQUIRED },
553
        { "iface", 'I', OPTPARSE_REQUIRED },
554
        { "icmp-timestamp", '0', OPTPARSE_NONE },
555
#ifdef SO_MARK
556
        { "fwmark", 'k', OPTPARSE_REQUIRED },
557
#endif
558
        { "loop", 'l', OPTPARSE_NONE },
559
        { "all", 'm', OPTPARSE_NONE },
560
        { "dontfrag", 'M', OPTPARSE_NONE },
561
        { "name", 'n', OPTPARSE_NONE },
562
        { "netdata", 'N', OPTPARSE_NONE },
563
        { "outage", 'o', OPTPARSE_NONE },
564
        { "tos", 'O', OPTPARSE_REQUIRED },
565
        { "period", 'p', OPTPARSE_REQUIRED },
566
        { "quiet", 'q', OPTPARSE_NONE },
567
        { "squiet", 'Q', OPTPARSE_REQUIRED },
568
        { "retry", 'r', OPTPARSE_REQUIRED },
569
        { "random", 'R', OPTPARSE_NONE },
570
        { "stats", 's', OPTPARSE_NONE },
571
        { "src", 'S', OPTPARSE_REQUIRED },
572
        { "timeout", 't', OPTPARSE_REQUIRED },
573
        { NULL, 'T', OPTPARSE_REQUIRED },
574
        { "unreach", 'u', OPTPARSE_NONE },
575
        { "version", 'v', OPTPARSE_NONE },
576
        { "reachable", 'x', OPTPARSE_REQUIRED },
577
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
578
        { "check-source", '0', OPTPARSE_NONE },
579
        { "print-tos", '0', OPTPARSE_NONE },
580
        { "print-ttl", '0', OPTPARSE_NONE },
581
#if defined(DEBUG) || defined(_DEBUG)
582
        { NULL, 'z', OPTPARSE_REQUIRED },
583
#endif
584
        { 0, 0, 0 }
585
    };
586

587
    float opt_value_float;
588
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
1,712✔
589
        switch (c) {
961✔
590
        case '0':
74✔
591
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
92✔
592
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
15✔
593
                  timestamp_format_flag = 1;
3✔
594
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
13✔
595
                  timestamp_format_flag = 2;
3✔
596
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
10✔
597
                  timestamp_format_flag = 3;
3✔
598
                }else{
1✔
599
                  usage(1);
6✔
600
                }
601
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
82✔
602
                check_source_flag = 1;
9✔
603
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
69✔
604
#ifdef IPV6
605
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
25✔
606
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
607
                    exit(1);
2✔
608
                }
609
                hints_ai_family = AF_INET;
23✔
610
#endif
611
                icmp_request_typ = 13;
23✔
612
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
23✔
613
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
46✔
614
                print_tos_flag = 1;
23✔
615
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
23✔
616
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTOS, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
NEW
617
                        perror("setsockopt IP_RECVTOS");
×
618
                    }
619
                }
620
#if defined(IPV6) && defined(IPV6_RECVTCLASS)
621
                if (socket6 >= 0) {
23✔
622
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVTCLASS, &sock_opt_on, sizeof(sock_opt_on))) {
23✔
NEW
623
                        perror("setsockopt IPV6_RECVTCLASS");
×
624
                    }
625
                }
5✔
626
#endif
627
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
25✔
628
                print_ttl_flag = 1;
20✔
629
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
20✔
630
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTTL, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
NEW
631
                        perror("setsockopt IP_RECVTTL");
×
632
                    }
633
                }
634
#if defined(IPV6) && defined(IPV6_RECVHOPLIMIT)
635
                if (socket6 >= 0) {
20✔
636
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &sock_opt_on, sizeof(sock_opt_on))) {
20✔
NEW
637
                        perror("setsockopt IPV6_RECVHOPLIMIT");
×
638
                    }
639
                }
4✔
640
#endif
641
            } else {
4✔
642
                usage(1);
×
643
            }
644
            break;
86✔
645
        case '4':
32✔
646
#ifdef IPV6
647
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
648
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
649
                exit(1);
3✔
650
            }
651
            hints_ai_family = AF_INET;
40✔
652
#endif
653
            break;
40✔
654
        case '6':
26✔
655
#ifdef IPV6
656
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
29✔
657
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
658
                exit(1);
5✔
659
            }
660
            hints_ai_family = AF_INET6;
24✔
661
#else
662
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
663
            exit(1);
664
#endif
665
            break;
24✔
666
        case 'M':
2✔
667
#ifdef IP_MTU_DISCOVER
668
            if (socket4 >= 0) {
2✔
669
                int val = IP_PMTUDISC_DO;
2✔
670
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
671
                    perror("setsockopt IP_MTU_DISCOVER");
672
                }
673
            }
674
#ifdef IPV6
675
            if (socket6 >= 0) {
2✔
676
                int val = IPV6_PMTUDISC_DO;
2✔
677
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
678
                    perror("setsockopt IPV6_MTU_DISCOVER");
679
                }
680
            }
681
#endif
682
#else
683
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
684
            exit(1);
685
#endif
686
            break;
2✔
687

688
        case 't':
40✔
689
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
45✔
690
                usage(1);
6✔
691
            if (opt_value_float < 0) {
41✔
692
                usage(1);
3✔
693
            }
1✔
694
            timeout = opt_value_float * 1000000;
39✔
695
            timeout_flag = 1;
39✔
696
            break;
39✔
697

698
        case 'r':
36✔
699
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
40✔
700
                usage(1);
6✔
701
            break;
36✔
702

703
        case 'i':
30✔
704
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
36✔
705
                usage(1);
6✔
706
            if (opt_value_float < 0) {
32✔
707
                usage(1);
3✔
708
            }
1✔
709
            interval = opt_value_float * 1000000;
30✔
710
            break;
30✔
711

712
        case 'p':
68✔
713
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
99✔
714
                usage(1);
6✔
715
            if (opt_value_float < 0) {
95✔
716
                usage(1);
3✔
717
            }
1✔
718
            perhost_interval = opt_value_float * 1000000;
93✔
719

720
            break;
93✔
721

722
        case 'c':
60✔
723
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
85✔
724
                usage(1);
6✔
725

726
            count_flag = 1;
81✔
727
            break;
81✔
728

729
        case 'C':
24✔
730
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
33✔
731
                usage(1);
6✔
732

733
            count_flag = 1;
29✔
734
            report_all_rtts_flag = 1;
29✔
735
            break;
29✔
736

737
        case 'b':
14✔
738
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
21✔
739
                usage(1);
6✔
740
            size_flag = 1;
17✔
741
            break;
17✔
742

743
        case 'h':
2✔
744
            usage(0);
3✔
745
            break;
1✔
746

747
        case 'q':
12✔
748
            verbose_flag = 0;
17✔
749
            quiet_flag = 1;
17✔
750
            break;
17✔
751

752
        case 'Q':
22✔
753
            verbose_flag = 0;
33✔
754
            quiet_flag = 1;
33✔
755
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
756
                usage(1);
6✔
757
            if (opt_value_float < 0) {
29✔
758
                usage(1);
3✔
759
            }
1✔
760
            report_interval = opt_value_float * 1e9;
27✔
761

762
            /* recognize keyword(s) after number, ignore everything else */
763
            {
764
                char *comma = strchr(optparse_state.optarg, ',');
27✔
765
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
27✔
766
                    cumulative_stats_flag = 1;
6✔
767
                }
2✔
768
            }
769

770
            break;
27✔
771

772
        case 'e':
6✔
773
            elapsed_flag = 1;
8✔
774
            break;
8✔
775

776
        case 'm':
777
            multif_flag = 1;
×
778
            break;
×
779

780
        case 'N':
2✔
781
            netdata_flag = 1;
3✔
782
            break;
3✔
783

784
        case 'n':
4✔
785
            name_flag = 1;
6✔
786
            if (rdns_flag) {
6✔
787
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
788
                exit(1);
3✔
789
            }
790
            break;
3✔
791

792
        case 'd':
6✔
793
            rdns_flag = 1;
9✔
794
            if (name_flag) {
9✔
795
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
796
                exit(1);
3✔
797
            }
798
            break;
6✔
799

800
        case 'A':
2✔
801
            addr_flag = 1;
3✔
802
            break;
3✔
803

804
        case 'B':
10✔
805
            if (!(backoff = atof(optparse_state.optarg)))
12✔
806
                usage(1);
6✔
807

808
            break;
8✔
809

810
        case 's':
6✔
811
            stats_flag = 1;
9✔
812
            break;
9✔
813

814
        case 'D':
14✔
815
            timestamp_flag = 1;
21✔
816
            break;
21✔
817

818
        case 'R':
4✔
819
            random_data_flag = 1;
5✔
820
            break;
5✔
821

822
        case 'l':
2✔
823
            loop_flag = 1;
3✔
824
            backoff_flag = 0;
3✔
825
            break;
3✔
826

827
        case 'u':
8✔
828
            unreachable_flag = 1;
12✔
829
            break;
12✔
830

831
        case 'a':
18✔
832
            alive_flag = 1;
26✔
833
            break;
26✔
834

835
        case 'H':
8✔
836
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
12✔
837
                usage(1);
6✔
838
            break;
8✔
839

840
#if defined(DEBUG) || defined(_DEBUG)
841
        case 'z':
842
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
843
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
844
                    usage(1);
845

846
            break;
847
#endif /* DEBUG || _DEBUG */
848

849
        case 'v':
4✔
850
            printf("%s: Version %s\n", prog, VERSION);
6✔
851
            exit(0);
6✔
852

853
        case 'x':
8✔
854
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
10✔
855
                usage(1);
6✔
856
            break;
6✔
857

858
        case 'X':
6✔
859
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
860
                usage(1);
6✔
861
            fast_reachable = 1;
5✔
862
            break;
5✔
863

864
        case 'f':
18✔
865
            filename = optparse_state.optarg;
27✔
866
            break;
27✔
867
#ifdef SO_MARK
868
        case 'k':
10✔
869
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
10✔
870
                usage(1);
4✔
871

872
            if (socket4 >= 0)
6✔
873
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
874
                    perror("fwmark ipv4");
4✔
875

876
#ifdef IPV6
877
            if (socket6 >= 0)
6✔
878
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
879
                    perror("fwmark ipv6");
4✔
880
#endif
881

882
            break;
6✔
883
#endif
884

885
        case 'g':
98✔
886
            /* use IP list generation */
887
            /* mutually exclusive with using file input or command line targets */
888
            generate_flag = 1;
117✔
889
            break;
117✔
890

891
        case 'S':
10✔
892
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
893
                src_addr_set = 1;
6✔
894
                break;
6✔
895
            }
896
#ifdef IPV6
897
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
898
                src_addr6_set = 1;
4✔
899
                break;
4✔
900
            }
901
#endif
902
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
903
            exit(1);
3✔
904

905
        case 'I':
906
#ifdef SO_BINDTODEVICE
907
            if (socket4 >= 0) {
908
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
909
                    perror("binding to specific interface (SO_BINTODEVICE)");
910
                    exit(1);
911
                }
912
            }
913
#ifdef IPV6
914
            if (socket6 >= 0) {
915
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
916
                    perror("binding to specific interface (SO_BINTODEVICE), IPV6");
917
                    exit(1);
918
                }
919
            }
920
#endif
921
#else
922
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
923
            exit(3);
924
            ;
925
#endif
926
            break;
927

928
        case 'T':
2✔
929
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
930
            break;
3✔
931

932
        case 'O':
10✔
933
            if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
15✔
934
                if (socket4 >= 0) {
9✔
935
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
936
                        perror("setting type of service octet IP_TOS");
×
937
                    }
938
                }
3✔
939
#if defined(IPV6) && defined(IPV6_TCLASS)
940
                if (socket6 >= 0) {
9✔
941
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
942
                        perror("setting type of service octet IPV6_TCLASS");
×
943
                    }
944
                }
3✔
945
#endif
946
            }
3✔
947
            else {
948
                usage(1);
6✔
949
            }
950
            break;
11✔
951

952
        case 'o':
6✔
953
            outage_flag = 1;
9✔
954
            break;
9✔
955

956
        case '?':
4✔
957
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
958
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
959
            exit(1);
6✔
960
            break;
961
        }
962
    }
530✔
963

964
    /* permanently drop privileges */
965
    if (suid != getuid() && setuid(getuid())) {
471✔
966
        perror("fatal: failed to permanently drop privileges");
×
967
        /* continuing would be a security hole */
968
        exit(4);
×
969
    }
970

971
    /* validate various option settings */
972

973
#ifndef IPV6
974
    if (socket4 < 0) {
975
        crash_and_burn("can't create socket (must run as root?)");
976
    }
977
#else
978
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
471✔
979
        crash_and_burn("can't create socket (must run as root?)");
×
980
    }
981
#endif
982

983
    if (ttl > 255) {
469✔
984
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
985
        exit(1);
3✔
986
    }
987

988
    if (unreachable_flag && alive_flag) {
466✔
989
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
990
        exit(1);
3✔
991
    }
992

993
    if (count_flag && loop_flag) {
463✔
994
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
995
        exit(1);
3✔
996
    }
997

998
#ifdef FPING_SAFE_LIMITS
999
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
460✔
1000
        && getuid()) {
118✔
1001
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
1002
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
1003
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
1004
        exit(1);
6✔
1005
    }
1006
#endif
1007

1008
    if (ping_data_size > MAX_PING_DATA) {
454✔
1009
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1010
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1011
        exit(1);
3✔
1012
    }
1013

1014
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
451✔
1015
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1016
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1017
        exit(1);
6✔
1018
    }
1019

1020
    if (icmp_request_typ == 13 && size_flag != 0) {
445✔
1021
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1022
        exit(1);
9✔
1023
    }
1024

1025
    if (count_flag) {
436✔
1026
        if (verbose_flag)
100✔
1027
            per_recv_flag = 1;
59✔
1028

1029
        alive_flag = unreachable_flag = verbose_flag = 0;
100✔
1030
    }
28✔
1031

1032
    if (loop_flag) {
436✔
1033
        if (!report_interval)
×
1034
            per_recv_flag = 1;
×
1035

1036
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1037
    }
1038

1039
    if (alive_flag || unreachable_flag || min_reachable)
436✔
1040
        verbose_flag = 0;
37✔
1041

1042
    trials = (count > retry + 1) ? count : retry + 1;
452✔
1043

1044
    /* auto-tune default timeout for count/loop modes
1045
     * see also github #32 */
1046
    if (loop_flag || count_flag) {
452✔
1047
        if (!timeout_flag) {
100✔
1048
            timeout = perhost_interval;
95✔
1049
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
95✔
1050
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1051
            }
1052
        }
27✔
1053
    }
28✔
1054

1055
#if defined(DEBUG) || defined(_DEBUG)
1056
    if (debugging & DBG_TRACE)
1057
        trace_flag = 1;
1058

1059
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1060
        randomly_lose_flag = 1;
1061
        lose_factor = 1; /* ie, 1/4 */
1062
    }
1063

1064
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1065
        randomly_lose_flag = 1;
1066
        lose_factor = 5; /* ie, 3/4 */
1067
    }
1068

1069
    if (debugging & DBG_PRINT_PER_SYSTEM)
1070
        print_per_system_flag = 1;
1071

1072
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1073
        report_all_rtts_flag = 1;
1074

1075
    if (trace_flag) {
1076
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1077
            prog, count, retry, interval / 1e6);
1078
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1079
            perhost_interval / 1e6, timeout / 1e6);
1080
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1081
            ping_data_size, trials);
1082

1083
        if (verbose_flag)
1084
            fprintf(stderr, "  verbose_flag set\n");
1085
        if (multif_flag)
1086
            fprintf(stderr, "  multif_flag set\n");
1087
        if (name_flag)
1088
            fprintf(stderr, "  name_flag set\n");
1089
        if (addr_flag)
1090
            fprintf(stderr, "  addr_flag set\n");
1091
        if (stats_flag)
1092
            fprintf(stderr, "  stats_flag set\n");
1093
        if (unreachable_flag)
1094
            fprintf(stderr, "  unreachable_flag set\n");
1095
        if (alive_flag)
1096
            fprintf(stderr, "  alive_flag set\n");
1097
        if (elapsed_flag)
1098
            fprintf(stderr, "  elapsed_flag set\n");
1099
        if (version_flag)
1100
            fprintf(stderr, "  version_flag set\n");
1101
        if (count_flag)
1102
            fprintf(stderr, "  count_flag set\n");
1103
        if (loop_flag)
1104
            fprintf(stderr, "  loop_flag set\n");
1105
        if (backoff_flag)
1106
            fprintf(stderr, "  backoff_flag set\n");
1107
        if (per_recv_flag)
1108
            fprintf(stderr, "  per_recv_flag set\n");
1109
        if (report_all_rtts_flag)
1110
            fprintf(stderr, "  report_all_rtts_flag set\n");
1111
        if (randomly_lose_flag)
1112
            fprintf(stderr, "  randomly_lose_flag set\n");
1113
        if (print_per_system_flag)
1114
            fprintf(stderr, "  print_per_system_flag set\n");
1115
        if (outage_flag)
1116
            fprintf(stderr, "  outage_flag set\n");
1117
        if (netdata_flag)
1118
            fprintf(stderr, "  netdata_flag set\n");
1119
    }
1120
#endif /* DEBUG || _DEBUG */
1121

1122
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1123
    if (ttl > 0) {
452✔
1124
        if (socket4 >= 0) {
3✔
1125
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1126
                perror("setting time to live");
×
1127
            }
1128
        }
1✔
1129
#ifdef IPV6
1130
        if (socket6 >= 0) {
3✔
1131
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1132
                perror("setting time to live");
×
1133
            }
1134
        }
1✔
1135
#endif
1136
    }
1✔
1137

1138
#if HAVE_SO_TIMESTAMPNS
1139
    {
1140
        int opt = 1;
330✔
1141
        if (socket4 >= 0) {
330✔
1142
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
330✔
1143
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1144
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1145
                }
1146
            }
1147
        }
1148
#ifdef IPV6
1149
        if (socket6 >= 0) {
330✔
1150
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
330✔
1151
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1152
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1153
                }
1154
            }
1155
        }
1156
#endif
1157
    }
1158
#endif
1159

1160
    update_current_time();
452✔
1161
    start_time = current_time_ns;
452✔
1162

1163
    /* handle host names supplied on command line or in a file */
1164
    /* if the generate_flag is on, then generate the IP list */
1165

1166
    argv = &argv[optparse_state.optind];
452✔
1167
    argc -= optparse_state.optind;
452✔
1168

1169
    /* calculate how many ping can be in-flight per host */
1170
    if (count_flag) {
452✔
1171
        event_storage_count = count;
115✔
1172
    }
43✔
1173
    else if (loop_flag) {
337✔
1174
        if (perhost_interval > timeout) {
×
1175
            event_storage_count = 1;
×
1176
        }
1177
        else {
1178
            event_storage_count = 1 + timeout / perhost_interval;
×
1179
        }
1180
    }
1181
    else {
1182
        event_storage_count = 1;
337✔
1183
    }
1184

1185
    /* file and generate are mutually exclusive */
1186
    /* file and command line are mutually exclusive */
1187
    /* generate requires command line parameters beyond the switches */
1188
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
452✔
1189
        usage(1);
19✔
1190

1191
    /* if no conditions are specified, then assume input from stdin */
1192
    if (!*argv && !filename && !generate_flag)
434✔
1193
        filename = "-";
69✔
1194

1195
    if (*argv && !generate_flag) {
434✔
1196
        while (*argv) {
512✔
1197
            add_name(*argv);
285✔
1198
            ++argv;
285✔
1199
        }
1200
    }
57✔
1201
    else if (filename) {
207✔
1202
        FILE *ping_file;
1203
        char line[MAX_TARGET_NAME_LEN + 1];
1204
        char host[MAX_TARGET_NAME_LEN + 1];
1205
        char scratch[MAX_TARGET_NAME_LEN + 1];
1206
        int skip, non_empty;
1207

1208
        if (strcmp(filename, "-") == 0)
96✔
1209
            ping_file = fdopen(0, "r");
72✔
1210
        else
1211
            ping_file = fopen(filename, "r");
24✔
1212

1213
        if (!ping_file)
96✔
1214
            errno_crash_and_burn("fopen");
3✔
1215

1216
        /*
1217
         * Read the first word of every non-comment line, skip everything else.
1218
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1219
         * character is a '#' are interpreted as comments and ignored.)
1220
        */
1221
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1222
            skip = non_empty = 0;
309✔
1223

1224
            /* skip over a prefix of the line where sscanf finds nothing */
1225
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1226
                continue;
99✔
1227
            }
1228

1229
            /* the first word of the line can indicate a comment line */
1230
            if (host[0] == '#') {
210✔
1231
                skip = 1; /* skip remainder of line */
42✔
1232
            } else {
14✔
1233
                non_empty = 1; /* we have something to add as a target name */
168✔
1234
                /*
1235
                 * We have found the start of a word.
1236
                 * This part of the line may contain all of the first word.
1237
                 */
1238
                if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
168✔
1239
                    char discard1[MAX_TARGET_NAME_LEN + 1];
1240
                    char discard2[MAX_TARGET_NAME_LEN + 1];
1241
                    if (sscanf(line, "%s%s", discard1, discard2) == 2) {
102✔
1242
                        skip = 1; /* a second word starts in this part */
6✔
1243
                    }
2✔
1244
                    if (isspace(line[sizeof(line) - 2])) {
102✔
1245
                        skip = 1; /* the first word ends in this part */
42✔
1246
                    }
14✔
1247
                }
34✔
1248
            }
1249
            /* read remainder of this input line */
1250
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
336✔
1251
                if (skip) {
150✔
1252
                    continue; /* skip rest of data in this input line */
102✔
1253
                }
1254
                if (isspace(line[0])) {
48✔
1255
                    skip = 1; /* first word ended in previous part */
6✔
1256
                    continue;
6✔
1257
                }
1258
                if ((sscanf(line, "%s", scratch) != 1) || (!*scratch)) {
42✔
1259
                    skip = 1; /* empty or blank part of line, skip the rest */
×
1260
                    continue;
×
1261
                }
1262
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
42✔
1263
                    fprintf(stderr, "%s: target name too long\n", prog);
24✔
1264
                    exit(1);
24✔
1265
                }
1266
                /* append remainder of word started in previous line part */
1267
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
18✔
1268
                /*
1269
                 * Since the "host" buffer is the same size as the "line"
1270
                 * buffer, a target name that fits into the "host" buffer
1271
                 * cannot use more than two consecutive line parts.
1272
                 * A target name that uses two consecutive line parts
1273
                 * and fits into the "host" buffer must end before the
1274
                 * end of the second "line" buffer.  Thus the rest of
1275
                 * the line can be skipped.
1276
                 */
1277
                skip = 1;
18✔
1278
            }
1279

1280
            if (non_empty)
186✔
1281
                add_name(host);
144✔
1282
        }
1283

1284
        fclose(ping_file);
70✔
1285
    }
24✔
1286
    else if (*argv && generate_flag) {
111✔
1287
        if (argc == 1) {
111✔
1288
            /* one target: we expect a cidr range (n.n.n.n/m) */
1289
            add_cidr(argv[0]);
56✔
1290
        }
10✔
1291
        else if (argc == 2) {
55✔
1292
            add_range(argv[0], argv[1]);
52✔
1293
        }
6✔
1294
        else {
1295
            usage(1);
3✔
1296
        }
1297
    }
17✔
1298
    else {
1299
        usage(1);
×
1300
    }
1301

1302
    if (!num_hosts) {
354✔
1303
        exit(num_noaddress ? 2 : 1);
33✔
1304
    }
1305

1306
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
321✔
1307
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
16✔
1308
    }
2✔
1309
#ifdef IPV6
1310
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
319✔
1311
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
14✔
1312
    }
1313
#endif
1314

1315
    /* allocate and initialize array to map host nr to host_entry */
1316
    {
1317
        struct event *cursor = event_queue_ping.first;
317✔
1318
        int i = 0;
317✔
1319
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
317✔
1320
        if (!table)
317✔
1321
            crash_and_burn("Can't malloc array of hosts");
×
1322
        /* initialize table of hosts. we know that we have ping events scheduled
1323
         * for each of them */
1324
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
818✔
1325
            table[i] = cursor->host;
501✔
1326
            cursor->host->i = i;
501✔
1327
            i++;
501✔
1328
        }
135✔
1329
    }
1330

1331
    init_ping_buffer_ipv4(ping_data_size);
317✔
1332
#ifdef IPV6
1333
    init_ping_buffer_ipv6(ping_data_size);
317✔
1334
#endif
1335

1336
#ifdef USE_SIGACTION
1337
    memset(&act, 0, sizeof(act));
317✔
1338
    act.sa_handler = signal_handler;
317✔
1339
    sigemptyset(&act.sa_mask);
317✔
1340
    sigaddset(&act.sa_mask, SIGINT);
317✔
1341
    sigaddset(&act.sa_mask, SIGQUIT);
317✔
1342
    act.sa_flags = SA_RESTART;
317✔
1343
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
317✔
1344
        crash_and_burn("failure to set signal handler");
24✔
1345
    }
24✔
1346
#else
1347
    signal(SIGINT, signal_handler);
1348
    signal(SIGQUIT, signal_handler);
1349
#endif
1350
    setlinebuf(stdout);
293✔
1351

1352
    if (report_interval) {
293✔
1353
        next_report_time = current_time_ns + report_interval;
24✔
1354
    }
8✔
1355

1356
    last_send_time = 0;
293✔
1357

1358
    seqmap_init();
293✔
1359

1360
    /* main loop */
1361
    main_loop();
293✔
1362

1363
/* Debug: CPU Performance */
1364
#if defined(DEBUG) || defined(_DEBUG)
1365
    perf_cpu_end = clock();
1366
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1367
    printf("[DEBUG] CPU time used: %f sec", perf_cpu_time_used);
1368
#endif /* DEBUG || _DEBUG */
1369

1370
    finish();
293✔
1371

1372
    return 0;
65✔
1373
}
1374

1375
static inline int64_t timespec_ns(struct timespec *a)
5,154✔
1376
{
1377
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,154✔
1378
}
1379

1380
#if HAVE_SO_TIMESTAMPNS
1381
/* convert a struct timeval to nanoseconds */
1382
static inline int64_t timeval_ns(struct timeval *a)
1383
{
1384
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1385
}
1386
#endif /* HAVE_SO_TIMESTAMPNS */
1387

1388
void add_cidr(char *addr)
54✔
1389
{
1390
    char *addr_end;
1391
    char *mask_str;
1392
    unsigned long mask;
1393
    int ret;
1394
    struct addrinfo addr_hints;
1395
    struct addrinfo *addr_res;
1396
    unsigned long net_addr;
1397
#ifdef IPV6
1398
    uint64_t net_upper, net_lower;
1399
    char *scope_str;
1400
#endif /* IPV6 */
1401

1402
    /* Split address from mask */
1403
    addr_end = strrchr(addr, '/');
54✔
1404
    if (addr_end == NULL) {
54✔
1405
        usage(1);
3✔
1406
    }
1✔
1407
    mask_str = addr_end + 1;
52✔
1408

1409
#ifdef IPV6
1410
    /* IPv6 addresses can have a scope */
1411
    scope_str = strchr(addr, '%');
52✔
1412
    if (scope_str && mask_str < scope_str) {
52✔
1413
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1414
        exit(1);
2✔
1415
    }
1416
#endif /*IPV6 */
1417

1418
    *addr_end = '\0';
50✔
1419
    mask = atoi(mask_str);
50✔
1420

1421
    /* parse address */
1422
    memset(&addr_hints, 0, sizeof(struct addrinfo));
50✔
1423
    addr_hints.ai_family = hints_ai_family;
50✔
1424
    addr_hints.ai_flags = AI_NUMERICHOST;
50✔
1425
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
50✔
1426
    if (ret) {
50✔
1427
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1428
        exit(1);
7✔
1429
    }
1430
    if (addr_res->ai_family == AF_INET) {
43✔
1431
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
21✔
1432
        freeaddrinfo(addr_res);
21✔
1433
        add_cidr_ipv4(net_addr, mask);
21✔
1434
#ifdef IPV6
1435
    } else if (addr_res->ai_family == AF_INET6) {
29✔
1436
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1437
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1438
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1439
        freeaddrinfo(addr_res);
22✔
1440
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1441
#endif /* IPV6 */
1442
    } else {
1443
        freeaddrinfo(addr_res);
×
1444
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1445
        exit(1);
×
1446
    }
1447
}
27✔
1448

1449
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
21✔
1450
{
1451
    unsigned long bitmask;
1452
    unsigned long net_last;
1453

1454
    /* check mask */
1455
    if (mask < 1 || mask > 32) {
21✔
1456
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1457
        exit(1);
6✔
1458
    }
1459

1460
    /* convert mask integer from 1 to 32 to a bitmask */
1461
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
15✔
1462

1463
    /* calculate network range */
1464
    net_addr &= bitmask;
15✔
1465
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
15✔
1466

1467
    /* exclude network and broadcast address for regular prefixes */
1468
    if (mask < 31) {
15✔
1469
        net_last--;
9✔
1470
        net_addr++;
9✔
1471
    }
3✔
1472

1473
    /* add all hosts in that network (net_addr and net_last inclusive) */
1474
    add_addr_range_ipv4(net_addr, net_last);
15✔
1475
}
13✔
1476

1477
#ifdef IPV6
1478
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1479
{
1480
    uint64_t bitmask_lower;
1481
    uint64_t last_lower;
1482

1483
    /* check mask -- 2^63 addresses should suffice for now */
1484
    if (mask < 65 || mask > 128) {
23✔
1485
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1486
        exit(1);
7✔
1487
    }
1488

1489
    /* convert mask integer from 65 to 128 to the lower part of a bitmask */
1490
    bitmask_lower = ((uint64_t)-1) << (128 - mask);
16✔
1491

1492
    /* calculate network range */
1493
    net_lower &= bitmask_lower;
16✔
1494
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1495

1496
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1497
}
12✔
1498
#endif /* IPV6 */
1499

1500
void add_range(char *start, char *end)
52✔
1501
{
1502
    struct addrinfo addr_hints;
1503
    struct addrinfo *addr_res;
1504
    unsigned long start_long;
1505
    unsigned long end_long;
1506
    int ret;
1507
#ifdef IPV6
1508
    uint64_t start_upper, start_lower;
1509
    uint64_t end_upper, end_lower;
1510
    char *start_scope_str, *end_scope_str;
1511

1512
    /*
1513
     * The compiler does not know that setting the address family hint to
1514
     * ensure that start and end are from the same address family also
1515
     * ensures that either start_long and end_long are initialized and used,
1516
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1517
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1518
     * supported to suppress compiler warnings.
1519
     */
1520
    start_long = -1;
52✔
1521
    end_long = 0;
52✔
1522
    start_upper = start_lower = -1;
52✔
1523
    end_upper = end_lower = 0;
52✔
1524
    start_scope_str = end_scope_str = NULL;
52✔
1525
#endif /* IPV6 */
1526

1527
    /* parse start address */
1528
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1529
    addr_hints.ai_family = hints_ai_family;
52✔
1530
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1531
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1532
    if (ret) {
52✔
1533
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1534
        exit(1);
9✔
1535
    }
1536
    /* start and end must be from the same address family */
1537
    hints_ai_family = addr_res->ai_family;
43✔
1538
    if (addr_res->ai_family == AF_INET) {
43✔
1539
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1540
        freeaddrinfo(addr_res);
17✔
1541
#ifdef IPV6
1542
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1543
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1544
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1545
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1546
        freeaddrinfo(addr_res);
26✔
1547
#endif /* IPV6 */
1548
    } else {
1549
        freeaddrinfo(addr_res);
×
1550
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1551
        exit(1);
×
1552
    }
1553

1554
    /* IPv6 addresses can have a scope */
1555
    if (hints_ai_family == AF_INET6) {
43✔
1556
        start_scope_str = strchr(start, '%');
26✔
1557
        end_scope_str = strchr(end, '%');
26✔
1558
        if ((!start_scope_str && end_scope_str) ||
26✔
1559
            (start_scope_str && !end_scope_str) ||
24✔
1560
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1561
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1562
                exit(1);
6✔
1563
        }
1564
    }
1565

1566
    /* parse end address */
1567
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1568
    addr_hints.ai_family = hints_ai_family;
37✔
1569
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1570
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1571
    if (ret) {
37✔
1572
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1573
        exit(1);
7✔
1574
    }
1575
    if (addr_res->ai_family == AF_INET) {
30✔
1576
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1577
        freeaddrinfo(addr_res);
12✔
1578
        add_addr_range_ipv4(start_long, end_long);
12✔
1579
#ifdef IPV6
1580
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1581
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1582
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1583
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1584
        freeaddrinfo(addr_res);
18✔
1585
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1586
#endif /* IPV6 */
1587
    } else {
1588
        freeaddrinfo(addr_res);
×
1589
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1590
        exit(1);
×
1591
    }
1592
}
24✔
1593

1594
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
27✔
1595
{
1596
    /* check if generator limit is exceeded */
1597
    if (end_long >= start_long + MAX_GENERATE) {
27✔
1598
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1599
        exit(1);
6✔
1600
    }
1601

1602
    /* generate */
1603
    for (; start_long <= end_long; start_long++) {
84✔
1604
        struct in_addr in_addr_tmp;
1605
        char buffer[20];
1606
        in_addr_tmp.s_addr = htonl(start_long);
63✔
1607
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
63✔
1608
        add_name(buffer);
63✔
1609
    }
21✔
1610
}
21✔
1611

1612
#ifdef IPV6
1613
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1614
{
1615
    int i;
1616
    uint64_t ret = 0;
134✔
1617
    for (i = 0; i < 8; i++) {
1,206✔
1618
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1619
    }
16✔
1620
    return ret;
134✔
1621
}
1622

1623
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1624
{
1625
    int i;
1626
    for (i = 0; i < 8; i++) {
720✔
1627
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1628
    }
1629
}
80✔
1630

1631
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1632
                         uint64_t end_upper, uint64_t end_lower,
1633
                         const char *scope_str)
1634
{
1635
    struct in6_addr in6_addr_tmp;
1636
    char buffer[100];
1637

1638
    /* prevent generating too many addresses */
1639
    if ((start_upper + 1 < end_upper) ||
34✔
1640
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1641
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1642
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1643
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1644
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1645
        exit(1);
8✔
1646
    }
1647

1648
    while ((start_upper < end_upper) ||
66✔
1649
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1650
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1651
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1652
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1653
        if (scope_str) {
40✔
1654
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1655
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1656
                exit(1);
×
1657
            }
1658
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1659
        }
1660
        add_name(buffer);
40✔
1661
        start_lower++;
40✔
1662
        if (start_lower == 0) {
40✔
1663
            start_upper++;
2✔
1664
        }
1665
    }
1666
}
26✔
1667
#endif /* IPv6 */
1668

1669
void main_loop()
305✔
1670
{
1671
    int64_t lt;
1672
    int64_t wait_time_ns;
1673
    struct event *event;
1674
    struct host_entry *h;
1675

1676
    while (event_queue_ping.first || event_queue_timeout.first) {
1,725✔
1677
        dbg_printf("%s", "# main_loop\n");
1678

1679
        /* timeout event ? */
1680
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,435✔
1681
            event = ev_dequeue(&event_queue_timeout);
95✔
1682
            h = event->host;
95✔
1683

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

1686
            stats_add(h, event->ping_index, 0, -1);
95✔
1687

1688
            if (per_recv_flag) {
95✔
1689
                if (timestamp_flag) {
19✔
1690
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1691
                }
1692
                printf("%-*s : [%d], timed out",
19✔
1693
                    max_hostname_len, h->host, event->ping_index);
5✔
1694
                if (h->num_recv > 0) {
19✔
1695
                    printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1696
                }
1697
                else {
1698
                    printf(" (NaN avg, ");
19✔
1699
                }
1700
                if (h->num_recv <= h->num_sent) {
19✔
1701
                    printf("%d%% loss)",
19✔
1702
                        ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1703
                }
5✔
1704
                else {
1705
                    printf("%d%% return)",
×
1706
                        (h->num_recv_total * 100) / h->num_sent);
×
1707
                }
1708
                printf("\n");
19✔
1709
            }
5✔
1710

1711
            /* do we need to send a retry? */
1712
            if (!loop_flag && !count_flag) {
95✔
1713
                if (h->num_sent < retry + 1) {
52✔
1714
                    if (backoff_flag) {
30✔
1715
                        h->timeout *= backoff;
30✔
1716
                    }
6✔
1717
                    send_ping(h, event->ping_index);
30✔
1718
                }
6✔
1719
            }
10✔
1720

1721
            /* note: we process first timeout events, because we might need to
1722
             * wait to process ping events, while we for sure never need to
1723
             * wait for timeout events.
1724
             */
1725
            continue;
95✔
1726
        }
1727

1728
        /* ping event ? */
1729
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,571✔
1730
            /* Make sure that we don't ping more than once every "interval" */
1731
            lt = current_time_ns - last_send_time;
1,057✔
1732
            if (lt < interval)
1,057✔
1733
                goto wait_for_reply;
256✔
1734

1735
            /* Dequeue the event */
1736
            event = ev_dequeue(&event_queue_ping);
801✔
1737
            h = event->host;
801✔
1738

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

1741
            /* Send the ping */
1742
            send_ping(h, event->ping_index);
801✔
1743

1744
            /* Loop and count mode: schedule next ping */
1745
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
801✔
1746
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
315✔
1747
            }
101✔
1748
        }
231✔
1749

1750
    wait_for_reply:
570✔
1751

1752
        /* When is the next ping next event? */
1753
        wait_time_ns = -1;
1,340✔
1754
        if (event_queue_ping.first) {
1,340✔
1755
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
977✔
1756
            if (wait_time_ns < 0)
977✔
1757
                wait_time_ns = 0;
541✔
1758
            /* make sure that we wait enough, so that the inter-ping delay is
1759
             * bigger than 'interval' */
1760
            if (wait_time_ns < interval) {
977✔
1761
                lt = current_time_ns - last_send_time;
547✔
1762
                if (lt < interval) {
547✔
1763
                    wait_time_ns = interval - lt;
547✔
1764
                }
199✔
1765
            }
199✔
1766

1767
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1768
        }
307✔
1769

1770
        /* When is the next timeout event? */
1771
        if (event_queue_timeout.first) {
1,340✔
1772
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
846✔
1773
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
846✔
1774
                wait_time_ns = wait_time_timeout;
362✔
1775
                if (wait_time_ns < 0) {
362✔
1776
                    wait_time_ns = 0;
×
1777
                }
1778
            }
90✔
1779

1780
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1781
        }
246✔
1782

1783
        /* When is the next report due? */
1784
        if (report_interval && (loop_flag || count_flag)) {
1,340✔
1785
            int64_t wait_time_next_report = next_report_time - current_time_ns;
204✔
1786
            if (wait_time_next_report < wait_time_ns) {
204✔
1787
                wait_time_ns = wait_time_next_report;
69✔
1788
                if (wait_time_ns < 0) {
69✔
1789
                    wait_time_ns = 0;
×
1790
                }
1791
            }
23✔
1792

1793
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1794
        }
66✔
1795

1796
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1797
        if (wait_time_ns == -1) {
1,340✔
1798
            break;
12✔
1799
        }
1800

1801
        /* end of loop was requested by interrupt signal handler */
1802
        if (finish_requested) {
1,328✔
1803
            break;
3✔
1804
        }
1805

1806
        /* Receive replies */
1807
        /* (this is what sleeps during each loop iteration) */
1808
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1809
        if (wait_for_reply(wait_time_ns)) {
1,325✔
1810
            while (wait_for_reply(0))
1,223✔
1811
                ; /* process other replies in the queue */
1812
        }
214✔
1813

1814
        update_current_time();
1,325✔
1815

1816
        if (status_snapshot) {
1,325✔
1817
            status_snapshot = 0;
×
1818
            print_per_system_splits();
×
1819
        }
1820

1821
        /* Print report */
1822
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,325✔
1823
            if (netdata_flag)
42✔
1824
                print_netdata();
3✔
1825
            else
1826
                print_per_system_splits();
39✔
1827

1828
            while (current_time_ns >= next_report_time) {
84✔
1829
                next_report_time += report_interval;
42✔
1830
            }
1831
        }
14✔
1832
    }
1833
}
305✔
1834

1835
/************************************************************
1836

1837
  Function: signal_handler
1838

1839
*************************************************************
1840

1841
  Inputs:  int signum
1842

1843
  Description:
1844

1845
  SIGQUIT signal handler - set flag and return
1846
  SIGINT signal handler - set flag and return
1847

1848
************************************************************/
1849

1850
void signal_handler(int signum)
×
1851
{
1852
    switch (signum) {
×
1853
    case SIGINT:
1854
        finish_requested = 1;
×
1855
        break;
×
1856

1857
    case SIGQUIT:
1858
        status_snapshot = 1;
×
1859
        break;
×
1860
    }
1861
}
×
1862

1863
/************************************************************
1864

1865
  Function: update_current_time
1866

1867
*************************************************************/
1868

1869
void update_current_time()
4,160✔
1870
{
1871
    clock_gettime(CLOCKID, &current_time);
4,160✔
1872
    current_time_ns = timespec_ns(&current_time);
4,160✔
1873
}
4,160✔
1874

1875
/************************************************************
1876

1877
  Function: finish
1878

1879
*************************************************************
1880

1881
  Inputs:  void (none)
1882

1883
  Description:
1884

1885
  Main program clean up and exit point
1886

1887
************************************************************/
1888

1889
void finish()
305✔
1890
{
1891
    int i;
1892
    HOST_ENTRY *h;
1893

1894
    update_current_time();
305✔
1895
    end_time = current_time_ns;
305✔
1896

1897
    /* tot up unreachables */
1898
    for (i = 0; i < num_hosts; i++) {
806✔
1899
        h = table[i];
501✔
1900

1901
        if (!h->num_recv) {
501✔
1902
            num_unreachable++;
78✔
1903

1904
            if (verbose_flag || unreachable_flag) {
78✔
1905
                printf("%s", h->host);
42✔
1906

1907
                if (verbose_flag)
42✔
1908
                    printf(" is unreachable");
39✔
1909

1910
                printf("\n");
42✔
1911
            }
2✔
1912
        }
12✔
1913
    }
135✔
1914

1915
    if (count_flag || loop_flag)
305✔
1916
        print_per_system_stats();
100✔
1917
#if defined(DEBUG) || defined(_DEBUG)
1918
    else if (print_per_system_flag)
1919
        print_per_system_stats();
1920
#endif /* DEBUG || _DEBUG */
1921

1922
    if (stats_flag)
305✔
1923
        print_global_stats();
9✔
1924

1925
    if (min_reachable) {
305✔
1926
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
1927
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1928
            exit(0);
6✔
1929
        }
1930
        else {
1931
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
1932
            exit(1);
3✔
1933
        }
1934
    }
1935

1936
    if (num_noaddress)
296✔
1937
        exit(2);
3✔
1938
    else if (num_alive != num_hosts)
293✔
1939
        exit(1);
39✔
1940

1941
    exit(0);
254✔
1942
}
1943

1944
/************************************************************
1945

1946
  Function: print_per_system_stats
1947

1948
*************************************************************
1949

1950
  Inputs:  void (none)
1951

1952
  Description:
1953

1954

1955
************************************************************/
1956

1957
void print_per_system_stats(void)
100✔
1958
{
1959
    int i, j, avg, outage_ms;
1960
    HOST_ENTRY *h;
1961
    int64_t resp;
1962

1963
    if (verbose_flag || per_recv_flag)
100✔
1964
        fprintf(stderr, "\n");
59✔
1965

1966
    for (i = 0; i < num_hosts; i++) {
234✔
1967
        h = table[i];
134✔
1968
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
1969

1970
        if (report_all_rtts_flag) {
134✔
1971
            for (j = 0; j < h->num_sent; j++) {
248✔
1972
                if ((resp = h->resp_times[j]) >= 0)
204✔
1973
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
1974
                else
1975
                    fprintf(stderr, " -");
8✔
1976
            }
64✔
1977

1978
            fprintf(stderr, "\n");
44✔
1979
        }
12✔
1980
        else {
1981
            if (h->num_recv <= h->num_sent) {
90✔
1982
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
1983
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
1984

1985
                if (outage_flag) {
90✔
1986
                    /* Time outage total */
1987
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
1988
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
1989
                }
3✔
1990
            }
26✔
1991
            else {
1992
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
1993
                    h->num_sent, h->num_recv,
1994
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
1995
            }
1996

1997
            if (h->num_recv) {
90✔
1998
                avg = h->total_time / h->num_recv;
79✔
1999
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
2000
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
2001
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
2002
            }
23✔
2003

2004
            fprintf(stderr, "\n");
90✔
2005
        }
2006
    }
38✔
2007
}
100✔
2008

2009
/************************************************************
2010

2011
  Function: print_netdata
2012

2013
*************************************************************
2014

2015
  Inputs:  void (none)
2016

2017
  Description:
2018

2019

2020
************************************************************/
2021

2022
void print_netdata(void)
3✔
2023
{
2024
    static int sent_charts = 0;
2025

2026
    int i;
2027
    int64_t avg;
2028
    HOST_ENTRY *h;
2029

2030
    for (i = 0; i < num_hosts; i++) {
6✔
2031
        h = table[i];
3✔
2032

2033
        if (!sent_charts) {
3✔
2034
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2035
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2036
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2037
        }
1✔
2038

2039
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2040
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2041
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2042
        printf("END\n");
3✔
2043

2044
        if (!sent_charts) {
3✔
2045
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2046
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2047
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2048
        }
1✔
2049

2050
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2051
        /*
2052
        if( h->num_recv_i <= h->num_sent_i )
2053
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2054
        else
2055
            printf("SET lost = 0\n");
2056
*/
2057

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

2061
        if (!sent_charts) {
3✔
2062
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2063
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2064
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2065
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2066
        }
1✔
2067

2068
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2069
        if (h->num_recv_i) {
3✔
2070
            avg = h->total_time_i / h->num_recv_i;
3✔
2071
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2072
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2073
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2074
        }
1✔
2075
        printf("END\n");
3✔
2076

2077
        stats_reset_interval(h);
3✔
2078
    }
1✔
2079

2080
    sent_charts = 1;
3✔
2081
}
3✔
2082

2083
/************************************************************
2084

2085
  Function: print_per_system_splits
2086

2087
*************************************************************
2088

2089
  Inputs:  void (none)
2090

2091
  Description:
2092

2093

2094
************************************************************/
2095

2096
void print_per_system_splits(void)
39✔
2097
{
2098
    int i, avg, outage_ms_i;
2099
    HOST_ENTRY *h;
2100
    struct tm *curr_tm;
2101

2102
    if (verbose_flag || per_recv_flag)
39✔
2103
        fprintf(stderr, "\n");
×
2104

2105
    update_current_time();
39✔
2106
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2107
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2108
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2109

2110
    for (i = 0; i < num_hosts; i++) {
78✔
2111
        h = table[i];
39✔
2112
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2113

2114
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2115
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2116
                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);
39✔
2117

2118
            if (outage_flag) {
39✔
2119
                /* Time outage  */
2120
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2121
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2122
            }
4✔
2123
        }
13✔
2124
        else {
2125
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2126
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2127
        }
2128

2129
        if (h->num_recv_i) {
39✔
2130
            avg = h->total_time_i / h->num_recv_i;
27✔
2131
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2132
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2133
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2134
        }
9✔
2135

2136
        fprintf(stderr, "\n");
39✔
2137
        if (!cumulative_stats_flag) {
39✔
2138
            stats_reset_interval(h);
27✔
2139
        }
9✔
2140
    }
13✔
2141
}
39✔
2142

2143
/************************************************************
2144

2145
  Function: print_global_stats
2146

2147
*************************************************************
2148

2149
  Inputs:  void (none)
2150

2151
  Description:
2152

2153

2154
************************************************************/
2155

2156
void print_global_stats(void)
9✔
2157
{
2158
    fprintf(stderr, "\n");
9✔
2159
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2160
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2161
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2162
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2163
    fprintf(stderr, "\n");
9✔
2164
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2165
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2166
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2167
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2168
    fprintf(stderr, "\n");
9✔
2169

2170
    if (total_replies == 0) {
9✔
2171
        min_reply = 0;
3✔
2172
        max_reply = 0;
3✔
2173
        total_replies = 1;
3✔
2174
        sum_replies = 0;
3✔
2175
    }
1✔
2176

2177
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2178
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2179
        sprint_tm(sum_replies / total_replies));
3✔
2180
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2181
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2182
        (end_time - start_time) / 1e9);
9✔
2183
    fprintf(stderr, "\n");
9✔
2184
}
9✔
2185

2186
/************************************************************
2187

2188
  Function: send_ping
2189

2190
*************************************************************
2191

2192
  Inputs:  int s, HOST_ENTRY *h
2193

2194
  Description:
2195

2196
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2197
  will be added on by the kernel.  The ID field is our UNIX process ID,
2198
  and the sequence number is an index into an array of outstanding
2199
  ping requests. The sequence number will later be used to quickly
2200
  figure out who the ping reply came from.
2201

2202
************************************************************/
2203

2204
int send_ping(HOST_ENTRY *h, int index)
831✔
2205
{
2206
    int n;
2207
    int myseq;
2208
    int ret = 1;
831✔
2209
    uint8_t proto = ICMP_ECHO;
831✔
2210

2211
    update_current_time();
831✔
2212
    h->last_send_time = current_time_ns;
831✔
2213
    myseq = seqmap_add(h->i, index, current_time_ns);
831✔
2214

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

2217
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
831✔
2218
        if(icmp_request_typ == 13)
710✔
2219
            proto = ICMP_TSTAMP;
14✔
2220
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
710✔
2221
    }
222✔
2222
#ifdef IPV6
2223
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
121✔
2224
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
121✔
2225
    }
15✔
2226
#endif
2227
    else {
2228
        return 0;
×
2229
    }
2230

2231
    /* error sending? */
2232
    if (
594✔
2233
        (n < 0)
237✔
2234
#if defined(EHOSTDOWN)
2235
        && errno != EHOSTDOWN
269✔
2236
#endif
2237
    ) {
2238
        if (verbose_flag) {
32✔
2239
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
26✔
2240
        }
2241
        else {
2242
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2243
        }
2244

2245
        h->num_sent++;
32✔
2246
        h->num_sent_i++;
32✔
2247
        if (!loop_flag)
32✔
2248
            h->resp_times[index] = RESP_ERROR;
32✔
2249

2250
        ret = 0;
32✔
2251
    }
2252
    else {
2253
        /* schedule timeout */
2254
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
799✔
2255

2256
        /* mark this trial as outstanding */
2257
        if (!loop_flag) {
799✔
2258
            h->resp_times[index] = RESP_WAITING;
799✔
2259
        }
237✔
2260
    }
2261

2262
    num_pingsent++;
831✔
2263
    last_send_time = h->last_send_time;
831✔
2264

2265
    return (ret);
831✔
2266
}
237✔
2267

2268
int socket_can_read(struct timeval *timeout)
2,548✔
2269
{
2270
    int nfound;
2271
    fd_set readset;
2272
    int socketmax;
2273

2274
#ifndef IPV6
2275
    socketmax = socket4;
2276
#else
2277
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,548✔
2278
#endif
2279

2280
select_again:
1,926✔
2281
    FD_ZERO(&readset);
17,956✔
2282
    if (socket4 >= 0)
2,548✔
2283
        FD_SET(socket4, &readset);
2,548✔
2284
#ifdef IPV6
2285
    if (socket6 >= 0)
2,548✔
2286
        FD_SET(socket6, &readset);
2,548✔
2287
#endif
2288

2289
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,548✔
2290
    if (nfound < 0) {
2,548✔
2291
        if (errno == EINTR) {
×
2292
            /* interrupted system call: redo the select */
2293
            goto select_again;
×
2294
        }
2295
        else {
2296
            perror("select");
×
2297
        }
2298
    }
2299

2300
    if (nfound > 0) {
2,548✔
2301
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,223✔
2302
            return socket4;
1,047✔
2303
        }
2304
#ifdef IPV6
2305
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
176✔
2306
            return socket6;
176✔
2307
        }
2308
#endif
2309
    }
2310

2311
    return -1;
1,325✔
2312
}
622✔
2313

2314
int receive_packet(int64_t wait_time,
2,548✔
2315
#if HAVE_SO_TIMESTAMPNS
2316
    int64_t *reply_timestamp,
2317
#else
2318
    int64_t *reply_timestamp __attribute__((unused)),
2319
#endif
2320
    struct sockaddr *reply_src_addr,
2321
    size_t reply_src_addr_len,
2322
    char *reply_buf,
2323
    size_t reply_buf_len,
2324
    int *ip_header_tos,
2325
    int *ip_header_ttl)
2326
{
2327
    struct timeval to;
2328
    int s = 0;
2,548✔
2329
    int recv_len;
2330
    static unsigned char msg_control[128];
2331
    struct iovec msg_iov = {
3,792✔
2332
        reply_buf,
622✔
2333
        reply_buf_len
622✔
2334
    };
2335
    struct msghdr recv_msghdr = {0};
2,548✔
2336
    recv_msghdr.msg_name = reply_src_addr;
2,548✔
2337
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,548✔
2338
    recv_msghdr.msg_iov = &msg_iov;
2,548✔
2339
    recv_msghdr.msg_iovlen = 1;
2,548✔
2340
    recv_msghdr.msg_control = &msg_control;
2,548✔
2341
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,548✔
2342
    struct cmsghdr *cmsg;
2343

2344
    /* Wait for a socket to become ready */
2345
    if (wait_time) {
2,548✔
2346
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,325✔
2347
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,325✔
2348
    }
393✔
2349
    else {
2350
        to.tv_sec = 0;
1,223✔
2351
        to.tv_usec = 0;
1,223✔
2352
    }
2353
    s = socket_can_read(&to);
2,548✔
2354
    if (s == -1) {
2,548✔
2355
        return 0; /* timeout */
1,325✔
2356
    }
2357

2358
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,223✔
2359
    if (recv_len <= 0) {
1,223✔
2360
        return 0;
×
2361
    }
2362

2363
    /* ancilliary data */
2364
    {
2365
#if HAVE_SO_TIMESTAMPNS
2366
        struct timespec reply_timestamp_ts;
2367
        struct timeval reply_timestamp_tv;
2368
#endif
2369
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,726✔
2370
             cmsg != NULL;
1,229✔
2371
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,006✔
2372
#if HAVE_SO_TIMESTAMPNS
2373
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,006✔
2374
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
994✔
2375
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
994✔
2376
            }
2377
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,006✔
2378
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2379
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2380
            }
2381
#endif
2382
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,006✔
2383
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2384
            }
2385
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,006✔
2386
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2387
            }
2388
#ifdef IPV6
2389
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,006✔
2390
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
4✔
2391
            }
2392
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,006✔
2393
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
4✔
2394
            }
2395
#endif
2396
        }
2397
    }
2398

2399
#if defined(DEBUG) || defined(_DEBUG)
2400
    if (randomly_lose_flag) {
2401
        if ((random() & 0x07) <= lose_factor)
2402
            return 0;
2403
    }
2404
#endif
2405

2406
    return recv_len;
1,223✔
2407
}
622✔
2408

2409
/* stats_add: update host statistics for a single packet that was received (or timed out)
2410
 * h: host entry to update
2411
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2412
 * success: 1 if response received, 0 otherwise
2413
 * latency: response time, in ns
2414
 */
2415
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
799✔
2416
{
2417
    /* sent count - we update only on receive/timeout, so that we don't get
2418
     * weird loss percentage, just because a packet was note recived yet.
2419
     */
2420
    h->num_sent++;
799✔
2421
    h->num_sent_i++;
799✔
2422

2423
    if (!success) {
799✔
2424
        if (!loop_flag && index >= 0) {
95✔
2425
            h->resp_times[index] = RESP_TIMEOUT;
95✔
2426
        }
23✔
2427
        num_timeout++;
95✔
2428
        return;
95✔
2429
    }
2430

2431
    /* received count */
2432
    h->num_recv++;
704✔
2433
    h->num_recv_i++;
704✔
2434

2435
    /* maximum */
2436
    if (!h->max_reply || latency > h->max_reply) {
704✔
2437
        h->max_reply = latency;
540✔
2438
    }
157✔
2439
    if (!h->max_reply_i || latency > h->max_reply_i) {
704✔
2440
        h->max_reply_i = latency;
554✔
2441
    }
163✔
2442

2443
    /* minimum */
2444
    if (!h->min_reply || latency < h->min_reply) {
704✔
2445
        h->min_reply = latency;
469✔
2446
    }
138✔
2447
    if (!h->min_reply_i || latency < h->min_reply_i) {
704✔
2448
        h->min_reply_i = latency;
494✔
2449
    }
147✔
2450

2451
    /* total time (for average) */
2452
    h->total_time += latency;
704✔
2453
    h->total_time_i += latency;
704✔
2454

2455
    /* response time per-packet (count mode) */
2456
    if (!loop_flag && index >= 0) {
704✔
2457
        h->resp_times[index] = latency;
704✔
2458
    }
214✔
2459
}
237✔
2460

2461
/* stats_reset_interval: reset interval statistics
2462
 * h: host entry to update
2463
 */
2464
void stats_reset_interval(HOST_ENTRY *h)
30✔
2465
{
2466
    h->num_sent_i = 0;
30✔
2467
    h->num_recv_i = 0;
30✔
2468
    h->max_reply_i = 0;
30✔
2469
    h->min_reply_i = 0;
30✔
2470
    h->total_time_i = 0;
30✔
2471
}
30✔
2472

2473
int decode_icmp_ipv4(
1,047✔
2474
    struct sockaddr *response_addr,
2475
    size_t response_addr_len,
2476
    char *reply_buf,
2477
    size_t reply_buf_len,
2478
    unsigned short *id,
2479
    unsigned short *seq,
2480
    int *ip_header_tos,
2481
    int *ip_header_ttl,
2482
    uint32_t *ip_header_otime_ms,
2483
    uint32_t *ip_header_rtime_ms,
2484
    uint32_t *ip_header_ttime_ms)
2485
{
2486
    struct icmp *icp;
2487
    int hlen = 0;
1,047✔
2488

2489
    if (!using_sock_dgram4) {
1,047✔
2490
        struct ip *ip = (struct ip *)reply_buf;
1,039✔
2491
        *ip_header_tos = ip->ip_tos;
1,039✔
2492
        *ip_header_ttl = ip->ip_ttl;
1,039✔
2493

2494
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2495
        /* The alpha headers are decidedly broken.
2496
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2497
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2498
         */
2499
        hlen = (ip->ip_vhl & 0x0F) << 2;
2500
#else
2501
        hlen = ip->ip_hl << 2;
1,039✔
2502
#endif
2503
    }
199✔
2504

2505
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,047✔
2506
        /* too short */
2507
        if (verbose_flag) {
×
2508
            char buf[INET6_ADDRSTRLEN];
2509
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2510
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2511
        }
2512
        return -1;
×
2513
    }
2514

2515
    icp = (struct icmp *)(reply_buf + hlen);
1,047✔
2516

2517
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,047✔
2518
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
442✔
2519
        /* Handle other ICMP packets */
2520
        struct icmp *sent_icmp;
2521
        SEQMAP_VALUE *seqmap_value;
2522
        char addr_ascii[INET6_ADDRSTRLEN];
2523
        HOST_ENTRY *h;
2524

2525
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2526
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
818✔
2527
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2528
            return -1;
×
2529
        }
2530

2531
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
420✔
2532

2533
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
420✔
2534
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
14✔
2535
            sent_icmp->icmp_id != ident4) {
×
2536
            /* not caused by us */
2537
            return -1;
420✔
2538
        }
2539

2540
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2541
        if (seqmap_value == NULL) {
×
2542
            return -1;
×
2543
        }
2544

2545
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2546

2547
        switch (icp->icmp_type) {
×
2548
        case ICMP_UNREACH:
2549
            h = table[seqmap_value->host_nr];
×
2550
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2551
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2552
                    addr_ascii, h->host);
2553
            }
2554
            else {
2555
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2556
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2557
            }
2558

2559
            print_warning("\n");
×
2560
            num_othericmprcvd++;
×
2561
            break;
×
2562

2563
        case ICMP_SOURCEQUENCH:
2564
        case ICMP_REDIRECT:
2565
        case ICMP_TIMXCEED:
2566
        case ICMP_PARAMPROB:
2567
            h = table[seqmap_value->host_nr];
×
2568
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2569
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2570
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2571
            }
2572
            else {
2573
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2574
                    icp->icmp_type, addr_ascii, h->host);
×
2575
            }
2576
            print_warning("\n");
×
2577
            num_othericmprcvd++;
×
2578
            break;
×
2579
        }
2580

2581
        return -1;
×
2582
    }
2583

2584
    *id = icp->icmp_id;
627✔
2585
    *seq = ntohs(icp->icmp_seq);
627✔
2586
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
627✔
2587

2588
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2589
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
14✔
2590
            if (verbose_flag) {
×
2591
                char buf[INET6_ADDRSTRLEN];
2592
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2593
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2594
            }
2595
            return -1;
×
2596
        }
2597

2598
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
14✔
2599
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
14✔
2600
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
14✔
2601
    }
2602

2603
    return hlen;
627✔
2604
}
199✔
2605

2606
#ifdef IPV6
2607
int decode_icmp_ipv6(
176✔
2608
    struct sockaddr *response_addr,
2609
    size_t response_addr_len,
2610
    char *reply_buf,
2611
    size_t reply_buf_len,
2612
    unsigned short *id,
2613
    unsigned short *seq)
2614
{
2615
    struct icmp6_hdr *icp;
2616

2617
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
176✔
2618
        if (verbose_flag) {
×
2619
            char buf[INET6_ADDRSTRLEN];
2620
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2621
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2622
        }
2623
        return 0; /* too short */
×
2624
    }
2625

2626
    icp = (struct icmp6_hdr *)reply_buf;
176✔
2627

2628
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
176✔
2629
        /* Handle other ICMP packets */
2630
        struct icmp6_hdr *sent_icmp;
2631
        SEQMAP_VALUE *seqmap_value;
2632
        char addr_ascii[INET6_ADDRSTRLEN];
2633
        HOST_ENTRY *h;
2634

2635
        /* reply icmp packet (ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2636
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
87✔
2637
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2638
            return 0;
×
2639
        }
2640

2641
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
87✔
2642

2643
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
87✔
2644
            /* not caused by us */
2645
            return 0;
87✔
2646
        }
2647

2648
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2649
        if (seqmap_value == NULL) {
×
2650
            return 0;
×
2651
        }
2652

2653
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2654

2655
        switch (icp->icmp6_type) {
×
2656
        case ICMP_UNREACH:
2657
            h = table[seqmap_value->host_nr];
×
2658
            if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
×
2659
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2660
                    addr_ascii, h->host);
2661
            }
2662
            else {
2663
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2664
                    icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2665
            }
2666

2667
            print_warning("\n");
×
2668
            num_othericmprcvd++;
×
2669
            break;
×
2670

2671
        case ICMP_SOURCEQUENCH:
2672
        case ICMP_REDIRECT:
2673
        case ICMP_TIMXCEED:
2674
        case ICMP_PARAMPROB:
2675
            h = table[seqmap_value->host_nr];
×
2676
            if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
×
2677
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2678
                    icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
×
2679
            }
2680
            else {
2681
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2682
                    icp->icmp6_type, addr_ascii, h->host);
×
2683
            }
2684
            print_warning("\n");
×
2685
            num_othericmprcvd++;
×
2686
            break;
×
2687
        }
2688

2689
        return 0;
×
2690
    }
2691

2692
    *id = icp->icmp6_id;
89✔
2693
    *seq = ntohs(icp->icmp6_seq);
89✔
2694

2695
    return 1;
89✔
2696
}
30✔
2697
#endif
2698

2699
int wait_for_reply(int64_t wait_time)
2,548✔
2700
{
2701
    int result;
2702
    static char buffer[RECV_BUFSIZE];
2703
    struct sockaddr_storage response_addr;
2704
    int n, avg;
2705
    HOST_ENTRY *h;
2706
    int64_t this_reply;
2707
    int this_count;
2708
    int64_t recv_time = 0;
2,548✔
2709
    SEQMAP_VALUE *seqmap_value;
2710
    unsigned short id;
2711
    unsigned short seq;
2712
    int ip_header_tos = -1;
2,548✔
2713
    int ip_header_ttl = -1;
2,548✔
2714
    // ICMP Timestamp
2715
    uint32_t ip_header_otime_ms = 0x80000000U;
2,548✔
2716
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,548✔
2717
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,548✔
2718

2719
    /* Receive packet */
2720
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,548✔
2721
        &recv_time, /* reply_timestamp */
2722
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2723
        sizeof(response_addr), /* reply_src_addr_len */
2724
        buffer, /* reply_buf */
2725
        sizeof(buffer), /* reply_buf_len */
2726
        &ip_header_tos, /* TOS resp. TC byte */
2727
        &ip_header_ttl /* TTL resp. hop limit */
2728
    );
2729

2730
    if (result <= 0) {
2,548✔
2731
        return 0;
1,325✔
2732
    }
2733

2734
    update_current_time();
1,223✔
2735
    if (recv_time == 0)
1,223✔
2736
        recv_time = current_time_ns;
229✔
2737

2738
    /* Process ICMP packet and retrieve id/seq */
2739
    if (response_addr.ss_family == AF_INET) {
1,223✔
2740
        int ip_hlen = decode_icmp_ipv4(
1,047✔
2741
            (struct sockaddr *)&response_addr,
2742
            sizeof(response_addr),
2743
            buffer,
2744
            sizeof(buffer),
2745
            &id,
2746
            &seq,
2747
            &ip_header_tos,
2748
            &ip_header_ttl,
2749
            &ip_header_otime_ms,
2750
            &ip_header_rtime_ms,
2751
            &ip_header_ttime_ms);
2752
        if (ip_hlen < 0) {
1,047✔
2753
            return 1;
420✔
2754
        }
2755
        if (id != ident4) {
627✔
2756
            return 1; /* packet received, but not the one we are looking for! */
×
2757
        }
2758
        if (!using_sock_dgram4) {
627✔
2759
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2760
             * with fping with IPv6 hosts */
2761
            result -= ip_hlen;
619✔
2762
        }
199✔
2763
    }
199✔
2764
#ifdef IPV6
2765
    else if (response_addr.ss_family == AF_INET6) {
176✔
2766
        if (!decode_icmp_ipv6(
176✔
2767
                (struct sockaddr *)&response_addr,
2768
                sizeof(response_addr),
2769
                buffer,
2770
                sizeof(buffer),
2771
                &id,
2772
                &seq)) {
2773
            return 1;
87✔
2774
        }
2775
        if (id != ident6) {
89✔
2776
            return 1; /* packet received, but not the one we are looking for! */
×
2777
        }
2778
    }
15✔
2779
#endif
2780
    else {
2781
        return 1;
×
2782
    }
2783

2784
    seqmap_value = seqmap_fetch(seq, current_time_ns);
716✔
2785
    if (seqmap_value == NULL) {
716✔
2786
        return 1;
×
2787
    }
2788

2789
    /* find corresponding host_entry */
2790
    n = seqmap_value->host_nr;
716✔
2791
    h = table[n];
716✔
2792
    this_count = seqmap_value->ping_count;
716✔
2793
    this_reply = recv_time - seqmap_value->ping_ts;
716✔
2794

2795
    /* update stats that include invalid replies */
2796
    h->num_recv_total++;
716✔
2797
    num_pingreceived++;
716✔
2798

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

2801
    /* optionally require reply source equal to target address */
2802
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
716✔
2803
        dbg_printf("discarding reply from wrong source address\n");
2804
        return 1;
12✔
2805
    }
2806

2807
    /* discard duplicates */
2808
    if (!loop_flag && h->resp_times[this_count] >= 0) {
704✔
2809
        if (!per_recv_flag) {
×
2810
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2811
                h->host, this_count, result, sprint_tm(this_reply));
2812

2813
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2814
                char buf[INET6_ADDRSTRLEN];
2815
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2816
                fprintf(stderr, " [<- %s]", buf);
×
2817
            }
2818
            fprintf(stderr, "\n");
×
2819
        }
2820
        return 1;
×
2821
    }
2822

2823
    /* discard reply if delay is larger than timeout
2824
     * (see also: github #32) */
2825
    if (this_reply > h->timeout) {
704✔
2826
        return 1;
×
2827
    }
2828

2829
    /* update stats */
2830
    stats_add(h, this_count, 1, this_reply);
704✔
2831
    // TODO: move to stats_add?
2832
    if (!max_reply || this_reply > max_reply)
704✔
2833
        max_reply = this_reply;
428✔
2834
    if (!min_reply || this_reply < min_reply)
704✔
2835
        min_reply = this_reply;
362✔
2836
    sum_replies += this_reply;
704✔
2837
    total_replies++;
704✔
2838

2839
    /* initialize timeout to initial timeout (without backoff) */
2840
    h->timeout = timeout;
704✔
2841

2842
    /* remove timeout event */
2843
    struct event *timeout_event = host_get_timeout_event(h, this_count);
704✔
2844
    if (timeout_event) {
704✔
2845
        ev_remove(&event_queue_timeout, timeout_event);
704✔
2846
    }
214✔
2847

2848
    /* print "is alive" */
2849
    if (h->num_recv == 1) {
704✔
2850
        num_alive++;
423✔
2851
        if (fast_reachable && num_alive >= min_reachable)
423✔
2852
                finish_requested = 1;
3✔
2853

2854
        if (verbose_flag || alive_flag) {
423✔
2855
            printf("%s", h->host);
292✔
2856

2857
            if (verbose_flag)
292✔
2858
                printf(" is alive");
272✔
2859
        }
84✔
2860
    }
123✔
2861

2862
    /* print received ping (unless --quiet) */
2863
    if (per_recv_flag) {
704✔
2864
        if (timestamp_flag) {
151✔
2865
            print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2866
        }
10✔
2867
        avg = h->total_time / h->num_recv;
151✔
2868
        printf("%-*s : [%d], %d bytes, %s ms",
151✔
2869
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2870
        printf(" (%s avg, ", sprint_tm(avg));
151✔
2871

2872
        if (h->num_recv <= h->num_sent) {
151✔
2873
            printf("%d%% loss)",
151✔
2874
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2875
        }
45✔
2876
        else {
2877
            printf("%d%% return)",
×
2878
                (h->num_recv_total * 100) / h->num_sent);
×
2879
        }
2880
    }
45✔
2881

2882
    if (verbose_flag || alive_flag || per_recv_flag) {
704✔
2883

2884
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
443✔
2885
            char buf[INET6_ADDRSTRLEN];
2886
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2887
            fprintf(stderr, " [<- %s]", buf);
6✔
2888
        }
2889

2890
        if (icmp_request_typ == 13) {
443✔
2891
            printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2892
                   alive_flag ? "" : ",",
14✔
2893
                   ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
2894
                   ms_since_midnight_utc(recv_time));
2895
        }
2896

2897
        if(print_tos_flag) {
443✔
2898
            if(ip_header_tos != -1) {
43✔
2899
                printf(" (TOS %d)", ip_header_tos);
43✔
2900
            }
11✔
2901
            else {
UNCOV
2902
                printf(" (TOS unknown)");
×
2903
            }
2904
        }
11✔
2905

2906
        if (print_ttl_flag) {
443✔
2907
          if(ip_header_ttl != -1) {
43✔
2908
              printf(" (TTL %d)", ip_header_ttl);
43✔
2909
          }
11✔
2910
          else {
UNCOV
2911
              printf(" (TTL unknown)");
×
2912
          }
2913
        }
11✔
2914

2915
        if (elapsed_flag && !per_recv_flag)
443✔
2916
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2917

2918
        printf("\n");
443✔
2919
    }
129✔
2920

2921
    return 1;
704✔
2922
}
622✔
2923

2924
/************************************************************
2925

2926
  Function: add_name
2927

2928
*************************************************************
2929

2930
  Inputs:  char* name
2931

2932
  Description:
2933

2934
  process input name for addition to target list
2935
  name can turn into multiple targets via multiple interfaces (-m)
2936
  or via NIS groups
2937

2938
************************************************************/
2939

2940
void add_name(char *name)
532✔
2941
{
2942
    struct addrinfo *res0, *res, hints;
2943
    int ret_ga;
2944
    char *printname;
2945
    char namebuf[256];
2946
    char addrbuf[256];
2947

2948
    /* getaddrinfo */
2949
    memset(&hints, 0, sizeof(struct addrinfo));
532✔
2950
    hints.ai_flags = AI_UNUSABLE;
532✔
2951
    hints.ai_socktype = SOCK_RAW;
532✔
2952
    hints.ai_family = hints_ai_family;
532✔
2953
    if (hints_ai_family == AF_INET) {
532✔
2954
        hints.ai_protocol = IPPROTO_ICMP;
77✔
2955
    }
21✔
2956
#ifdef IPV6
2957
    else if (hints_ai_family == AF_INET6) {
455✔
2958
        hints.ai_protocol = IPPROTO_ICMPV6;
29✔
2959
    }
1✔
2960
#endif
2961
    else {
2962
        hints.ai_socktype = SOCK_STREAM;
426✔
2963
        hints.ai_protocol = 0;
426✔
2964
    }
2965
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
532✔
2966
    if (ret_ga) {
532✔
2967
        if (!quiet_flag)
26✔
2968
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
2969
        num_noaddress++;
26✔
2970
        return;
26✔
2971
    }
2972

2973
    /* NOTE: we could/should loop with res on all addresses like this:
2974
     * for (res = res0; res; res = res->ai_next) {
2975
     * We don't do it yet, however, because is is an incompatible change
2976
     * (need to implement a separate option for this)
2977
     */
2978
    for (res = res0; res; res = res->ai_next) {
506✔
2979
        /* name_flag: addr -> name lookup requested) */
2980
        if (name_flag || rdns_flag) {
508✔
2981
            int do_rdns = rdns_flag ? 1 : 0;
3✔
2982
            if (name_flag) {
3✔
2983
                /* Was it a numerical address? Only then do a rdns-query */
2984
                struct addrinfo *nres;
2985
                hints.ai_flags = AI_NUMERICHOST;
×
2986
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
2987
                    do_rdns = 1;
×
2988
                    freeaddrinfo(nres);
×
2989
                }
2990
            }
2991

2992
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2993
                printname = namebuf;
3✔
2994
            }
1✔
2995
            else {
2996
                printname = name;
×
2997
            }
2998
        }
1✔
2999
        else {
3000
            printname = name;
503✔
3001
        }
3002

3003
        /* addr_flag: name -> addr lookup requested */
3004
        if (addr_flag) {
506✔
3005
            int ret;
3006
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3007
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3008
            if (ret) {
3✔
3009
                if (!quiet_flag) {
×
3010
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3011
                }
3012
                continue;
×
3013
            }
3014

3015
            if (name_flag || rdns_flag) {
3✔
3016
                char nameaddrbuf[512 + 3];
3017
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3018
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3019
            }
3020
            else {
3021
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3022
            }
3023
        }
1✔
3024
        else {
3025
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
503✔
3026
        }
3027

3028
        if (!multif_flag) {
506✔
3029
            break;
506✔
3030
        }
3031
    }
3032

3033
    freeaddrinfo(res0);
506✔
3034
}
144✔
3035

3036
/************************************************************
3037

3038
  Function: add_addr
3039

3040
*************************************************************
3041

3042
  Description:
3043

3044
  add single address to list of hosts to be pinged
3045

3046
************************************************************/
3047

3048
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
506✔
3049
{
3050
    HOST_ENTRY *p;
3051
    int n;
3052
    int64_t *i;
3053

3054
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
506✔
3055
    if (!p)
506✔
3056
        crash_and_burn("can't allocate HOST_ENTRY");
×
3057

3058
    p->name = strdup(name);
506✔
3059
    p->host = strdup(host);
506✔
3060
    memcpy(&p->saddr, ipaddr, ipaddr_len);
506✔
3061
    p->saddr_len = ipaddr_len;
506✔
3062
    p->timeout = timeout;
506✔
3063
    p->min_reply = 0;
506✔
3064

3065
    if (netdata_flag) {
506✔
3066
        char *s = p->name;
3✔
3067
        while (*s) {
30✔
3068
            if (!isalnum(*s))
27✔
3069
                *s = '_';
9✔
3070
            s++;
27✔
3071
        }
3072
    }
1✔
3073

3074
    if (strlen(p->host) > max_hostname_len)
506✔
3075
        max_hostname_len = strlen(p->host);
319✔
3076

3077
    /* array for response time results */
3078
    if (!loop_flag) {
506✔
3079
        i = (int64_t *)malloc(trials * sizeof(int64_t));
506✔
3080
        if (!i)
506✔
3081
            crash_and_burn("can't allocate resp_times array");
×
3082

3083
        for (n = 1; n < trials; n++)
2,015✔
3084
            i[n] = RESP_UNUSED;
1,509✔
3085

3086
        p->resp_times = i;
506✔
3087
    }
136✔
3088

3089
    /* allocate event storage */
3090
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
506✔
3091
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
506✔
3092

3093
    /* schedule first ping */
3094
    host_add_ping_event(p, 0, current_time_ns);
506✔
3095

3096
    num_hosts++;
506✔
3097
}
506✔
3098

3099
/************************************************************
3100

3101
  Function: crash_and_burn
3102

3103
*************************************************************
3104

3105
  Inputs:  char* message
3106

3107
  Description:
3108

3109
************************************************************/
3110

3111
void crash_and_burn(char *message)
×
3112
{
3113
    fprintf(stderr, "%s: %s\n", prog, message);
×
3114
    exit(4);
×
3115
}
3116

3117
/************************************************************
3118

3119
  Function: errno_crash_and_burn
3120

3121
*************************************************************
3122

3123
  Inputs:  char* message
3124

3125
  Description:
3126

3127
************************************************************/
3128

3129
void errno_crash_and_burn(char *message)
8✔
3130
{
3131
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3132
    exit(4);
8✔
3133
}
3134

3135
/************************************************************
3136

3137
  Function: print_warning
3138

3139
  Description: fprintf(stderr, ...), unless running with -q
3140

3141
*************************************************************/
3142

3143
void print_warning(char *format, ...)
52✔
3144
{
3145
    va_list args;
3146
    if (!quiet_flag) {
52✔
3147
        va_start(args, format);
52✔
3148
        vfprintf(stderr, format, args);
52✔
3149
        va_end(args);
52✔
3150
    }
8✔
3151
}
52✔
3152

3153
/************************************************************
3154

3155
  Function: sprint_tm
3156

3157
*************************************************************
3158

3159
  render nanosecond int64_t value into milliseconds string with three digits of
3160
  precision.
3161

3162
************************************************************/
3163

3164
const char *sprint_tm(int64_t ns)
851✔
3165
{
3166
    static char buf[10];
3167
    double t = (double)ns / 1e6;
851✔
3168

3169
    if (t < 0.0) {
851✔
3170
        /* negative (unexpected) */
3171
        sprintf(buf, "%.2g", t);
×
3172
    }
3173
    else if (t < 1.0) {
851✔
3174
        /* <= 0.99 ms */
3175
        sprintf(buf, "%.3f", t);
848✔
3176
    }
258✔
3177
    else if (t < 10.0) {
3✔
3178
        /* 1.00 - 9.99 ms */
3179
        sprintf(buf, "%.2f", t);
3✔
3180
    }
3✔
3181
    else if (t < 100.0) {
×
3182
        /* 10.0 - 99.9 ms */
3183
        sprintf(buf, "%.1f", t);
×
3184
    }
3185
    else if (t < 1000000.0) {
×
3186
        /* 100 - 1'000'000 ms */
3187
        sprintf(buf, "%.0f", t);
×
3188
    }
3189
    else {
3190
        sprintf(buf, "%.3e", t);
×
3191
    }
3192

3193
    return (buf);
851✔
3194
}
3195

3196
/************************************************************
3197

3198
  Function: addr_cmp
3199

3200
*************************************************************/
3201
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
465✔
3202
{
3203
    if (a->sa_family != b->sa_family) {
465✔
3204
        return a->sa_family - b->sa_family;
×
3205
    }
3206
    else {
3207
        if (a->sa_family == AF_INET) {
465✔
3208
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
403✔
3209
        }
3210
        else if (a->sa_family == AF_INET6) {
62✔
3211
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
70✔
3212
                &((struct sockaddr_in6 *)b)->sin6_addr,
62✔
3213
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3214
        }
3215
    }
3216

3217
    return 0;
×
3218
}
131✔
3219

3220
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
821✔
3221
{
3222
    struct event *event = &h->event_storage_ping[index % event_storage_count];
821✔
3223
    event->host = h;
821✔
3224
    event->ping_index = index;
821✔
3225
    event->ev_time = ev_time;
821✔
3226
    ev_enqueue(&event_queue_ping, event);
821✔
3227

3228
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3229
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3230
}
821✔
3231

3232
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
799✔
3233
{
3234
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
799✔
3235
    event->host = h;
799✔
3236
    event->ping_index = index;
799✔
3237
    event->ev_time = ev_time;
799✔
3238
    ev_enqueue(&event_queue_timeout, event);
799✔
3239

3240
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3241
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3242
}
799✔
3243

3244
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
704✔
3245
{
3246
    return &h->event_storage_timeout[index % event_storage_count];
704✔
3247
}
3248

3249
/************************************************************
3250

3251
  Function: ev_enqueue
3252

3253
  Enqueue an event
3254

3255
  The queue is sorted by event->ev_time, so that queue->first always points to
3256
  the earliest event.
3257

3258
  We start scanning the queue from the tail, because we assume
3259
  that new events mostly get inserted with a event time higher
3260
  than the others.
3261

3262
*************************************************************/
3263
void ev_enqueue(struct event_queue *queue, struct event *event)
1,620✔
3264
{
3265
    struct event *i;
3266
    struct event *i_prev;
3267

3268
    /* Empty list */
3269
    if (queue->last == NULL) {
1,620✔
3270
        event->ev_next = NULL;
1,231✔
3271
        event->ev_prev = NULL;
1,231✔
3272
        queue->first = event;
1,231✔
3273
        queue->last = event;
1,231✔
3274
        return;
1,231✔
3275
    }
3276

3277
    /* Insert on tail? */
3278
    if (event->ev_time - queue->last->ev_time >= 0) {
389✔
3279
        event->ev_next = NULL;
389✔
3280
        event->ev_prev = queue->last;
389✔
3281
        queue->last->ev_next = event;
389✔
3282
        queue->last = event;
389✔
3283
        return;
389✔
3284
    }
3285

3286
    /* Find insertion point */
3287
    i = queue->last;
×
3288
    while (1) {
3289
        i_prev = i->ev_prev;
×
3290
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3291
            event->ev_prev = i_prev;
×
3292
            event->ev_next = i;
×
3293
            i->ev_prev = event;
×
3294
            if (i_prev != NULL) {
×
3295
                i_prev->ev_next = event;
×
3296
            }
3297
            else {
3298
                queue->first = event;
×
3299
            }
3300
            return;
×
3301
        }
3302
        i = i_prev;
×
3303
    }
3304
}
474✔
3305

3306
/************************************************************
3307

3308
  Function: ev_dequeue
3309

3310
*************************************************************/
3311
struct event *ev_dequeue(struct event_queue *queue)
896✔
3312
{
3313
    struct event *dequeued;
3314

3315
    if (queue->first == NULL) {
896✔
3316
        return NULL;
×
3317
    }
3318
    dequeued = queue->first;
896✔
3319
    ev_remove(queue, dequeued);
896✔
3320

3321
    return dequeued;
896✔
3322
}
254✔
3323

3324
/************************************************************
3325

3326
  Function: ev_remove
3327

3328
*************************************************************/
3329
void ev_remove(struct event_queue *queue, struct event *event)
1,600✔
3330
{
3331
    if (queue->first == event) {
1,600✔
3332
        queue->first = event->ev_next;
1,591✔
3333
    }
465✔
3334
    if (queue->last == event) {
1,600✔
3335
        queue->last = event->ev_prev;
1,232✔
3336
    }
354✔
3337
    if (event->ev_prev) {
1,600✔
3338
        event->ev_prev->ev_next = event->ev_next;
9✔
3339
    }
3✔
3340
    if (event->ev_next) {
1,600✔
3341
        event->ev_next->ev_prev = event->ev_prev;
368✔
3342
    }
114✔
3343
    event->ev_prev = NULL;
1,600✔
3344
    event->ev_next = NULL;
1,600✔
3345
}
1,600✔
3346

3347
/************************************************************
3348

3349
  Function: print_human_readable_time from current_time_ns
3350

3351
*************************************************************/
3352
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
30✔
3353
{
3354
    char time_buffer[100];
3355
    time_t current_time_s;
3356
    struct tm *local_time;
3357

3358
    current_time_s = current_time_ns / 1000000000;
30✔
3359
    local_time = localtime(&current_time_s);
30✔
3360
    switch(timestamp_format) {
30✔
3361
        case 1:
4✔
3362
            // timestamp-format ctime
3363
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
6✔
3364
            printf("[%s] ", time_buffer);
6✔
3365
            break;
6✔
3366
        case 2:
4✔
3367
            // timestamp-format iso
3368
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
6✔
3369
            printf("[%s] ", time_buffer);
6✔
3370
            break;
6✔
3371
        case 3:
4✔
3372
            // timestamp-format rfc3339
3373
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
6✔
3374
            printf("[%s] ", time_buffer);
6✔
3375
            break;
6✔
3376
        default:
8✔
3377
            printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3378
    }
4✔
3379
}
30✔
3380

3381
/************************************************************
3382

3383
  Function: ms_since_midnight_utc
3384

3385
*************************************************************
3386

3387
  Input: int64_t: current UTC time in ns
3388

3389
  Output: uint32_t: current time in ms since midnight UTC
3390

3391
  Description:
3392

3393
  Return ICMP Timestamp value corresponding to the given time value.
3394
  The given time value must be in UTC.
3395

3396
*************************************************************/
3397
static uint32_t ms_since_midnight_utc(int64_t time_val)
14✔
3398
{
3399
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
14✔
3400
}
3401

3402
/************************************************************
3403

3404
  Function: usage
3405

3406
*************************************************************
3407

3408
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3409

3410
  Description:
3411

3412
************************************************************/
3413

3414
void usage(int is_error)
115✔
3415
{
3416
    FILE *out = is_error ? stderr : stdout;
115✔
3417
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
115✔
3418
    fprintf(out, "\n");
115✔
3419
    fprintf(out, "Probing options:\n");
115✔
3420
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
115✔
3421
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
115✔
3422
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
115✔
3423
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
115✔
3424
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
115✔
3425
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
115✔
3426
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
115✔
3427
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
115✔
3428
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
115✔
3429
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
115✔
3430
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
115✔
3431
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
115✔
3432
#ifdef SO_BINDTODEVICE
3433
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
78✔
3434
#endif
3435
#ifdef SO_MARK
3436
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
78✔
3437
#endif
3438
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
115✔
3439
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
115✔
3440
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
115✔
3441
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
115✔
3442
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
115✔
3443
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
115✔
3444
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
115✔
3445
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
115✔
3446
    fprintf(out, "   -S, --src=IP       set source address\n");
115✔
3447
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
115✔
3448
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
115✔
3449
    fprintf(out, "       --check-source discard replies not from target address\n");
115✔
3450
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
115✔
3451
    fprintf(out, "\n");
115✔
3452
    fprintf(out, "Output options:\n");
115✔
3453
    fprintf(out, "   -a, --alive        show targets that are alive\n");
115✔
3454
    fprintf(out, "   -A, --addr         show targets by address\n");
115✔
3455
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
115✔
3456
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
115✔
3457
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
115✔
3458
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
115✔
3459
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
115✔
3460
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
115✔
3461
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
115✔
3462
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
115✔
3463
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
115✔
3464
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
115✔
3465
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
115✔
3466
    fprintf(out, "   -s, --stats        print final stats\n");
115✔
3467
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
115✔
3468
    fprintf(out, "   -v, --version      show version\n");
115✔
3469
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
115✔
3470
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
115✔
3471
    fprintf(out, "       --print-tos    show received TOS value\n");
115✔
3472
    fprintf(out, "       --print-ttl    show IP TTL value\n");
115✔
3473
    exit(is_error);
115✔
3474
}
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