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

schweikert / fping / 15510562670

07 Jun 2025 06:29PM UTC coverage: 87.756% (-0.01%) from 87.768%
15510562670

push

github

auerswal
print TOS & TTL also for IPv6 and w/o privileges

When using IPv4 raw sockets, fping has access to the received
IP packet data including the IP header.  This allows to simply
read the TOS byte and/or TTL value from the IP header.

But, using IPv4 raw sockets requires privileges.  Additionally,
IPv6 raw sockets do not provide access to the IPv6 header.

For IPv6, or when using IPv4 datagram sockets, i.e., when fping
runs without privileges, a different method needs to be used to
access TOS / TC and TTL / Hop Count of received packets.  The
so called "control messages", also called "ancillary data", can
be used for this.  This requires several actions:

* set the respective socket options to request additional data,
* ensure the buffer used to receive additional data is sized
  large enough,
* read and process the requested additional data.

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

2 existing lines in 1 file now uncovered.

1412 of 1609 relevant lines covered (87.76%)

331.64 hits per line

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

87.95
/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 "config.h"
38
#include "fping.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,
22✔
441
    const void *optval, socklen_t optlen)
442
{
443
    const uid_t saved_uid = geteuid();
22✔
444
    int res;
445

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

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

452
    if (p_uid != saved_uid && seteuid(saved_uid)) {
22✔
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;
22✔
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)
641✔
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();
641✔
486
    int tos = 0;
641✔
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];
641✔
495
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
641✔
496
        usage(0);
3✔
497
    }
1✔
498

499
    socket4 = open_ping_socket_ipv4(&socktype4);
639✔
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);
452✔
505
#endif
506

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

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

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

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

533
    /* get command line options */
534

535
    struct optparse_long longopts[] = {
639✔
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,507✔
589
        switch (c) {
972✔
590
        case '0':
76✔
591
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
94✔
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) {
84✔
602
                check_source_flag = 1;
9✔
603
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
71✔
604
#ifdef IPV6
605
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
27✔
606
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
607
                    exit(1);
2✔
608
                }
609
                hints_ai_family = AF_INET;
25✔
610
#endif
611
                icmp_request_typ = 13;
25✔
612
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
25✔
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;
88✔
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':
28✔
655
#ifdef IPV6
656
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
31✔
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;
26✔
661
#else
662
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
663
            exit(1);
664
#endif
665
            break;
26✔
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':
6✔
906
#ifdef SO_BINDTODEVICE
907
            if (socket4 >= 0) {
6✔
908
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
909
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
910
                    exit(1);
2✔
911
                }
912
            }
913
#ifdef IPV6
914
            if (socket6 >= 0) {
4✔
915
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
916
                    perror("binding to specific interface (SO_BINDTODEVICE), 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);
1✔
923
            exit(3);
1✔
924
            ;
925
#endif
926
            break;
4✔
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
    }
963

964
    /* permanently drop privileges */
965
    if (suid != getuid() && setuid(getuid())) {
477✔
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)) {
477✔
979
        crash_and_burn("can't create socket (must run as root?)");
×
980
    }
981
#endif
982

983
    if (ttl > 255) {
475✔
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) {
472✔
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) {
469✔
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)
466✔
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) {
460✔
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)) {
457✔
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) {
451✔
1021
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1022
        exit(1);
9✔
1023
    }
1024

1025
    if (count_flag) {
442✔
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) {
442✔
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)
442✔
1040
        verbose_flag = 0;
37✔
1041

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

1044
    /* auto-tune default timeout for count/loop modes
1045
     * see also github #32 */
1046
    if (loop_flag || count_flag) {
458✔
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) {
458✔
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;
336✔
1141
        if (socket4 >= 0) {
336✔
1142
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
336✔
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) {
336✔
1150
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
336✔
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();
458✔
1161
    start_time = current_time_ns;
458✔
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];
458✔
1167
    argc -= optparse_state.optind;
458✔
1168

1169
    /* calculate how many ping can be in-flight per host */
1170
    if (count_flag) {
458✔
1171
        event_storage_count = count;
115✔
1172
    }
43✔
1173
    else if (loop_flag) {
343✔
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;
343✔
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))
458✔
1189
        usage(1);
