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

schweikert / fping / 15227194365

24 May 2025 12:46PM UTC coverage: 87.768% (-0.1%) from 87.902%
15227194365

push

github

auerswal
fix build without IPv6 support

In commit 57d91b13f, I missed
one set of "#ifdev IPV6 ... #endif" guards for a piece of code
only relevant for IPv6 support in the generator.  This broke
the build with --disable-ipv6, or without IPv6 support on the
build system.

1392 of 1586 relevant lines covered (87.77%)

333.62 hits per line

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

87.97
/src/fping.c
1
/*
2
 * fping: fast-ping, file-ping, favorite-ping, funky-ping
3
 *
4
 *   Ping a list of target hosts in a round robin fashion.
5
 *   A better ping overall.
6
 *
7
 * fping website:  http://www.fping.org
8
 *
9
 * Current maintainer of fping: David Schweikert
10
 * Please send suggestions and patches to: david@schweikert.ch
11
 *
12
 *
13
 * Original author:  Roland Schemers  <schemers@stanford.edu>
14
 * IPv6 Support:     Jeroen Massar    <jeroen@unfix.org / jeroen@ipng.nl>
15
 * Improved main loop: David Schweikert <david@schweikert.ch>
16
 * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
17
 * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
18
 *
19
 *
20
 * Redistribution and use in source and binary forms are permitted
21
 * provided that the above copyright notice and this paragraph are
22
 * duplicated in all such forms and that any documentation,
23
 * advertising materials, and other materials related to such
24
 * distribution and use acknowledge that the software was developed
25
 * by Stanford University.  The name of the University may not be used
26
 * to endorse or promote products derived from this software without
27
 * specific prior written permission.
28
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31
 */
32

33
#ifdef __cplusplus
34
extern "C" {
35
#endif /* __cplusplus */
36

37
#include "config.h"
38
#include "fping.h"
39
#include "options.h"
40
#include "optparse.h"
41

42
#include <errno.h>
43
#include <inttypes.h>
44
#include <signal.h>
45
#include <stdarg.h>
46
#include <stdint.h>
47
#include <stdio.h>
48
#include <time.h>
49

50
#include "seqmap.h"
51

52
#ifdef HAVE_UNISTD_H
53
#include <unistd.h>
54
#endif /* HAVE_UNISTD_H */
55

56
#ifdef HAVE_STDLIB_H
57
#include <stdlib.h>
58
#endif /* HAVE_STDLIB_H */
59

60
#include <stddef.h>
61
#include <string.h>
62

63
#include <sys/socket.h>
64
#include <sys/time.h>
65
#include <sys/types.h>
66

67
#if HAVE_SYS_FILE_H
68
#include <sys/file.h>
69
#endif /* HAVE_SYS_FILE_H */
70

71
#ifdef IPV6
72
#include <netinet/icmp6.h>
73
#endif
74
#include <netinet/in_systm.h>
75

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

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

83
#include <sys/select.h>
84

85
/*** compatibility ***/
86

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

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

105
/*** externals ***/
106

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

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

117
/*** Constants ***/
118

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

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

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

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

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

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

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

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

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

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

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

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

219
#define ICMP_UNREACH_MAXTYPE 15
220

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

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

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

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

268
#define EV_TYPE_PING 1
269
#define EV_TYPE_TIMEOUT 2
270

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

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

284
/*** globals ***/
285

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

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

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

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

314
unsigned int debugging = 0;
315

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

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

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

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

377
unsigned int fwmark = 0;
378

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

381
/*** forward declarations ***/
382

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

424
/************************************************************
425

426
  Function: p_setsockopt
427

428
*************************************************************
429

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

432
  Description:
433

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

437
************************************************************/
438

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

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

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

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

457
    return res;
22✔
458
}
459

460
/************************************************************
461

462
  Function: main
463

464
*************************************************************
465

466
  Inputs:  int argc, char** argv
467

468
  Description:
469

470
  Main program entry point
471

472
************************************************************/
473

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

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

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

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

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

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

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

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

532
    /* get command line options */
533

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

586
    float opt_value_float;
587
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
1,507✔
588
        switch (c) {
972✔
589
        case '0':
76✔
590
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
94✔
591
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
15✔
592
                  timestamp_format_flag = 1;
3✔
593
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
13✔
594
                  timestamp_format_flag = 2;
3✔
595
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
10✔
596
                  timestamp_format_flag = 3;
3✔
597
                }else{
1✔
598
                  usage(1);
6✔
599
                }
600
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
84✔
601
                check_source_flag = 1;
9✔
602
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
71✔
603
#ifdef IPV6
604
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
27✔
605
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
606
                    exit(1);
2✔
607
                }
608
                hints_ai_family = AF_INET;
25✔
609
#endif
610
                icmp_request_typ = 13;
25✔
611
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
25✔
612
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
46✔
613
                print_tos_flag = 1;
23✔
614
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
25✔
615
                print_ttl_flag = 1;
20✔
616
            } else {
4✔
617
                usage(1);
×
618
            }
619
            break;
88✔
620
        case '4':
32✔
621
#ifdef IPV6
622
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
623
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
624
                exit(1);
3✔
625
            }
626
            hints_ai_family = AF_INET;
40✔
627
#endif
628
            break;
40✔
629
        case '6':
28✔
630
#ifdef IPV6
631
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
31✔
632
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
633
                exit(1);
5✔
634
            }
635
            hints_ai_family = AF_INET6;
26✔
636
#else
637
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
638
            exit(1);
639
#endif
640
            break;
26✔
641
        case 'M':
2✔
642
#ifdef IP_MTU_DISCOVER
643
            if (socket4 >= 0) {
2✔
644
                int val = IP_PMTUDISC_DO;
2✔
645
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
646
                    perror("setsockopt IP_MTU_DISCOVER");
647
                }
648
            }
649
#ifdef IPV6
650
            if (socket6 >= 0) {
2✔
651
                int val = IPV6_PMTUDISC_DO;
2✔
652
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
653
                    perror("setsockopt IPV6_MTU_DISCOVER");
654
                }
655
            }
656
#endif
657
#else
658
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
659
            exit(1);
660
#endif
661
            break;
2✔
662

663
        case 't':
40✔
664
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
45✔
665
                usage(1);
6✔
666
            if (opt_value_float < 0) {
41✔
667
                usage(1);
3✔
668
            }
1✔
669
            timeout = opt_value_float * 1000000;
39✔
670
            timeout_flag = 1;
39✔
671
            break;
39✔
672

673
        case 'r':
36✔
674
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
40✔
675
                usage(1);
6✔
676
            break;
36✔
677

678
        case 'i':
30✔
679
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
36✔
680
                usage(1);
6✔
681
            if (opt_value_float < 0) {
32✔
682
                usage(1);
3✔
683
            }
1✔
684
            interval = opt_value_float * 1000000;
30✔
685
            break;
30✔
686

687
        case 'p':
68✔
688
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
99✔
689
                usage(1);
6✔
690
            if (opt_value_float < 0) {
95✔
691
                usage(1);
3✔
692
            }
1✔
693
            perhost_interval = opt_value_float * 1000000;
93✔
694

695
            break;
93✔
696

697
        case 'c':
60✔
698
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
85✔
699
                usage(1);
6✔
700

701
            count_flag = 1;
81✔
702
            break;
81✔
703

704
        case 'C':
24✔
705
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
33✔
706
                usage(1);
6✔
707

708
            count_flag = 1;
29✔
709
            report_all_rtts_flag = 1;
29✔
710
            break;
29✔
711

712
        case 'b':
14✔
713
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
21✔
714
                usage(1);
6✔
715
            size_flag = 1;
17✔
716
            break;
17✔
717

718
        case 'h':
2✔
719
            usage(0);
3✔
720
            break;
1✔
721

722
        case 'q':
12✔
723
            verbose_flag = 0;
17✔
724
            quiet_flag = 1;
17✔
725
            break;
17✔
726

727
        case 'Q':
22✔
728
            verbose_flag = 0;
33✔
729
            quiet_flag = 1;
33✔
730
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
731
                usage(1);
6✔
732
            if (opt_value_float < 0) {
29✔
733
                usage(1);
3✔
734
            }
1✔
735
            report_interval = opt_value_float * 1e9;
27✔
736

737
            /* recognize keyword(s) after number, ignore everything else */
738
            {
739
                char *comma = strchr(optparse_state.optarg, ',');
27✔
740
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
27✔
741
                    cumulative_stats_flag = 1;
6✔
742
                }
2✔
743
            }
744

745
            break;
27✔
746

747
        case 'e':
6✔
748
            elapsed_flag = 1;
8✔
749
            break;
8✔
750

751
        case 'm':
752
            multif_flag = 1;
×
753
            break;
×
754

755
        case 'N':
2✔
756
            netdata_flag = 1;
3✔
757
            break;
3✔
758

759
        case 'n':
4✔
760
            name_flag = 1;
6✔
761
            if (rdns_flag) {
6✔
762
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
763
                exit(1);
3✔
764
            }
765
            break;
3✔
766

767
        case 'd':
6✔
768
            rdns_flag = 1;
9✔
769
            if (name_flag) {
9✔
770
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
771
                exit(1);
3✔
772
            }
773
            break;
6✔
774

775
        case 'A':
2✔
776
            addr_flag = 1;
3✔
777
            break;
3✔
778

779
        case 'B':
10✔
780
            if (!(backoff = atof(optparse_state.optarg)))
12✔
781
                usage(1);
6✔
782

783
            break;
8✔
784

785
        case 's':
6✔
786
            stats_flag = 1;
9✔
787
            break;
9✔
788

789
        case 'D':
14✔
790
            timestamp_flag = 1;
21✔
791
            break;
21✔
792

793
        case 'R':
4✔
794
            random_data_flag = 1;
5✔
795
            break;
5✔
796

797
        case 'l':
2✔
798
            loop_flag = 1;
3✔
799
            backoff_flag = 0;
3✔
800
            break;
3✔
801

802
        case 'u':
8✔
803
            unreachable_flag = 1;
12✔
804
            break;
12✔
805

806
        case 'a':
18✔
807
            alive_flag = 1;
26✔
808
            break;
26✔
809

810
        case 'H':
8✔
811
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
12✔
812
                usage(1);
6✔
813
            break;
8✔
814

815
#if defined(DEBUG) || defined(_DEBUG)
816
        case 'z':
817
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
818
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
819
                    usage(1);
820

821
            break;
822
#endif /* DEBUG || _DEBUG */
823

824
        case 'v':
4✔
825
            printf("%s: Version %s\n", prog, VERSION);
6✔
826
            exit(0);
6✔
827

828
        case 'x':
8✔
829
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
10✔
830
                usage(1);
6✔
831
            break;
6✔
832

833
        case 'X':
6✔
834
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
835
                usage(1);
6✔
836
            fast_reachable = 1;
5✔
837
            break;
5✔
838

839
        case 'f':
18✔
840
            filename = optparse_state.optarg;
27✔
841
            break;
27✔
842
#ifdef SO_MARK
843
        case 'k':
10✔
844
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
10✔
845
                usage(1);
4✔
846

847
            if (socket4 >= 0)
6✔
848
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
849
                    perror("fwmark ipv4");
