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

schweikert / fping / 13347979785

15 Feb 2025 07:23PM UTC coverage: 87.698% (-0.06%) from 87.758%
13347979785

Pull #378

github

web-flow
Merge 266690ee2 into 57d91b13f
Pull Request #378: Fix for GitHub issue #377

28 of 30 new or added lines in 1 file covered. (93.33%)

2 existing lines in 1 file now uncovered.

1383 of 1577 relevant lines covered (87.7%)

323.64 hits per line

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

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

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

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

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

50
#include "seqmap.h"
51

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

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

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

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

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

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

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

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

83
#include <sys/select.h>
84

85
/*** compatibility ***/
86

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

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

105
/*** externals ***/
106

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

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

117
/*** Constants ***/
118

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

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

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

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

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

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

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

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

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

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

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

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

219
#define ICMP_UNREACH_MAXTYPE 15
220

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

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

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

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

268
#define EV_TYPE_PING 1
269
#define EV_TYPE_TIMEOUT 2
270

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

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

284
/*** globals ***/
285

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

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

297
char *prog;
298
int ident4 = 0; /* our icmp identity field */
299
int ident6 = 0;
300
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,
12✔
440
    const void *optval, socklen_t optlen)
441
{
442
    const uid_t saved_uid = geteuid();
12✔
443
    int res;
444

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

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

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

498
    socket4 = open_ping_socket_ipv4(&socktype4);
630✔
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);
444✔
504
#endif
505

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

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

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

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

532
    /* get command line options */
533

534
    struct optparse_long longopts[] = {
630✔
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,712✔
588
        switch (c) {
961✔
589
        case '0':
74✔
590
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
92✔
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) {
82✔
601
                check_source_flag = 1;
9✔
602
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
69✔
603
#ifdef IPV6
604
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
25✔
605
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
606
                    exit(1);
2✔
607
                }
608
                hints_ai_family = AF_INET;
23✔
609
#endif
610
                icmp_request_typ = 13;
23✔
611
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
23✔
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;
86✔
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':
26✔
630
#ifdef IPV6
631
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
29✔
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;
24✔
636
#else
637
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
638
            exit(1);
639
#endif
640
            break;
24✔
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':
881
#ifdef SO_BINDTODEVICE
882
            if (socket4 >= 0) {
883
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
884
                    perror("binding to specific interface (SO_BINTODEVICE)");
885
                    exit(1);
886
                }
887
            }
888
#ifdef IPV6
889
            if (socket6 >= 0) {
890
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
891
                    perror("binding to specific interface (SO_BINTODEVICE), 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);
898
            exit(3);
899
            ;
900
#endif
901
            break;
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
    }
530✔
938

939
    /* permanently drop privileges */
940
    if (suid != getuid() && setuid(getuid())) {
471✔
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)) {
471✔
954
        crash_and_burn("can't create socket (must run as root?)");
×
955
    }
956
#endif
957

958
    if (ttl > 255) {
469✔
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) {
466✔
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) {
463✔
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)
460✔
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) {
454✔
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)) {
451✔
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) {
445✔
996
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
997
        exit(1);
9✔
998
    }
999

1000
    if (count_flag) {
436✔
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) {
436✔
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)
436✔
1015
        verbose_flag = 0;
37✔
1016

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

1019
    /* auto-tune default timeout for count/loop modes
1020
     * see also github #32 */
1021
    if (loop_flag || count_flag) {
452✔
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) {
452✔
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;
330✔
1116
        if (socket4 >= 0) {
330✔
1117
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
330✔
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) {
330✔
1125
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
330✔
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();
452✔
1136
    start_time = current_time_ns;
452✔
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];
452✔
1142
    argc -= optparse_state.optind;
452✔
1143

1144
    /* calculate how many ping can be in-flight per host */
1145
    if (count_flag) {
452✔
1146
        event_storage_count = count;
115✔
1147
    }
43✔
1148
    else if (loop_flag) {
337✔
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;
337✔
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))
452✔
1164
        usage(1);