19✔
1190

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

1195
    if (*argv && !generate_flag) {
440✔
1196
        while (*argv) {
524✔
1197
            add_name(*argv);
291✔
1198
            ++argv;
291✔
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) {
360✔
1303
        exit(num_noaddress ? 2 : 1);
33✔
1304
    }
1305

1306
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
327✔
1307
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1308
    }
2✔
1309
#ifdef IPV6
1310
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
325✔
1311
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
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;
323✔
1318
        int i = 0;
323✔
1319
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
323✔
1320
        if (!table)
323✔
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) {
830✔
1325
            table[i] = cursor->host;
507✔
1326
            cursor->host->i = i;
507✔
1327
            i++;
507✔
1328
        }
135✔
1329
    }
1330

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

1336
#ifdef USE_SIGACTION
1337
    memset(&act, 0, sizeof(act));
323✔
1338
    act.sa_handler = signal_handler;
323✔
1339
    sigemptyset(&act.sa_mask);
323✔
1340
    sigaddset(&act.sa_mask, SIGINT);
323✔
1341
    sigaddset(&act.sa_mask, SIGQUIT);
323✔
1342
    act.sa_flags = SA_RESTART;
323✔
1343
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
323✔
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);
299✔
1351

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

1356
    last_send_time = 0;
299✔
1357

1358
    seqmap_init();
299✔
1359

1360
    /* main loop */
1361
    main_loop();
299✔
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\n", perf_cpu_time_used);
1368
#endif /* DEBUG || _DEBUG */
1369

1370
    finish();
299✔
1371

1372
    return 0;
65✔
1373
}
1374

1375
static inline int64_t timespec_ns(struct timespec *a)
5,188✔
1376
{
1377
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,188✔
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
#ifdef IPV6
1555
    /* IPv6 addresses can have a scope */
1556
    if (hints_ai_family == AF_INET6) {
43✔
1557
        start_scope_str = strchr(start, '%');
26✔
1558
        end_scope_str = strchr(end, '%');
26✔
1559
        if ((!start_scope_str && end_scope_str) ||
26✔
1560
            (start_scope_str && !end_scope_str) ||
24✔
1561
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1562
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1563
                exit(1);
6✔
1564
        }
1565
    }
1566
#endif
1567

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

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

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

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

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

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

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

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

1671
void main_loop()
311✔
1672
{
1673
    int64_t lt;
1674
    int64_t wait_time_ns;
1675
    struct event *event;
1676
    struct host_entry *h;
1677

1678
    while (event_queue_ping.first || event_queue_timeout.first) {
1,731✔
1679
        dbg_printf("%s", "# main_loop\n");
1680

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

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

1688
            stats_add(h, event->ping_index, 0, -1);
95✔
1689

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

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

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

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

1737
            /* Dequeue the event */
1738
            event = ev_dequeue(&event_queue_ping);
807✔
1739
            h = event->host;
807✔
1740

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

1743
            /* Send the ping */
1744
            send_ping(h, event->ping_index);
807✔
1745

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

1752
    wait_for_reply:
575✔
1753

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

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

1772
        /* When is the next timeout event? */
1773
        if (event_queue_timeout.first) {
1,342✔
1774
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
846✔
1775
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
846✔
1776
                wait_time_ns = wait_time_timeout;
363✔
1777
                if (wait_time_ns < 0) {
363✔
1778
                    wait_time_ns = 0;
1✔
1779
                }
1780
            }
88✔
1781

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

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

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

1798
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1799
        if (wait_time_ns == -1) {
1,342✔
1800
            break;
14✔
1801
        }
1802

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

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

1816
        update_current_time();
1,325✔
1817

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

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

1830
            while (current_time_ns >= next_report_time) {
84✔
1831
                next_report_time += report_interval;
42✔
1832
            }
1833
        }
14✔
1834
    }
1835
}
311✔
1836