4✔
850

851
#ifdef IPV6
852
            if (socket6 >= 0)
6✔
853
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
854
                    perror("fwmark ipv6");
4✔
855
#endif
856

857
            break;
6✔
858
#endif
859

860
        case 'g':
98✔
861
            /* use IP list generation */
862
            /* mutually exclusive with using file input or command line targets */
863
            generate_flag = 1;
117✔
864
            break;
117✔
865

866
        case 'S':
10✔
867
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
868
                src_addr_set = 1;
6✔
869
                break;
6✔
870
            }
871
#ifdef IPV6
872
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
873
                src_addr6_set = 1;
4✔
874
                break;
4✔
875
            }
876
#endif
877
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
878
            exit(1);
3✔
879

880
        case 'I':
6✔
881
#ifdef SO_BINDTODEVICE
882
            if (socket4 >= 0) {
6✔
883
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
884
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
885
                    exit(1);
2✔
886
                }
887
            }
888
#ifdef IPV6
889
            if (socket6 >= 0) {
4✔
890
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
891
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
892
                    exit(1);
893
                }
894
            }
895
#endif
896
#else
897
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
1✔
898
            exit(3);
1✔
899
            ;
900
#endif
901
            break;
4✔
902

903
        case 'T':
2✔
904
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
905
            break;
3✔
906

907
        case 'O':
10✔
908
            if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
15✔
909
                if (socket4 >= 0) {
9✔
910
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
911
                        perror("setting type of service octet IP_TOS");
×
912
                    }
913
                }
3✔
914
#if defined(IPV6) && defined(IPV6_TCLASS)
915
                if (socket6 >= 0) {
9✔
916
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
917
                        perror("setting type of service octet IPV6_TCLASS");
×
918
                    }
919
                }
3✔
920
#endif
921
            }
3✔
922
            else {
923
                usage(1);
6✔
924
            }
925
            break;
11✔
926

927
        case 'o':
6✔
928
            outage_flag = 1;
9✔
929
            break;
9✔
930

931
        case '?':
4✔
932
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
933
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
934
            exit(1);
6✔
935
            break;
936
        }
937
    }
938

939
    /* permanently drop privileges */
940
    if (suid != getuid() && setuid(getuid())) {
477✔
941
        perror("fatal: failed to permanently drop privileges");
×
942
        /* continuing would be a security hole */
943
        exit(4);
×
944
    }
945

946
    /* validate various option settings */
947

948
#ifndef IPV6
949
    if (socket4 < 0) {
950
        crash_and_burn("can't create socket (must run as root?)");
951
    }
952
#else
953
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
477✔
954
        crash_and_burn("can't create socket (must run as root?)");
×
955
    }
956
#endif
957

958
    if (ttl > 255) {
475✔
959
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
960
        exit(1);
3✔
961
    }
962

963
    if (unreachable_flag && alive_flag) {
472✔
964
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
965
        exit(1);
3✔
966
    }
967

968
    if (count_flag && loop_flag) {
469✔
969
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
970
        exit(1);
3✔
971
    }
972

973
#ifdef FPING_SAFE_LIMITS
974
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
466✔
975
        && getuid()) {
118✔
976
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
977
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
978
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
979
        exit(1);
6✔
980
    }
981
#endif
982

983
    if (ping_data_size > MAX_PING_DATA) {
460✔
984
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
985
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
986
        exit(1);
3✔
987
    }
988

989
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
457✔
990
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
991
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
992
        exit(1);
6✔
993
    }
994

995
    if (icmp_request_typ == 13 && size_flag != 0) {
451✔
996
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
997
        exit(1);
9✔
998
    }
999

1000
    if (count_flag) {
442✔
1001
        if (verbose_flag)
100✔
1002
            per_recv_flag = 1;
59✔
1003

1004
        alive_flag = unreachable_flag = verbose_flag = 0;
100✔
1005
    }
28✔
1006

1007
    if (loop_flag) {
442✔
1008
        if (!report_interval)
×
1009
            per_recv_flag = 1;
×
1010

1011
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1012
    }
1013

1014
    if (alive_flag || unreachable_flag || min_reachable)
442✔
1015
        verbose_flag = 0;
37✔
1016

1017
    trials = (count > retry + 1) ? count : retry + 1;
458✔
1018

1019
    /* auto-tune default timeout for count/loop modes
1020
     * see also github #32 */
1021
    if (loop_flag || count_flag) {
458✔
1022
        if (!timeout_flag) {
100✔
1023
            timeout = perhost_interval;
95✔
1024
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
95✔
1025
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1026
            }
1027
        }
27✔
1028
    }
28✔
1029

1030
#if defined(DEBUG) || defined(_DEBUG)
1031
    if (debugging & DBG_TRACE)
1032
        trace_flag = 1;
1033

1034
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1035
        randomly_lose_flag = 1;
1036
        lose_factor = 1; /* ie, 1/4 */
1037
    }
1038

1039
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1040
        randomly_lose_flag = 1;
1041
        lose_factor = 5; /* ie, 3/4 */
1042
    }
1043

1044
    if (debugging & DBG_PRINT_PER_SYSTEM)
1045
        print_per_system_flag = 1;
1046

1047
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1048
        report_all_rtts_flag = 1;
1049

1050
    if (trace_flag) {
1051
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1052
            prog, count, retry, interval / 1e6);
1053
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1054
            perhost_interval / 1e6, timeout / 1e6);
1055
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1056
            ping_data_size, trials);
1057

1058
        if (verbose_flag)
1059
            fprintf(stderr, "  verbose_flag set\n");
1060
        if (multif_flag)
1061
            fprintf(stderr, "  multif_flag set\n");
1062
        if (name_flag)
1063
            fprintf(stderr, "  name_flag set\n");
1064
        if (addr_flag)
1065
            fprintf(stderr, "  addr_flag set\n");
1066
        if (stats_flag)
1067
            fprintf(stderr, "  stats_flag set\n");
1068
        if (unreachable_flag)
1069
            fprintf(stderr, "  unreachable_flag set\n");
1070
        if (alive_flag)
1071
            fprintf(stderr, "  alive_flag set\n");
1072
        if (elapsed_flag)
1073
            fprintf(stderr, "  elapsed_flag set\n");
1074
        if (version_flag)
1075
            fprintf(stderr, "  version_flag set\n");
1076
        if (count_flag)
1077
            fprintf(stderr, "  count_flag set\n");
1078
        if (loop_flag)
1079
            fprintf(stderr, "  loop_flag set\n");
1080
        if (backoff_flag)
1081
            fprintf(stderr, "  backoff_flag set\n");
1082
        if (per_recv_flag)
1083
            fprintf(stderr, "  per_recv_flag set\n");
1084
        if (report_all_rtts_flag)
1085
            fprintf(stderr, "  report_all_rtts_flag set\n");
1086
        if (randomly_lose_flag)
1087
            fprintf(stderr, "  randomly_lose_flag set\n");
1088
        if (print_per_system_flag)
1089
            fprintf(stderr, "  print_per_system_flag set\n");
1090
        if (outage_flag)
1091
            fprintf(stderr, "  outage_flag set\n");
1092
        if (netdata_flag)
1093
            fprintf(stderr, "  netdata_flag set\n");
1094
    }
1095
#endif /* DEBUG || _DEBUG */
1096

1097
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1098
    if (ttl > 0) {
458✔
1099
        if (socket4 >= 0) {
3✔
1100
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1101
                perror("setting time to live");
×
1102
            }
1103
        }
1✔
1104
#ifdef IPV6
1105
        if (socket6 >= 0) {
3✔
1106
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1107
                perror("setting time to live");
×
1108
            }
1109
        }
1✔
1110
#endif
1111
    }
1✔
1112

1113
#if HAVE_SO_TIMESTAMPNS
1114
    {
1115
        int opt = 1;
336✔
1116
        if (socket4 >= 0) {
336✔
1117
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
336✔
1118
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1119
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1120
                }
1121
            }
1122
        }
1123
#ifdef IPV6
1124
        if (socket6 >= 0) {
336✔
1125
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
336✔
1126
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1127
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1128
                }
1129
            }
1130
        }
1131
#endif
1132
    }
1133
#endif
1134

1135
    update_current_time();
458✔
1136
    start_time = current_time_ns;
458✔
1137

1138
    /* handle host names supplied on command line or in a file */
1139
    /* if the generate_flag is on, then generate the IP list */
1140

1141
    argv = &argv[optparse_state.optind];
458✔
1142
    argc -= optparse_state.optind;
458✔
1143

1144
    /* calculate how many ping can be in-flight per host */
1145
    if (count_flag) {
458✔
1146
        event_storage_count = count;
115✔
1147
    }
43✔
1148
    else if (loop_flag) {
343✔
1149
        if (perhost_interval > timeout) {
×
1150
            event_storage_count = 1;
×
1151
        }
1152
        else {
1153
            event_storage_count = 1 + timeout / perhost_interval;
×
1154
        }
1155
    }
1156
    else {
1157
        event_storage_count = 1;
343✔
1158
    }
1159

1160
    /* file and generate are mutually exclusive */
1161
    /* file and command line are mutually exclusive */
1162
    /* generate requires command line parameters beyond the switches */
1163
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
458✔
1164
        usage(1);
19✔
1165

1166
    /* if no conditions are specified, then assume input from stdin */
1167
    if (!*argv && !filename && !generate_flag)
440✔
1168
        filename = "-";
69✔
1169

1170
    if (*argv && !generate_flag) {
440✔
1171
        while (*argv) {
524✔
1172
            add_name(*argv);
291✔
1173
            ++argv;
291✔
1174
        }
1175
    }
57✔
1176
    else if (filename) {
207✔
1177
        FILE *ping_file;
1178
        char line[MAX_TARGET_NAME_LEN + 1];
1179
        char host[MAX_TARGET_NAME_LEN + 1];
1180
        char scratch[MAX_TARGET_NAME_LEN + 1];
1181
        int skip, non_empty;
1182

1183
        if (strcmp(filename, "-") == 0)
96✔
1184
            ping_file = fdopen(0, "r");
72✔
1185
        else
1186
            ping_file = fopen(filename, "r");
24✔
1187

1188
        if (!ping_file)
96✔
1189
            errno_crash_and_burn("fopen");
3✔
1190

1191
        /*
1192
         * Read the first word of every non-comment line, skip everything else.
1193
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1194
         * character is a '#' are interpreted as comments and ignored.)
1195
        */
1196
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1197
            skip = non_empty = 0;
309✔
1198

1199
            /* skip over a prefix of the line where sscanf finds nothing */
1200
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1201
                continue;
99✔
1202
            }
1203

1204
            /* the first word of the line can indicate a comment line */
1205
            if (host[0] == '#') {
210✔
1206
                skip = 1; /* skip remainder of line */
42✔
1207
            } else {
14✔
1208
                non_empty = 1; /* we have something to add as a target name */
168✔
1209
                /*
1210
                 * We have found the start of a word.
1211
                 * This part of the line may contain all of the first word.
1212
                 */
1213
                if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
168✔
1214
                    char discard1[MAX_TARGET_NAME_LEN + 1];
1215
                    char discard2[MAX_TARGET_NAME_LEN + 1];
1216
                    if (sscanf(line, "%s%s", discard1, discard2) == 2) {
102✔
1217
                        skip = 1; /* a second word starts in this part */
6✔
1218
                    }
2✔
1219
                    if (isspace(line[sizeof(line) - 2])) {
102✔
1220
                        skip = 1; /* the first word ends in this part */
42✔
1221
                    }
14✔
1222
                }
