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

schweikert / fping / 14822794363

04 May 2025 03:44PM UTC coverage: 86.609% (-1.3%) from 87.902%
14822794363

Pull #391

github

web-flow
Merge 6dfdf9bf6 into 913982a8c
Pull Request #391: fix ICMPv6 error message handling and reporting

7 of 41 new or added lines in 2 files covered. (17.07%)

4 existing lines in 1 file now uncovered.

1397 of 1613 relevant lines covered (86.61%)

325.09 hits per line

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

86.69
/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
#include <netinet/ip6.h>
74
#endif
75
#include <netinet/in_systm.h>
76

77
#include <netinet/ip.h>
78
#include <netinet/ip_icmp.h>
79

80
#include <arpa/inet.h>
81
#include <ctype.h>
82
#include <netdb.h>
83

84
#include <sys/select.h>
85

86
/*** compatibility ***/
87

88
/* Mac OS X's getaddrinfo() does not fail if we use an invalid combination,
89
 * e.g. AF_INET6 with "127.0.0.1". If we pass AI_UNUSABLE to flags, it behaves
90
 * like other platforms. But AI_UNUSABLE isn't available on other platforms,
91
 * and we can safely use 0 for flags instead.
92
 */
93
#ifndef AI_UNUSABLE
94
#define AI_UNUSABLE 0
95
#endif
96

97
/* MSG_TRUNC available on Linux kernel 2.2+, makes recvmsg return the full
98
 * length of the raw packet received, even if the buffer is smaller */
99
#ifndef MSG_TRUNC
100
#define MSG_TRUNC 0
101
#define RECV_BUFSIZE 4096
102
#else
103
#define RECV_BUFSIZE 128
104
#endif
105

106
/*** externals ***/
107

108
extern char *optarg;
109
extern int optind, opterr;
110
#ifndef h_errno
111
extern int h_errno;
112
#endif
113

114
#ifdef __cplusplus
115
}
116
#endif /* __cplusplus */
117

118
/*** Constants ***/
119

120
/* CLOCK_MONTONIC starts under macOS, OpenBSD and FreeBSD with undefined positive point and can not be use
121
 * see github PR #217
122
 * The configure script detect the predefined operating systems an set CLOCK_REALTIME using over ONLY_CLOCK_REALTIME variable
123
 */
124
#if HAVE_SO_TIMESTAMPNS || ONLY_CLOCK_REALTIME
125
#define CLOCKID CLOCK_REALTIME
126
#endif
127

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

136
/*** Ping packet defines ***/
137

138
#define MAX_IP_PACKET 65535 /* (theoretical) max IPv4 packet size */
139
#define SIZE_IP_HDR 20 /* min IPv4 header size */
140
#define SIZE_ICMP_HDR 8 /* from ip_icmp.h */
141
#define MAX_PING_DATA (MAX_IP_PACKET - SIZE_IP_HDR - SIZE_ICMP_HDR)
142

143
#define MAX_GENERATE 131072 /* maximum number of hosts that -g can generate */
144
#define MAX_TARGET_NAME_LEN 255 /* maximum target name length read from file */
145

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

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

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

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

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

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

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

220
#define ICMP_UNREACH_MAXTYPE 15
221

222
#ifdef IPV6
223
/* Long names for ICMPv6 unreachable codes */
224
#define ICMP6_UNREACH_MAXCODE 9
225
char *icmp6_unreach_str[ICMP6_UNREACH_MAXCODE + 1] = {
226
    "No route to destination", /* 0 */
227
    "Communication with destination administratively prohibited", /* 1 */
228
    "Beyond scope of source address", /* 2 */
229
    "Address unreachable", /* 3 */
230
    "Port unreachable", /* 4 */
231
    "Source address failed ingress/egress policy", /* 5 */
232
    "Reject route to destination", /* 6 */
233
    "Error in Source Routing Header", /* 7 */
234
    "Headers too long", /* 8 */
235
    "Error in P-Route", /* 9 */
236
};
237

238
/* Long names for ICMPv6 time exceeded codes */
239
#define ICMP6_TIME_EXCEEDED_MAXCODE 1
240
char *icmp6_time_exceeded_str[ICMP6_TIME_EXCEEDED_MAXCODE + 1] = {
241
    "Hop limit exceeded in transit", /* 0 */
242
    "Fragment reassembly time exceeded", /* 1 */
243
};
244

245
/* Long names for ICMPv6 parameter problem codes */
246
#define ICMP6_PARAM_PROB_MAXCODE 10
247
char *icmp6_param_prob_str[ICMP6_PARAM_PROB_MAXCODE + 1] = {
248
    "Erroneous header field encountered", /* 0 */
249
    "Unrecognized Next Header type encountered", /* 1 */
250
    "Unrecognized IPv6 option encountered", /* 2 */
251
    "IPv6 First Fragment has incomplete IPv6 Header Chain", /* 3 */
252
    "SR Upper-layer Header Error", /* 4 */
253
    "Unrecognized Next Header type encountered by intermediate node", /* 5 */
254
    "Extension header too big", /* 6 */
255
    "Extension header chain too long", /* 7 */
256
    "Too many extension headers", /* 8 */
257
    "Too many options in extension header", /* 9 */
258
    "Option too big", /* 10 */
259
};
260
#endif
261

262
struct event;
263
typedef struct host_entry {
264
    int i; /* index into array */
265
    char *name; /* name as given by user */
266
    char *host; /* text description of host */
267
    struct sockaddr_storage saddr; /* internet address */
268
    socklen_t saddr_len;
269
    int64_t timeout; /* time to wait for response */
270
    int64_t last_send_time; /* time of last packet sent */
271
    int num_sent; /* number of ping packets sent (for statistics) */
272
    int num_recv; /* number of pings received (duplicates ignored) */
273
    int num_recv_total; /* number of pings received, including duplicates */
274
    int64_t max_reply; /* longest response time */
275
    int64_t min_reply; /* shortest response time */
276
    int64_t total_time; /* sum of response times */
277
    /* _i -> splits (reset on every report interval) */
278
    int num_sent_i; /* number of ping packets sent */
279
    int num_recv_i; /* number of pings received */
280
    int64_t max_reply_i; /* longest response time */
281
    int64_t min_reply_i; /* shortest response time */
282
    int64_t total_time_i; /* sum of response times */
283
    int64_t *resp_times; /* individual response times */
284

285
    /* to avoid allocating two struct events each time that we send a ping, we
286
     * preallocate here two struct events for each ping that we might send for
287
     * this host. */
288
    struct event *event_storage_ping;
289
    struct event *event_storage_timeout;
290
} HOST_ENTRY;
291

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

294
/* basic algorithm to ensure that we have correct data at all times:
295
 *
296
 * 1. when a ping is sent:
297
 *    - two events get added into event_queue:
298
 *      - t+PERIOD: ping event
299
 *      - t+TIMEOUT: timeout event
300
 *
301
 * 2. when a ping is received:
302
 *    - record statistics (increase num_sent and num_received)
303
 *    - remove timeout event (we store the event in seqmap, so that we can retrieve it when the response is received)
304
 *
305
 * 3. when a timeout happens:
306
 *    - record statistics (increase num_sent only)
307
 */
308

309
#define EV_TYPE_PING 1
310
#define EV_TYPE_TIMEOUT 2
311

312
struct event {
313
    struct event *ev_prev;
314
    struct event *ev_next;
315
    int64_t ev_time;
316
    struct host_entry *host;
317
    int ping_index;
318
};
319

320
struct event_queue {
321
    struct event *first;
322
    struct event *last;
323
};
324

325
/*** globals ***/
326

327
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
328

329
/* we keep two separate queues: a ping queue, for when the next ping should be
330
 * sent, and a timeout queue. the reason for having two separate queues is that
331
 * the ping period and the timeout value are different, so if we put them in
332
 * the same event queue, we would need to scan many more entries when inserting
333
 * into the sorted list.
334
 */
335
struct event_queue event_queue_ping;
336
struct event_queue event_queue_timeout;
337

338
char *prog;
339
int ident4 = 0; /* our icmp identity field */
340
int ident6 = 0;
341
int socket4 = -1;
342
int socktype4 = -1;
343
int using_sock_dgram4 = 0;
344
#ifndef IPV6
345
int hints_ai_family = AF_INET;
346
#else
347
int socket6 = -1;
348
int socktype6 = -1;
349
int hints_ai_family = AF_UNSPEC;
350
#endif
351

352
volatile sig_atomic_t status_snapshot = 0;
353
volatile sig_atomic_t finish_requested = 0;
354

355
unsigned int debugging = 0;
356

357
/* all time-related values are int64_t nanoseconds */
358
unsigned int retry = DEFAULT_RETRY;
359
int64_t timeout = (int64_t)DEFAULT_TIMEOUT * 1000000;
360
int64_t interval = (int64_t)DEFAULT_INTERVAL * 1000000;
361
int64_t perhost_interval = (int64_t)DEFAULT_PERHOST_INTERVAL * 1000000;
362
float backoff = DEFAULT_BACKOFF_FACTOR;
363
unsigned int ping_data_size = DEFAULT_PING_DATA_SIZE;
364
unsigned int count = 1, min_reachable = 0;
365
unsigned int trials;
366
int64_t report_interval = 0;
367
unsigned int ttl = 0;
368
int src_addr_set = 0;
369
struct in_addr src_addr;
370
#ifdef IPV6
371
int src_addr6_set = 0;
372
struct in6_addr src_addr6;
373
#endif
374

375
/* global stats */
376
int64_t max_reply = 0;
377
int64_t min_reply = 0;
378
int64_t total_replies = 0;
379
int64_t sum_replies = 0;
380
int max_hostname_len = 0;
381
int num_hosts = 0; /* total number of hosts */
382
int num_alive = 0, /* total number alive */
383
    num_unreachable = 0, /* total number unreachable */
384
    num_noaddress = 0; /* total number of addresses not found */
385
int num_timeout = 0, /* number of times select timed out */
386
    num_pingsent = 0, /* total pings sent */
387
    num_pingreceived = 0, /* total pings received */
388
    num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
389

390
struct timespec current_time; /* current time (pseudo) */
391
int64_t current_time_ns;
392
int64_t start_time;
393
int64_t end_time;
394
int64_t last_send_time; /* time last ping was sent */
395
int64_t next_report_time; /* time next -Q report is expected */
396

397
/* switches */
398
int generate_flag = 0; /* flag for IP list generation */
399
int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
400
int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag;
401
int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
402
int multif_flag, timeout_flag, fast_reachable;
403
int outage_flag = 0;
404
int timestamp_flag = 0;
405
int timestamp_format_flag = 0;
406
int random_data_flag = 0;
407
int cumulative_stats_flag = 0;
408
int check_source_flag = 0;
409
int icmp_request_typ = 0;
410
int print_tos_flag = 0;
411
int print_ttl_flag = 0;
412
int size_flag = 0;
413
#if defined(DEBUG) || defined(_DEBUG)
414
int randomly_lose_flag, trace_flag, print_per_system_flag;
415
int lose_factor;
416
#endif /* DEBUG || _DEBUG */
417

418
unsigned int fwmark = 0;
419

420
char *filename = NULL; /* file containing hosts to ping */
421

422
/*** forward declarations ***/
423