1837
/************************************************************
1838

1839
  Function: signal_handler
1840

1841
*************************************************************
1842

1843
  Inputs:  int signum
1844

1845
  Description:
1846

1847
  SIGQUIT signal handler - set flag and return
1848
  SIGINT signal handler - set flag and return
1849

1850
************************************************************/
1851

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

1859
    case SIGQUIT:
1860
        status_snapshot = 1;
×
1861
        break;
×
1862
    }
1863
}
×
1864

1865
/************************************************************
1866

1867
  Function: update_current_time
1868

1869
*************************************************************/
1870

1871
void update_current_time()
4,186✔
1872
{
1873
    clock_gettime(CLOCKID, &current_time);
4,186✔
1874
    current_time_ns = timespec_ns(&current_time);
4,186✔
1875
}
4,186✔
1876

1877
/************************************************************
1878

1879
  Function: finish
1880

1881
*************************************************************
1882

1883
  Inputs:  void (none)
1884

1885
  Description:
1886

1887
  Main program clean up and exit point
1888

1889
************************************************************/
1890

1891
void finish()
311✔
1892
{
1893
    int i;
1894
    HOST_ENTRY *h;
1895

1896
    update_current_time();
311✔
1897
    end_time = current_time_ns;
311✔
1898

1899
    /* tot up unreachables */
1900
    for (i = 0; i < num_hosts; i++) {
818✔
1901
        h = table[i];
507✔
1902

1903
        if (!h->num_recv) {
507✔
1904
            num_unreachable++;
80✔
1905

1906
            if (verbose_flag || unreachable_flag) {
80✔
1907
                printf("%s", h->host);
44✔
1908

1909
                if (verbose_flag)
44✔
1910
                    printf(" is unreachable");
41✔
1911

1912
                printf("\n");
44✔
1913
            }
2✔
1914
        }
12✔
1915
    }
135✔
1916

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

1924
    if (stats_flag)
311✔
1925
        print_global_stats();
9✔
1926

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

1938
    if (num_noaddress)
302✔
1939
        exit(2);
3✔
1940
    else if (num_alive != num_hosts)
299✔
1941
        exit(1);
41✔
1942

1943
    exit(0);
258✔
1944
}
1945

1946
/************************************************************
1947

1948
  Function: print_per_system_stats
1949

1950
*************************************************************
1951

1952
  Inputs:  void (none)
1953

1954
  Description:
1955

1956

1957
************************************************************/
1958

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

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

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

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

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

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

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

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

2011
/************************************************************
2012

2013
  Function: print_netdata
2014

2015
*************************************************************
2016

2017
  Inputs:  void (none)
2018

2019
  Description:
2020

2021

2022
************************************************************/
2023

2024
void print_netdata(void)
3✔
2025
{
2026
    static int sent_charts = 0;
2027

2028
    int i;
2029
    int64_t avg;
2030
    HOST_ENTRY *h;
2031

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

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

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

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

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

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

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

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

2079
        stats_reset_interval(h);
3✔
2080
    }
1✔
2081

2082
    sent_charts = 1;
3✔
2083
}
3✔
2084

2085
/************************************************************
2086

2087
  Function: print_per_system_splits
2088

2089
*************************************************************
2090

2091
  Inputs:  void (none)
2092

2093
  Description:
2094

2095

2096
************************************************************/
2097

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

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

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

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

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

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

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

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

2145
/************************************************************
2146

2147
  Function: print_global_stats
2148

2149
*************************************************************
2150

2151
  Inputs:  void (none)
2152

2153
  Description:
2154

2155

2156
************************************************************/
2157

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

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

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

2188
/************************************************************
2189

2190
  Function: send_ping
2191

2192
*************************************************************
2193

2194
  Inputs:  int s, HOST_ENTRY *h
2195

2196
  Description:
2197

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

2204
************************************************************/
2205

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