34✔
1223
            }
1224
            /* read remainder of this input line */
1225
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
336✔
1226
                if (skip) {
150✔
1227
                    continue; /* skip rest of data in this input line */
102✔
1228
                }
1229
                if (isspace(line[0])) {
48✔
1230
                    skip = 1; /* first word ended in previous part */
6✔
1231
                    continue;
6✔
1232
                }
1233
                if ((sscanf(line, "%s", scratch) != 1) || (!*scratch)) {
42✔
1234
                    skip = 1; /* empty or blank part of line, skip the rest */
×
1235
                    continue;
×
1236
                }
1237
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
42✔
1238
                    fprintf(stderr, "%s: target name too long\n", prog);
24✔
1239
                    exit(1);
24✔
1240
                }
1241
                /* append remainder of word started in previous line part */
1242
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
18✔
1243
                /*
1244
                 * Since the "host" buffer is the same size as the "line"
1245
                 * buffer, a target name that fits into the "host" buffer
1246
                 * cannot use more than two consecutive line parts.
1247
                 * A target name that uses two consecutive line parts
1248
                 * and fits into the "host" buffer must end before the
1249
                 * end of the second "line" buffer.  Thus the rest of
1250
                 * the line can be skipped.
1251
                 */
1252
                skip = 1;
18✔
1253
            }
1254

1255
            if (non_empty)
186✔
1256
                add_name(host);
144✔
1257
        }
1258

1259
        fclose(ping_file);
70✔
1260
    }
24✔
1261
    else if (*argv && generate_flag) {
111✔
1262
        if (argc == 1) {
111✔
1263
            /* one target: we expect a cidr range (n.n.n.n/m) */
1264
            add_cidr(argv[0]);
56✔
1265
        }
10✔
1266
        else if (argc == 2) {
55✔
1267
            add_range(argv[0], argv[1]);
52✔
1268
        }
6✔
1269
        else {
1270
            usage(1);
3✔
1271
        }
1272
    }
17✔
1273
    else {
1274
        usage(1);
×
1275
    }
1276

1277
    if (!num_hosts) {
360✔
1278
        exit(num_noaddress ? 2 : 1);
33✔
1279
    }
1280

1281
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
327✔
1282
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1283
    }
2✔
1284
#ifdef IPV6
1285
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
325✔
1286
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1287
    }
1288
#endif
1289

1290
    /* allocate and initialize array to map host nr to host_entry */
1291
    {
1292
        struct event *cursor = event_queue_ping.first;
323✔
1293
        int i = 0;
323✔
1294
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
323✔
1295
        if (!table)
323✔
1296
            crash_and_burn("Can't malloc array of hosts");
×
1297
        /* initialize table of hosts. we know that we have ping events scheduled
1298
         * for each of them */
1299
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
830✔
1300
            table[i] = cursor->host;
507✔
1301
            cursor->host->i = i;
507✔
1302
            i++;
507✔
1303
        }
135✔
1304
    }
1305

1306
    init_ping_buffer_ipv4(ping_data_size);
323✔
1307
#ifdef IPV6
1308
    init_ping_buffer_ipv6(ping_data_size);
323✔
1309
#endif
1310

1311
#ifdef USE_SIGACTION
1312
    memset(&act, 0, sizeof(act));
323✔
1313
    act.sa_handler = signal_handler;
323✔
1314
    sigemptyset(&act.sa_mask);
323✔
1315
    sigaddset(&act.sa_mask, SIGINT);
323✔
1316
    sigaddset(&act.sa_mask, SIGQUIT);
323✔
1317
    act.sa_flags = SA_RESTART;
323✔
1318
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
323✔
1319
        crash_and_burn("failure to set signal handler");
24✔
1320
    }
24✔
1321
#else
1322
    signal(SIGINT, signal_handler);
1323
    signal(SIGQUIT, signal_handler);
1324
#endif
1325
    setlinebuf(stdout);
299✔
1326

1327
    if (report_interval) {
299✔
1328
        next_report_time = current_time_ns + report_interval;
24✔
1329
    }
8✔
1330

1331
    last_send_time = 0;
299✔
1332

1333
    seqmap_init();
299✔
1334

1335
    /* main loop */
1336
    main_loop();
299✔
1337

1338
/* Debug: CPU Performance */
1339
#if defined(DEBUG) || defined(_DEBUG)
1340
    perf_cpu_end = clock();
1341
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1342
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1343
#endif /* DEBUG || _DEBUG */
1344

1345
    finish();
299✔
1346

1347
    return 0;
65✔
1348
}
1349

1350
static inline int64_t timespec_ns(struct timespec *a)
5,191✔
1351
{
1352
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,191✔
1353
}
1354

1355
#if HAVE_SO_TIMESTAMPNS
1356
/* convert a struct timeval to nanoseconds */
1357
static inline int64_t timeval_ns(struct timeval *a)
1358
{
1359
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1360
}
1361
#endif /* HAVE_SO_TIMESTAMPNS */
1362

1363
void add_cidr(char *addr)
54✔
1364
{
1365
    char *addr_end;
1366
    char *mask_str;
1367
    unsigned long mask;
1368
    int ret;
1369
    struct addrinfo addr_hints;
1370
    struct addrinfo *addr_res;
1371
    unsigned long net_addr;
1372
#ifdef IPV6
1373
    uint64_t net_upper, net_lower;
1374
    char *scope_str;
1375
#endif /* IPV6 */
1376

1377
    /* Split address from mask */
1378
    addr_end = strrchr(addr, '/');
54✔
1379
    if (addr_end == NULL) {
54✔
1380
        usage(1);
3✔
1381
    }
1✔
1382
    mask_str = addr_end + 1;
52✔
1383

1384
#ifdef IPV6
1385
    /* IPv6 addresses can have a scope */
1386
    scope_str = strchr(addr, '%');
52✔
1387
    if (scope_str && mask_str < scope_str) {
52✔
1388
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1389
        exit(1);
2✔
1390
    }
1391
#endif /*IPV6 */
1392

1393
    *addr_end = '\0';
50✔
1394
    mask = atoi(mask_str);
50✔
1395

1396
    /* parse address */
1397
    memset(&addr_hints, 0, sizeof(struct addrinfo));
50✔
1398
    addr_hints.ai_family = hints_ai_family;
50✔
1399
    addr_hints.ai_flags = AI_NUMERICHOST;
50✔
1400
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
50✔
1401
    if (ret) {
50✔
1402
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1403
        exit(1);
7✔
1404
    }
1405
    if (addr_res->ai_family == AF_INET) {
43✔
1406
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
21✔
1407
        freeaddrinfo(addr_res);
21✔
1408
        add_cidr_ipv4(net_addr, mask);
21✔
1409
#ifdef IPV6
1410
    } else if (addr_res->ai_family == AF_INET6) {
29✔
1411
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1412
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1413
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1414
        freeaddrinfo(addr_res);
22✔
1415
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1416
#endif /* IPV6 */
1417
    } else {
1418
        freeaddrinfo(addr_res);
×
1419
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1420
        exit(1);
×
1421
    }
1422
}
27✔
1423

1424
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
21✔
1425
{
1426
    unsigned long bitmask;
1427
    unsigned long net_last;
1428

1429
    /* check mask */
1430
    if (mask < 1 || mask > 32) {
21✔
1431
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1432
        exit(1);
6✔
1433
    }
1434

1435
    /* convert mask integer from 1 to 32 to a bitmask */
1436
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
15✔
1437

1438
    /* calculate network range */
1439
    net_addr &= bitmask;
15✔
1440
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
15✔
1441

1442
    /* exclude network and broadcast address for regular prefixes */
1443
    if (mask < 31) {
15✔
1444
        net_last--;
9✔
1445
        net_addr++;
9✔
1446
    }
3✔
1447

1448
    /* add all hosts in that network (net_addr and net_last inclusive) */
1449
    add_addr_range_ipv4(net_addr, net_last);
15✔
1450
}
13✔
1451

1452
#ifdef IPV6
1453
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1454
{
1455
    uint64_t bitmask_lower;
1456
    uint64_t last_lower;
1457

1458
    /* check mask -- 2^63 addresses should suffice for now */
1459
    if (mask < 65 || mask > 128) {
23✔
1460
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1461
        exit(1);
7✔
1462
    }
1463

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

1467
    /* calculate network range */
1468
    net_lower &= bitmask_lower;
16✔
1469
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1470

1471
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1472
}
12✔
1473
#endif /* IPV6 */
1474

1475
void add_range(char *start, char *end)
52✔
1476
{
1477
    struct addrinfo addr_hints;
1478
    struct addrinfo *addr_res;
1479
    unsigned long start_long;
1480
    unsigned long end_long;
1481
    int ret;
1482
#ifdef IPV6
1483
    uint64_t start_upper, start_lower;
1484
    uint64_t end_upper, end_lower;
1485
    char *start_scope_str, *end_scope_str;
1486

1487
    /*
1488
     * The compiler does not know that setting the address family hint to
1489
     * ensure that start and end are from the same address family also
1490
     * ensures that either start_long and end_long are initialized and used,
1491
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1492
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1493
     * supported to suppress compiler warnings.
1494
     */
1495
    start_long = -1;
52✔
1496
    end_long = 0;
52✔
1497
    start_upper = start_lower = -1;
52✔
1498
    end_upper = end_lower = 0;
52✔
1499
    start_scope_str = end_scope_str = NULL;
52✔
1500
#endif /* IPV6 */
1501

1502
    /* parse start address */
1503
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1504
    addr_hints.ai_family = hints_ai_family;
52✔
1505
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1506
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1507
    if (ret) {
52✔
1508
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1509
        exit(1);
9✔
1510
    }
1511
    /* start and end must be from the same address family */
1512
    hints_ai_family = addr_res->ai_family;
43✔
1513
    if (addr_res->ai_family == AF_INET) {
43✔
1514
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1515
        freeaddrinfo(addr_res);
17✔
1516
#ifdef IPV6
1517
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1518
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1519
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1520
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1521
        freeaddrinfo(addr_res);
26✔
1522
#endif /* IPV6 */
1523
    } else {
1524
        freeaddrinfo(addr_res);
×
1525
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1526
        exit(1);
×
1527
    }
1528

1529
#ifdef IPV6
1530
    /* IPv6 addresses can have a scope */
1531
    if (hints_ai_family == AF_INET6) {
43✔
1532
        start_scope_str = strchr(start, '%');
26✔
1533
        end_scope_str = strchr(end, '%');
26✔
1534
        if ((!start_scope_str && end_scope_str) ||
26✔
1535
            (start_scope_str && !end_scope_str) ||
24✔
1536
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1537
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1538
                exit(1);
6✔
1539
        }
1540
    }
1541
#endif
1542

1543
    /* parse end address */
1544
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1545
    addr_hints.ai_family = hints_ai_family;
37✔
1546
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1547
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1548
    if (ret) {
37✔
1549
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1550
        exit(1);
7✔
1551
    }
1552
    if (addr_res->ai_family == AF_INET) {
30✔
1553
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1554
        freeaddrinfo(addr_res);
12✔
1555
        add_addr_range_ipv4(start_long, end_long);
12✔
1556
#ifdef IPV6
1557
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1558
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1559
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1560
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1561
        freeaddrinfo(addr_res);
18✔
1562
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
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
}
24✔
1570

1571
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
27✔
1572
{
1573
    /* check if generator limit is exceeded */
1574
    if (end_long >= start_long + MAX_GENERATE) {
27✔
1575
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1576
        exit(1);
6✔
1577
    }
1578

1579
    /* generate */
1580
    for (; start_long <= end_long; start_long++) {
84✔
1581
        struct in_addr in_addr_tmp;
1582
        char buffer[20];
1583
        in_addr_tmp.s_addr = htonl(start_long);
63✔
1584
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
63✔
1585
        add_name(buffer);
63✔
1586
    }
21✔
1587
}
21✔
1588

1589
#ifdef IPV6
1590
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1591
{
1592
    int i;
1593
    uint64_t ret = 0;
134✔
1594
    for (i = 0; i < 8; i++) {
1,206✔
1595
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1596
    }
16✔
1597
    return ret;
134✔
1598
}
1599

1600
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1601
{
1602
    int i;
1603
    for (i = 0; i < 8; i++) {
720✔
1604
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1605
    }
1606
}
80✔
1607

1608
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1609
                         uint64_t end_upper, uint64_t end_lower,
1610
                         const char *scope_str)