424
void add_name(char *name);
425
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len);
426
char *na_cat(char *name, struct in_addr ipaddr);
427
void crash_and_burn(char *message);
428
void errno_crash_and_burn(char *message);
429
char *get_host_by_address(struct in_addr in);
430
int send_ping(HOST_ENTRY *h, int index);
431
void usage(int);
432
int wait_for_reply(int64_t);
433
void print_per_system_stats(void);
434
void print_per_system_splits(void);
435
void stats_reset_interval(HOST_ENTRY *h);
436
void print_netdata(void);
437
void print_global_stats(void);
438
void main_loop();
439
void signal_handler(int);
440
void finish();
441
const char *sprint_tm(int64_t t);
442
void ev_enqueue(struct event_queue *queue, struct event *event);
443
struct event *ev_dequeue(struct event_queue *queue);
444
void ev_remove(struct event_queue *queue, struct event *event);
445
void add_cidr(char *);
446
void add_cidr_ipv4(unsigned long, unsigned long);
447
void add_range(char *, char *);
448
void add_addr_range_ipv4(unsigned long, unsigned long);
449
#ifdef IPV6
450
uint64_t be_octets_to_uint64(uint8_t*);
451
void uint64_to_be_octets(uint64_t, uint8_t*);
452
void add_cidr_ipv6(uint64_t, uint64_t, unsigned long, const char *);
453
void add_addr_range_ipv6(uint64_t, uint64_t, uint64_t, uint64_t, const char *);
454
#endif
455
void print_warning(char *fmt, ...);
456
int addr_cmp(struct sockaddr *a, struct sockaddr *b);
457
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
458
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
459
struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
460
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
461
void update_current_time();
462
void print_timestamp_format(int64_t current_time_ns, int timestamp_format);
463
static uint32_t ms_since_midnight_utc(int64_t time_val);
464

465
/************************************************************
466

467
  Function: p_setsockopt
468

469
*************************************************************
470

471
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
472

473
  Description:
474

475
  Elevates privileges to p_uid when required, calls
476
  setsockopt, and drops privileges back.
477

478
************************************************************/
479

480
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
18✔
481
    const void *optval, socklen_t optlen)
482
{
483
    const uid_t saved_uid = geteuid();
18✔
484
    int res;
485

486
    if (p_uid != saved_uid && seteuid(p_uid)) {
18✔
487
        perror("cannot elevate privileges for setsockopt");
×
488
    }
489

490
    res = setsockopt(sockfd, level, optname, optval, optlen);
18✔
491

492
    if (p_uid != saved_uid && seteuid(saved_uid)) {
18✔
493
        perror("fatal error: could not drop privileges after setsockopt");
×
494
        /* continuing would be a security hole */
495
        exit(4);
×
496
    }
497

498
    return res;
18✔
499
}
500

501
/************************************************************
502

503
  Function: main
504

505
*************************************************************
506

507
  Inputs:  int argc, char** argv
508

509
  Description:
510

511
  Main program entry point
512

513
************************************************************/
514

515
int main(int argc, char **argv)
639✔
516
{
517
/* Debug: CPU Performance */
518
#if defined(DEBUG) || defined(_DEBUG)
519
    clock_t perf_cpu_start, perf_cpu_end;
520
    double perf_cpu_time_used;
521
    perf_cpu_start = clock();
522
#endif /* DEBUG || _DEBUG */
523

524
    int c;
525
    const uid_t suid = geteuid();
639✔
526
    int tos = 0;
639✔
527
    struct optparse optparse_state;
528
#ifdef USE_SIGACTION
529
    struct sigaction act;
530
#endif
531

532
    /* pre-parse -h/--help, so that we also can output help information
533
     * without trying to open the socket, which might fail */
534
    prog = argv[0];
639✔
535
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
639✔
536
        usage(0);
3✔
537
    }
1✔
538

539
    socket4 = open_ping_socket_ipv4(&socktype4);
637✔
540
#ifdef __linux__
541
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
542
     * structure is missing in the message.
543
     */
544
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
450✔
545
#endif
546

547
#ifdef IPV6
548
    socket6 = open_ping_socket_ipv6(&socktype6);
637✔
549
    /* if called (sym-linked) via 'fping6', imply '-6'
550
     * for backward compatibility */
551
    if (strstr(prog, "fping6")) {
637✔
552
        hints_ai_family = AF_INET6;
×
553
    }
554
#endif
555

556
    memset(&src_addr, 0, sizeof(src_addr));
637✔
557
#ifdef IPV6
558
    memset(&src_addr6, 0, sizeof(src_addr6));
637✔
559
#endif
560

561
    if (!suid && suid != getuid()) {
637✔
562
        /* *temporarily* drop privileges */
563
        if (seteuid(getuid()) == -1)
601✔
564
            perror("cannot setuid");
×
565
    }
161✔
566

567
    optparse_init(&optparse_state, argv);
637✔
568
    ident4 = ident6 = htons(getpid() & 0xFFFF);
637✔
569
    verbose_flag = 1;
637✔
570
    backoff_flag = 1;
637✔
571
    opterr = 1;
637✔
572

573
    /* get command line options */
574

575
    struct optparse_long longopts[] = {
637✔
576
        { "ipv4", '4', OPTPARSE_NONE },
577
        { "ipv6", '6', OPTPARSE_NONE },
578
        { "alive", 'a', OPTPARSE_NONE },
579
        { "addr", 'A', OPTPARSE_NONE },
580
        { "size", 'b', OPTPARSE_REQUIRED },
581
        { "backoff", 'B', OPTPARSE_REQUIRED },
582
        { "count", 'c', OPTPARSE_REQUIRED },
583
        { "vcount", 'C', OPTPARSE_REQUIRED },
584
        { "rdns", 'd', OPTPARSE_NONE },
585
        { "timestamp", 'D', OPTPARSE_NONE },
586
        { "timestamp-format", '0', OPTPARSE_REQUIRED },
587
        { "elapsed", 'e', OPTPARSE_NONE },
588
        { "file", 'f', OPTPARSE_REQUIRED },
589
        { "generate", 'g', OPTPARSE_NONE },
590
        { "help", 'h', OPTPARSE_NONE },
591
        { "ttl", 'H', OPTPARSE_REQUIRED },
592
        { "interval", 'i', OPTPARSE_REQUIRED },
593
        { "iface", 'I', OPTPARSE_REQUIRED },
594
        { "icmp-timestamp", '0', OPTPARSE_NONE },
595
#ifdef SO_MARK
596
        { "fwmark", 'k', OPTPARSE_REQUIRED },
597
#endif
598
        { "loop", 'l', OPTPARSE_NONE },
599
        { "all", 'm', OPTPARSE_NONE },
600
        { "dontfrag", 'M', OPTPARSE_NONE },
601
        { "name", 'n', OPTPARSE_NONE },
602
        { "netdata", 'N', OPTPARSE_NONE },
603
        { "outage", 'o', OPTPARSE_NONE },
604
        { "tos", 'O', OPTPARSE_REQUIRED },
605
        { "period", 'p', OPTPARSE_REQUIRED },
606
        { "quiet", 'q', OPTPARSE_NONE },
607
        { "squiet", 'Q', OPTPARSE_REQUIRED },
608
        { "retry", 'r', OPTPARSE_REQUIRED },
609
        { "random", 'R', OPTPARSE_NONE },
610
        { "stats", 's', OPTPARSE_NONE },
611
        { "src", 'S', OPTPARSE_REQUIRED },
612
        { "timeout", 't', OPTPARSE_REQUIRED },
613
        { NULL, 'T', OPTPARSE_REQUIRED },
614
        { "unreach", 'u', OPTPARSE_NONE },
615
        { "version", 'v', OPTPARSE_NONE },
616
        { "reachable", 'x', OPTPARSE_REQUIRED },
617
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
618
        { "check-source", '0', OPTPARSE_NONE },
619
        { "print-tos", '0', OPTPARSE_NONE },
620
        { "print-ttl", '0', OPTPARSE_NONE },
621
#if defined(DEBUG) || defined(_DEBUG)
622
        { NULL, 'z', OPTPARSE_REQUIRED },
623
#endif
624
        { 0, 0, 0 }
625
    };
626

627
    float opt_value_float;
628
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
1,501✔
629
        switch (c) {
968✔
630
        case '0':
76✔
631
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
94✔
632
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
15✔
633
                  timestamp_format_flag = 1;
3✔
634
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
13✔
635
                  timestamp_format_flag = 2;
3✔
636
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
10✔
637
                  timestamp_format_flag = 3;
3✔
638
                }else{
1✔
639
                  usage(1);
6✔
640
                }
641
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
84✔
642
                check_source_flag = 1;
9✔
643
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
71✔
644
#ifdef IPV6
645
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
27✔
646
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
647
                    exit(1);
2✔
648
                }
649
                hints_ai_family = AF_INET;
25✔
650
#endif
651
                icmp_request_typ = 13;
25✔
652
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
25✔
653
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
46✔
654
                print_tos_flag = 1;
23✔
655
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
25✔
656
                print_ttl_flag = 1;
20✔
657
            } else {
4✔
658
                usage(1);
×
659
            }
660
            break;
88✔
661
        case '4':
30✔
662
#ifdef IPV6
663
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
41✔
664
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
665
                exit(1);
3✔
666
            }
667
            hints_ai_family = AF_INET;
38✔
668
#endif
669
            break;
38✔
670
        case '6':
28✔
671
#ifdef IPV6
672
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
31✔
673
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
674
                exit(1);
5✔
675
            }
676
            hints_ai_family = AF_INET6;
26✔
677
#else
678
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
679
            exit(1);
680
#endif
681
            break;
26✔
682
        case 'M':
2✔
683
#ifdef IP_MTU_DISCOVER
684
            if (socket4 >= 0) {
2✔
685
                int val = IP_PMTUDISC_DO;
2✔
686
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
687
                    perror("setsockopt IP_MTU_DISCOVER");
688
                }
689
            }
690
#ifdef IPV6
691
            if (socket6 >= 0) {
2✔
692
                int val = IPV6_PMTUDISC_DO;
2✔
693
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
694
                    perror("setsockopt IPV6_MTU_DISCOVER");
695
                }
696
            }
697
#endif
698
#else
699
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
700
            exit(1);
701
#endif
702
            break;
2✔
703

704
        case 't':
40✔
705
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
45✔
706
                usage(1);
6✔
707
            if (opt_value_float < 0) {
41✔
708
                usage(1);
3✔
709
            }
1✔
710
            timeout = opt_value_float * 1000000;
39✔
711
            timeout_flag = 1;
39✔
712
            break;
39✔
713

714
        case 'r':
36✔
715
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
40✔
716
                usage(1);
6✔
717
            break;
36✔
718

719
        case 'i':
30✔
720
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
36✔
721
                usage(1);
6✔
722
            if (opt_value_float < 0) {
32✔
723
                usage(1);
3✔
724
            }
1✔
725
            interval = opt_value_float * 1000000;
30✔
726
            break;
30✔
727

728
        case 'p':
68✔
729
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
99✔
730
                usage(1);
6✔
731
            if (opt_value_float < 0) {
95✔
732
                usage(1);
3✔
733
            }
1✔
734
            perhost_interval = opt_value_float * 1000000;
93✔
735

736
            break;
93✔
737

738
        case 'c':
60✔
739
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
85✔
740
                usage(1);
6✔
741

742
            count_flag = 1;
81✔
743
            break;
81✔
744

745
        case 'C':
24✔
746
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
33✔
747
                usage(1);
6✔
748

749
            count_flag = 1;
29✔
750
            report_all_rtts_flag = 1;
29✔
751
            break;
29✔
752

753
        case 'b':
14✔
754
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
21✔
755
                usage(1);
6✔
756
            size_flag = 1;
17✔
757
            break;
17✔
758

759
        case 'h':
2✔
760
            usage(0);
3✔
761
            break;
1✔
762

763
        case 'q':
12✔
764
            verbose_flag = 0;
17✔
765
            quiet_flag = 1;
17✔
766
            break;
17✔
767

768
        case 'Q':
22✔
769
            verbose_flag = 0;
33✔
770
            quiet_flag = 1;
33✔
771
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
772
                usage(1);
6✔
773
            if (opt_value_float < 0) {
29✔
774
                usage(1);
3✔
775
            }
1✔
776
            report_interval = opt_value_float * 1e9;
27✔
777

778
            /* recognize keyword(s) after number, ignore everything else */
779
            {
780
                char *comma = strchr(optparse_state.optarg, ',');
27✔
781
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
27✔
782
                    cumulative_stats_flag = 1;
6✔
783
                }
2✔
784
            }
785

786
            break;
27✔
787

788
        case 'e':