2213
    update_current_time();
837✔
2214
    h->last_send_time = current_time_ns;
837✔
2215
    myseq = seqmap_add(h->i, index, current_time_ns);
837✔
2216

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

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

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

2247
        h->num_sent++;
34✔
2248
        h->num_sent_i++;
34✔
2249
        if (!loop_flag)
34✔
2250
            h->resp_times[index] = RESP_ERROR;
34✔
2251

2252
        ret = 0;
34✔
2253
    }
2254
    else {
2255
        /* schedule timeout */
2256
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
803✔
2257

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

2264
    num_pingsent++;
837✔
2265
    last_send_time = h->last_send_time;
837✔
2266

2267
    return (ret);
837✔
2268
}
237✔
2269

2270
int socket_can_read(struct timeval *timeout)
2,556✔
2271
{
2272
    int nfound;
2273
    fd_set readset;
2274
    int socketmax;
2275

2276
#ifndef IPV6
2277
    socketmax = socket4;
2278
#else
2279
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,556✔
2280
#endif
2281

2282
select_again:
1,937✔
2283
    FD_ZERO(&readset);
33,548✔
2284
    if (socket4 >= 0)
2,556✔
2285
        FD_SET(socket4, &readset);
2,556✔
2286
#ifdef IPV6
2287
    if (socket6 >= 0)
2,556✔
2288
        FD_SET(socket6, &readset);
2,556✔
2289
#endif
2290

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

2302
    if (nfound > 0) {
2,556✔
2303
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,231✔
2304
            return socket4;
1,051✔
2305
        }
2306
#ifdef IPV6
2307
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
180✔
2308
            return socket6;
180✔
2309
        }
2310
#endif
2311
    }
2312

2313
    return -1;
1,325✔
2314
}
619✔
2315

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

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

2360
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,231✔
2361
    if (recv_len <= 0) {
1,231✔
2362
        return 0;
×
2363
    }
2364

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

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

2408
    return recv_len;
1,231✔
2409
}
619✔
2410

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

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

2433
    /* received count */
2434
    h->num_recv++;
708✔
2435
    h->num_recv_i++;
708✔
2436

2437
    /* maximum */
2438
    if (!h->max_reply || latency > h->max_reply) {
708✔
2439
        h->max_reply = latency;
554✔
2440
    }
157✔
2441
    if (!h->max_reply_i || latency > h->max_reply_i) {
708✔
2442
        h->max_reply_i = latency;
564✔
2443
    }
160✔
2444

2445
    /* minimum */
2446
    if (!h->min_reply || latency < h->min_reply) {
708✔
2447
        h->min_reply = latency;
471✔
2448
    }
147✔
2449
    if (!h->min_reply_i || latency < h->min_reply_i) {
708✔
2450
        h->min_reply_i = latency;
496✔
2451
    }
154✔
2452

2453
    /* total time (for average) */
2454
    h->total_time += latency;
708✔
2455
    h->total_time_i += latency;
708✔
2456

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

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

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

2491
    if (!using_sock_dgram4) {
1,051✔
2492
        struct ip *ip = (struct ip *)reply_buf;
1,043✔
2493
        *ip_header_tos = ip->ip_tos;
1,043✔
2494
        *ip_header_ttl = ip->ip_ttl;
1,043✔
2495

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

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

2517
    icp = (struct icmp *)(reply_buf + hlen);
1,051✔
2518

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

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

2533
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
422✔
2534

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

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

2547
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2548

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

2561
            print_warning("\n");
×
2562
            num_othericmprcvd++;
×
2563
            break;
×
2564

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

2583
        return -1;
×
2584
    }
2585

2586
    *id = icp->icmp_id;
629✔
2587
    *seq = ntohs(icp->icmp_seq);
629✔
2588
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
629✔
2589

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

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

2605
    return hlen;
629✔
2606
}
199✔
2607

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

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