1611
{
1612
    struct in6_addr in6_addr_tmp;
1613
    char buffer[100];
1614

1615
    /* prevent generating too many addresses */
1616
    if ((start_upper + 1 < end_upper) ||
34✔
1617
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1618
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1619
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1620
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1621
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1622
        exit(1);
8✔
1623
    }
1624

1625
    while ((start_upper < end_upper) ||
66✔
1626
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1627
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1628
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1629
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1630
        if (scope_str) {
40✔
1631
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1632
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1633
                exit(1);
×
1634
            }
1635
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1636
        }
1637
        add_name(buffer);
40✔
1638
        start_lower++;
40✔
1639
        if (start_lower == 0) {
40✔
1640
            start_upper++;
2✔
1641
        }
1642
    }
1643
}
26✔
1644
#endif /* IPv6 */
1645

1646
void main_loop()
311✔
1647
{
1648
    int64_t lt;
1649
    int64_t wait_time_ns;
1650
    struct event *event;
1651
    struct host_entry *h;
1652

1653
    while (event_queue_ping.first || event_queue_timeout.first) {
1,734✔
1654
        dbg_printf("%s", "# main_loop\n");
1655

1656
        /* timeout event ? */
1657
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,440✔
1658
            event = ev_dequeue(&event_queue_timeout);
95✔
1659
            h = event->host;
95✔
1660

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

1663
            stats_add(h, event->ping_index, 0, -1);
95✔
1664

1665
            if (per_recv_flag) {
95✔
1666
                if (timestamp_flag) {
19✔
1667
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1668
                }
1669
                printf("%-*s : [%d], timed out",
19✔
1670
                    max_hostname_len, h->host, event->ping_index);
5✔
1671
                if (h->num_recv > 0) {
19✔
1672
                    printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1673
                }
1674
                else {
1675
                    printf(" (NaN avg, ");
19✔
1676
                }
1677
                if (h->num_recv <= h->num_sent) {
19✔
1678
                    printf("%d%% loss)",
19✔
1679
                        ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1680
                }
5✔
1681
                else {
1682
                    printf("%d%% return)",
×
1683
                        (h->num_recv_total * 100) / h->num_sent);
×
1684
                }
1685
                printf("\n");
19✔
1686
            }
5✔
1687

1688
            /* do we need to send a retry? */
1689
            if (!loop_flag && !count_flag) {
95✔
1690
                if (h->num_sent < retry + 1) {
52✔
1691
                    if (backoff_flag) {
30✔
1692
                        h->timeout *= backoff;
30✔
1693
                    }
6✔
1694
                    send_ping(h, event->ping_index);
30✔
1695
                }
6✔
1696
            }
10✔
1697

1698
            /* note: we process first timeout events, because we might need to
1699
             * wait to process ping events, while we for sure never need to
1700
             * wait for timeout events.
1701
             */
1702
            continue;
95✔
1703
        }
1704

1705
        /* ping event ? */
1706
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,576✔
1707
            /* Make sure that we don't ping more than once every "interval" */
1708
            lt = current_time_ns - last_send_time;
1,060✔
1709
            if (lt < interval)
1,060✔
1710
                goto wait_for_reply;
253✔
1711

1712
            /* Dequeue the event */
1713
            event = ev_dequeue(&event_queue_ping);
807✔
1714
            h = event->host;
807✔
1715

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

1718
            /* Send the ping */
1719
            send_ping(h, event->ping_index);
807✔
1720

1721
            /* Loop and count mode: schedule next ping */
1722
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
807✔
1723
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
315✔
1724
            }
101✔
1725
        }
231✔
1726

1727
    wait_for_reply:
574✔
1728

1729
        /* When is the next ping next event? */
1730
        wait_time_ns = -1;
1,345✔
1731
        if (event_queue_ping.first) {
1,345✔
1732
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
978✔
1733
            if (wait_time_ns < 0)
978✔
1734
                wait_time_ns = 0;
533✔
1735
            /* make sure that we wait enough, so that the inter-ping delay is
1736
             * bigger than 'interval' */
1737
            if (wait_time_ns < interval) {
978✔
1738
                lt = current_time_ns - last_send_time;
541✔
1739
                if (lt < interval) {
541✔
1740
                    wait_time_ns = interval - lt;
541✔
1741
                }
193✔
1742
            }
193✔
1743

1744
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1745
        }
308✔
1746

1747
        /* When is the next timeout event? */
1748
        if (event_queue_timeout.first) {
1,345✔
1749
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
847✔
1750
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
847✔
1751
                wait_time_ns = wait_time_timeout;
364✔
1752
                if (wait_time_ns < 0) {
364✔
1753
                    wait_time_ns = 0;
×
1754
                }
1755
            }
90✔
1756

1757
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1758
        }
245✔
1759

1760
        /* When is the next report due? */
1761
        if (report_interval && (loop_flag || count_flag)) {
1,345✔
1762
            int64_t wait_time_next_report = next_report_time - current_time_ns;
205✔
1763
            if (wait_time_next_report < wait_time_ns) {
205✔
1764
                wait_time_ns = wait_time_next_report;
69✔
1765
                if (wait_time_ns < 0) {
69✔
1766
                    wait_time_ns = 0;
×
1767
                }
1768
            }
23✔
1769

1770
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1771
        }
67✔
1772

1773
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1774
        if (wait_time_ns == -1) {
1,345✔
1775
            break;
14✔
1776
        }
1777

1778
        /* end of loop was requested by interrupt signal handler */
1779
        if (finish_requested) {
1,331✔
1780
            break;
3✔
1781
        }
1782

1783
        /* Receive replies */
1784
        /* (this is what sleeps during each loop iteration) */
1785
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1786
        if (wait_for_reply(wait_time_ns)) {
1,328✔
1787
            while (wait_for_reply(0))
1,231✔
1788
                ; /* process other replies in the queue */
1789
        }
214✔
1790

1791
        update_current_time();
1,328✔
1792

1793
        if (status_snapshot) {
1,328✔
1794
            status_snapshot = 0;
×
1795
            print_per_system_splits();
×
1796
        }
1797

1798
        /* Print report */
1799
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,328✔
1800
            if (netdata_flag)
42✔
1801
                print_netdata();
3✔
1802
            else
1803
                print_per_system_splits();
39✔
1804

1805
            while (current_time_ns >= next_report_time) {
84✔
1806
                next_report_time += report_interval;
42✔
1807
            }
1808
        }
14✔
1809
    }
1810
}
311✔
1811

1812
/************************************************************
1813

1814
  Function: signal_handler
1815

1816
*************************************************************
1817

1818
  Inputs:  int signum
1819

1820
  Description:
1821

1822
  SIGQUIT signal handler - set flag and return
1823
  SIGINT signal handler - set flag and return
1824

1825
************************************************************/
1826

1827
void signal_handler(int signum)
×
1828
{
1829
    switch (signum) {
×
1830
    case SIGINT:
1831
        finish_requested = 1;
×
1832
        break;
×
1833

1834
    case SIGQUIT:
1835
        status_snapshot = 1;
×
1836
        break;
×
1837
    }
1838
}
×
1839

1840
/************************************************************
1841

1842
  Function: update_current_time
1843

1844
*************************************************************/
1845

1846
void update_current_time()
4,189✔
1847
{
1848
    clock_gettime(CLOCKID, &current_time);
4,189✔
1849
    current_time_ns = timespec_ns(&current_time);
4,189✔
1850
}
4,189✔
1851

1852
/************************************************************
1853

1854
  Function: finish
1855

1856
*************************************************************
1857

1858
  Inputs:  void (none)
1859

1860
  Description:
1861

1862
  Main program clean up and exit point
1863

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

1866
void finish()
311✔
1867
{
1868
    int i;
1869
    HOST_ENTRY *h;
1870

1871
    update_current_time();
311✔
1872
    end_time = current_time_ns;
311✔
1873

1874
    /* tot up unreachables */
1875
    for (i = 0; i < num_hosts; i++) {
818✔
1876
        h = table[i];
507✔
1877

1878
        if (!h->num_recv) {
507✔
1879
            num_unreachable++;
80✔
1880

1881
            if (verbose_flag || unreachable_flag) {
80✔
1882
                printf("%s", h->host);
44✔
1883

1884
                if (verbose_flag)
44✔
1885
                    printf(" is unreachable");
41✔
1886

1887
                printf("\n");
44✔
1888
            }
2✔
1889
        }
12✔
1890
    }
135✔
1891

1892
    if (count_flag || loop_flag)
311✔
1893
        print_per_system_stats();
100✔
1894
#if defined(DEBUG) || defined(_DEBUG)
1895
    else if (print_per_system_flag)
1896
        print_per_system_stats();
1897
#endif /* DEBUG || _DEBUG */
1898

1899
    if (stats_flag)
311✔
1900
        print_global_stats();
9✔
1901

1902
    if (min_reachable) {
311✔
1903
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
1904
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1905
            exit(0);
6✔
1906
        }
1907
        else {
1908
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
1909
            exit(1);
3✔
1910
        }
1911
    }
1912

1913
    if (num_noaddress)
302✔
1914
        exit(2);
3✔
1915
    else if (num_alive != num_hosts)
299✔
1916
        exit(1);
41✔
1917

1918
    exit(0);
258✔
1919
}
1920

1921
/************************************************************
1922

1923
  Function: print_per_system_stats
1924

1925
*************************************************************
1926

1927
  Inputs:  void (none)
1928

1929
  Description:
1930

1931

1932
************************************************************/
1933