6✔
789
            elapsed_flag = 1;
8✔
790
            break;
8✔
791

792
        case 'm':
793
            multif_flag = 1;
×
794
            break;
×
795

796
        case 'N':
2✔
797
            netdata_flag = 1;
3✔
798
            break;
3✔
799

800
        case 'n':
4✔
801
            name_flag = 1;
6✔
802
            if (rdns_flag) {
6✔
803
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
804
                exit(1);
3✔
805
            }
806
            break;
3✔
807

808
        case 'd':
6✔
809
            rdns_flag = 1;
9✔
810
            if (name_flag) {
9✔
811
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
812
                exit(1);
3✔
813
            }
814
            break;
6✔
815

816
        case 'A':
2✔
817
            addr_flag = 1;
3✔
818
            break;
3✔
819

820
        case 'B':
10✔
821
            if (!(backoff = atof(optparse_state.optarg)))
12✔
822
                usage(1);
6✔
823

824
            break;
8✔
825

826
        case 's':
6✔
827
            stats_flag = 1;
9✔
828
            break;
9✔
829

830
        case 'D':
14✔
831
            timestamp_flag = 1;
21✔
832
            break;
21✔
833

834
        case 'R':
4✔
835
            random_data_flag = 1;
5✔
836
            break;
5✔
837

838
        case 'l':
2✔
839
            loop_flag = 1;
3✔
840
            backoff_flag = 0;
3✔
841
            break;
3✔
842

843
        case 'u':
8✔
844
            unreachable_flag = 1;
12✔
845
            break;
12✔
846

847
        case 'a':
18✔
848
            alive_flag = 1;
26✔
849
            break;
26✔
850

851
        case 'H':
8✔
852
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
12✔
853
                usage(1);
6✔
854
            break;
8✔
855

856
#if defined(DEBUG) || defined(_DEBUG)
857
        case 'z':
858
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
859
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
860
                    usage(1);
861

862
            break;
863
#endif /* DEBUG || _DEBUG */
864

865
        case 'v':
4✔
866
            printf("%s: Version %s\n", prog, VERSION);
6✔
867
            exit(0);
6✔
868

869
        case 'x':
8✔
870
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
10✔
871
                usage(1);
6✔
872
            break;
6✔
873

874
        case 'X':
6✔
875
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
876
                usage(1);
6✔
877
            fast_reachable = 1;
5✔
878
            break;
5✔
879

880
        case 'f':
18✔
881
            filename = optparse_state.optarg;
27✔
882
            break;
27✔
883
#ifdef SO_MARK
884
        case 'k':
8✔
885
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
8✔
886
                usage(1);
4✔
887

888
            if (socket4 >= 0)
4✔
889
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
4✔
890
                    perror("fwmark ipv4");
2✔
891

892
#ifdef IPV6
893
            if (socket6 >= 0)
4✔
894
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
4✔
895
                    perror("fwmark ipv6");
2✔
896
#endif
897

898
            break;
4✔
899
#endif
900

901
        case 'g':
98✔
902
            /* use IP list generation */
903
            /* mutually exclusive with using file input or command line targets */
904
            generate_flag = 1;
117✔
905
            break;
117✔
906

907
        case 'S':
10✔
908
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
909
                src_addr_set = 1;
6✔
910
                break;
6✔
911
            }
912
#ifdef IPV6
913
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
914
                src_addr6_set = 1;
4✔
915
                break;
4✔
916
            }
917
#endif
918
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
919
            exit(1);
3✔
920

921
        case 'I':
6✔
922
#ifdef SO_BINDTODEVICE
923
            if (socket4 >= 0) {
6✔
924
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
925
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
926
                    exit(1);
2✔
927
                }
928
            }
929
#ifdef IPV6
930
            if (socket6 >= 0) {
4✔
931
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
932
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
933
                    exit(1);
934
                }
935
            }
936
#endif
937
#else
938
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
1✔
939
            exit(3);
1✔
940
            ;
941
#endif
942
            break;
4✔
943

944
        case 'T':
2✔
945
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
946
            break;
3✔
947

948
        case 'O':
10✔
949
            if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
15✔
950
                if (socket4 >= 0) {
9✔
951
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
952
                        perror("setting type of service octet IP_TOS");
×
953
                    }
954
                }
3✔
955
#if defined(IPV6) && defined(IPV6_TCLASS)
956
                if (socket6 >= 0) {
9✔
957
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
958
                        perror("setting type of service octet IPV6_TCLASS");
×
959
                    }
960
                }
3✔
961
#endif
962
            }
3✔
963
            else {
964
                usage(1);
6✔
965
            }
966
            break;
11✔
967

968
        case 'o':
6✔
969
            outage_flag = 1;
9✔
970
            break;
9✔
971

972
        case '?':
4✔
973
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
974
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
975
            exit(1);
6✔
976
            break;
977
        }
978
    }
979

980
    /* permanently drop privileges */
981
    if (suid != getuid() && setuid(getuid())) {
475✔
982
        perror("fatal: failed to permanently drop privileges");
×
983
        /* continuing would be a security hole */
984
        exit(4);
×
985
    }
986

987
    /* validate various option settings */
988

989
#ifndef IPV6
990
    if (socket4 < 0) {
991
        crash_and_burn("can't create socket (must run as root?)");
992
    }
993
#else
994
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
475✔
995
        crash_and_burn("can't create socket (must run as root?)");
×
996
    }
997
#endif
998

999
    if (ttl > 255) {
473✔
1000
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
1001
        exit(1);
3✔
1002
    }
1003

1004
    if (unreachable_flag && alive_flag) {
470✔
1005
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
1006
        exit(1);
3✔
1007
    }
1008

1009
    if (count_flag && loop_flag) {
467✔
1010
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
1011
        exit(1);
3✔
1012
    }
1013

1014
#ifdef FPING_SAFE_LIMITS
1015
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
464✔
1016
        && getuid()) {
118✔
1017
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
1018
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
1019
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
1020
        exit(1);
6✔
1021
    }
1022
#endif
1023

1024
    if (ping_data_size > MAX_PING_DATA) {
458✔
1025
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1026
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1027
        exit(1);
3✔
1028
    }
1029

1030
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
455✔
1031
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1032
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1033
        exit(1);
6✔
1034
    }
1035

1036
    if (icmp_request_typ == 13 && size_flag != 0) {
449✔
1037
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1038
        exit(1);
9✔
1039
    }
1040

1041
    if (count_flag) {
440✔
1042
        if (verbose_flag)
100✔
1043
            per_recv_flag = 1;
59✔
1044

1045
        alive_flag = unreachable_flag = verbose_flag = 0;
100✔
1046
    }
28✔
1047

1048
    if (loop_flag) {
440✔
1049
        if (!report_interval)
×
1050
            per_recv_flag = 1;
×
1051

1052
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1053
    }
1054

1055
    if (alive_flag || unreachable_flag || min_reachable)
440✔
1056
        verbose_flag = 0;
37✔
1057

1058
    trials = (count > retry + 1) ? count : retry + 1;
456✔
1059

1060
    /* auto-tune default timeout for count/loop modes
1061
     * see also github #32 */
1062
    if (loop_flag || count_flag) {
456✔
1063
        if (!timeout_flag) {
100✔
1064
            timeout = perhost_interval;
95✔
1065
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
95✔
1066
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1067
            }
1068
        }
27✔
1069
    }
28✔
1070

1071
#if defined(DEBUG) || defined(_DEBUG)
1072
    if (debugging & DBG_TRACE)
1073
        trace_flag = 1;
1074

1075
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1076
        randomly_lose_flag = 1;
1077
        lose_factor = 1; /* ie, 1/4 */
1078
    }
1079

1080
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1081
        randomly_lose_flag = 1;
1082
        lose_factor = 5; /* ie, 3/4 */
1083
    }
1084

1085
    if (debugging & DBG_PRINT_PER_SYSTEM)
1086
        print_per_system_flag = 1;
1087

1088
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1089
        report_all_rtts_flag = 1;
1090

1091
    if (trace_flag) {
1092
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1093
            prog, count, retry, interval / 1e6);
1094
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1095
            perhost_interval / 1e6, timeout / 1e6);
1096
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1097
            ping_data_size, trials);
1098

1099
        if (verbose_flag)
1100
            fprintf(stderr, "  verbose_flag set\n");
1101
        if (multif_flag)
1102
            fprintf(stderr, "  multif_flag set\n");
1103
        if (name_flag)
1104
            fprintf(stderr, "  name_flag set\n");
1105
        if (addr_flag)
1106
            fprintf(stderr, "  addr_flag set\n");
1107
        if (stats_flag)
1108
            fprintf(stderr, "  stats_flag set\n");
1109
        if (unreachable_flag)
1110
            fprintf(stderr, "  unreachable_flag set\n");
1111
        if (alive_flag)
1112
            fprintf(stderr, "  alive_flag set\n");
1113
        if (elapsed_flag)
1114
            fprintf(stderr, "  elapsed_flag set\n");
1115
        if (version_flag)
1116
            fprintf(stderr, "  version_flag set\n");
1117
        if (count_flag)
1118
            fprintf(stderr, "  count_flag set\n");
1119
        if (loop_flag)
1120
            fprintf(stderr, "  loop_flag set\n");
1121
        if (backoff_flag)
1122
            fprintf(stderr, "  backoff_flag set\n");
1123
        if (per_recv_flag)
1124
            fprintf(stderr, "  per_recv_flag set\n");
1125
        if (report_all_rtts_flag)
1126
            fprintf(stderr, "  report_all_rtts_flag set\n");
1127
        if (randomly_lose_flag)
1128
            fprintf(stderr, "  randomly_lose_flag set\n");
1129
        if (print_per_system_flag)
1130
            fprintf(stderr, "  print_per_system_flag set\n");
1131
        if (outage_flag)
1132
            fprintf(stderr, "  outage_flag set\n");
1133
        if (netdata_flag)
1134
            fprintf(stderr, "  netdata_flag set\n");
1135
    }
1136
#endif /* DEBUG || _DEBUG */
1137

1138
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1139
    if (ttl > 0) {
456✔
1140
        if (socket4 >= 0) {
3✔
1141
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1142
                perror("setting time to live");
×
1143
            }
1144
        }
1✔
1145
#ifdef IPV6
1146
        if (socket6 >= 0) {
3✔
1147
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1148
                perror("setting time to live");
×
1149
            }
1150
        }
1✔
1151
#endif
1152
    }
1✔
1153

1154
#if HAVE_SO_TIMESTAMPNS
1155
    {
1156
        int opt = 1;
334✔
1157
        if (socket4 >= 0) {
334✔
1158
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
334✔
1159
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1160
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1161
                }
1162
            }
1163
        }
1164
#ifdef IPV6
1165
        if (socket6 >= 0) {
334✔
1166
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
334✔
1167
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1168
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1169
                }
1170
            }
1171
        }
1172
#endif
1173
    }
1174
#endif
1175

1176
    update_current_time();
456✔
1177
    start_time = current_time_ns;
456✔
1178

1179
    /* handle host names supplied on command line or in a file */
1180
    /* if the generate_flag is on, then generate the IP list */
1181

1182
    argv = &argv[optparse_state.optind];
456✔
1183
    argc -= optparse_state.optind;
456✔
1184

1185
    /* calculate how many ping can be in-flight per host */
1186
    if (count_flag) {
456✔
1187
        event_storage_count = count;
115✔
1188
    }
43✔
1189
    else if (loop_flag) {
341✔
1190
        if (perhost_interval > timeout) {
×
1191
            event_storage_count = 1;
×
1192
        }
1193
        else {
1194
            event_storage_count = 1 + timeout / perhost_interval;
×
1195
        }
1196
    }
1197
    else {
1198
        event_storage_count = 1;
341✔
1199
    }
1200

1201
    /* file and generate are mutually exclusive */
1202
    /* file and command line are mutually exclusive */
1203
    /* generate requires command line parameters beyond the switches */