19✔
1165

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

1170
    if (*argv && !generate_flag) {
434✔
1171
        while (*argv) {
512✔
1172
            add_name(*argv);
285✔
1173
            ++argv;
285✔
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✔
NEW
1234
                    skip = 1; /* empty or blank part of line, skip the rest */
×
NEW
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) {
354✔
1278
        exit(num_noaddress ? 2 : 1);
33✔
1279
    }
1280

1281
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
321✔
1282
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
16✔
1283
    }
2✔
1284
#ifdef IPV6
1285
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
319✔
1286
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
14✔
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;
317✔
1293
        int i = 0;
317✔
1294
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
317✔
1295
        if (!table)
317✔
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) {
818✔
1300
            table[i] = cursor->host;
501✔
1301
            cursor->host->i = i;
501✔
1302
            i++;
501✔
1303
        }
135✔
1304
    }
1305

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

1311
#ifdef USE_SIGACTION
1312
    memset(&act, 0, sizeof(act));
317✔
1313
    act.sa_handler = signal_handler;
317✔
1314
    sigemptyset(&act.sa_mask);
317✔
1315
    sigaddset(&act.sa_mask, SIGINT);
317✔
1316
    sigaddset(&act.sa_mask, SIGQUIT);
317✔
1317
    act.sa_flags = SA_RESTART;
317✔
1318
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
317✔
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);
293✔
1326

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

1331
    last_send_time = 0;
293✔
1332

1333
    seqmap_init();
293✔
1334

1335
    /* main loop */
1336
    main_loop();
293✔
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", perf_cpu_time_used);
1343
#endif /* DEBUG || _DEBUG */
1344

1345
    finish();
293✔
1346

1347
    return 0;
65✔
1348
}
1349

1350
static inline int64_t timespec_ns(struct timespec *a)
5,153✔
1351
{
1352
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,153✔
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
    /* IPv6 addresses can have a scope */
1530
    if (hints_ai_family == AF_INET6) {
43✔
1531
        start_scope_str = strchr(start, '%');
26✔
1532
        end_scope_str = strchr(end, '%');
26✔
1533
        if ((!start_scope_str && end_scope_str) ||
26✔
1534
            (start_scope_str && !end_scope_str) ||
24✔
1535
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1536
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1537
                exit(1);
6✔
1538
        }
1539
    }
1540

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

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

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

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

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

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

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

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

1644
void main_loop()
305✔
1645
{
1646
    int64_t lt;
1647
    int64_t wait_time_ns;
1648
    struct event *event;
1649
    struct host_entry *h;
1650

1651
    while (event_queue_ping.first || event_queue_timeout.first) {
1,724✔
1652
        dbg_printf("%s", "# main_loop\n");
1653

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

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

1661
            stats_add(h, event->ping_index, 0, -1);
95✔
1662

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

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

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

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

1710
            /* Dequeue the event */
1711
            event = ev_dequeue(&event_queue_ping);
801✔
1712
            h = event->host;
801✔
1713

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

1716
            /* Send the ping */
1717
            send_ping(h, event->ping_index);
801✔
1718

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

1725
    wait_for_reply:
568✔
1726

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

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

1745
        /* When is the next timeout event? */
1746
        if (event_queue_timeout.first) {
1,339✔
1747
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
845✔
1748
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
845✔
1749
                wait_time_ns = wait_time_timeout;
361✔
1750
                if (wait_time_ns < 0) {
361✔
1751
                    wait_time_ns = 0;
×
1752
                }
1753
            }
91✔
1754

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

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

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

1771
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1772
        if (wait_time_ns == -1) {
1,339✔
1773
            break;
12✔
1774
        }
1775

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

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

1789
        update_current_time();
1,324✔
1790

1791
        if (status_snapshot) {
1,324✔
1792
            status_snapshot = 0;
×
1793
            print_per_system_splits();
×
1794
        }
1795

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

1803
            while (current_time_ns >= next_report_time) {
84✔
1804
                next_report_time += report_interval;
42✔
1805
            }
1806
        }
14✔
1807
    }
1808
}
305✔
1809