1934
void print_per_system_stats(void)
100✔
1935
{
1936
    int i, j, avg, outage_ms;
1937
    HOST_ENTRY *h;
1938
    int64_t resp;
1939

1940
    if (verbose_flag || per_recv_flag)
100✔
1941
        fprintf(stderr, "\n");
59✔
1942

1943
    for (i = 0; i < num_hosts; i++) {
234✔
1944
        h = table[i];
134✔
1945
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
1946

1947
        if (report_all_rtts_flag) {
134✔
1948
            for (j = 0; j < h->num_sent; j++) {
248✔
1949
                if ((resp = h->resp_times[j]) >= 0)
204✔
1950
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
1951
                else
1952
                    fprintf(stderr, " -");
8✔
1953
            }
64✔
1954

1955
            fprintf(stderr, "\n");
44✔
1956
        }
12✔
1957
        else {
1958
            if (h->num_recv <= h->num_sent) {
90✔
1959
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
1960
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
1961

1962
                if (outage_flag) {
90✔
1963
                    /* Time outage total */
1964
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
1965
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
1966
                }
3✔
1967
            }
26✔
1968
            else {
1969
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
1970
                    h->num_sent, h->num_recv,
1971
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
1972
            }
1973

1974
            if (h->num_recv) {
90✔
1975
                avg = h->total_time / h->num_recv;
79✔
1976
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
1977
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
1978
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
1979
            }
23✔
1980

1981
            fprintf(stderr, "\n");
90✔
1982
        }
1983
    }
38✔
1984
}
100✔
1985

1986
/************************************************************
1987

1988
  Function: print_netdata
1989

1990
*************************************************************
1991

1992
  Inputs:  void (none)
1993

1994
  Description:
1995

1996

1997
************************************************************/
1998

1999
void print_netdata(void)
3✔
2000
{
2001
    static int sent_charts = 0;
2002

2003
    int i;
2004
    int64_t avg;
2005
    HOST_ENTRY *h;
2006

2007
    for (i = 0; i < num_hosts; i++) {
6✔
2008
        h = table[i];
3✔
2009

2010
        if (!sent_charts) {
3✔
2011
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2012
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2013
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2014
        }
1✔
2015

2016
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2017
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2018
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2019
        printf("END\n");
3✔
2020

2021
        if (!sent_charts) {
3✔
2022
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2023
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2024
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2025
        }
1✔
2026

2027
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2028
        /*
2029
        if( h->num_recv_i <= h->num_sent_i )
2030
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2031
        else
2032
            printf("SET lost = 0\n");
2033
*/
2034

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

2038
        if (!sent_charts) {
3✔
2039
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2040
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2041
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2042
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2043
        }
1✔
2044

2045
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2046
        if (h->num_recv_i) {
3✔
2047
            avg = h->total_time_i / h->num_recv_i;
3✔
2048
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2049
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2050
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2051
        }
1✔
2052
        printf("END\n");
3✔
2053

2054
        stats_reset_interval(h);
3✔
2055
    }
1✔
2056

2057
    sent_charts = 1;
3✔
2058
}
3✔
2059

2060
/************************************************************
2061

2062
  Function: print_per_system_splits
2063

2064
*************************************************************
2065

2066
  Inputs:  void (none)
2067

2068
  Description:
2069

2070

2071
************************************************************/
2072

2073
void print_per_system_splits(void)
39✔
2074
{
2075
    int i, avg, outage_ms_i;
2076
    HOST_ENTRY *h;
2077
    struct tm *curr_tm;
2078

2079
    if (verbose_flag || per_recv_flag)
39✔
2080
        fprintf(stderr, "\n");
×
2081

2082
    update_current_time();
39✔
2083
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2084
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2085
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2086

2087
    for (i = 0; i < num_hosts; i++) {
78✔
2088
        h = table[i];
39✔
2089
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2090

2091
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2092
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2093
                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✔
2094

2095
            if (outage_flag) {
39✔
2096
                /* Time outage  */
2097
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2098
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2099
            }
4✔
2100
        }
13✔
2101
        else {
2102
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2103
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2104
        }
2105

2106
        if (h->num_recv_i) {
39✔
2107
            avg = h->total_time_i / h->num_recv_i;
27✔
2108
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2109
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2110
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2111
        }
9✔
2112

2113
        fprintf(stderr, "\n");
39✔
2114
        if (!cumulative_stats_flag) {
39✔
2115
            stats_reset_interval(h);
27✔
2116
        }
9✔
2117
    }
13✔
2118
}
39✔
2119

2120
/************************************************************
2121

2122
  Function: print_global_stats
2123

2124
*************************************************************
2125

2126
  Inputs:  void (none)
2127

2128
  Description:
2129

2130

2131
************************************************************/
2132

2133
void print_global_stats(void)
9✔
2134
{
2135
    fprintf(stderr, "\n");
9✔
2136
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2137
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2138
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2139
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2140
    fprintf(stderr, "\n");
9✔
2141
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2142
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2143
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2144
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2145
    fprintf(stderr, "\n");
9✔
2146

2147
    if (total_replies == 0) {
9✔
2148
        min_reply = 0;
3✔
2149
        max_reply = 0;
3✔
2150
        total_replies = 1;
3✔
2151
        sum_replies = 0;
3✔
2152
    }
1✔
2153

2154
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2155
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2156
        sprint_tm(sum_replies / total_replies));
3✔
2157
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2158
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2159
        (end_time - start_time) / 1e9);
9✔
2160
    fprintf(stderr, "\n");
9✔
2161
}
9✔
2162

2163
/************************************************************
2164

2165
  Function: send_ping
2166

2167
*************************************************************
2168

2169
  Inputs:  int s, HOST_ENTRY *h
2170

2171
  Description:
2172

2173
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2174
  will be added on by the kernel.  The ID field is our UNIX process ID,
2175
  and the sequence number is an index into an array of outstanding
2176
  ping requests. The sequence number will later be used to quickly
2177
  figure out who the ping reply came from.
2178

2179
************************************************************/
2180

2181
int send_ping(HOST_ENTRY *h, int index)
837✔
2182
{
2183
    int n;
2184
    int myseq;
2185
    int ret = 1;
837✔
2186
    uint8_t proto = ICMP_ECHO;
837✔
2187

2188
    update_current_time();
837✔
2189
    h->last_send_time = current_time_ns;
837✔
2190
    myseq = seqmap_add(h->i, index, current_time_ns);
837✔
2191

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

2194
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
837✔
2195
        if(icmp_request_typ == 13)
714✔
2196
            proto = ICMP_TSTAMP;
16✔
2197
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
714✔
2198
    }
222✔
2199
#ifdef IPV6
2200
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2201
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2202
    }
15✔
2203
#endif
2204
    else {
2205
        return 0;
×
2206
    }
2207

2208
    /* error sending? */
2209
    if (
600✔
2210
        (n < 0)
237✔
2211
#if defined(EHOSTDOWN)
2212
        && errno != EHOSTDOWN
271✔
2213
#endif
2214
    ) {
2215
        if (verbose_flag) {
34✔
2216
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2217
        }
2218
        else {
2219
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2220
        }
2221

2222
        h->num_sent++;
34✔
2223
        h->num_sent_i++;
34✔
2224
        if (!loop_flag)
34✔
2225
            h->resp_times[index] = RESP_ERROR;
34✔
2226

2227
        ret = 0;
34✔
2228
    }
2229
    else {
2230
        /* schedule timeout */
2231
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
803✔
2232

2233
        /* mark this trial as outstanding */
2234
        if (!loop_flag) {
803✔
2235
            h->resp_times[index] = RESP_WAITING;
803✔
2236
        }
237✔
2237
    }
2238

2239
    num_pingsent++;
837✔
2240
    last_send_time = h->last_send_time;
837✔
2241

2242
    return (ret);
837✔
2243
}
237✔
2244

2245
int socket_can_read(struct timeval *timeout)
2,559✔
2246
{
2247
    int nfound;
2248
    fd_set readset;
2249
    int socketmax;
2250

2251
#ifndef IPV6
2252
    socketmax = socket4;
2253
#else
2254
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,559✔
2255
#endif
2256

2257
select_again:
1,936✔
2258
    FD_ZERO(&readset);
33,535✔
2259
    if (socket4 >= 0)
2,559✔
2260
        FD_SET(socket4, &readset);
2,559✔
2261
#ifdef IPV6
2262
    if (socket6 >= 0)
2,559✔
2263
        FD_SET(socket6, &readset);
2,559✔
2264
#endif
2265

2266
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,559✔
2267
    if (nfound < 0) {
2,559✔
2268
        if (errno == EINTR) {
×
2269
            /* interrupted system call: redo the select */
2270
            goto select_again;
×
2271
        }
2272
        else {
2273
            perror("select");
×
2274
        }
2275
    }
2276

2277
    if (nfound > 0) {
2,559✔
2278
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,231✔
2279
            return socket4;
1,051✔
2280
        }
2281
#ifdef IPV6
2282
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
180✔
2283
            return socket6;
180✔
2284
        }
2285
#endif
2286
    }
2287

2288
    return -1;
1,328✔
2289
}
623✔
2290

2291
int receive_packet(int64_t wait_time,
2,559✔
2292
#if HAVE_SO_TIMESTAMPNS
2293
    int64_t *reply_timestamp,
2294
#else
2295
    int64_t *reply_timestamp __attribute__((unused)),
2296
#endif
2297
    struct sockaddr *reply_src_addr,
2298
    size_t reply_src_addr_len,
2299
    char *reply_buf,
2300
    size_t reply_buf_len)
2301
{
2302
    struct timeval to;
2303
    int s = 0;
2,559✔
2304
    int recv_len;
2305
    static unsigned char msg_control[40];
2306
    struct iovec msg_iov = {
3,805✔
2307
        reply_buf,
623✔
2308
        reply_buf_len
623✔
2309
    };
2310
    struct msghdr recv_msghdr = {0};
2,559✔
2311
    recv_msghdr.msg_name = reply_src_addr;
2,559✔
2312
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,559✔
2313
    recv_msghdr.msg_iov = &msg_iov;
2,559✔
2314
    recv_msghdr.msg_iovlen = 1;
2,559✔
2315
    recv_msghdr.msg_control = &msg_control;
2,559✔
2316
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,559✔
2317
#if HAVE_SO_TIMESTAMPNS
2318
    struct cmsghdr *cmsg;
2319
#endif
2320

2321
    /* Wait for a socket to become ready */
2322
    if (wait_time) {
2,559✔
2323
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,328✔
2324
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,328✔
2325
    }
394✔
2326
    else {
2327
        to.tv_sec = 0;
1,231✔
2328
        to.tv_usec = 0;
1,231✔
2329
    }
2330
    s = socket_can_read(&to);
2,559✔
2331
    if (s == -1) {
2,559✔
2332
        return 0; /* timeout */
1,328✔
2333
    }
2334

2335
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,231✔
2336
    if (recv_len <= 0) {
1,231✔
2337
        return 0;
×
2338
    }
2339

2340
#if HAVE_SO_TIMESTAMPNS
2341
    /* ancilliary data */
2342
    {
2343
        struct timespec reply_timestamp_ts;
2344
        struct timeval reply_timestamp_tv;
2345
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,002✔
2346
             cmsg != NULL;
2,004✔
2347
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,002✔
2348
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,002✔
2349
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,002✔
2350
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,002✔
2351
            }
2352
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,002✔
2353
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2354
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2355
            }