1204
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
456✔
1205
        usage(1);
19✔
1206

1207
    /* if no conditions are specified, then assume input from stdin */
1208
    if (!*argv && !filename && !generate_flag)
438✔
1209
        filename = "-";
69✔
1210

1211
    if (*argv && !generate_flag) {
438✔
1212
        while (*argv) {
520✔
1213
            add_name(*argv);
289✔
1214
            ++argv;
289✔
1215
        }
1216
    }
57✔
1217
    else if (filename) {
207✔
1218
        FILE *ping_file;
1219
        char line[MAX_TARGET_NAME_LEN + 1];
1220
        char host[MAX_TARGET_NAME_LEN + 1];
1221
        char scratch[MAX_TARGET_NAME_LEN + 1];
1222
        int skip, non_empty;
1223

1224
        if (strcmp(filename, "-") == 0)
96✔
1225
            ping_file = fdopen(0, "r");
72✔
1226
        else
1227
            ping_file = fopen(filename, "r");
24✔
1228

1229
        if (!ping_file)
96✔
1230
            errno_crash_and_burn("fopen");
3✔
1231

1232
        /*
1233
         * Read the first word of every non-comment line, skip everything else.
1234
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1235
         * character is a '#' are interpreted as comments and ignored.)
1236
        */
1237
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1238
            skip = non_empty = 0;
309✔
1239

1240
            /* skip over a prefix of the line where sscanf finds nothing */
1241
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1242
                continue;
99✔
1243
            }
1244

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

1296
            if (non_empty)
186✔
1297
                add_name(host);
144✔
1298
        }
1299

1300
        fclose(ping_file);
70✔
1301
    }
24✔
1302
    else if (*argv && generate_flag) {
111✔
1303
        if (argc == 1) {
111✔
1304
            /* one target: we expect a cidr range (n.n.n.n/m) */
1305
            add_cidr(argv[0]);
56✔
1306
        }
10✔
1307
        else if (argc == 2) {
55✔
1308
            add_range(argv[0], argv[1]);
52✔
1309
        }
6✔
1310
        else {
1311
            usage(1);
3✔
1312
        }
1313
    }
17✔
1314
    else {
1315
        usage(1);
×
1316
    }
1317

1318
    if (!num_hosts) {
358✔
1319
        exit(num_noaddress ? 2 : 1);
33✔
1320
    }
1321

1322
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
325✔
1323
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
16✔
1324
    }
2✔
1325
#ifdef IPV6
1326
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
323✔
1327
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
14✔
1328
    }
1329
#endif
1330

1331
    /* allocate and initialize array to map host nr to host_entry */
1332
    {
1333
        struct event *cursor = event_queue_ping.first;
321✔
1334
        int i = 0;
321✔
1335
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
321✔
1336
        if (!table)
321✔
1337
            crash_and_burn("Can't malloc array of hosts");
×
1338
        /* initialize table of hosts. we know that we have ping events scheduled
1339
         * for each of them */
1340
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
826✔
1341
            table[i] = cursor->host;
505✔
1342
            cursor->host->i = i;
505✔
1343
            i++;
505✔
1344
        }
135✔
1345
    }
1346

1347
    init_ping_buffer_ipv4(ping_data_size);
321✔
1348
#ifdef IPV6
1349
    init_ping_buffer_ipv6(ping_data_size);
321✔
1350
#endif
1351

1352
#ifdef USE_SIGACTION
1353
    memset(&act, 0, sizeof(act));
321✔
1354
    act.sa_handler = signal_handler;
321✔
1355
    sigemptyset(&act.sa_mask);
321✔
1356
    sigaddset(&act.sa_mask, SIGINT);
321✔
1357
    sigaddset(&act.sa_mask, SIGQUIT);
321✔
1358
    act.sa_flags = SA_RESTART;
321✔
1359
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
321✔
1360
        crash_and_burn("failure to set signal handler");
24✔
1361
    }
24✔
1362
#else
1363
    signal(SIGINT, signal_handler);
1364
    signal(SIGQUIT, signal_handler);
1365
#endif
1366
    setlinebuf(stdout);
297✔
1367

1368
    if (report_interval) {
297✔
1369
        next_report_time = current_time_ns + report_interval;
24✔
1370
    }
8✔
1371

1372
    last_send_time = 0;
297✔
1373

1374
    seqmap_init();
297✔
1375

1376
    /* main loop */
1377
    main_loop();
297✔
1378

1379
/* Debug: CPU Performance */
1380
#if defined(DEBUG) || defined(_DEBUG)
1381
    perf_cpu_end = clock();
1382
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1383
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1384
#endif /* DEBUG || _DEBUG */
1385

1386
    finish();
297✔
1387

1388
    return 0;
65✔
1389
}
1390

1391
static inline int64_t timespec_ns(struct timespec *a)
5,014✔
1392
{
1393
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,014✔
1394
}
1395

1396
#if HAVE_SO_TIMESTAMPNS
1397
/* convert a struct timeval to nanoseconds */
1398
static inline int64_t timeval_ns(struct timeval *a)
1399
{
1400
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1401
}
1402
#endif /* HAVE_SO_TIMESTAMPNS */
1403

1404
void add_cidr(char *addr)
54✔
1405
{
1406
    char *addr_end;
1407
    char *mask_str;
1408
    unsigned long mask;
1409
    int ret;
1410
    struct addrinfo addr_hints;
1411
    struct addrinfo *addr_res;
1412
    unsigned long net_addr;
1413
#ifdef IPV6
1414
    uint64_t net_upper, net_lower;
1415
    char *scope_str;
1416
#endif /* IPV6 */
1417

1418
    /* Split address from mask */
1419
    addr_end = strrchr(addr, '/');
54✔
1420
    if (addr_end == NULL) {
54✔
1421
        usage(1);
3✔
1422
    }
1✔
1423
    mask_str = addr_end + 1;
52✔
1424

1425
#ifdef IPV6
1426
    /* IPv6 addresses can have a scope */
1427
    scope_str = strchr(addr, '%');
52✔
1428
    if (scope_str && mask_str < scope_str) {
52✔
1429
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1430
        exit(1);
2✔
1431
    }
1432
#endif /*IPV6 */
1433

1434
    *addr_end = '\0';
50✔
1435
    mask = atoi(mask_str);
50✔
1436

1437
    /* parse address */
1438
    memset(&addr_hints, 0, sizeof(struct addrinfo));
50✔
1439
    addr_hints.ai_family = hints_ai_family;
50✔
1440
    addr_hints.ai_flags = AI_NUMERICHOST;
50✔
1441
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
50✔
1442
    if (ret) {
50✔
1443
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1444
        exit(1);
7✔
1445
    }
1446
    if (addr_res->ai_family == AF_INET) {
43✔
1447
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
21✔
1448
        freeaddrinfo(addr_res);
21✔
1449
        add_cidr_ipv4(net_addr, mask);
21✔
1450
#ifdef IPV6
1451
    } else if (addr_res->ai_family == AF_INET6) {
29✔
1452
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1453
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1454
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1455
        freeaddrinfo(addr_res);
22✔
1456
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1457
#endif /* IPV6 */
1458
    } else {
1459
        freeaddrinfo(addr_res);
×
1460
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1461
        exit(1);
×
1462
    }
1463
}
27✔
1464

1465
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
21✔
1466
{
1467
    unsigned long bitmask;
1468
    unsigned long net_last;
1469

1470
    /* check mask */
1471
    if (mask < 1 || mask > 32) {
21✔
1472
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1473
        exit(1);
6✔
1474
    }
1475

1476
    /* convert mask integer from 1 to 32 to a bitmask */
1477
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
15✔
1478

1479
    /* calculate network range */
1480
    net_addr &= bitmask;
15✔
1481
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
15✔
1482

1483
    /* exclude network and broadcast address for regular prefixes */
1484
    if (mask < 31) {
15✔
1485
        net_last--;
9✔
1486
        net_addr++;
9✔
1487
    }
3✔
1488

1489
    /* add all hosts in that network (net_addr and net_last inclusive) */
1490
    add_addr_range_ipv4(net_addr, net_last);
15✔
1491
}
13✔
1492

1493
#ifdef IPV6
1494
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1495
{
1496
    uint64_t bitmask_lower;
1497
    uint64_t last_lower;
1498

1499
    /* check mask -- 2^63 addresses should suffice for now */
1500
    if (mask < 65 || mask > 128) {
23✔
1501
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1502
        exit(1);
7✔
1503
    }
1504

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

1508
    /* calculate network range */
1509
    net_lower &= bitmask_lower;
16✔
1510
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1511

1512
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1513
}
12✔
1514
#endif /* IPV6 */
1515

1516
void add_range(char *start, char *end)
52✔
1517
{
1518
    struct addrinfo addr_hints;
1519
    struct addrinfo *addr_res;
1520
    unsigned long start_long;
1521
    unsigned long end_long;
1522
    int ret;
1523
#ifdef IPV6
1524
    uint64_t start_upper, start_lower;
1525
    uint64_t end_upper, end_lower;
1526
    char *start_scope_str, *end_scope_str;
1527

1528
    /*
1529
     * The compiler does not know that setting the address family hint to
1530
     * ensure that start and end are from the same address family also
1531
     * ensures that either start_long and end_long are initialized and used,
1532
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1533
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1534
     * supported to suppress compiler warnings.
1535
     */
1536
    start_long = -1;
52✔
1537
    end_long = 0;
52✔
1538
    start_upper = start_lower = -1;
52✔
1539
    end_upper = end_lower = 0;
52✔
1540
    start_scope_str = end_scope_str = NULL;
52✔
1541
#endif /* IPV6 */
1542

1543
    /* parse start address */
1544
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1545
    addr_hints.ai_family = hints_ai_family;
52✔
1546
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1547
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1548
    if (ret) {
52✔
1549
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1550
        exit(1);
9✔
1551
    }
1552
    /* start and end must be from the same address family */
1553
    hints_ai_family = addr_res->ai_family;
43✔
1554
    if (addr_res->ai_family == AF_INET) {
43✔
1555
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1556
        freeaddrinfo(addr_res);
17✔
1557
#ifdef IPV6
1558
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1559
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1560
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1561
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1562
        freeaddrinfo(addr_res);
26✔
1563
#endif /* IPV6 */
1564
    } else {
1565
        freeaddrinfo(addr_res);
×
1566
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1567
        exit(1);
×
1568
    }
1569

1570
    /* IPv6 addresses can have a scope */
1571
    if (hints_ai_family == AF_INET6) {
43✔
1572
        start_scope_str = strchr(start, '%');
26✔
1573
        end_scope_str = strchr(end, '%');
26✔
1574
        if ((!start_scope_str && end_scope_str) ||
26✔
1575
            (start_scope_str && !end_scope_str) ||
24✔
1576
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1577
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1578
                exit(1);
6✔
1579
        }
1580
    }
1581

1582
    /* parse end address */
1583
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1584
    addr_hints.ai_family = hints_ai_family;
37✔
1585
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1586
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1587
    if (ret) {
37✔
1588
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1589
        exit(1);
7✔
1590
    }
1591
    if (addr_res->ai_family == AF_INET) {
30✔
1592
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1593
        freeaddrinfo(addr_res);
12✔
1594
        add_addr_range_ipv4(start_long, end_long);
12✔
1595
#ifdef IPV6
1596
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1597
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1598
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1599
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1600
        freeaddrinfo(addr_res);
18✔
1601
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1602
#endif /* IPV6 */
1603
    } else {
1604
        freeaddrinfo(addr_res);
×
1605
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1606
        exit(1);
×
1607
    }
1608
}
24✔
1609

1610
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
27✔
1611
{
1612
    /* check if generator limit is exceeded */
1613
    if (end_long >= start_long + MAX_GENERATE) {
27✔
1614
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1615
        exit(1);
6✔
1616
    }
1617

1618
    /* generate */
1619
    for (; start_long <= end_long; start_long++) {
84✔
1620
        struct in_addr in_addr_tmp;
1621
        char buffer[20];
1622
        in_addr_tmp.s_addr = htonl(start_long);
63✔
1623
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
63✔
1624
        add_name(buffer);
63✔
1625
    }
21✔
1626
}
21✔
1627