1810
/************************************************************
1811

1812
  Function: signal_handler
1813

1814
*************************************************************
1815

1816
  Inputs:  int signum
1817

1818
  Description:
1819

1820
  SIGQUIT signal handler - set flag and return
1821
  SIGINT signal handler - set flag and return
1822

1823
************************************************************/
1824

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

1832
    case SIGQUIT:
1833
        status_snapshot = 1;
×
1834
        break;
×
1835
    }
1836
}
×
1837

1838
/************************************************************
1839

1840
  Function: update_current_time
1841

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

1844
void update_current_time()
4,159✔
1845
{
1846
    clock_gettime(CLOCKID, &current_time);
4,159✔
1847
    current_time_ns = timespec_ns(&current_time);
4,159✔
1848
}
4,159✔
1849

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

1852
  Function: finish
1853

1854
*************************************************************
1855

1856
  Inputs:  void (none)
1857

1858
  Description:
1859

1860
  Main program clean up and exit point
1861

1862
************************************************************/
1863

1864
void finish()
305✔
1865
{
1866
    int i;
1867
    HOST_ENTRY *h;
1868

1869
    update_current_time();
305✔
1870
    end_time = current_time_ns;
305✔
1871

1872
    /* tot up unreachables */
1873
    for (i = 0; i < num_hosts; i++) {
806✔
1874
        h = table[i];
501✔
1875

1876
        if (!h->num_recv) {
501✔
1877
            num_unreachable++;
78✔
1878

1879
            if (verbose_flag || unreachable_flag) {
78✔
1880
                printf("%s", h->host);
42✔
1881

1882
                if (verbose_flag)
42✔
1883
                    printf(" is unreachable");
39✔
1884

1885
                printf("\n");
42✔
1886
            }
2✔
1887
        }
12✔
1888
    }
135✔
1889

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

1897
    if (stats_flag)
305✔
1898
        print_global_stats();
9✔
1899

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

1911
    if (num_noaddress)
296✔
1912
        exit(2);
3✔
1913
    else if (num_alive != num_hosts)
293✔
1914
        exit(1);
39✔
1915

1916
    exit(0);
254✔
1917
}
1918

1919
/************************************************************
1920

1921
  Function: print_per_system_stats
1922

1923
*************************************************************
1924

1925
  Inputs:  void (none)
1926

1927
  Description:
1928

1929

1930
************************************************************/
1931

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

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

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

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

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

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

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

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

1984
/************************************************************
1985

1986
  Function: print_netdata
1987

1988
*************************************************************
1989

1990
  Inputs:  void (none)
1991

1992
  Description:
1993

1994

1995
************************************************************/
1996

1997
void print_netdata(void)
3✔
1998
{
1999
    static int sent_charts = 0;
2000

2001
    int i;
2002
    int64_t avg;
2003
    HOST_ENTRY *h;
2004

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

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

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

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

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

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

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

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

2052
        stats_reset_interval(h);
3✔
2053
    }
1✔
2054

2055
    sent_charts = 1;
3✔
2056
}
3✔
2057

2058
/************************************************************
2059

2060
  Function: print_per_system_splits
2061

2062
*************************************************************
2063

2064
  Inputs:  void (none)
2065

2066
  Description:
2067

2068

2069
************************************************************/
2070

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

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

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

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

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

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

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

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

2118
/************************************************************
2119

2120
  Function: print_global_stats
2121

2122
*************************************************************
2123

2124
  Inputs:  void (none)
2125

2126
  Description:
2127

2128

2129
************************************************************/
2130

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

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

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

2161
/************************************************************
2162

2163
  Function: send_ping
2164

2165
*************************************************************
2166

2167
  Inputs:  int s, HOST_ENTRY *h
2168

2169
  Description:
2170

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

2177
************************************************************/
2178

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