2356
        }
2357
    }
2358
#endif
2359

2360
#if defined(DEBUG) || defined(_DEBUG)
2361
    if (randomly_lose_flag) {
2362
        if ((random() & 0x07) <= lose_factor)
2363
            return 0;
2364
    }
2365
#endif
2366

2367
    return recv_len;
1,231✔
2368
}
623✔
2369

2370
/* stats_add: update host statistics for a single packet that was received (or timed out)
2371
 * h: host entry to update
2372
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2373
 * success: 1 if response received, 0 otherwise
2374
 * latency: response time, in ns
2375
 */
2376
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
803✔
2377
{
2378
    /* sent count - we update only on receive/timeout, so that we don't get
2379
     * weird loss percentage, just because a packet was note recived yet.
2380
     */
2381
    h->num_sent++;
803✔
2382
    h->num_sent_i++;
803✔
2383

2384
    if (!success) {
803✔
2385
        if (!loop_flag && index >= 0) {
95✔
2386
            h->resp_times[index] = RESP_TIMEOUT;
95✔
2387
        }
23✔
2388
        num_timeout++;
95✔
2389
        return;
95✔
2390
    }
2391

2392
    /* received count */
2393
    h->num_recv++;
708✔
2394
    h->num_recv_i++;
708✔
2395

2396
    /* maximum */
2397
    if (!h->max_reply || latency > h->max_reply) {
708✔
2398
        h->max_reply = latency;
550✔
2399
    }
159✔
2400
    if (!h->max_reply_i || latency > h->max_reply_i) {
708✔
2401
        h->max_reply_i = latency;
570✔
2402
    }
164✔
2403

2404
    /* minimum */
2405
    if (!h->min_reply || latency < h->min_reply) {
708✔
2406
        h->min_reply = latency;
469✔
2407
    }
139✔
2408
    if (!h->min_reply_i || latency < h->min_reply_i) {
708✔
2409
        h->min_reply_i = latency;
494✔
2410
    }
148✔
2411

2412
    /* total time (for average) */
2413
    h->total_time += latency;
708✔
2414
    h->total_time_i += latency;
708✔
2415

2416
    /* response time per-packet (count mode) */
2417
    if (!loop_flag && index >= 0) {
708✔
2418
        h->resp_times[index] = latency;
708✔
2419
    }
214✔
2420
}
237✔
2421

2422
/* stats_reset_interval: reset interval statistics
2423
 * h: host entry to update
2424
 */
2425
void stats_reset_interval(HOST_ENTRY *h)
30✔
2426
{
2427
    h->num_sent_i = 0;
30✔
2428
    h->num_recv_i = 0;
30✔
2429
    h->max_reply_i = 0;
30✔
2430
    h->min_reply_i = 0;
30✔
2431
    h->total_time_i = 0;
30✔
2432
}
30✔
2433

2434
int decode_icmp_ipv4(
1,051✔
2435
    struct sockaddr *response_addr,
2436
    size_t response_addr_len,
2437
    char *reply_buf,
2438
    size_t reply_buf_len,
2439
    unsigned short *id,
2440
    unsigned short *seq,
2441
    int *ip_header_tos,
2442
    int *ip_header_ttl,
2443
    uint32_t *ip_header_otime_ms,
2444
    uint32_t *ip_header_rtime_ms,
2445
    uint32_t *ip_header_ttime_ms)
2446
{
2447
    struct icmp *icp;
2448
    int hlen = 0;
1,051✔
2449

2450
    if (!using_sock_dgram4) {
1,051✔
2451
        struct ip *ip = (struct ip *)reply_buf;
1,043✔
2452
        *ip_header_tos = ip->ip_tos;
1,043✔
2453
        *ip_header_ttl = ip->ip_ttl;
1,043✔
2454

2455
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2456
        /* The alpha headers are decidedly broken.
2457
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2458
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2459
         */
2460
        hlen = (ip->ip_vhl & 0x0F) << 2;
2461
#else
2462
        hlen = ip->ip_hl << 2;
1,043✔
2463
#endif
2464
    }
199✔
2465

2466
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,051✔
2467
        /* too short */
2468
        if (verbose_flag) {
×
2469
            char buf[INET6_ADDRSTRLEN];
2470
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2471
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2472
        }
2473
        return -1;
×
2474
    }
2475

2476
    icp = (struct icmp *)(reply_buf + hlen);
1,051✔
2477

2478
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,051✔
2479
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
444✔
2480
        /* Handle other ICMP packets */
2481
        struct icmp *sent_icmp;
2482
        SEQMAP_VALUE *seqmap_value;
2483
        char addr_ascii[INET6_ADDRSTRLEN];
2484
        HOST_ENTRY *h;
2485

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

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

2494
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
422✔
2495
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
14✔
2496
            sent_icmp->icmp_id != ident4) {
×
2497
            /* not caused by us */
2498
            return -1;
422✔
2499
        }
2500

2501
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2502
        if (seqmap_value == NULL) {
×
2503
            return -1;
×
2504
        }
2505

2506
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2507

2508
        switch (icp->icmp_type) {
×
2509
        case ICMP_UNREACH:
2510
            h = table[seqmap_value->host_nr];
×
2511
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2512
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2513
                    addr_ascii, h->host);
2514
            }
2515
            else {
2516
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2517
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2518
            }
2519

2520
            print_warning("\n");
×
2521
            num_othericmprcvd++;
×
2522
            break;
×
2523

2524
        case ICMP_SOURCEQUENCH:
2525
        case ICMP_REDIRECT:
2526
        case ICMP_TIMXCEED:
2527
        case ICMP_PARAMPROB:
2528
            h = table[seqmap_value->host_nr];
×
2529
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2530
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2531
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2532
            }
2533
            else {
2534
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2535
                    icp->icmp_type, addr_ascii, h->host);
×
2536
            }
2537
            print_warning("\n");
×
2538
            num_othericmprcvd++;
×
2539
            break;
×
2540
        }
2541

2542
        return -1;
×
2543
    }
2544

2545
    *id = icp->icmp_id;
629✔
2546
    *seq = ntohs(icp->icmp_seq);
629✔
2547
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
629✔
2548

2549
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2550
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
14✔
2551
            if (verbose_flag) {
×
2552
                char buf[INET6_ADDRSTRLEN];
2553
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2554
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2555
            }
2556
            return -1;
×
2557
        }
2558

2559
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
14✔
2560
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
14✔
2561
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
14✔
2562
    }
2563

2564
    return hlen;
629✔
2565
}
199✔
2566

2567
#ifdef IPV6
2568
int decode_icmp_ipv6(
180✔
2569
    struct sockaddr *response_addr,
2570
    size_t response_addr_len,
2571
    char *reply_buf,
2572
    size_t reply_buf_len,
2573
    unsigned short *id,
2574
    unsigned short *seq)
2575
{
2576
    struct icmp6_hdr *icp;
2577

2578
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
180✔
2579
        if (verbose_flag) {
×
2580
            char buf[INET6_ADDRSTRLEN];
2581
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2582
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2583
        }
2584
        return 0; /* too short */
×
2585
    }
2586

2587
    icp = (struct icmp6_hdr *)reply_buf;
180✔
2588

2589
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
180✔
2590
        /* Handle other ICMP packets */
2591
        struct icmp6_hdr *sent_icmp;
2592
        SEQMAP_VALUE *seqmap_value;
2593
        char addr_ascii[INET6_ADDRSTRLEN];
2594
        HOST_ENTRY *h;
2595

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

2602
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
89✔
2603

2604
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
89✔
2605
            /* not caused by us */
2606
            return 0;
89✔
2607
        }
2608

2609
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2610
        if (seqmap_value == NULL) {
×
2611
            return 0;
×
2612
        }
2613

2614
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2615

2616
        switch (icp->icmp6_type) {
×
2617
        case ICMP_UNREACH:
2618
            h = table[seqmap_value->host_nr];
×
2619
            if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
×
2620
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2621
                    addr_ascii, h->host);
2622
            }
2623
            else {
2624
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2625
                    icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2626
            }
2627

2628
            print_warning("\n");
×
2629
            num_othericmprcvd++;
×
2630
            break;
×
2631

2632
        case ICMP_SOURCEQUENCH:
2633
        case ICMP_REDIRECT:
2634
        case ICMP_TIMXCEED:
2635
        case ICMP_PARAMPROB:
2636
            h = table[seqmap_value->host_nr];
×
2637
            if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
×
2638
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2639
                    icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
×
2640
            }
2641
            else {
2642
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2643
                    icp->icmp6_type, addr_ascii, h->host);
×
2644
            }
2645
            print_warning("\n");
×
2646
            num_othericmprcvd++;
×
2647
            break;
×
2648
        }
2649

2650
        return 0;
×
2651
    }
2652

2653
    *id = icp->icmp6_id;
91✔
2654
    *seq = ntohs(icp->icmp6_seq);
91✔
2655

2656
    return 1;
91✔
2657
}
30✔
2658
#endif
2659

2660
int wait_for_reply(int64_t wait_time)
2,559✔
2661
{
2662
    int result;
2663
    static char buffer[RECV_BUFSIZE];
2664
    struct sockaddr_storage response_addr;
2665
    int n, avg;
2666
    HOST_ENTRY *h;
2667
    int64_t this_reply;
2668
    int this_count;
2669
    int64_t recv_time = 0;
2,559✔
2670
    SEQMAP_VALUE *seqmap_value;
2671
    unsigned short id;
2672
    unsigned short seq;
2673
    int ip_header_tos = -1;
2,559✔
2674
    int ip_header_ttl = -1;
2,559✔
2675
    // ICMP Timestamp
2676
    uint32_t ip_header_otime_ms = 0x80000000U;
2,559✔
2677
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,559✔
2678
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,559✔
2679

2680
    /* Receive packet */
2681
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,559✔
2682
        &recv_time, /* reply_timestamp */
2683
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2684
        sizeof(response_addr), /* reply_src_addr_len */
2685
        buffer, /* reply_buf */
2686
        sizeof(buffer) /* reply_buf_len */
2687
    );
2688

2689
    if (result <= 0) {
2,559✔
2690
        return 0;
1,328✔
2691
    }
2692

2693
    update_current_time();
1,231✔
2694
    if (recv_time == 0)
1,231✔
2695
        recv_time = current_time_ns;
229✔
2696

2697
    /* Process ICMP packet and retrieve id/seq */
2698
    if (response_addr.ss_family == AF_INET) {
1,231✔
2699
        int ip_hlen = decode_icmp_ipv4(
1,051✔
2700
            (struct sockaddr *)&response_addr,
2701
            sizeof(response_addr),
2702
            buffer,
2703
            sizeof(buffer),
2704
            &id,
2705
            &seq,
2706
            &ip_header_tos,
2707
            &ip_header_ttl,
2708
            &ip_header_otime_ms,
2709
            &ip_header_rtime_ms,
2710
            &ip_header_ttime_ms);
2711
        if (ip_hlen < 0) {
1,051✔
2712
            return 1;
422✔
2713
        }
2714
        if (id != ident4) {
629✔
2715
            return 1; /* packet received, but not the one we are looking for! */
×
2716
        }
2717
        if (!using_sock_dgram4) {
629✔
2718
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2719
             * with fping with IPv6 hosts */
2720
            result -= ip_hlen;
621✔
2721
        }
199✔
2722
    }