1628
#ifdef IPV6
1629
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1630
{
1631
    int i;
1632
    uint64_t ret = 0;
134✔
1633
    for (i = 0; i < 8; i++) {
1,206✔
1634
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1635
    }
16✔
1636
    return ret;
134✔
1637
}
1638

1639
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1640
{
1641
    int i;
1642
    for (i = 0; i < 8; i++) {
720✔
1643
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1644
    }
1645
}
80✔
1646

1647
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1648
                         uint64_t end_upper, uint64_t end_lower,
1649
                         const char *scope_str)
1650
{
1651
    struct in6_addr in6_addr_tmp;
1652
    char buffer[100];
1653

1654
    /* prevent generating too many addresses */
1655
    if ((start_upper + 1 < end_upper) ||
34✔
1656
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1657
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1658
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1659
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1660
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1661
        exit(1);
8✔
1662
    }
1663

1664
    while ((start_upper < end_upper) ||
66✔
1665
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1666
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1667
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1668
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1669
        if (scope_str) {
40✔
1670
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1671
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1672
                exit(1);
×
1673
            }
1674
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1675
        }
1676
        add_name(buffer);
40✔
1677
        start_lower++;
40✔
1678
        if (start_lower == 0) {
40✔
1679
            start_upper++;
2✔
1680
        }
1681
    }
1682
}
26✔
1683
#endif /* IPv6 */
1684

1685
void main_loop()
309✔
1686
{
1687
    int64_t lt;
1688
    int64_t wait_time_ns;
1689
    struct event *event;
1690
    struct host_entry *h;
1691

1692
    while (event_queue_ping.first || event_queue_timeout.first) {
1,728✔
1693
        dbg_printf("%s", "# main_loop\n");
1694

1695
        /* timeout event ? */
1696
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,436✔
1697
            event = ev_dequeue(&event_queue_timeout);
95✔
1698
            h = event->host;
95✔
1699

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

1702
            stats_add(h, event->ping_index, 0, -1);
95✔
1703

1704
            if (per_recv_flag) {
95✔
1705
                if (timestamp_flag) {
19✔
1706
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1707
                }
1708
                printf("%-*s : [%d], timed out",
19✔
1709
                    max_hostname_len, h->host, event->ping_index);
5✔
1710
                if (h->num_recv > 0) {
19✔
1711
                    printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1712
                }
1713
                else {
1714
                    printf(" (NaN avg, ");
19✔
1715
                }
1716
                if (h->num_recv <= h->num_sent) {
19✔
1717
                    printf("%d%% loss)",
19✔
1718
                        ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1719
                }
5✔
1720
                else {
1721
                    printf("%d%% return)",
×
1722
                        (h->num_recv_total * 100) / h->num_sent);
×
1723
                }
1724
                printf("\n");
19✔
1725
            }
5✔
1726

1727
            /* do we need to send a retry? */
1728
            if (!loop_flag && !count_flag) {
95✔
1729
                if (h->num_sent < retry + 1) {
52✔
1730
                    if (backoff_flag) {
30✔
1731
                        h->timeout *= backoff;
30✔
1732
                    }
6✔
1733
                    send_ping(h, event->ping_index);
30✔
1734
                }
6✔
1735
            }
10✔
1736

1737
            /* note: we process first timeout events, because we might need to
1738
             * wait to process ping events, while we for sure never need to
1739
             * wait for timeout events.
1740
             */
1741
            continue;
95✔
1742
        }
1743

1744
        /* ping event ? */
1745
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,572✔
1746
            /* Make sure that we don't ping more than once every "interval" */
1747
            lt = current_time_ns - last_send_time;
1,060✔
1748
            if (lt < interval)
1,060✔
1749
                goto wait_for_reply;
255✔
1750

1751
            /* Dequeue the event */
1752
            event = ev_dequeue(&event_queue_ping);
805✔
1753
            h = event->host;
805✔
1754

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

1757
            /* Send the ping */
1758
            send_ping(h, event->ping_index);
805✔
1759

1760
            /* Loop and count mode: schedule next ping */
1761
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
805✔
1762
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
315✔
1763
            }
101✔
1764
        }
231✔
1765

1766
    wait_for_reply:
572✔
1767

1768
        /* When is the next ping next event? */
1769
        wait_time_ns = -1;
1,341✔
1770
        if (event_queue_ping.first) {
1,341✔
1771
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
974✔
1772
            if (wait_time_ns < 0)
974✔
1773
                wait_time_ns = 0;
540✔
1774
            /* make sure that we wait enough, so that the inter-ping delay is
1775
             * bigger than 'interval' */
1776
            if (wait_time_ns < interval) {
974✔
1777
                lt = current_time_ns - last_send_time;
548✔
1778
                if (lt < interval) {
548✔
1779
                    wait_time_ns = interval - lt;
548✔
1780
                }
200✔
1781
            }
200✔
1782

1783
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1784
        }
305✔
1785

1786
        /* When is the next timeout event? */
1787
        if (event_queue_timeout.first) {
1,341✔
1788
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
844✔
1789
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
844✔
1790
                wait_time_ns = wait_time_timeout;
360✔
1791
                if (wait_time_ns < 0) {
360✔
1792
                    wait_time_ns = 0;
×
1793
                }
1794
            }
88✔
1795

1796
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1797
        }
244✔
1798

1799
        /* When is the next report due? */
1800
        if (report_interval && (loop_flag || count_flag)) {
1,341✔
1801
            int64_t wait_time_next_report = next_report_time - current_time_ns;
203✔
1802
            if (wait_time_next_report < wait_time_ns) {
203✔
1803
                wait_time_ns = wait_time_next_report;
69✔
1804
                if (wait_time_ns < 0) {
69✔
1805
                    wait_time_ns = 0;
×
1806
                }
1807
            }
23✔
1808

1809
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1810
        }
65✔
1811

1812
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1813
        if (wait_time_ns == -1) {
1,341✔
1814
            break;
14✔
1815
        }
1816

1817
        /* end of loop was requested by interrupt signal handler */
1818
        if (finish_requested) {
1,327✔
1819
            break;
3✔
1820
        }
1821

1822
        /* Receive replies */
1823
        /* (this is what sleeps during each loop iteration) */
1824
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1825
        if (wait_for_reply(wait_time_ns)) {
1,324✔
1826
            while (wait_for_reply(0))
1,140✔
1827
                ; /* process other replies in the queue */
1828
        }
214✔
1829

1830
        update_current_time();
1,324✔
1831

1832
        if (status_snapshot) {
1,324✔
1833
            status_snapshot = 0;
×
1834
            print_per_system_splits();
×
1835
        }
1836

1837
        /* Print report */
1838
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,324✔
1839
            if (netdata_flag)
42✔
1840
                print_netdata();
3✔
1841
            else
1842
                print_per_system_splits();
39✔
1843

1844
            while (current_time_ns >= next_report_time) {
84✔
1845
                next_report_time += report_interval;
42✔
1846
            }
1847
        }
14✔
1848
    }
1849
}
309✔
1850

1851
/************************************************************
1852

1853
  Function: signal_handler
1854

1855
*************************************************************
1856

1857
  Inputs:  int signum
1858

1859
  Description:
1860

1861
  SIGQUIT signal handler - set flag and return
1862
  SIGINT signal handler - set flag and return
1863

1864
************************************************************/
1865

1866
void signal_handler(int signum)
×
1867
{
1868
    switch (signum) {
×
1869
    case SIGINT:
1870
        finish_requested = 1;
×
1871
        break;
×
1872

1873
    case SIGQUIT:
1874
        status_snapshot = 1;
×
1875
        break;
×
1876
    }
1877
}
×
1878

1879
/************************************************************
1880

1881
  Function: update_current_time
1882

1883
*************************************************************/
1884

1885
void update_current_time()
4,088✔
1886
{
1887
    clock_gettime(CLOCKID, &current_time);
4,088✔
1888
    current_time_ns = timespec_ns(&current_time);
4,088✔
1889
}
4,088✔
1890

1891
/************************************************************
1892

1893
  Function: finish
1894

1895
*************************************************************
1896

1897
  Inputs:  void (none)
1898

1899
  Description:
1900

1901
  Main program clean up and exit point
1902

1903
************************************************************/
1904

1905
void finish()
309✔
1906
{
1907
    int i;
1908
    HOST_ENTRY *h;
1909

1910
    update_current_time();
309✔
1911
    end_time = current_time_ns;
309✔
1912

1913
    /* tot up unreachables */
1914
    for (i = 0; i < num_hosts; i++) {
814✔
1915
        h = table[i];
505✔
1916

1917
        if (!h->num_recv) {
505✔
1918
            num_unreachable++;
80✔
1919

1920
            if (verbose_flag || unreachable_flag) {
80✔
1921
                printf("%s", h->host);
44✔
1922

1923
                if (verbose_flag)
44✔
1924
                    printf(" is unreachable");
41✔
1925

1926
                printf("\n");
44✔
1927
            }
2✔
1928
        }
12✔
1929
    }
135✔
1930

1931
    if (count_flag || loop_flag)
309✔
1932
        print_per_system_stats();
100✔
1933
#if defined(DEBUG) || defined(_DEBUG)
1934
    else if (print_per_system_flag)
1935
        print_per_system_stats();
1936
#endif /* DEBUG || _DEBUG */
1937

1938
    if (stats_flag)
309✔
1939
        print_global_stats();
9✔
1940

1941
    if (min_reachable) {
309✔
1942
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
1943
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1944
            exit(0);
6✔
1945
        }
1946
        else {
1947
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
1948
            exit(1);
3✔
1949
        }
1950
    }
1951

1952
    if (num_noaddress)
300✔
1953
        exit(2);
3✔
1954
    else if (num_alive != num_hosts)
297✔
1955
        exit(1);
41✔
1956

1957
    exit(0);
256✔
1958
}
1959

1960
/************************************************************
1961

1962
  Function: print_per_system_stats
1963

1964
*************************************************************
1965

1966
  Inputs:  void (none)
1967

1968
  Description:
1969

1970

1971
************************************************************/
1972

1973
void print_per_system_stats(void)
100✔
1974
{
1975
    int i, j, avg, outage_ms;
1976
    HOST_ENTRY *h;
1977
    int64_t resp;
1978

1979
    if (verbose_flag || per_recv_flag)
100✔
1980
        fprintf(stderr, "\n");
59✔
1981

1982
    for (i = 0; i < num_hosts; i++) {
234✔
1983
        h = table[i];
134✔
1984
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
1985

1986
        if (report_all_rtts_flag) {
134✔
1987
            for (j = 0; j < h->num_sent; j++) {
248✔
1988
                if ((resp = h->resp_times[j]) >= 0)
204✔
1989
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
1990
                else
1991
                    fprintf(stderr, " -");
8✔
1992
            }
64✔
1993

1994
            fprintf(stderr, "\n");
44✔
1995
        }
12✔
1996
        else {
1997
            if (h->num_recv <= h->num_sent) {
90✔
1998
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
1999
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
2000

2001
                if (outage_flag) {
90✔
2002
                    /* Time outage total */
2003
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
2004
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
2005
                }
3✔
2006
            }
26✔
2007
            else {
2008
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2009
                    h->num_sent, h->num_recv,
2010
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2011
            }
2012

2013
            if (h->num_recv) {
90✔
2014
                avg = h->total_time / h->num_recv;
79✔
2015
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
2016
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
2017
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
2018
            }
23✔
2019

2020
            fprintf(stderr, "\n");
90✔
2021
        }
2022
    }
38✔
2023
}
100✔
2024

2025
/************************************************************
2026

2027
  Function: print_netdata
2028

2029
*************************************************************
2030

2031
  Inputs:  void (none)
2032

2033
  Description:
2034

2035

2036
************************************************************/
2037