2628
    icp = (struct icmp6_hdr *)reply_buf;
180✔
2629

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

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

2643
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
89✔
2644

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

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

2655
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2656

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

2669
            print_warning("\n");
×
2670
            num_othericmprcvd++;
×
2671
            break;
×
2672

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

2691
        return 0;
×
2692
    }
2693

2694
    *id = icp->icmp6_id;
91✔
2695
    *seq = ntohs(icp->icmp6_seq);
91✔
2696

2697
    return 1;
91✔
2698
}
30✔
2699
#endif
2700

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

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

2732
    if (result <= 0) {
2,556✔
2733
        return 0;
1,325✔
2734
    }
2735

2736
    update_current_time();
1,231✔
2737
    if (recv_time == 0)
1,231✔
2738
        recv_time = current_time_ns;
229✔
2739

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

2786
    seqmap_value = seqmap_fetch(seq, current_time_ns);
720✔
2787
    if (seqmap_value == NULL) {
720✔
2788
        return 1;
×
2789
    }
2790

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

2797
    /* update stats that include invalid replies */
2798
    h->num_recv_total++;
720✔
2799
    num_pingreceived++;
720✔
2800

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

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

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

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

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

2831
    /* update stats */
2832
    stats_add(h, this_count, 1, this_reply);
708✔
2833
    // TODO: move to stats_add?
2834
    if (!max_reply || this_reply > max_reply)
708✔
2835
        max_reply = this_reply;
423✔
2836
    if (!min_reply || this_reply < min_reply)
708✔
2837
        min_reply = this_reply;
383✔
2838
    sum_replies += this_reply;
708✔
2839
    total_replies++;
708✔
2840

2841
    /* initialize timeout to initial timeout (without backoff) */
2842
    h->timeout = timeout;
708✔
2843

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

2850
    /* print "is alive" */
2851
    if (h->num_recv == 1) {
708✔
2852
        num_alive++;
427✔
2853
        if (fast_reachable && num_alive >= min_reachable)
427✔
2854
                finish_requested = 1;
3✔
2855

2856
        if (verbose_flag || alive_flag) {
427✔
2857
            printf("%s", h->host);
296✔
2858

2859
            if (verbose_flag)
296✔
2860
                printf(" is alive");
276✔
2861
        }
84✔
2862
    }
123✔
2863

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

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

2884
    if (verbose_flag || alive_flag || per_recv_flag) {
708✔
2885

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

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

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

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

2917
        if (elapsed_flag && !per_recv_flag)
447✔
2918
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2919

2920
        printf("\n");
447✔
2921
    }
129✔
2922

2923
    return 1;
708✔
2924
}
619✔
2925

2926
/************************************************************
2927

2928
  Function: add_name
2929

2930
*************************************************************
2931

2932
  Inputs:  char* name
2933

2934
  Description:
2935

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

2940
************************************************************/
2941

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

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

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

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

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

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

3030
        if (!multif_flag) {
512✔
3031
            break;
512✔
3032
        }
3033
    }
3034

3035
    freeaddrinfo(res0);
512✔
3036
}
144✔
3037

3038
/************************************************************
3039

3040
  Function: add_addr
3041

3042
*************************************************************
3043

3044
  Description:
3045

3046
  add single address to list of hosts to be pinged
3047

3048
************************************************************/
3049

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

3056
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
512✔
3057
    if (!p)
512✔
3058
        crash_and_burn("can't allocate HOST_ENTRY");
×
3059

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

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

3076
    if (strlen(p->host) > max_hostname_len)
512✔
3077
        max_hostname_len = strlen(p->host);
325✔
3078

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

3085
        for (n = 1; n < trials; n++)
2,039✔
3086
            i[n] = RESP_UNUSED;
1,527✔
3087

3088
        p->resp_times = i;
512✔
3089
    }
136✔
3090

3091
    /* allocate event storage */
3092
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
512✔
3093
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
512✔
3094

3095
    /* schedule first ping */