199✔
2723
#ifdef IPV6
2724
    else if (response_addr.ss_family == AF_INET6) {
180✔
2725
        if (!decode_icmp_ipv6(
180✔
2726
                (struct sockaddr *)&response_addr,
2727
                sizeof(response_addr),
2728
                buffer,
2729
                sizeof(buffer),
2730
                &id,
2731
                &seq)) {
2732
            return 1;
89✔
2733
        }
2734
        if (id != ident6) {
91✔
2735
            return 1; /* packet received, but not the one we are looking for! */
×
2736
        }
2737
    }
15✔
2738
#endif
2739
    else {
2740
        return 1;
×
2741
    }
2742

2743
    seqmap_value = seqmap_fetch(seq, current_time_ns);
720✔
2744
    if (seqmap_value == NULL) {
720✔
2745
        return 1;
×
2746
    }
2747

2748
    /* find corresponding host_entry */
2749
    n = seqmap_value->host_nr;
720✔
2750
    h = table[n];
720✔
2751
    this_count = seqmap_value->ping_count;
720✔
2752
    this_reply = recv_time - seqmap_value->ping_ts;
720✔
2753

2754
    /* update stats that include invalid replies */
2755
    h->num_recv_total++;
720✔
2756
    num_pingreceived++;
720✔
2757

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

2760
    /* optionally require reply source equal to target address */
2761
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
720✔
2762
        dbg_printf("%s\n", "discarding reply from wrong source address");
2763
        return 1;
12✔
2764
    }
2765

2766
    /* discard duplicates */
2767
    if (!loop_flag && h->resp_times[this_count] >= 0) {
708✔
2768
        if (!per_recv_flag) {
×
2769
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2770
                h->host, this_count, result, sprint_tm(this_reply));
2771

2772
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2773
                char buf[INET6_ADDRSTRLEN];
2774
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2775
                fprintf(stderr, " [<- %s]", buf);
×
2776
            }
2777
            fprintf(stderr, "\n");
×
2778
        }
2779
        return 1;
×
2780
    }
2781

2782
    /* discard reply if delay is larger than timeout
2783
     * (see also: github #32) */
2784
    if (this_reply > h->timeout) {
708✔
2785
        return 1;
×
2786
    }
2787

2788
    /* update stats */
2789
    stats_add(h, this_count, 1, this_reply);
708✔
2790
    // TODO: move to stats_add?
2791
    if (!max_reply || this_reply > max_reply)
708✔
2792
        max_reply = this_reply;
427✔
2793
    if (!min_reply || this_reply < min_reply)
708✔
2794
        min_reply = this_reply;
388✔
2795
    sum_replies += this_reply;
708✔
2796
    total_replies++;
708✔
2797

2798
    /* initialize timeout to initial timeout (without backoff) */
2799
    h->timeout = timeout;
708✔
2800

2801
    /* remove timeout event */
2802
    struct event *timeout_event = host_get_timeout_event(h, this_count);
708✔
2803
    if (timeout_event) {
708✔
2804
        ev_remove(&event_queue_timeout, timeout_event);
708✔
2805
    }
214✔
2806

2807
    /* print "is alive" */
2808
    if (h->num_recv == 1) {
708✔
2809
        num_alive++;
427✔
2810
        if (fast_reachable && num_alive >= min_reachable)
427✔
2811
                finish_requested = 1;
3✔
2812

2813
        if (verbose_flag || alive_flag) {
427✔
2814
            printf("%s", h->host);
296✔
2815

2816
            if (verbose_flag)
296✔
2817
                printf(" is alive");
276✔
2818
        }
84✔
2819
    }
123✔
2820

2821
    /* print received ping (unless --quiet) */
2822
    if (per_recv_flag) {
708✔
2823
        if (timestamp_flag) {
151✔
2824
            print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2825
        }
10✔
2826
        avg = h->total_time / h->num_recv;
151✔
2827
        printf("%-*s : [%d], %d bytes, %s ms",
151✔
2828
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2829
        printf(" (%s avg, ", sprint_tm(avg));
151✔
2830

2831
        if (h->num_recv <= h->num_sent) {
151✔
2832
            printf("%d%% loss)",
151✔
2833
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2834
        }
45✔
2835
        else {
2836
            printf("%d%% return)",
×
2837
                (h->num_recv_total * 100) / h->num_sent);
×
2838
        }
2839
    }
45✔
2840

2841
    if (verbose_flag || alive_flag || per_recv_flag) {
708✔
2842

2843
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
447✔
2844
            char buf[INET6_ADDRSTRLEN];
2845
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2846
            fprintf(stderr, " [<- %s]", buf);
6✔
2847
        }
2848

2849
        if (icmp_request_typ == 13) {
447✔
2850
            printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2851
                   alive_flag ? "" : ",",
14✔
2852
                   ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
2853
                   ms_since_midnight_utc(recv_time));
2854
        }
2855

2856
        if(print_tos_flag) {
447✔
2857
            if(ip_header_tos != -1) {
43✔
2858
                printf(" (TOS %d)", ip_header_tos);
39✔
2859
            }
11✔
2860
            else {
2861
                printf(" (TOS unknown)");
4✔
2862
            }
2863
        }
11✔
2864

2865
        if (print_ttl_flag) {
447✔
2866
          if(ip_header_ttl != -1) {
43✔
2867
              printf(" (TTL %d)", ip_header_ttl);
39✔
2868
          }
11✔
2869
          else {
2870
              printf(" (TTL unknown)");
4✔
2871
          }
2872
        }
11✔
2873

2874
        if (elapsed_flag && !per_recv_flag)
447✔
2875
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2876

2877
        printf("\n");
447✔
2878
    }
129✔
2879

2880
    return 1;
708✔
2881
}
623✔
2882

2883
/************************************************************
2884

2885
  Function: add_name
2886

2887
*************************************************************
2888

2889
  Inputs:  char* name
2890

2891
  Description:
2892

2893
  process input name for addition to target list
2894
  name can turn into multiple targets via multiple interfaces (-m)
2895
  or via NIS groups
2896

2897
************************************************************/
2898

2899
void add_name(char *name)
538✔
2900
{
2901
    struct addrinfo *res0, *res, hints;
2902
    int ret_ga;
2903
    char *printname;
2904
    char namebuf[256];
2905
    char addrbuf[256];
2906

2907
    /* getaddrinfo */
2908
    memset(&hints, 0, sizeof(struct addrinfo));
538✔
2909
    hints.ai_flags = AI_UNUSABLE;
538✔
2910
    hints.ai_socktype = SOCK_RAW;
538✔
2911
    hints.ai_family = hints_ai_family;
538✔
2912
    if (hints_ai_family == AF_INET) {
538✔
2913
        hints.ai_protocol = IPPROTO_ICMP;
79✔
2914
    }
21✔
2915
#ifdef IPV6
2916
    else if (hints_ai_family == AF_INET6) {
459✔
2917
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
2918
    }
1✔
2919
#endif
2920
    else {
2921
        hints.ai_socktype = SOCK_STREAM;
428✔
2922
        hints.ai_protocol = 0;
428✔
2923
    }
2924
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
538✔
2925
    if (ret_ga) {
538✔
2926
        if (!quiet_flag)
26✔
2927
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
2928
        num_noaddress++;
26✔
2929
        return;
26✔
2930
    }
2931

2932
    /* NOTE: we could/should loop with res on all addresses like this:
2933
     * for (res = res0; res; res = res->ai_next) {
2934
     * We don't do it yet, however, because is is an incompatible change
2935
     * (need to implement a separate option for this)
2936
     */
2937
    for (res = res0; res; res = res->ai_next) {
512✔
2938
        /* name_flag: addr -> name lookup requested) */
2939
        if (name_flag || rdns_flag) {
514✔
2940
            int do_rdns = rdns_flag ? 1 : 0;
3✔
2941
            if (name_flag) {
3✔
2942
                /* Was it a numerical address? Only then do a rdns-query */
2943
                struct addrinfo *nres;
2944
                hints.ai_flags = AI_NUMERICHOST;
×
2945
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
2946
                    do_rdns = 1;
×
2947
                    freeaddrinfo(nres);
×
2948
                }
2949
            }
2950

2951
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2952
                printname = namebuf;
3✔
2953
            }
1✔
2954
            else {
2955
                printname = name;
×
2956
            }
2957
        }
1✔
2958
        else {
2959
            printname = name;
509✔
2960
        }
2961

2962
        /* addr_flag: name -> addr lookup requested */
2963
        if (addr_flag) {
512✔
2964
            int ret;
2965
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2966
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2967
            if (ret) {
3✔
2968
                if (!quiet_flag) {
×
2969
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2970
                }
2971
                continue;
×
2972
            }
2973

2974
            if (name_flag || rdns_flag) {
3✔
2975
                char nameaddrbuf[512 + 3];
2976
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
2977
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2978
            }
2979
            else {
2980
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2981
            }
2982
        }
1✔
2983
        else {
2984
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
509✔
2985
        }
2986

2987
        if (!multif_flag) {
512✔
2988
            break;
512✔
2989
        }
2990
    }
2991

2992
    freeaddrinfo(res0);
512✔
2993
}
144✔
2994

2995
/************************************************************
2996

2997
  Function: add_addr
2998

2999
*************************************************************
3000

3001
  Description:
3002

3003
  add single address to list of hosts to be pinged
3004

3005
************************************************************/
3006

3007
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
512✔
3008
{
3009
    HOST_ENTRY *p;
3010
    int n;
3011
    int64_t *i;
3012

3013
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
512✔
3014
    if (!p)
512✔
3015
        crash_and_burn("can't allocate HOST_ENTRY");
×
3016

3017
    p->name = strdup(name);
512✔
3018
    p->host = strdup(host);
512✔
3019
    memcpy(&p->saddr, ipaddr, ipaddr_len);
512✔
3020
    p->saddr_len = ipaddr_len;
512✔
3021
    p->timeout = timeout;
512✔
3022
    p->min_reply = 0;
512✔
3023

3024
    if (netdata_flag) {
512✔
3025
        char *s = p->name;
3✔
3026
        while (*s) {
30✔
3027
            if (!isalnum(*s))
27✔
3028
                *s = '_';
9✔
3029
            s++;
27✔
3030
        }
3031
    }
1✔
3032

3033
    if (strlen(p->host) > max_hostname_len)
512✔
3034
        max_hostname_len = strlen(p->host);
325✔
3035

3036
    /* array for response time results */
3037
    if (!loop_flag) {
512✔
3038
        i = (int64_t *)malloc(trials * sizeof(int64_t));
512✔
3039
        if (!i)
512✔
3040
            crash_and_burn("can't allocate resp_times array");
×
3041

3042
        for (n = 1; n < trials; n++)
2,039✔
3043
            i[n] = RESP_UNUSED;
1,527✔
3044

3045
        p->resp_times = i;
512✔
3046
    }
136✔
3047

3048
    /* allocate event storage */
3049
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
512✔
3050
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
512✔
3051

3052
    /* schedule first ping */
3053
    host_add_ping_event(p, 0, current_time_ns);
512✔
3054

3055
    num_hosts++;
512✔
3056
}
512✔
3057