2038
void print_netdata(void)
3✔
2039
{
2040
    static int sent_charts = 0;
2041

2042
    int i;
2043
    int64_t avg;
2044
    HOST_ENTRY *h;
2045

2046
    for (i = 0; i < num_hosts; i++) {
6✔
2047
        h = table[i];
3✔
2048

2049
        if (!sent_charts) {
3✔
2050
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2051
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2052
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2053
        }
1✔
2054

2055
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2056
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2057
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2058
        printf("END\n");
3✔
2059

2060
        if (!sent_charts) {
3✔
2061
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2062
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2063
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2064
        }
1✔
2065

2066
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2067
        /*
2068
        if( h->num_recv_i <= h->num_sent_i )
2069
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2070
        else
2071
            printf("SET lost = 0\n");
2072
*/
2073

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

2077
        if (!sent_charts) {
3✔
2078
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2079
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2080
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2081
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2082
        }
1✔
2083

2084
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2085
        if (h->num_recv_i) {
3✔
2086
            avg = h->total_time_i / h->num_recv_i;
3✔
2087
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2088
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2089
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2090
        }
1✔
2091
        printf("END\n");
3✔
2092

2093
        stats_reset_interval(h);
3✔
2094
    }
1✔
2095

2096
    sent_charts = 1;
3✔
2097
}
3✔
2098

2099
/************************************************************
2100

2101
  Function: print_per_system_splits
2102

2103
*************************************************************
2104

2105
  Inputs:  void (none)
2106

2107
  Description:
2108

2109

2110
************************************************************/
2111

2112
void print_per_system_splits(void)
39✔
2113
{
2114
    int i, avg, outage_ms_i;
2115
    HOST_ENTRY *h;
2116
    struct tm *curr_tm;
2117

2118
    if (verbose_flag || per_recv_flag)
39✔
2119
        fprintf(stderr, "\n");
×
2120

2121
    update_current_time();
39✔
2122
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2123
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2124
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2125

2126
    for (i = 0; i < num_hosts; i++) {
78✔
2127
        h = table[i];
39✔
2128
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2129

2130
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2131
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2132
                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✔
2133

2134
            if (outage_flag) {
39✔
2135
                /* Time outage  */
2136
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2137
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2138
            }
4✔
2139
        }
13✔
2140
        else {
2141
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2142
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2143
        }
2144

2145
        if (h->num_recv_i) {
39✔
2146
            avg = h->total_time_i / h->num_recv_i;
27✔
2147
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2148
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2149
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2150
        }
9✔
2151

2152
        fprintf(stderr, "\n");
39✔
2153
        if (!cumulative_stats_flag) {
39✔
2154
            stats_reset_interval(h);
27✔
2155
        }
9✔
2156
    }
13✔
2157
}
39✔
2158

2159
/************************************************************
2160

2161
  Function: print_global_stats
2162

2163
*************************************************************
2164

2165
  Inputs:  void (none)
2166

2167
  Description:
2168

2169

2170
************************************************************/
2171

2172
void print_global_stats(void)
9✔
2173
{
2174
    fprintf(stderr, "\n");
9✔
2175
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2176
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2177
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2178
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2179
    fprintf(stderr, "\n");
9✔
2180
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2181
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2182
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2183
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2184
    fprintf(stderr, "\n");
9✔
2185

2186
    if (total_replies == 0) {
9✔
2187
        min_reply = 0;
3✔
2188
        max_reply = 0;
3✔
2189
        total_replies = 1;
3✔
2190
        sum_replies = 0;
3✔
2191
    }
1✔
2192

2193
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2194
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2195
        sprint_tm(sum_replies / total_replies));
3✔
2196
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2197
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2198
        (end_time - start_time) / 1e9);
9✔
2199
    fprintf(stderr, "\n");
9✔
2200
}
9✔
2201

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

2204
  Function: send_ping
2205

2206
*************************************************************
2207

2208
  Inputs:  int s, HOST_ENTRY *h
2209

2210
  Description:
2211

2212
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2213
  will be added on by the kernel.  The ID field is our UNIX process ID,
2214
  and the sequence number is an index into an array of outstanding
2215
  ping requests. The sequence number will later be used to quickly
2216
  figure out who the ping reply came from.
2217

2218
************************************************************/
2219

2220
int send_ping(HOST_ENTRY *h, int index)
835✔
2221
{
2222
    int n;
2223
    int myseq;
2224
    int ret = 1;
835✔
2225
    uint8_t proto = ICMP_ECHO;
835✔
2226

2227
    update_current_time();
835✔
2228
    h->last_send_time = current_time_ns;
835✔
2229
    myseq = seqmap_add(h->i, index, current_time_ns);
835✔
2230

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

2233
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
835✔
2234
        if(icmp_request_typ == 13)
712✔
2235
            proto = ICMP_TSTAMP;
16✔
2236
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
712✔
2237
    }
222✔
2238
#ifdef IPV6
2239
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2240
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2241
    }
15✔
2242
#endif
2243
    else {
2244
        return 0;
×
2245
    }
2246

2247
    /* error sending? */
2248
    if (
598✔
2249
        (n < 0)
237✔
2250
#if defined(EHOSTDOWN)
2251
        && errno != EHOSTDOWN
271✔
2252
#endif
2253
    ) {
2254
        if (verbose_flag) {
34✔
2255
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2256
        }
2257
        else {
2258
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2259
        }
2260

2261
        h->num_sent++;
34✔
2262
        h->num_sent_i++;
34✔
2263
        if (!loop_flag)
34✔
2264
            h->resp_times[index] = RESP_ERROR;
34✔
2265

2266
        ret = 0;
34✔
2267
    }
2268
    else {
2269
        /* schedule timeout */
2270
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
801✔
2271

2272
        /* mark this trial as outstanding */
2273
        if (!loop_flag) {
801✔
2274
            h->resp_times[index] = RESP_WAITING;
801✔
2275
        }
237✔
2276
    }
2277

2278
    num_pingsent++;
835✔
2279
    last_send_time = h->last_send_time;
835✔
2280

2281
    return (ret);
835✔
2282
}
237✔
2283

2284
int socket_can_read(struct timeval *timeout)
2,464✔
2285
{
2286
    int nfound;
2287
    fd_set readset;
2288
    int socketmax;
2289

2290
#ifndef IPV6
2291
    socketmax = socket4;
2292
#else
2293
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,464✔
2294
#endif
2295

2296
select_again:
1,858✔
2297
    FD_ZERO(&readset);
32,192✔
2298
    if (socket4 >= 0)
2,464✔
2299
        FD_SET(socket4, &readset);
2,464✔
2300
#ifdef IPV6
2301
    if (socket6 >= 0)
2,464✔
2302
        FD_SET(socket6, &readset);
2,464✔
2303
#endif
2304

2305
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,464✔
2306
    if (nfound < 0) {
2,464✔
2307
        if (errno == EINTR) {
×
2308
            /* interrupted system call: redo the select */
2309
            goto select_again;
×
2310
        }
2311
        else {
2312
            perror("select");
×
2313
        }
2314
    }
2315

2316
    if (nfound > 0) {
2,464✔
2317
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,140✔
2318
            return socket4;
1,049✔
2319
        }
2320
#ifdef IPV6
2321
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
91✔
2322
            return socket6;
91✔
2323
        }
2324
#endif
2325
    }
2326

2327
    return -1;
1,324✔
2328
}
606✔
2329

2330
int receive_packet(int64_t wait_time,
2,464✔
2331
#if HAVE_SO_TIMESTAMPNS
2332
    int64_t *reply_timestamp,
2333
#else
2334
    int64_t *reply_timestamp __attribute__((unused)),
2335
#endif
2336
    struct sockaddr *reply_src_addr,
2337
    size_t reply_src_addr_len,
2338
    char *reply_buf,
2339
    size_t reply_buf_len)
2340
{
2341
    struct timeval to;
2342
    int s = 0;
2,464✔
2343
    int recv_len;
2344
    static unsigned char msg_control[40];
2345
    struct iovec msg_iov = {
3,676✔
2346
        reply_buf,
606✔
2347
        reply_buf_len
606✔
2348
    };
2349
    struct msghdr recv_msghdr = {0};
2,464✔
2350
    recv_msghdr.msg_name = reply_src_addr;
2,464✔
2351
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,464✔
2352
    recv_msghdr.msg_iov = &msg_iov;
2,464✔
2353
    recv_msghdr.msg_iovlen = 1;
2,464✔
2354
    recv_msghdr.msg_control = &msg_control;
2,464✔
2355
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,464✔
2356
#if HAVE_SO_TIMESTAMPNS
2357
    struct cmsghdr *cmsg;
2358
#endif
2359

2360
    /* Wait for a socket to become ready */
2361
    if (wait_time) {
2,464✔
2362
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,324✔
2363
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,324✔
2364
    }
392✔
2365
    else {
2366
        to.tv_sec = 0;
1,140✔
2367
        to.tv_usec = 0;
1,140✔
2368
    }
2369
    s = socket_can_read(&to);
2,464✔
2370
    if (s == -1) {
2,464✔
2371
        return 0; /* timeout */
1,324✔
2372
    }
2373

2374
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,140✔
2375
    if (recv_len <= 0) {
1,140✔
2376
        return 0;
×
2377
    }
2378

2379
#if HAVE_SO_TIMESTAMPNS
2380
    /* ancilliary data */
2381
    {
2382
        struct timespec reply_timestamp_ts;
2383
        struct timeval reply_timestamp_tv;
2384
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
926✔
2385
             cmsg != NULL;
1,852✔
2386
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
926✔
2387
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
926✔
2388
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
926✔
2389
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
926✔
2390
            }
2391
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
926✔
2392
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2393
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2394
            }
2395
        }
2396
    }
2397
#endif
2398

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

2406
    return recv_len;
1,140✔
2407
}
606✔
2408

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

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

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

2435
    /* maximum */
2436
    if (!h->max_reply || latency > h->max_reply) {
706✔
2437
        h->max_reply = latency;
548✔
2438
    }
157✔
2439
    if (!h->max_reply_i || latency > h->max_reply_i) {
706✔
2440
        h->max_reply_i = latency;
566✔
2441
    }
161✔
2442

2443
    /* minimum */
2444
    if (!h->min_reply || latency < h->min_reply) {
706✔
2445
        h->min_reply = latency;
470✔
2446
    }
148✔
2447
    if (!h->min_reply_i || latency < h->min_reply_i) {
706✔
2448
        h->min_reply_i = latency;
498✔
2449
    }
158✔
2450

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2581
        return -1;
×
2582
    }
2583

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

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

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

2603
    return hlen;
627✔
2604
}
199✔
2605

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

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

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

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

2636
        /* reply icmp packet (ICMPv6 header) followed by "sent packet" (IPv6 + ICMPv6 header) */
NEW
2637
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
×
2638
            /* discard ICMPv6 message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
UNCOV
2639
            return 0;
×
2640
        }
2641

NEW
2642
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
×
NEW
2643
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
×
2644
            /* discard ICMPv6 message if we can't tell that it was caused by
2645
             * us, because the IPv6 header is not directly followed by an
2646
             * ICMPv6 header
2647
             */
2648
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
NEW
2649
            return 0;
×
2650
        }
NEW
2651
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
×
2652

NEW
2653
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
×
2654
            /* not caused by us */
UNCOV
2655
            return 0;
×
2656
        }
2657

2658
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2659
        if (seqmap_value == NULL) {
×
2660
            return 0;
×
2661
        }
2662

2663
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
NEW
2664
        h = table[seqmap_value->host_nr];
×
2665

2666
        switch (icp->icmp6_type) {
×
2667
        case ICMP6_DST_UNREACH:
NEW
2668
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
NEW
2669
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2670
                    icp->icmp6_code, addr_ascii, h->host);
×
2671
            } else {
NEW
2672
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2673
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2674
            }
UNCOV
2675
            print_warning("\n");
×
2676
            num_othericmprcvd++;
×
2677
            break;
×
2678