3096
    host_add_ping_event(p, 0, current_time_ns);
512✔
3097

3098
    num_hosts++;
512✔
3099
}
512✔
3100

3101
/************************************************************
3102

3103
  Function: crash_and_burn
3104

3105
*************************************************************
3106

3107
  Inputs:  char* message
3108

3109
  Description:
3110

3111
************************************************************/
3112

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

3119
/************************************************************
3120

3121
  Function: errno_crash_and_burn
3122

3123
*************************************************************
3124

3125
  Inputs:  char* message
3126

3127
  Description:
3128

3129
************************************************************/
3130

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

3137
/************************************************************
3138

3139
  Function: print_warning
3140

3141
  Description: fprintf(stderr, ...), unless running with -q
3142

3143
*************************************************************/
3144

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

3155
/************************************************************
3156

3157
  Function: sprint_tm
3158

3159
*************************************************************
3160

3161
  render nanosecond int64_t value into milliseconds string with three digits of
3162
  precision.
3163

3164
************************************************************/
3165

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

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

3195
    return (buf);
851✔
3196
}
3197

3198
/************************************************************
3199

3200
  Function: addr_cmp
3201

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

3219
    return 0;
×
3220
}
131✔
3221

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

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

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

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

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

3251
/************************************************************
3252

3253
  Function: ev_enqueue
3254

3255
  Enqueue an event
3256

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

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

3264
*************************************************************/
3265
void ev_enqueue(struct event_queue *queue, struct event *event)
1,630✔
3266
{
3267
    struct event *i;
3268
    struct event *i_prev;
3269

3270
    /* Empty list */
3271
    if (queue->last == NULL) {
1,630✔
3272
        event->ev_next = NULL;
1,243✔
3273
        event->ev_prev = NULL;
1,243✔
3274
        queue->first = event;
1,243✔
3275
        queue->last = event;
1,243✔
3276
        return;
1,243✔
3277
    }
3278

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

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

3308
/************************************************************
3309

3310
  Function: ev_dequeue
3311

3312
*************************************************************/
3313
struct event *ev_dequeue(struct event_queue *queue)
902✔
3314
{
3315
    struct event *dequeued;
3316

3317
    if (queue->first == NULL) {
902✔
3318
        return NULL;
×
3319
    }
3320
    dequeued = queue->first;
902✔
3321
    ev_remove(queue, dequeued);
902✔
3322

3323
    return dequeued;
902✔
3324
}
254✔
3325

3326
/************************************************************
3327

3328
  Function: ev_remove
3329

3330
*************************************************************/
3331
void ev_remove(struct event_queue *queue, struct event *event)
1,610✔
3332
{
3333
    if (queue->first == event) {
1,610✔
3334
        queue->first = event->ev_next;
1,601✔
3335
    }
465✔
3336
    if (queue->last == event) {
1,610✔
3337
        queue->last = event->ev_prev;
1,244✔
3338
    }
355✔
3339
    if (event->ev_prev) {
1,610✔
3340
        event->ev_prev->ev_next = event->ev_next;
9✔
3341
    }
3✔
3342
    if (event->ev_next) {
1,610✔
3343
        event->ev_next->ev_prev = event->ev_prev;
366✔
3344
    }
113✔
3345
    event->ev_prev = NULL;
1,610✔
3346
    event->ev_next = NULL;
1,610✔
3347
}
1,610✔
3348

3349
/************************************************************
3350

3351
  Function: print_human_readable_time from current_time_ns
3352

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

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

3383
/************************************************************
3384

3385
  Function: ms_since_midnight_utc
3386

3387
*************************************************************
3388

3389
  Input: int64_t: current UTC time in ns
3390

3391
  Output: uint32_t: current time in ms since midnight UTC
3392

3393
  Description:
3394

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

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

3404
/************************************************************
3405

3406
  Function: usage
3407

3408
*************************************************************
3409

3410
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3411

3412
  Description:
3413

3414
************************************************************/
3415

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