2186
    update_current_time();
831✔
2187
    h->last_send_time = current_time_ns;
831✔
2188
    myseq = seqmap_add(h->i, index, current_time_ns);
831✔
2189

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

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

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

2220
        h->num_sent++;
32✔
2221
        h->num_sent_i++;
32✔
2222
        if (!loop_flag)
32✔
2223
            h->resp_times[index] = RESP_ERROR;
32✔
2224

2225
        ret = 0;
32✔
2226
    }
2227
    else {
2228
        /* schedule timeout */
2229
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
799✔
2230

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

2237
    num_pingsent++;
831✔
2238
    last_send_time = h->last_send_time;
831✔
2239

2240
    return (ret);
831✔
2241
}
237✔
2242

2243
int socket_can_read(struct timeval *timeout)
2,547✔
2244
{
2245
    int nfound;
2246
    fd_set readset;
2247
    int socketmax;
2248

2249
#ifndef IPV6
2250
    socketmax = socket4;
2251
#else
2252
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,547✔
2253
#endif
2254

2255
select_again:
1,924✔
2256
    FD_ZERO(&readset);
17,939✔
2257
    if (socket4 >= 0)
2,547✔
2258
        FD_SET(socket4, &readset);
2,547✔
2259
#ifdef IPV6
2260
    if (socket6 >= 0)
2,547✔
2261
        FD_SET(socket6, &readset);
2,547✔
2262
#endif
2263

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

2275
    if (nfound > 0) {
2,547✔
2276
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,223✔
2277
            return socket4;
1,047✔
2278
        }
2279
#ifdef IPV6
2280
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
176✔
2281
            return socket6;
176✔
2282
        }
2283
#endif
2284
    }
2285

2286
    return -1;
1,324✔
2287
}
623✔
2288

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

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

2333
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,223✔
2334
    if (recv_len <= 0) {
1,223✔
2335
        return 0;
×
2336
    }
2337

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

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

2365
    return recv_len;
1,223✔
2366
}
623✔
2367

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

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

2390
    /* received count */
2391
    h->num_recv++;
704✔
2392
    h->num_recv_i++;
704✔
2393

2394
    /* maximum */
2395
    if (!h->max_reply || latency > h->max_reply) {
704✔
2396
        h->max_reply = latency;
533✔
2397
    }
157✔
2398
    if (!h->max_reply_i || latency > h->max_reply_i) {
704✔
2399
        h->max_reply_i = latency;
547✔
2400
    }
161✔
2401

2402
    /* minimum */
2403
    if (!h->min_reply || latency < h->min_reply) {
704✔
2404
        h->min_reply = latency;
474✔
2405
    }
140✔
2406
    if (!h->min_reply_i || latency < h->min_reply_i) {
704✔
2407
        h->min_reply_i = latency;
503✔
2408
    }
150✔
2409

2410
    /* total time (for average) */
2411
    h->total_time += latency;
704✔
2412
    h->total_time_i += latency;
704✔
2413

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

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

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

2448
    if (!using_sock_dgram4) {
1,047✔
2449
        struct ip *ip = (struct ip *)reply_buf;
1,039✔
2450
        *ip_header_tos = ip->ip_tos;
1,039✔
2451
        *ip_header_ttl = ip->ip_ttl;
1,039✔
2452

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

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

2474
    icp = (struct icmp *)(reply_buf + hlen);
1,047✔
2475

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

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

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

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

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

2504
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2505

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

2518
            print_warning("\n");
×
2519
            num_othericmprcvd++;
×
2520
            break;
×
2521

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

2540
        return -1;
×
2541
    }
2542

2543
    *id = icp->icmp_id;
627✔
2544
    *seq = ntohs(icp->icmp_seq);
627✔
2545
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
627✔
2546

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

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

2562
    return hlen;
627✔
2563
}
199✔
2564

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

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