2679
        case ICMP6_PACKET_TOO_BIG:
NEW
2680
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
2681
                addr_ascii, h->host);
NEW
2682
            num_othericmprcvd++;
×
NEW
2683
            break;
×
2684

2685
        case ICMP6_TIME_EXCEEDED:
NEW
2686
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
NEW
2687
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2688
                    icp->icmp6_code, addr_ascii, h->host);
×
2689
            } else {
NEW
2690
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2691
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
2692
            }
NEW
2693
            print_warning("\n");
×
NEW
2694
            num_othericmprcvd++;
×
NEW
2695
            break;
×
2696

2697
        case ICMP6_PARAM_PROB:
NEW
2698
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
NEW
2699
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2700
                    icp->icmp6_code, addr_ascii, h->host);
×
2701
            } else {
NEW
2702
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2703
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
2704
            }
2705
            print_warning("\n");
×
2706
            num_othericmprcvd++;
×
2707
            break;
×
2708

2709
        default:
NEW
2710
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
NEW
2711
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
NEW
2712
            num_othericmprcvd++;
×
NEW
2713
            break;
×
2714
        }
2715

2716
        return 0;
×
2717
    }
2718

2719
    *id = icp->icmp6_id;
91✔
2720
    *seq = ntohs(icp->icmp6_seq);
91✔
2721

2722
    return 1;
91✔
2723
}
15✔
2724
#endif
2725

2726
int wait_for_reply(int64_t wait_time)
2,464✔
2727
{
2728
    int result;
2729
    static char buffer[RECV_BUFSIZE];
2730
    struct sockaddr_storage response_addr;
2731
    int n, avg;
2732
    HOST_ENTRY *h;
2733
    int64_t this_reply;
2734
    int this_count;
2735
    int64_t recv_time = 0;
2,464✔
2736
    SEQMAP_VALUE *seqmap_value;
2737
    unsigned short id;
2738
    unsigned short seq;
2739
    int ip_header_tos = -1;
2,464✔
2740
    int ip_header_ttl = -1;
2,464✔
2741
    // ICMP Timestamp
2742
    uint32_t ip_header_otime_ms = 0x80000000U;
2,464✔
2743
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,464✔
2744
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,464✔
2745

2746
    /* Receive packet */
2747
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,464✔
2748
        &recv_time, /* reply_timestamp */
2749
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2750
        sizeof(response_addr), /* reply_src_addr_len */
2751
        buffer, /* reply_buf */
2752
        sizeof(buffer) /* reply_buf_len */
2753
    );
2754

2755
    if (result <= 0) {
2,464✔
2756
        return 0;
1,324✔
2757
    }
2758

2759
    update_current_time();
1,140✔
2760
    if (recv_time == 0)
1,140✔
2761
        recv_time = current_time_ns;
214✔
2762

2763
    /* Process ICMP packet and retrieve id/seq */
2764
    if (response_addr.ss_family == AF_INET) {
1,140✔
2765
        int ip_hlen = decode_icmp_ipv4(
1,049✔
2766
            (struct sockaddr *)&response_addr,
2767
            sizeof(response_addr),
2768
            buffer,
2769
            sizeof(buffer),
2770
            &id,
2771
            &seq,
2772
            &ip_header_tos,
2773
            &ip_header_ttl,
2774
            &ip_header_otime_ms,
2775
            &ip_header_rtime_ms,
2776
            &ip_header_ttime_ms);
2777
        if (ip_hlen < 0) {
1,049✔
2778
            return 1;
422✔
2779
        }
2780
        if (id != ident4) {
627✔
2781
            return 1; /* packet received, but not the one we are looking for! */
×
2782
        }
2783
        if (!using_sock_dgram4) {
627✔
2784
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2785
             * with fping with IPv6 hosts */
2786
            result -= ip_hlen;
621✔
2787
        }
199✔
2788
    }
199✔
2789
#ifdef IPV6
2790
    else if (response_addr.ss_family == AF_INET6) {
91✔
2791
        if (!decode_icmp_ipv6(
91✔
2792
                (struct sockaddr *)&response_addr,
2793
                sizeof(response_addr),
2794
                buffer,
2795
                sizeof(buffer),
2796
                &id,
2797
                &seq)) {
UNCOV
2798
            return 1;
×
2799
        }
2800
        if (id != ident6) {
91✔
2801
            return 1; /* packet received, but not the one we are looking for! */
×
2802
        }
2803
    }
15✔
2804
#endif
2805
    else {
2806
        return 1;
×
2807
    }
2808

2809
    seqmap_value = seqmap_fetch(seq, current_time_ns);
718✔
2810
    if (seqmap_value == NULL) {
718✔
2811
        return 1;
×
2812
    }
2813

2814
    /* find corresponding host_entry */
2815
    n = seqmap_value->host_nr;
718✔
2816
    h = table[n];
718✔
2817
    this_count = seqmap_value->ping_count;
718✔
2818
    this_reply = recv_time - seqmap_value->ping_ts;
718✔
2819

2820
    /* update stats that include invalid replies */
2821
    h->num_recv_total++;
718✔
2822
    num_pingreceived++;
718✔
2823

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

2826
    /* optionally require reply source equal to target address */
2827
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
718✔
2828
        dbg_printf("%s\n", "discarding reply from wrong source address");
2829
        return 1;
12✔
2830
    }
2831

2832
    /* discard duplicates */
2833
    if (!loop_flag && h->resp_times[this_count] >= 0) {
706✔
2834
        if (!per_recv_flag) {
×
2835
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2836
                h->host, this_count, result, sprint_tm(this_reply));
2837

2838
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2839
                char buf[INET6_ADDRSTRLEN];
2840
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2841
                fprintf(stderr, " [<- %s]", buf);
×
2842
            }
2843
            fprintf(stderr, "\n");
×
2844
        }
2845
        return 1;
×
2846
    }
2847

2848
    /* discard reply if delay is larger than timeout
2849
     * (see also: github #32) */
2850
    if (this_reply > h->timeout) {
706✔
2851
        return 1;
×
2852
    }
2853

2854
    /* update stats */
2855
    stats_add(h, this_count, 1, this_reply);
706✔
2856
    // TODO: move to stats_add?
2857
    if (!max_reply || this_reply > max_reply)
706✔
2858
        max_reply = this_reply;
416✔
2859
    if (!min_reply || this_reply < min_reply)
706✔
2860
        min_reply = this_reply;
390✔
2861
    sum_replies += this_reply;
706✔
2862
    total_replies++;
706✔
2863

2864
    /* initialize timeout to initial timeout (without backoff) */
2865
    h->timeout = timeout;
706✔
2866

2867
    /* remove timeout event */
2868
    struct event *timeout_event = host_get_timeout_event(h, this_count);
706✔
2869
    if (timeout_event) {
706✔
2870
        ev_remove(&event_queue_timeout, timeout_event);
706✔
2871
    }
214✔
2872

2873
    /* print "is alive" */
2874
    if (h->num_recv == 1) {
706✔
2875
        num_alive++;
425✔
2876
        if (fast_reachable && num_alive >= min_reachable)
425✔
2877
                finish_requested = 1;
3✔
2878

2879
        if (verbose_flag || alive_flag) {
425✔
2880
            printf("%s", h->host);
294✔
2881

2882
            if (verbose_flag)
294✔
2883
                printf(" is alive");
274✔
2884
        }
84✔
2885
    }
123✔
2886

2887
    /* print received ping (unless --quiet) */
2888
    if (per_recv_flag) {
706✔
2889
        if (timestamp_flag) {
151✔
2890
            print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2891
        }
10✔
2892
        avg = h->total_time / h->num_recv;
151✔
2893
        printf("%-*s : [%d], %d bytes, %s ms",
151✔
2894
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2895
        printf(" (%s avg, ", sprint_tm(avg));
151✔
2896

2897
        if (h->num_recv <= h->num_sent) {
151✔
2898
            printf("%d%% loss)",
151✔
2899
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2900
        }
45✔
2901
        else {
2902
            printf("%d%% return)",
×
2903
                (h->num_recv_total * 100) / h->num_sent);
×
2904
        }
2905
    }
45✔
2906

2907
    if (verbose_flag || alive_flag || per_recv_flag) {
706✔
2908

2909
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
445✔
2910
            char buf[INET6_ADDRSTRLEN];
2911
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2912
            fprintf(stderr, " [<- %s]", buf);
6✔
2913
        }
2914

2915
        if (icmp_request_typ == 13) {
445✔
2916
            printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2917
                   alive_flag ? "" : ",",
14✔
2918
                   ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
2919
                   ms_since_midnight_utc(recv_time));
2920
        }
2921

2922
        if(print_tos_flag) {
445✔
2923
            if(ip_header_tos != -1) {
43✔
2924
                printf(" (TOS %d)", ip_header_tos);
39✔
2925
            }
11✔
2926
            else {
2927
                printf(" (TOS unknown)");
4✔
2928
            }
2929
        }
11✔
2930

2931
        if (print_ttl_flag) {
445✔
2932
          if(ip_header_ttl != -1) {
43✔
2933
              printf(" (TTL %d)", ip_header_ttl);
39✔
2934
          }
11✔
2935
          else {
2936
              printf(" (TTL unknown)");
4✔
2937
          }
2938
        }
11✔
2939

2940
        if (elapsed_flag && !per_recv_flag)
445✔
2941
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2942

2943
        printf("\n");
445✔
2944
    }
129✔
2945

2946
    return 1;
706✔
2947
}
606✔
2948

2949
/************************************************************
2950

2951
  Function: add_name
2952

2953
*************************************************************
2954

2955
  Inputs:  char* name
2956

2957
  Description:
2958

2959
  process input name for addition to target list
2960
  name can turn into multiple targets via multiple interfaces (-m)
2961
  or via NIS groups
2962

2963
************************************************************/
2964

2965
void add_name(char *name)
536✔
2966
{
2967
    struct addrinfo *res0, *res, hints;
2968
    int ret_ga;
2969
    char *printname;
2970
    char namebuf[256];
2971
    char addrbuf[256];
2972

2973
    /* getaddrinfo */
2974
    memset(&hints, 0, sizeof(struct addrinfo));
536✔
2975
    hints.ai_flags = AI_UNUSABLE;
536✔
2976
    hints.ai_socktype = SOCK_RAW;
536✔
2977
    hints.ai_family = hints_ai_family;
536✔
2978
    if (hints_ai_family == AF_INET) {
536✔
2979
        hints.ai_protocol = IPPROTO_ICMP;
77✔
2980
    }
21✔
2981
#ifdef IPV6
2982
    else if (hints_ai_family == AF_INET6) {
459✔
2983
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
2984
    }
1✔
2985
#endif
2986
    else {
2987
        hints.ai_socktype = SOCK_STREAM;
428✔
2988
        hints.ai_protocol = 0;
428✔
2989
    }
2990
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
536✔
2991
    if (ret_ga) {
536✔
2992
        if (!quiet_flag)
26✔
2993
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
2994
        num_noaddress++;
26✔
2995
        return;
26✔
2996
    }
2997

2998
    /* NOTE: we could/should loop with res on all addresses like this:
2999
     * for (res = res0; res; res = res->ai_next) {
3000
     * We don't do it yet, however, because is is an incompatible change
3001
     * (need to implement a separate option for this)
3002
     */
3003
    for (res = res0; res; res = res->ai_next) {
510✔
3004
        /* name_flag: addr -> name lookup requested) */
3005
        if (name_flag || rdns_flag) {
512✔
3006
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3007
            if (name_flag) {
3✔
3008
                /* Was it a numerical address? Only then do a rdns-query */
3009
                struct addrinfo *nres;
3010
                hints.ai_flags = AI_NUMERICHOST;
×
3011
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3012
                    do_rdns = 1;
×
3013
                    freeaddrinfo(nres);
×
3014
                }
3015
            }
3016

3017
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3018
                printname = namebuf;
3✔
3019
            }
1✔
3020
            else {
3021
                printname = name;
×
3022
            }
3023
        }