3058
/************************************************************
3059

3060
  Function: crash_and_burn
3061

3062
*************************************************************
3063

3064
  Inputs:  char* message
3065

3066
  Description:
3067

3068
************************************************************/
3069

3070
void crash_and_burn(char *message)
×
3071
{
3072
    fprintf(stderr, "%s: %s\n", prog, message);
×
3073
    exit(4);
×
3074
}
3075

3076
/************************************************************
3077

3078
  Function: errno_crash_and_burn
3079

3080
*************************************************************
3081

3082
  Inputs:  char* message
3083

3084
  Description:
3085

3086
************************************************************/
3087

3088
void errno_crash_and_burn(char *message)
8✔
3089
{
3090
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3091
    exit(4);
8✔
3092
}
3093

3094
/************************************************************
3095

3096
  Function: print_warning
3097

3098
  Description: fprintf(stderr, ...), unless running with -q
3099

3100
*************************************************************/
3101

3102
void print_warning(char *format, ...)
54✔
3103
{
3104
    va_list args;
3105
    if (!quiet_flag) {
54✔
3106
        va_start(args, format);
54✔
3107
        vfprintf(stderr, format, args);
54✔
3108
        va_end(args);
54✔
3109
    }
8✔
3110
}
54✔
3111

3112
/************************************************************
3113

3114
  Function: sprint_tm
3115

3116
*************************************************************
3117

3118
  render nanosecond int64_t value into milliseconds string with three digits of
3119
  precision.
3120

3121
************************************************************/
3122

3123
const char *sprint_tm(int64_t ns)
851✔
3124
{
3125
    static char buf[10];
3126
    double t = (double)ns / 1e6;
851✔
3127

3128
    if (t < 0.0) {
851✔
3129
        /* negative (unexpected) */
3130
        sprintf(buf, "%.2g", t);
×
3131
    }
3132
    else if (t < 1.0) {
851✔
3133
        /* <= 0.99 ms */
3134
        sprintf(buf, "%.3f", t);
851✔
3135
    }
261✔
3136
    else if (t < 10.0) {
×
3137
        /* 1.00 - 9.99 ms */
3138
        sprintf(buf, "%.2f", t);
×
3139
    }
3140
    else if (t < 100.0) {
×
3141
        /* 10.0 - 99.9 ms */
3142
        sprintf(buf, "%.1f", t);
×
3143
    }
3144
    else if (t < 1000000.0) {
×
3145
        /* 100 - 1'000'000 ms */
3146
        sprintf(buf, "%.0f", t);
×
3147
    }
3148
    else {
3149
        sprintf(buf, "%.3e", t);
×
3150
    }
3151

3152
    return (buf);
851✔
3153
}
3154

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

3157
  Function: addr_cmp
3158

3159
*************************************************************/
3160
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
469✔
3161
{
3162
    if (a->sa_family != b->sa_family) {
469✔
3163
        return a->sa_family - b->sa_family;
×
3164
    }
3165
    else {
3166
        if (a->sa_family == AF_INET) {
469✔
3167
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
405✔
3168
        }
3169
        else if (a->sa_family == AF_INET6) {
64✔
3170
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3171
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3172
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3173
        }
3174
    }
3175

3176
    return 0;
×
3177
}
131✔
3178

3179
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
827✔
3180
{
3181
    struct event *event = &h->event_storage_ping[index % event_storage_count];
827✔
3182
    event->host = h;
827✔
3183
    event->ping_index = index;
827✔
3184
    event->ev_time = ev_time;
827✔
3185
    ev_enqueue(&event_queue_ping, event);
827✔
3186

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

3191
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
803✔
3192
{
3193
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
803✔
3194
    event->host = h;
803✔
3195
    event->ping_index = index;
803✔
3196
    event->ev_time = ev_time;
803✔
3197
    ev_enqueue(&event_queue_timeout, event);
803✔
3198

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

3203
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
708✔
3204
{
3205
    return &h->event_storage_timeout[index % event_storage_count];
708✔
3206
}
3207

3208
/************************************************************
3209

3210
  Function: ev_enqueue
3211

3212
  Enqueue an event
3213

3214
  The queue is sorted by event->ev_time, so that queue->first always points to
3215
  the earliest event.
3216

3217
  We start scanning the queue from the tail, because we assume
3218
  that new events mostly get inserted with a event time higher
3219
  than the others.
3220

3221
*************************************************************/
3222
void ev_enqueue(struct event_queue *queue, struct event *event)
1,630✔
3223
{
3224
    struct event *i;
3225
    struct event *i_prev;
3226

3227
    /* Empty list */
3228
    if (queue->last == NULL) {
1,630✔
3229
        event->ev_next = NULL;
1,243✔
3230
        event->ev_prev = NULL;
1,243✔
3231
        queue->first = event;
1,243✔
3232
        queue->last = event;
1,243✔
3233
        return;
1,243✔
3234
    }
3235

3236
    /* Insert on tail? */
3237
    if (event->ev_time - queue->last->ev_time >= 0) {
387✔
3238
        event->ev_next = NULL;
387✔
3239
        event->ev_prev = queue->last;
387✔
3240
        queue->last->ev_next = event;
387✔
3241
        queue->last = event;
387✔
3242
        return;
387✔
3243
    }
3244

3245
    /* Find insertion point */
3246
    i = queue->last;
×
3247
    while (1) {
3248
        i_prev = i->ev_prev;
×
3249
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3250
            event->ev_prev = i_prev;
×
3251
            event->ev_next = i;
×
3252
            i->ev_prev = event;
×
3253
            if (i_prev != NULL) {
×
3254
                i_prev->ev_next = event;
×
3255
            }
3256
            else {
3257
                queue->first = event;
×
3258
            }
3259
            return;
×
3260
        }
3261
        i = i_prev;
×
3262
    }
3263
}
474✔
3264

3265
/************************************************************
3266

3267
  Function: ev_dequeue
3268

3269
*************************************************************/
3270
struct event *ev_dequeue(struct event_queue *queue)
902✔
3271
{
3272
    struct event *dequeued;
3273

3274
    if (queue->first == NULL) {
902✔
3275
        return NULL;
×
3276
    }
3277
    dequeued = queue->first;
902✔
3278
    ev_remove(queue, dequeued);
902✔
3279

3280
    return dequeued;
902✔
3281
}
254✔
3282

3283
/************************************************************
3284

3285
  Function: ev_remove
3286

3287
*************************************************************/
3288
void ev_remove(struct event_queue *queue, struct event *event)
1,610✔
3289
{
3290
    if (queue->first == event) {
1,610✔
3291
        queue->first = event->ev_next;
1,601✔
3292
    }
465✔
3293
    if (queue->last == event) {
1,610✔
3294
        queue->last = event->ev_prev;
1,244✔
3295
    }
354✔
3296
    if (event->ev_prev) {
1,610✔
3297
        event->ev_prev->ev_next = event->ev_next;
9✔
3298
    }
3✔
3299
    if (event->ev_next) {
1,610✔
3300
        event->ev_next->ev_prev = event->ev_prev;
366✔
3301
    }
114✔
3302
    event->ev_prev = NULL;
1,610✔
3303
    event->ev_next = NULL;
1,610✔
3304
}
1,610✔
3305

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

3308
  Function: print_human_readable_time from current_time_ns
3309

3310
*************************************************************/
3311
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
30✔
3312
{
3313
    char time_buffer[100];
3314
    time_t current_time_s;
3315
    struct tm *local_time;
3316

3317
    current_time_s = current_time_ns / 1000000000;
30✔
3318
    local_time = localtime(&current_time_s);
30✔
3319
    switch(timestamp_format) {
30✔
3320
        case 1:
4✔
3321
            // timestamp-format ctime
3322
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
6✔
3323
            printf("[%s] ", time_buffer);
6✔
3324
            break;
6✔
3325
        case 2:
4✔
3326
            // timestamp-format iso
3327
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
6✔
3328
            printf("[%s] ", time_buffer);
6✔
3329
            break;
6✔
3330
        case 3:
4✔
3331
            // timestamp-format rfc3339
3332
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
6✔
3333
            printf("[%s] ", time_buffer);
6✔
3334
            break;
6✔
3335
        default:
8✔
3336
            printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3337
    }
4✔
3338
}
30✔
3339

3340
/************************************************************
3341

3342
  Function: ms_since_midnight_utc
3343

3344
*************************************************************
3345

3346
  Input: int64_t: current UTC time in ns
3347

3348
  Output: uint32_t: current time in ms since midnight UTC
3349

3350
  Description:
3351

3352
  Return ICMP Timestamp value corresponding to the given time value.
3353
  The given time value must be in UTC.
3354

3355
*************************************************************/
3356
static uint32_t ms_since_midnight_utc(int64_t time_val)
14✔
3357
{
3358
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
14✔
3359
}
3360

3361
/************************************************************
3362

3363
  Function: usage
3364

3365
*************************************************************
3366

3367
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3368

3369
  Description:
3370

3371
************************************************************/
3372

3373
void usage(int is_error)
115✔
3374
{
3375
    FILE *out = is_error ? stderr : stdout;
115✔
3376
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
115✔
3377
    fprintf(out, "\n");
115✔
3378
    fprintf(out, "Probing options:\n");
115✔
3379
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
115✔
3380
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
115✔
3381
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
115✔
3382
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
115✔
3383
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
115✔
3384
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
115✔
3385
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
115✔
3386
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
115✔
3387
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
115✔
3388
    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✔
3389
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
115✔
3390
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
115✔
3391
#ifdef SO_BINDTODEVICE
3392
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
78✔
3393
#endif
3394
#ifdef SO_MARK
3395
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
78✔
3396
#endif
3397
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
115✔
3398
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
115✔
3399
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
115✔
3400
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
115✔
3401
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
115✔
3402
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
115✔
3403
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
115✔
3404
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
115✔
3405
    fprintf(out, "   -S, --src=IP       set source address\n");
115✔
3406
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
115✔
3407
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
115✔
3408
    fprintf(out, "       --check-source discard replies not from target address\n");
115✔
3409
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
115✔
3410
    fprintf(out, "\n");
115✔
3411
    fprintf(out, "Output options:\n");
115✔
3412
    fprintf(out, "   -a, --alive        show targets that are alive\n");
115✔
3413
    fprintf(out, "   -A, --addr         show targets by address\n");
115✔
3414
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
115✔
3415
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
115✔
3416
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
115✔
3417
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
115✔
3418
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
115✔
3419
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
115✔
3420
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
115✔
3421
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
115✔
3422
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
115✔
3423
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
115✔
3424
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
115✔
3425
    fprintf(out, "   -s, --stats        print final stats\n");
115✔
3426
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
115✔
3427
    fprintf(out, "   -v, --version      show version\n");
115✔
3428
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
115✔
3429
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
115✔
3430
    fprintf(out, "       --print-tos    show received TOS value\n");
115✔
3431
    fprintf(out, "       --print-ttl    show IP TTL value\n");
115✔
3432
    exit(is_error);
115✔
3433
}
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