2585
    icp = (struct icmp6_hdr *)reply_buf;
176✔
2586

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

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

2600
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
87✔
2601

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

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

2612
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2613

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

2626
            print_warning("\n");
×
2627
            num_othericmprcvd++;
×
2628
            break;
×
2629

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

2648
        return 0;
×
2649
    }
2650

2651
    *id = icp->icmp6_id;
89✔
2652
    *seq = ntohs(icp->icmp6_seq);
89✔
2653

2654
    return 1;
89✔
2655
}
30✔
2656
#endif
2657

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

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

2687
    if (result <= 0) {
2,547✔
2688
        return 0;
1,324✔
2689
    }
2690

2691
    update_current_time();
1,223✔
2692
    if (recv_time == 0)
1,223✔
2693
        recv_time = current_time_ns;
229✔
2694

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

2741
    seqmap_value = seqmap_fetch(seq, current_time_ns);
716✔
2742
    if (seqmap_value == NULL) {
716✔
2743
        return 1;
×
2744
    }
2745

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

2752
    /* update stats that include invalid replies */
2753
    h->num_recv_total++;
716✔
2754
    num_pingreceived++;
716✔
2755

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

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

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

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

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

2786
    /* update stats */
2787
    stats_add(h, this_count, 1, this_reply);
704✔
2788
    // TODO: move to stats_add?
2789
    if (!max_reply || this_reply > max_reply)
704✔
2790
        max_reply = this_reply;
419✔
2791
    if (!min_reply || this_reply < min_reply)
704✔
2792
        min_reply = this_reply;
362✔
2793
    sum_replies += this_reply;
704✔
2794
    total_replies++;
704✔
2795

2796
    /* initialize timeout to initial timeout (without backoff) */
2797
    h->timeout = timeout;
704✔
2798

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

2805
    /* print "is alive" */
2806
    if (h->num_recv == 1) {
704✔
2807
        num_alive++;
423✔
2808
        if (fast_reachable && num_alive >= min_reachable)
423✔
2809
                finish_requested = 1;
3✔
2810

2811
        if (verbose_flag || alive_flag) {
423✔
2812
            printf("%s", h->host);
292✔
2813

2814
            if (verbose_flag)
292✔
2815
                printf(" is alive");
272✔
2816
        }
84✔
2817
    }
123✔
2818

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

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

2839
    if (verbose_flag || alive_flag || per_recv_flag) {
704✔
2840

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

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

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

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

2872
        if (elapsed_flag && !per_recv_flag)
443✔
2873
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2874

2875
        printf("\n");
443✔
2876
    }
129✔
2877

2878
    return 1;
704✔
2879
}
623✔
2880

2881
/************************************************************
2882

2883
  Function: add_name
2884

2885
*************************************************************
2886

2887
  Inputs:  char* name
2888

2889
  Description:
2890

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

2895
************************************************************/
2896

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

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

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

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

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

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

2985
        if (!multif_flag) {
506✔
2986
            break;
506✔
2987
        }
2988
    }
2989

2990
    freeaddrinfo(res0);
506✔
2991
}
144✔
2992

2993
/************************************************************
2994

2995
  Function: add_addr
2996

2997
*************************************************************
2998

2999
  Description:
3000

3001
  add single address to list of hosts to be pinged
3002

3003
************************************************************/
3004

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

3011
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
506✔
3012
    if (!p)
506✔
3013
        crash_and_burn("can't allocate HOST_ENTRY");
×
3014

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

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

3031
    if (strlen(p->host) > max_hostname_len)
506✔
3032
        max_hostname_len = strlen(p->host);
319✔
3033

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

3040
        for (n = 1; n < trials; n++)
2,015✔
3041
            i[n] = RESP_UNUSED;
1,509✔
3042

3043
        p->resp_times = i;
506✔
3044
    }
136✔
3045

3046
    /* allocate event storage */
3047
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
506✔
3048
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
506✔
3049