1✔
3024
        else {
3025
            printname = name;
507✔
3026
        }
3027

3028
        /* addr_flag: name -> addr lookup requested */
3029
        if (addr_flag) {
510✔
3030
            int ret;
3031
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3032
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3033
            if (ret) {
3✔
3034
                if (!quiet_flag) {
×
3035
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3036
                }
3037
                continue;
×
3038
            }
3039

3040
            if (name_flag || rdns_flag) {
3✔
3041
                char nameaddrbuf[512 + 3];
3042
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3043
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3044
            }
3045
            else {
3046
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3047
            }
3048
        }
1✔
3049
        else {
3050
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
507✔
3051
        }
3052

3053
        if (!multif_flag) {
510✔
3054
            break;
510✔
3055
        }
3056
    }
3057

3058
    freeaddrinfo(res0);
510✔
3059
}
144✔
3060

3061
/************************************************************
3062

3063
  Function: add_addr
3064

3065
*************************************************************
3066

3067
  Description:
3068

3069
  add single address to list of hosts to be pinged
3070

3071
************************************************************/
3072

3073
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
510✔
3074
{
3075
    HOST_ENTRY *p;
3076
    int n;
3077
    int64_t *i;
3078

3079
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
510✔
3080
    if (!p)
510✔
3081
        crash_and_burn("can't allocate HOST_ENTRY");
×
3082

3083
    p->name = strdup(name);
510✔
3084
    p->host = strdup(host);
510✔
3085
    memcpy(&p->saddr, ipaddr, ipaddr_len);
510✔
3086
    p->saddr_len = ipaddr_len;
510✔
3087
    p->timeout = timeout;
510✔
3088
    p->min_reply = 0;
510✔
3089

3090
    if (netdata_flag) {
510✔
3091
        char *s = p->name;
3✔
3092
        while (*s) {
30✔
3093
            if (!isalnum(*s))
27✔
3094
                *s = '_';
9✔
3095
            s++;
27✔
3096
        }
3097
    }
1✔
3098

3099
    if (strlen(p->host) > max_hostname_len)
510✔
3100
        max_hostname_len = strlen(p->host);
323✔
3101

3102
    /* array for response time results */
3103
    if (!loop_flag) {
510✔
3104
        i = (int64_t *)malloc(trials * sizeof(int64_t));
510✔
3105
        if (!i)
510✔
3106
            crash_and_burn("can't allocate resp_times array");
×
3107

3108
        for (n = 1; n < trials; n++)
2,031✔
3109
            i[n] = RESP_UNUSED;
1,521✔
3110

3111
        p->resp_times = i;
510✔
3112
    }
136✔
3113

3114
    /* allocate event storage */
3115
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
510✔
3116
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
510✔
3117

3118
    /* schedule first ping */
3119
    host_add_ping_event(p, 0, current_time_ns);
510✔
3120

3121
    num_hosts++;
510✔
3122
}
510✔
3123

3124
/************************************************************
3125

3126
  Function: crash_and_burn
3127

3128
*************************************************************
3129

3130
  Inputs:  char* message
3131

3132
  Description:
3133

3134
************************************************************/
3135

3136
void crash_and_burn(char *message)
×
3137
{
3138
    fprintf(stderr, "%s: %s\n", prog, message);
×
3139
    exit(4);
×
3140
}
3141

3142
/************************************************************
3143

3144
  Function: errno_crash_and_burn
3145

3146
*************************************************************
3147

3148
  Inputs:  char* message
3149

3150
  Description:
3151

3152
************************************************************/
3153

3154
void errno_crash_and_burn(char *message)
8✔
3155
{
3156
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3157
    exit(4);
8✔
3158
}
3159

3160
/************************************************************
3161

3162
  Function: print_warning
3163

3164
  Description: fprintf(stderr, ...), unless running with -q
3165

3166
*************************************************************/
3167

3168
void print_warning(char *format, ...)
54✔
3169
{
3170
    va_list args;
3171
    if (!quiet_flag) {
54✔
3172
        va_start(args, format);
54✔
3173
        vfprintf(stderr, format, args);
54✔
3174
        va_end(args);
54✔
3175
    }
8✔
3176
}
54✔
3177

3178
/************************************************************
3179

3180
  Function: sprint_tm
3181

3182
*************************************************************
3183

3184
  render nanosecond int64_t value into milliseconds string with three digits of
3185
  precision.
3186

3187
************************************************************/
3188

3189
const char *sprint_tm(int64_t ns)
851✔
3190
{
3191
    static char buf[10];
3192
    double t = (double)ns / 1e6;
851✔
3193

3194
    if (t < 0.0) {
851✔
3195
        /* negative (unexpected) */
3196
        sprintf(buf, "%.2g", t);
×
3197
    }
3198
    else if (t < 1.0) {
851✔
3199
        /* <= 0.99 ms */
3200
        sprintf(buf, "%.3f", t);
850✔
3201
    }
260✔
3202
    else if (t < 10.0) {
1✔
3203
        /* 1.00 - 9.99 ms */
3204
        sprintf(buf, "%.2f", t);
1✔
3205
    }
1✔
3206
    else if (t < 100.0) {
×
3207
        /* 10.0 - 99.9 ms */
3208
        sprintf(buf, "%.1f", t);
×
3209
    }
3210
    else if (t < 1000000.0) {
×
3211
        /* 100 - 1'000'000 ms */
3212
        sprintf(buf, "%.0f", t);
×
3213
    }
3214
    else {
3215
        sprintf(buf, "%.3e", t);
×
3216
    }
3217

3218
    return (buf);
851✔
3219
}
3220

3221
/************************************************************
3222

3223
  Function: addr_cmp
3224

3225
*************************************************************/
3226
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
467✔
3227
{
3228
    if (a->sa_family != b->sa_family) {
467✔
3229
        return a->sa_family - b->sa_family;
×
3230
    }
3231
    else {
3232
        if (a->sa_family == AF_INET) {
467✔
3233
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
403✔
3234
        }
3235
        else if (a->sa_family == AF_INET6) {
64✔
3236
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3237
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3238
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3239
        }
3240
    }
3241

3242
    return 0;
×
3243
}
131✔
3244

3245
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
825✔
3246
{
3247
    struct event *event = &h->event_storage_ping[index % event_storage_count];
825✔
3248
    event->host = h;
825✔
3249
    event->ping_index = index;
825✔
3250
    event->ev_time = ev_time;
825✔
3251
    ev_enqueue(&event_queue_ping, event);
825✔
3252

3253
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3254
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3255
}
825✔
3256

3257
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
801✔
3258
{
3259
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
801✔
3260
    event->host = h;
801✔
3261
    event->ping_index = index;
801✔
3262
    event->ev_time = ev_time;
801✔
3263
    ev_enqueue(&event_queue_timeout, event);
801✔
3264

3265
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3266
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3267
}
801✔
3268

3269
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
706✔
3270
{
3271
    return &h->event_storage_timeout[index % event_storage_count];
706✔
3272
}
3273

3274
/************************************************************
3275

3276
  Function: ev_enqueue
3277

3278
  Enqueue an event
3279

3280
  The queue is sorted by event->ev_time, so that queue->first always points to
3281
  the earliest event.
3282

3283
  We start scanning the queue from the tail, because we assume
3284
  that new events mostly get inserted with a event time higher
3285
  than the others.
3286

3287
*************************************************************/
3288
void ev_enqueue(struct event_queue *queue, struct event *event)
1,626✔
3289
{
3290
    struct event *i;
3291
    struct event *i_prev;
3292

3293
    /* Empty list */
3294
    if (queue->last == NULL) {
1,626✔
3295
        event->ev_next = NULL;
1,241✔
3296
        event->ev_prev = NULL;
1,241✔
3297
        queue->first = event;
1,241✔
3298
        queue->last = event;
1,241✔
3299
        return;
1,241✔
3300
    }
3301

3302
    /* Insert on tail? */
3303
    if (event->ev_time - queue->last->ev_time >= 0) {
385✔
3304
        event->ev_next = NULL;
385✔
3305
        event->ev_prev = queue->last;
385✔
3306
        queue->last->ev_next = event;
385✔
3307
        queue->last = event;
385✔
3308
        return;
385✔
3309
    }
3310

3311
    /* Find insertion point */
3312
    i = queue->last;
×
3313
    while (1) {
3314
        i_prev = i->ev_prev;
×
3315
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3316
            event->ev_prev = i_prev;
×
3317
            event->ev_next = i;
×
3318
            i->ev_prev = event;
×
3319
            if (i_prev != NULL) {
×
3320
                i_prev->ev_next = event;
×
3321
            }
3322
            else {
3323
                queue->first = event;
×
3324
            }
3325
            return;
×
3326
        }
3327
        i = i_prev;
×
3328
    }
3329
}
474✔
3330

3331
/************************************************************
3332

3333
  Function: ev_dequeue
3334

3335
*************************************************************/
3336
struct event *ev_dequeue(struct event_queue *queue)
900✔
3337
{
3338
    struct event *dequeued;
3339

3340
    if (queue->first == NULL) {
900✔
3341
        return NULL;
×
3342
    }
3343
    dequeued = queue->first;
900✔
3344
    ev_remove(queue, dequeued);
900✔
3345

3346
    return dequeued;
900✔
3347
}
254✔
3348

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

3351
  Function: ev_remove
3352

3353
*************************************************************/
3354
void ev_remove(struct event_queue *queue, struct event *event)
1,606✔
3355
{
3356
    if (queue->first == event) {
1,606✔
3357
        queue->first = event->ev_next;
1,597✔
3358
    }
465✔
3359
    if (queue->last == event) {
1,606✔
3360
        queue->last = event->ev_prev;
1,242✔
3361
    }
356✔
3362
    if (event->ev_prev) {
1,606✔
3363
        event->ev_prev->ev_next = event->ev_next;
9✔
3364
    }
3✔
3365
    if (event->ev_next) {
1,606✔
3366
        event->ev_next->ev_prev = event->ev_prev;
364✔
3367
    }
112✔
3368
    event->ev_prev = NULL;
1,606✔
3369
    event->ev_next = NULL;
1,606✔
3370
}
1,606✔
3371

3372
/************************************************************
3373

3374
  Function: print_human_readable_time from current_time_ns
3375

3376
*************************************************************/
3377
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
30✔
3378
{
3379
    char time_buffer[100];
3380
    time_t current_time_s;
3381
    struct tm *local_time;
3382

3383
    current_time_s = current_time_ns / 1000000000;
30✔
3384
    local_time = localtime(&current_time_s);
30✔
3385
    switch(timestamp_format) {
30✔
3386
        case 1:
4✔
3387
            // timestamp-format ctime
3388
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
6✔
3389
            printf("[%s] ", time_buffer);
6✔
3390
            break;
6✔
3391
        case 2:
4✔
3392
            // timestamp-format iso
3393
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
6✔
3394
            printf("[%s] ", time_buffer);
6✔
3395
            break;
6✔
3396
        case 3:
4✔
3397
            // timestamp-format rfc3339
3398
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
6✔
3399
            printf("[%s] ", time_buffer);
6✔
3400
            break;
6✔
3401
        default:
8✔
3402
            printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3403
    }
4✔
3404
}
30✔
3405

3406
/************************************************************
3407

3408
  Function: ms_since_midnight_utc
3409

3410
*************************************************************
3411

3412
  Input: int64_t: current UTC time in ns
3413

3414
  Output: uint32_t: current time in ms since midnight UTC
3415

3416
  Description:
3417

3418
  Return ICMP Timestamp value corresponding to the given time value.
3419
  The given time value must be in UTC.
3420

3421
*************************************************************/
3422
static uint32_t ms_since_midnight_utc(int64_t time_val)
14✔
3423
{
3424
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
14✔
3425
}
3426

3427
/************************************************************
3428

3429
  Function: usage
3430

3431
*************************************************************
3432

3433
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3434

3435
  Description:
3436

3437
************************************************************/
3438

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