3050
    /* schedule first ping */
3051
    host_add_ping_event(p, 0, current_time_ns);
506✔
3052

3053
    num_hosts++;
506✔
3054
}
506✔
3055

3056
/************************************************************
3057

3058
  Function: crash_and_burn
3059

3060
*************************************************************
3061

3062
  Inputs:  char* message
3063

3064
  Description:
3065

3066
************************************************************/
3067

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

3074
/************************************************************
3075

3076
  Function: errno_crash_and_burn
3077

3078
*************************************************************
3079

3080
  Inputs:  char* message
3081

3082
  Description:
3083

3084
************************************************************/
3085

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

3092
/************************************************************
3093

3094
  Function: print_warning
3095

3096
  Description: fprintf(stderr, ...), unless running with -q
3097

3098
*************************************************************/
3099

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

3110
/************************************************************
3111

3112
  Function: sprint_tm
3113

3114
*************************************************************
3115

3116
  render nanosecond int64_t value into milliseconds string with three digits of
3117
  precision.
3118

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

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

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

3150
    return (buf);
851✔
3151
}
3152

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

3155
  Function: addr_cmp
3156

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

3174
    return 0;
×
3175
}
131✔
3176

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

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

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

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

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

3206
/************************************************************
3207

3208
  Function: ev_enqueue
3209

3210
  Enqueue an event
3211

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

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

3219
*************************************************************/
3220
void ev_enqueue(struct event_queue *queue, struct event *event)
1,620✔
3221
{
3222
    struct event *i;
3223
    struct event *i_prev;
3224

3225
    /* Empty list */
3226
    if (queue->last == NULL) {
1,620✔
3227
        event->ev_next = NULL;
1,232✔
3228
        event->ev_prev = NULL;
1,232✔
3229
        queue->first = event;
1,232✔
3230
        queue->last = event;
1,232✔
3231
        return;
1,232✔
3232
    }
3233

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

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

3263
/************************************************************
3264

3265
  Function: ev_dequeue
3266

3267
*************************************************************/
3268
struct event *ev_dequeue(struct event_queue *queue)
896✔
3269
{
3270
    struct event *dequeued;
3271

3272
    if (queue->first == NULL) {
896✔
3273
        return NULL;
×
3274
    }
3275
    dequeued = queue->first;
896✔
3276
    ev_remove(queue, dequeued);
896✔
3277

3278
    return dequeued;
896✔
3279
}
254✔
3280

3281
/************************************************************
3282

3283
  Function: ev_remove
3284

3285
*************************************************************/
3286
void ev_remove(struct event_queue *queue, struct event *event)
1,600✔
3287
{
3288
    if (queue->first == event) {
1,600✔
3289
        queue->first = event->ev_next;
1,591✔
3290
    }
465✔
3291
    if (queue->last == event) {
1,600✔
3292
        queue->last = event->ev_prev;
1,233✔
3293
    }
353✔
3294
    if (event->ev_prev) {
1,600✔
3295
        event->ev_prev->ev_next = event->ev_next;
9✔
3296
    }
3✔
3297
    if (event->ev_next) {
1,600✔
3298
        event->ev_next->ev_prev = event->ev_prev;
367✔
3299
    }
115✔
3300
    event->ev_prev = NULL;
1,600✔
3301
    event->ev_next = NULL;
1,600✔
3302
}
1,600✔
3303

3304
/************************************************************
3305

3306
  Function: print_human_readable_time from current_time_ns
3307

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

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

3338
/************************************************************
3339

3340
  Function: ms_since_midnight_utc
3341

3342
*************************************************************
3343

3344
  Input: int64_t: current UTC time in ns
3345

3346
  Output: uint32_t: current time in ms since midnight UTC
3347

3348
  Description:
3349

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

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

3359
/************************************************************
3360

3361
  Function: usage
3362

3363
*************************************************************
3364

3365
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3366

3367
  Description:
3368

3369
************************************************************/
3370

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