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

schweikert / fping / 13091160987

01 Feb 2025 06:11PM UTC coverage: 87.685% (+0.07%) from 87.611%
13091160987

Pull #376

github

web-flow
Merge 7e69b7860 into 511aa3720
Pull Request #376: IPv6 support for -g, --generate

92 of 100 new or added lines in 1 file covered. (92.0%)

3 existing lines in 1 file now uncovered.

1360 of 1551 relevant lines covered (87.69%)

280.91 hits per line

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

87.88
/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

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

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

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

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

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

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

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

218
#define ICMP_UNREACH_MAXTYPE 15
219

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

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

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

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

267
#define EV_TYPE_PING 1
268
#define EV_TYPE_TIMEOUT 2
269

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

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

283
/*** globals ***/
284

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

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

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

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

313
unsigned int debugging = 0;
314

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

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

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

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

376
unsigned int fwmark = 0;
377

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

380
/*** forward declarations ***/
381

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

423
/************************************************************
424

425
  Function: p_setsockopt
426

427
*************************************************************
428

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

431
  Description:
432

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

436
************************************************************/
437

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

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

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

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

456
    return res;
12✔
457
}
458

459
/************************************************************
460

461
  Function: main
462

463
*************************************************************
464

465
  Inputs:  int argc, char** argv
466

467
  Description:
468

469
  Main program entry point
470

471
************************************************************/
472

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

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

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

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

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

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

519
    if (!suid && suid != getuid()) {
549✔
520
        /* *temporarily* drop privileges */
521
        if (seteuid(getuid()) == -1)
513✔
522
            perror("cannot setuid");
×
523
    }
133✔
524

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

531
    /* get command line options */
532

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

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

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

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

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

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

694
            break;
93✔
695

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

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

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

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

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

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

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

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

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

744
            break;
27✔
745

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

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

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

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

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

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

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

782
            break;
8✔
783

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

856
            break;
6✔
857
#endif
858

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

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

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

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

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

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

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

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

945
    /* validate various option settings */
946

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

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

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

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

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

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

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

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

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

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

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

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

1013
    if (alive_flag || unreachable_flag || min_reachable)
357✔
1014
        verbose_flag = 0;
29✔
1015

1016
    trials = (count > retry + 1) ? count : retry + 1;
361✔
1017

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

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

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

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

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

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

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

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

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

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

1134
    update_current_time();
361✔
1135
    start_time = current_time_ns;
361✔
1136

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

1140
    argv = &argv[optparse_state.optind];
361✔
1141
    argc -= optparse_state.optind;
361✔
1142

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

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

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

1169
    if (*argv && !generate_flag) {
353✔
1170
        while (*argv) {
512✔
1171
            add_name(*argv);
285✔
1172
            ++argv;
285✔
1173
        }
1174
    }
57✔
1175
    else if (filename) {
126✔
1176
        FILE *ping_file;
1177
        char line[132];
1178
        char host[132];
1179

1180
        if (strcmp(filename, "-") == 0)
15✔
1181
            ping_file = fdopen(0, "r");
6✔
1182
        else
1183
            ping_file = fopen(filename, "r");
9✔
1184

1185
        if (!ping_file)
15✔
1186
            errno_crash_and_burn("fopen");
3✔
1187

1188
        while (fgets(line, sizeof(line), ping_file)) {
43✔
1189
            if (sscanf(line, "%s", host) != 1)
30✔
1190
                continue;
3✔
1191

1192
            if ((!*host) || (host[0] == '#')) /* magic to avoid comments */
27✔
1193
                continue;
3✔
1194

1195
            add_name(host);
24✔
1196
        }
1197

1198
        fclose(ping_file);
13✔
1199
    }
5✔
1200
    else if (*argv && generate_flag) {
111✔
1201
        if (argc == 1) {
111✔
1202
            /* one target: we expect a cidr range (n.n.n.n/m) */
1203
            add_cidr(argv[0]);
56✔
1204
        }
10✔
1205
        else if (argc == 2) {
55✔
1206
            add_range(argv[0], argv[1]);
52✔
1207
        }
6✔
1208
        else {
1209
            usage(1);
3✔
1210
        }
1211
    }
17✔
1212
    else {
1213
        usage(1);
×
1214
    }
1215

1216
    if (!num_hosts) {
297✔
1217
        exit(num_noaddress ? 2 : 1);
21✔
1218
    }
1219

1220
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
276✔
1221
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
16✔
1222
    }
2✔
1223
#ifdef IPV6
1224
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
274✔
1225
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
14✔
1226
    }
1227
#endif
1228

1229
    /* allocate and initialize array to map host nr to host_entry */
1230
    {
1231
        struct event *cursor = event_queue_ping.first;
272✔
1232
        int i = 0;
272✔
1233
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
272✔
1234
        if (!table)
272✔
1235
            crash_and_burn("Can't malloc array of hosts");
×
1236
        /* initialize table of hosts. we know that we have ping events scheduled
1237
         * for each of them */
1238
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
662✔
1239
            table[i] = cursor->host;
390✔
1240
            cursor->host->i = i;
390✔
1241
            i++;
390✔
1242
        }
98✔
1243
    }
1244

1245
    init_ping_buffer_ipv4(ping_data_size);
272✔
1246
#ifdef IPV6
1247
    init_ping_buffer_ipv6(ping_data_size);
272✔
1248
#endif
1249

1250
#ifdef USE_SIGACTION
1251
    memset(&act, 0, sizeof(act));
272✔
1252
    act.sa_handler = signal_handler;
272✔
1253
    sigemptyset(&act.sa_mask);
272✔
1254
    sigaddset(&act.sa_mask, SIGINT);
272✔
1255
    sigaddset(&act.sa_mask, SIGQUIT);
272✔
1256
    act.sa_flags = SA_RESTART;
272✔
1257
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
272✔
1258
        crash_and_burn("failure to set signal handler");
24✔
1259
    }
24✔
1260
#else
1261
    signal(SIGINT, signal_handler);
1262
    signal(SIGQUIT, signal_handler);
1263
#endif
1264
    setlinebuf(stdout);
248✔
1265

1266
    if (report_interval) {
248✔
1267
        next_report_time = current_time_ns + report_interval;
24✔
1268
    }
8✔
1269

1270
    last_send_time = 0;
248✔
1271

1272
    seqmap_init();
248✔
1273

1274
    /* main loop */
1275
    main_loop();
248✔
1276

1277
/* Debug: CPU Performance */
1278
#if defined(DEBUG) || defined(_DEBUG)
1279
    perf_cpu_end = clock();
1280
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1281
    printf("[DEBUG] CPU time used: %f sec", perf_cpu_time_used);
1282
#endif /* DEBUG || _DEBUG */
1283

1284
    finish();
248✔
1285

1286
    return 0;
50✔
1287
}
1288

1289
static inline int64_t timespec_ns(struct timespec *a)
4,407✔
1290
{
1291
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
4,407✔
1292
}
1293

1294
#if HAVE_SO_TIMESTAMPNS
1295
/* convert a struct timeval to nanoseconds */
1296
static inline int64_t timeval_ns(struct timeval *a)
1297
{
1298
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1299
}
1300
#endif /* HAVE_SO_TIMESTAMPNS */
1301

1302
void add_cidr(char *addr)
54✔
1303
{
1304
    char *addr_end;
1305
    char *mask_str;
1306
    unsigned long mask;
1307
    int ret;
1308
    struct addrinfo addr_hints;
1309
    struct addrinfo *addr_res;
1310
    unsigned long net_addr;
1311
#ifdef IPV6
1312
    uint64_t net_upper, net_lower;
1313
    char *scope_str;
1314
#endif /* IPV6 */
1315

1316
    /* Split address from mask */
1317
    addr_end = strrchr(addr, '/');
54✔
1318
    if (addr_end == NULL) {
54✔
1319
        usage(1);
3✔
1320
    }
1✔
1321
    mask_str = addr_end + 1;
52✔
1322

1323
#ifdef IPV6
1324
    /* IPv6 addresses can have a scope */
1325
    scope_str = strchr(addr, '%');
52✔
1326
    if (scope_str && mask_str < scope_str) {
52✔
1327
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1328
        exit(1);
2✔
1329
    }
1330
#endif /*IPV6 */
1331

1332
    *addr_end = '\0';
50✔
1333
    mask = atoi(mask_str);
50✔
1334

1335
    /* parse address */
1336
    memset(&addr_hints, 0, sizeof(struct addrinfo));
50✔
1337
    addr_hints.ai_family = hints_ai_family;
50✔
1338
    addr_hints.ai_flags = AI_NUMERICHOST;
50✔
1339
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
50✔
1340
    if (ret) {
50✔
1341
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1342
        exit(1);
7✔
1343
    }
1344
    if (addr_res->ai_family == AF_INET) {
43✔
1345
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
21✔
1346
        freeaddrinfo(addr_res);
21✔
1347
        add_cidr_ipv4(net_addr, mask);
21✔
1348
#ifdef IPV6
1349
    } else if (addr_res->ai_family == AF_INET6) {
29✔
1350
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1351
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1352
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1353
        freeaddrinfo(addr_res);
22✔
1354
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1355
#endif /* IPV6 */
1356
    } else {
NEW
1357
        freeaddrinfo(addr_res);
×
NEW
1358
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
UNCOV
1359
        exit(1);
×
1360
    }
1361
}
27✔
1362

1363
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
21✔
1364
{
1365
    unsigned long bitmask;
1366
    unsigned long net_last;
1367

1368
    /* check mask */
1369
    if (mask < 1 || mask > 32) {
21✔
1370
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1371
        exit(1);
6✔
1372
    }
1373

1374
    /* convert mask integer from 1 to 32 to a bitmask */
1375
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
15✔
1376

1377
    /* calculate network range */
1378
    net_addr &= bitmask;
15✔
1379
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
15✔
1380

1381
    /* exclude network and broadcast address for regular prefixes */
1382
    if (mask < 31) {
15✔
1383
        net_last--;
9✔
1384
        net_addr++;
9✔
1385
    }
3✔
1386

1387
    /* add all hosts in that network (net_addr and net_last inclusive) */
1388
    add_addr_range_ipv4(net_addr, net_last);
15✔
1389
}
13✔
1390

1391
#ifdef IPV6
1392
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1393
{
1394
    uint64_t bitmask_lower;
1395
    uint64_t last_lower;
1396

1397
    /* check mask -- 2^63 addresses should suffice for now */
1398
    if (mask < 65 || mask > 128) {
23✔
1399
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1400
        exit(1);
7✔
1401
    }
1402

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

1406
    /* calculate network range */
1407
    net_lower &= bitmask_lower;
16✔
1408
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1409

1410
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1411
}
12✔
1412
#endif /* IPV6 */
1413

1414
void add_range(char *start, char *end)
52✔
1415
{
1416
    struct addrinfo addr_hints;
1417
    struct addrinfo *addr_res;
1418
    unsigned long start_long;
1419
    unsigned long end_long;
1420
    int ret;
1421
#ifdef IPV6
1422
    uint64_t start_upper, start_lower;
1423
    uint64_t end_upper, end_lower;
1424
    char *start_scope_str, *end_scope_str;
1425

1426
    /*
1427
     * The compiler does not know that setting the address family hint to
1428
     * ensure that start and end are from the same address family also
1429
     * ensures that either start_long and end_long are initialized and used,
1430
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1431
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1432
     * supported to suppress compiler warnings.
1433
     */
1434
    start_long = -1;
52✔
1435
    end_long = 0;
52✔
1436
    start_upper = start_lower = -1;
52✔
1437
    end_upper = end_lower = 0;
52✔
1438
    start_scope_str = end_scope_str = NULL;
52✔
1439
#endif /* IPV6 */
1440

1441
    /* parse start address */
1442
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1443
    addr_hints.ai_family = hints_ai_family;
52✔
1444
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1445
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1446
    if (ret) {
52✔
1447
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1448
        exit(1);
9✔
1449
    }
1450
    /* start and end must be from the same address family */
1451
    hints_ai_family = addr_res->ai_family;
43✔
1452
    if (addr_res->ai_family == AF_INET) {
43✔
1453
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1454
        freeaddrinfo(addr_res);
17✔
1455
#ifdef IPV6
1456
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1457
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1458
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1459
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1460
        freeaddrinfo(addr_res);
26✔
1461
#endif /* IPV6 */
1462
    } else {
NEW
1463
        freeaddrinfo(addr_res);
×
NEW
1464
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
UNCOV
1465
        exit(1);
×
1466
    }
1467

1468
    /* IPv6 addresses can have a scope */
1469
    if (hints_ai_family == AF_INET6) {
43✔
1470
        start_scope_str = strchr(start, '%');
26✔
1471
        end_scope_str = strchr(end, '%');
26✔
1472
        if ((!start_scope_str && end_scope_str) ||
26✔
1473
            (start_scope_str && !end_scope_str) ||
24✔
1474
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1475
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1476
                exit(1);
6✔
1477
        }
1478
    }
1479

1480
    /* parse end address */
1481
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1482
    addr_hints.ai_family = hints_ai_family;
37✔
1483
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1484
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1485
    if (ret) {
37✔
1486
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1487
        exit(1);
7✔
1488
    }
1489
    if (addr_res->ai_family == AF_INET) {
30✔
1490
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1491
        freeaddrinfo(addr_res);
12✔
1492
        add_addr_range_ipv4(start_long, end_long);
12✔
1493
#ifdef IPV6
1494
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1495
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1496
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1497
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1498
        freeaddrinfo(addr_res);
18✔
1499
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1500
#endif /* IPV6 */
1501
    } else {
NEW
1502
        freeaddrinfo(addr_res);
×
NEW
1503
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
UNCOV
1504
        exit(1);
×
1505
    }
1506
}
24✔
1507

1508
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
27✔
1509
{
1510
    /* check if generator limit is exceeded */
1511
    if (end_long >= start_long + MAX_GENERATE) {
27✔
1512
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1513
        exit(1);
6✔
1514
    }
1515

1516
    /* generate */
1517
    for (; start_long <= end_long; start_long++) {
84✔
1518
        struct in_addr in_addr_tmp;
1519
        char buffer[20];
1520
        in_addr_tmp.s_addr = htonl(start_long);
63✔
1521
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
63✔
1522
        add_name(buffer);
63✔
1523
    }
21✔
1524
}
21✔
1525

1526
#ifdef IPV6
1527
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1528
{
1529
    int i;
1530
    uint64_t ret = 0;
134✔
1531
    for (i = 0; i < 8; i++) {
1,206✔
1532
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1533
    }
16✔
1534
    return ret;
134✔
1535
}
1536

1537
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1538
{
1539
    int i;
1540
    for (i = 0; i < 8; i++) {
720✔
1541
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1542
    }
1543
}
80✔
1544

1545
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1546
                         uint64_t end_upper, uint64_t end_lower,
1547
                         const char *scope_str)
1548
{
1549
    struct in6_addr in6_addr_tmp;
1550
    char buffer[100];
1551

1552
    /* prevent generating too many addresses */
1553
    if ((start_upper + 1 < end_upper) ||
34✔
1554
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1555
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1556
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1557
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1558
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1559
        exit(1);
8✔
1560
    }
1561

1562
    while ((start_upper < end_upper) ||
66✔
1563
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1564
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1565
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1566
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1567
        if (scope_str) {
40✔
1568
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
NEW
1569
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
NEW
1570
                exit(1);
×
1571
            }
1572
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1573
        }
1574
        add_name(buffer);
40✔
1575
        start_lower++;
40✔
1576
        if (start_lower == 0) {
40✔
1577
            start_upper++;
2✔
1578
        }
1579
    }
1580
}
26✔
1581
#endif /* IPv6 */
1582

1583
void main_loop()
260✔
1584
{
1585
    int64_t lt;
1586
    int64_t wait_time_ns;
1587
    struct event *event;
1588
    struct host_entry *h;
1589

1590
    while (event_queue_ping.first || event_queue_timeout.first) {
1,503✔
1591
        dbg_printf("%s", "# main_loop\n");
1592

1593
        /* timeout event ? */
1594
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,258✔
1595
            event = ev_dequeue(&event_queue_timeout);
95✔
1596
            h = event->host;
95✔
1597

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

1600
            stats_add(h, event->ping_index, 0, -1);
95✔
1601

1602
            if (per_recv_flag) {
95✔
1603
                if (timestamp_flag) {
19✔
1604
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1605
                }
1606
                printf("%-*s : [%d], timed out",
19✔
1607
                    max_hostname_len, h->host, event->ping_index);
5✔
1608
                if (h->num_recv > 0) {
19✔
1609
                    printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1610
                }
1611
                else {
1612
                    printf(" (NaN avg, ");
19✔
1613
                }
1614
                if (h->num_recv <= h->num_sent) {
19✔
1615
                    printf("%d%% loss)",
19✔
1616
                        ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1617
                }
5✔
1618
                else {
1619
                    printf("%d%% return)",
×
1620
                        (h->num_recv_total * 100) / h->num_sent);
×
1621
                }
1622
                printf("\n");
19✔
1623
            }
5✔
1624

1625
            /* do we need to send a retry? */
1626
            if (!loop_flag && !count_flag) {
95✔
1627
                if (h->num_sent < retry + 1) {
52✔
1628
                    if (backoff_flag) {
30✔
1629
                        h->timeout *= backoff;
30✔
1630
                    }
6✔
1631
                    send_ping(h, event->ping_index);
30✔
1632
                }
6✔
1633
            }
10✔
1634

1635
            /* note: we process first timeout events, because we might need to
1636
             * wait to process ping events, while we for sure never need to
1637
             * wait for timeout events.
1638
             */
1639
            continue;
95✔
1640
        }
1641

1642
        /* ping event ? */
1643
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,357✔
1644
            /* Make sure that we don't ping more than once every "interval" */
1645
            lt = current_time_ns - last_send_time;
879✔
1646
            if (lt < interval)
879✔
1647
                goto wait_for_reply;
189✔
1648

1649
            /* Dequeue the event */
1650
            event = ev_dequeue(&event_queue_ping);
690✔
1651
            h = event->host;
690✔
1652

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

1655
            /* Send the ping */
1656
            send_ping(h, event->ping_index);
690✔
1657

1658
            /* Loop and count mode: schedule next ping */
1659
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
690✔
1660
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
315✔
1661
            }
101✔
1662
        }
194✔
1663

1664
    wait_for_reply:
495✔
1665

1666
        /* When is the next ping next event? */
1667
        wait_time_ns = -1;
1,163✔
1668
        if (event_queue_ping.first) {
1,163✔
1669
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
843✔
1670
            if (wait_time_ns < 0)
843✔
1671
                wait_time_ns = 0;
407✔
1672
            /* make sure that we wait enough, so that the inter-ping delay is
1673
             * bigger than 'interval' */
1674
            if (wait_time_ns < interval) {
843✔
1675
                lt = current_time_ns - last_send_time;
414✔
1676
                if (lt < interval) {
414✔
1677
                    wait_time_ns = interval - lt;
413✔
1678
                }
153✔
1679
            }
154✔
1680

1681
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1682
        }
262✔
1683

1684
        /* When is the next timeout event? */
1685
        if (event_queue_timeout.first) {
1,163✔
1686
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
734✔
1687
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
734✔
1688
                wait_time_ns = wait_time_timeout;
316✔
1689
                if (wait_time_ns < 0) {
316✔
1690
                    wait_time_ns = 0;
1✔
1691
                }
1692
            }
75✔
1693

1694
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1695
        }
209✔
1696

1697
        /* When is the next report due? */
1698
        if (report_interval && (loop_flag || count_flag)) {
1,163✔
1699
            int64_t wait_time_next_report = next_report_time - current_time_ns;
205✔
1700
            if (wait_time_next_report < wait_time_ns) {
205✔
1701
                wait_time_ns = wait_time_next_report;
69✔
1702
                if (wait_time_ns < 0) {
69✔
1703
                    wait_time_ns = 0;
×
1704
                }
1705
            }
23✔
1706

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

1710
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1711
        if (wait_time_ns == -1) {
1,163✔
1712
            break;
12✔
1713
        }
1714

1715
        /* end of loop was requested by interrupt signal handler */
1716
        if (finish_requested) {
1,151✔
1717
            break;
3✔
1718
        }
1719

1720
        /* Receive replies */
1721
        /* (this is what sleeps during each loop iteration) */
1722
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1723
        if (wait_for_reply(wait_time_ns)) {
1,148✔
1724
            while (wait_for_reply(0))
1,038✔
1725
                ; /* process other replies in the queue */
1726
        }
177✔
1727

1728
        update_current_time();
1,148✔
1729

1730
        if (status_snapshot) {
1,148✔
1731
            status_snapshot = 0;
×
1732
            print_per_system_splits();
×
1733
        }
1734

1735
        /* Print report */
1736
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,148✔
1737
            if (netdata_flag)
42✔
1738
                print_netdata();
3✔
1739
            else
1740
                print_per_system_splits();
39✔
1741

1742
            while (current_time_ns >= next_report_time) {
84✔
1743
                next_report_time += report_interval;
42✔
1744
            }
1745
        }
14✔
1746
    }
1747
}
260✔
1748

1749
/************************************************************
1750

1751
  Function: signal_handler
1752

1753
*************************************************************
1754

1755
  Inputs:  int signum
1756

1757
  Description:
1758

1759
  SIGQUIT signal handler - set flag and return
1760
  SIGINT signal handler - set flag and return
1761

1762
************************************************************/
1763

1764
void signal_handler(int signum)
×
1765
{
1766
    switch (signum) {
×
1767
    case SIGINT:
1768
        finish_requested = 1;
×
1769
        break;
×
1770

1771
    case SIGQUIT:
1772
        status_snapshot = 1;
×
1773
        break;
×
1774
    }
1775
}
×
1776

1777
/************************************************************
1778

1779
  Function: update_current_time
1780

1781
*************************************************************/
1782

1783
void update_current_time()
3,561✔
1784
{
1785
    clock_gettime(CLOCKID, &current_time);
3,561✔
1786
    current_time_ns = timespec_ns(&current_time);
3,561✔
1787
}
3,561✔
1788

1789
/************************************************************
1790

1791
  Function: finish
1792

1793
*************************************************************
1794

1795
  Inputs:  void (none)
1796

1797
  Description:
1798

1799
  Main program clean up and exit point
1800

1801
************************************************************/
1802

1803
void finish()
260✔
1804
{
1805
    int i;
1806
    HOST_ENTRY *h;
1807

1808
    update_current_time();
260✔
1809
    end_time = current_time_ns;
260✔
1810

1811
    /* tot up unreachables */
1812
    for (i = 0; i < num_hosts; i++) {
650✔
1813
        h = table[i];
390✔
1814

1815
        if (!h->num_recv) {
390✔
1816
            num_unreachable++;
78✔
1817

1818
            if (verbose_flag || unreachable_flag) {
78✔
1819
                printf("%s", h->host);
42✔
1820

1821
                if (verbose_flag)
42✔
1822
                    printf(" is unreachable");
39✔
1823

1824
                printf("\n");
42✔
1825
            }
2✔
1826
        }
12✔
1827
    }
98✔
1828

1829
    if (count_flag || loop_flag)
260✔
1830
        print_per_system_stats();
100✔
1831
#if defined(DEBUG) || defined(_DEBUG)
1832
    else if (print_per_system_flag)
1833
        print_per_system_stats();
1834
#endif /* DEBUG || _DEBUG */
1835

1836
    if (stats_flag)
260✔
1837
        print_global_stats();
9✔
1838

1839
    if (min_reachable) {
260✔
1840
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
1841
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1842
            exit(0);
6✔
1843
        }
1844
        else {
1845
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
1846
            exit(1);
3✔
1847
        }
1848
    }
1849

1850
    if (num_noaddress)
251✔
1851
        exit(2);
3✔
1852
    else if (num_alive != num_hosts)
248✔
1853
        exit(1);
39✔
1854

1855
    exit(0);
209✔
1856
}
1857

1858
/************************************************************
1859

1860
  Function: print_per_system_stats
1861

1862
*************************************************************
1863

1864
  Inputs:  void (none)
1865

1866
  Description:
1867

1868

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

1871
void print_per_system_stats(void)
100✔
1872
{
1873
    int i, j, avg, outage_ms;
1874
    HOST_ENTRY *h;
1875
    int64_t resp;
1876

1877
    if (verbose_flag || per_recv_flag)
100✔
1878
        fprintf(stderr, "\n");
59✔
1879

1880
    for (i = 0; i < num_hosts; i++) {
234✔
1881
        h = table[i];
134✔
1882
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
1883

1884
        if (report_all_rtts_flag) {
134✔
1885
            for (j = 0; j < h->num_sent; j++) {
248✔
1886
                if ((resp = h->resp_times[j]) >= 0)
204✔
1887
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
1888
                else
1889
                    fprintf(stderr, " -");
8✔
1890
            }
64✔
1891

1892
            fprintf(stderr, "\n");
44✔
1893
        }
12✔
1894
        else {
1895
            if (h->num_recv <= h->num_sent) {
90✔
1896
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
1897
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
1898

1899
                if (outage_flag) {
90✔
1900
                    /* Time outage total */
1901
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
1902
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
1903
                }
3✔
1904
            }
26✔
1905
            else {
1906
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
1907
                    h->num_sent, h->num_recv,
1908
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
1909
            }
1910

1911
            if (h->num_recv) {
90✔
1912
                avg = h->total_time / h->num_recv;
79✔
1913
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
1914
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
1915
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
1916
            }
23✔
1917

1918
            fprintf(stderr, "\n");
90✔
1919
        }
1920
    }
38✔
1921
}
100✔
1922

1923
/************************************************************
1924

1925
  Function: print_netdata
1926

1927
*************************************************************
1928

1929
  Inputs:  void (none)
1930

1931
  Description:
1932

1933

1934
************************************************************/
1935

1936
void print_netdata(void)
3✔
1937
{
1938
    static int sent_charts = 0;
1939

1940
    int i;
1941
    int64_t avg;
1942
    HOST_ENTRY *h;
1943

1944
    for (i = 0; i < num_hosts; i++) {
6✔
1945
        h = table[i];
3✔
1946

1947
        if (!sent_charts) {
3✔
1948
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1949
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
1950
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
1951
        }
1✔
1952

1953
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
1954
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
1955
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
1956
        printf("END\n");
3✔
1957

1958
        if (!sent_charts) {
3✔
1959
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1960
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
1961
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
1962
        }
1✔
1963

1964
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
1965
        /*
1966
        if( h->num_recv_i <= h->num_sent_i )
1967
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
1968
        else
1969
            printf("SET lost = 0\n");
1970
*/
1971

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

1975
        if (!sent_charts) {
3✔
1976
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1977
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
1978
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
1979
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
1980
        }
1✔
1981

1982
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
1983
        if (h->num_recv_i) {
3✔
1984
            avg = h->total_time_i / h->num_recv_i;
3✔
1985
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
1986
            printf("SET avg = %" PRId64 "\n", avg);
3✔
1987
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
1988
        }
1✔
1989
        printf("END\n");
3✔
1990

1991
        stats_reset_interval(h);
3✔
1992
    }
1✔
1993

1994
    sent_charts = 1;
3✔
1995
}
3✔
1996

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

1999
  Function: print_per_system_splits
2000

2001
*************************************************************
2002

2003
  Inputs:  void (none)
2004

2005
  Description:
2006

2007

2008
************************************************************/
2009

2010
void print_per_system_splits(void)
39✔
2011
{
2012
    int i, avg, outage_ms_i;
2013
    HOST_ENTRY *h;
2014
    struct tm *curr_tm;
2015

2016
    if (verbose_flag || per_recv_flag)
39✔
2017
        fprintf(stderr, "\n");
×
2018

2019
    update_current_time();
39✔
2020
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2021
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2022
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2023

2024
    for (i = 0; i < num_hosts; i++) {
78✔
2025
        h = table[i];
39✔
2026
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2027

2028
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2029
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2030
                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✔
2031

2032
            if (outage_flag) {
39✔
2033
                /* Time outage  */
2034
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2035
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2036
            }
4✔
2037
        }
13✔
2038
        else {
2039
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2040
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2041
        }
2042

2043
        if (h->num_recv_i) {
39✔
2044
            avg = h->total_time_i / h->num_recv_i;
27✔
2045
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2046
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2047
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2048
        }
9✔
2049

2050
        fprintf(stderr, "\n");
39✔
2051
        if (!cumulative_stats_flag) {
39✔
2052
            stats_reset_interval(h);
27✔
2053
        }
9✔
2054
    }
13✔
2055
}
39✔
2056

2057
/************************************************************
2058

2059
  Function: print_global_stats
2060

2061
*************************************************************
2062

2063
  Inputs:  void (none)
2064

2065
  Description:
2066

2067

2068
************************************************************/
2069

2070
void print_global_stats(void)
9✔
2071
{
2072
    fprintf(stderr, "\n");
9✔
2073
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2074
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2075
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2076
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2077
    fprintf(stderr, "\n");
9✔
2078
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2079
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2080
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2081
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2082
    fprintf(stderr, "\n");
9✔
2083

2084
    if (total_replies == 0) {
9✔
2085
        min_reply = 0;
3✔
2086
        max_reply = 0;
3✔
2087
        total_replies = 1;
3✔
2088
        sum_replies = 0;
3✔
2089
    }
1✔
2090

2091
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2092
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2093
        sprint_tm(sum_replies / total_replies));
3✔
2094
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2095
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2096
        (end_time - start_time) / 1e9);
9✔
2097
    fprintf(stderr, "\n");
9✔
2098
}
9✔
2099

2100
/************************************************************
2101

2102
  Function: send_ping
2103

2104
*************************************************************
2105

2106
  Inputs:  int s, HOST_ENTRY *h
2107

2108
  Description:
2109

2110
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2111
  will be added on by the kernel.  The ID field is our UNIX process ID,
2112
  and the sequence number is an index into an array of outstanding
2113
  ping requests. The sequence number will later be used to quickly
2114
  figure out who the ping reply came from.
2115

2116
************************************************************/
2117

2118
int send_ping(HOST_ENTRY *h, int index)
720✔
2119
{
2120
    int n;
2121
    int myseq;
2122
    int ret = 1;
720✔
2123
    uint8_t proto = ICMP_ECHO;
720✔
2124

2125
    update_current_time();
720✔
2126
    h->last_send_time = current_time_ns;
720✔
2127
    myseq = seqmap_add(h->i, index, current_time_ns);
720✔
2128

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

2131
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
720✔
2132
        if(icmp_request_typ == 13)
599✔
2133
            proto = ICMP_TSTAMP;
14✔
2134
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
599✔
2135
    }
185✔
2136
#ifdef IPV6
2137
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
121✔
2138
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
121✔
2139
    }
15✔
2140
#endif
2141
    else {
2142
        return 0;
×
2143
    }
2144

2145
    /* error sending? */
2146
    if (
520✔
2147
        (n < 0)
200✔
2148
#if defined(EHOSTDOWN)
2149
        && errno != EHOSTDOWN
232✔
2150
#endif
2151
    ) {
2152
        if (verbose_flag) {
32✔
2153
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
26✔
2154
        }
2155
        else {
2156
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2157
        }
2158

2159
        h->num_sent++;
32✔
2160
        h->num_sent_i++;
32✔
2161
        if (!loop_flag)
32✔
2162
            h->resp_times[index] = RESP_ERROR;
32✔
2163

2164
        ret = 0;
32✔
2165
    }
2166
    else {
2167
        /* schedule timeout */
2168
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
688✔
2169

2170
        /* mark this trial as outstanding */
2171
        if (!loop_flag) {
688✔
2172
            h->resp_times[index] = RESP_WAITING;
688✔
2173
        }
200✔
2174
    }
2175

2176
    num_pingsent++;
720✔
2177
    last_send_time = h->last_send_time;
720✔
2178

2179
    return (ret);
720✔
2180
}
200✔
2181

2182
int socket_can_read(struct timeval *timeout)
2,186✔
2183
{
2184
    int nfound;
2185
    fd_set readset;
2186
    int socketmax;
2187

2188
#ifndef IPV6
2189
    socketmax = socket4;
2190
#else
2191
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,186✔
2192
#endif
2193

2194
select_again:
1,659✔
2195
    FD_ZERO(&readset);
15,450✔
2196
    if (socket4 >= 0)
2,186✔
2197
        FD_SET(socket4, &readset);
2,186✔
2198
#ifdef IPV6
2199
    if (socket6 >= 0)
2,186✔
2200
        FD_SET(socket6, &readset);
2,186✔
2201
#endif
2202

2203
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,186✔
2204
    if (nfound < 0) {
2,186✔
2205
        if (errno == EINTR) {
×
2206
            /* interrupted system call: redo the select */
2207
            goto select_again;
×
2208
        }
2209
        else {
2210
            perror("select");
×
2211
        }
2212
    }
2213

2214
    if (nfound > 0) {
2,186✔
2215
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,038✔
2216
            return socket4;
862✔
2217
        }
2218
#ifdef IPV6
2219
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
176✔
2220
            return socket6;
176✔
2221
        }
2222
#endif
2223
    }
2224

2225
    return -1;
1,148✔
2226
}
527✔
2227

2228
int receive_packet(int64_t wait_time,
2,186✔
2229
#if HAVE_SO_TIMESTAMPNS
2230
    int64_t *reply_timestamp,
2231
#else
2232
    int64_t *reply_timestamp __attribute__((unused)),
2233
#endif
2234
    struct sockaddr *reply_src_addr,
2235
    size_t reply_src_addr_len,
2236
    char *reply_buf,
2237
    size_t reply_buf_len)
2238
{
2239
    struct timeval to;
2240
    int s = 0;
2,186✔
2241
    int recv_len;
2242
    static unsigned char msg_control[40];
2243
    struct iovec msg_iov = {
3,240✔
2244
        reply_buf,
527✔
2245
        reply_buf_len
527✔
2246
    };
2247
    struct msghdr recv_msghdr = {0};
2,186✔
2248
    recv_msghdr.msg_name = reply_src_addr;
2,186✔
2249
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,186✔
2250
    recv_msghdr.msg_iov = &msg_iov;
2,186✔
2251
    recv_msghdr.msg_iovlen = 1;
2,186✔
2252
    recv_msghdr.msg_control = &msg_control;
2,186✔
2253
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,186✔
2254
#if HAVE_SO_TIMESTAMPNS
2255
    struct cmsghdr *cmsg;
2256
#endif
2257

2258
    /* Wait for a socket to become ready */
2259
    if (wait_time) {
2,186✔
2260
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,147✔
2261
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,147✔
2262
    }
335✔
2263
    else {
2264
        to.tv_sec = 0;
1,039✔
2265
        to.tv_usec = 0;
1,039✔
2266
    }
2267
    s = socket_can_read(&to);
2,186✔
2268
    if (s == -1) {
2,186✔
2269
        return 0; /* timeout */
1,148✔
2270
    }
2271

2272
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,038✔
2273
    if (recv_len <= 0) {
1,038✔
2274
        return 0;
×
2275
    }
2276

2277
#if HAVE_SO_TIMESTAMPNS
2278
    /* ancilliary data */
2279
    {
2280
        struct timespec reply_timestamp_ts;
2281
        struct timeval reply_timestamp_tv;
2282
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,269✔
2283
             cmsg != NULL;
846✔
2284
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
846✔
2285
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
846✔
2286
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
846✔
2287
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
846✔
2288
            }
2289
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
846✔
2290
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2291
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2292
            }
2293
        }
2294
    }
2295
#endif
2296

2297
#if defined(DEBUG) || defined(_DEBUG)
2298
    if (randomly_lose_flag) {
2299
        if ((random() & 0x07) <= lose_factor)
2300
            return 0;
2301
    }
2302
#endif
2303

2304
    return recv_len;
1,038✔
2305
}
527✔
2306

2307
/* stats_add: update host statistics for a single packet that was received (or timed out)
2308
 * h: host entry to update
2309
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2310
 * success: 1 if response received, 0 otherwise
2311
 * latency: response time, in ns
2312
 */
2313
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
688✔
2314
{
2315
    /* sent count - we update only on receive/timeout, so that we don't get
2316
     * weird loss percentage, just because a packet was note recived yet.
2317
     */
2318
    h->num_sent++;
688✔
2319
    h->num_sent_i++;
688✔
2320

2321
    if (!success) {
688✔
2322
        if (!loop_flag && index >= 0) {
95✔
2323
            h->resp_times[index] = RESP_TIMEOUT;
95✔
2324
        }
23✔
2325
        num_timeout++;
95✔
2326
        return;
95✔
2327
    }
2328

2329
    /* received count */
2330
    h->num_recv++;
593✔
2331
    h->num_recv_i++;
593✔
2332

2333
    /* maximum */
2334
    if (!h->max_reply || latency > h->max_reply) {
593✔
2335
        h->max_reply = latency;
432✔
2336
    }
129✔
2337
    if (!h->max_reply_i || latency > h->max_reply_i) {
593✔
2338
        h->max_reply_i = latency;
448✔
2339
    }
133✔
2340

2341
    /* minimum */
2342
    if (!h->min_reply || latency < h->min_reply) {
593✔
2343
        h->min_reply = latency;
356✔
2344
    }
104✔
2345
    if (!h->min_reply_i || latency < h->min_reply_i) {
593✔
2346
        h->min_reply_i = latency;
383✔
2347
    }
111✔
2348

2349
    /* total time (for average) */
2350
    h->total_time += latency;
593✔
2351
    h->total_time_i += latency;
593✔
2352

2353
    /* response time per-packet (count mode) */
2354
    if (!loop_flag && index >= 0) {
593✔
2355
        h->resp_times[index] = latency;
593✔
2356
    }
177✔
2357
}
200✔
2358

2359
/* stats_reset_interval: reset interval statistics
2360
 * h: host entry to update
2361
 */
2362
void stats_reset_interval(HOST_ENTRY *h)
30✔
2363
{
2364
    h->num_sent_i = 0;
30✔
2365
    h->num_recv_i = 0;
30✔
2366
    h->max_reply_i = 0;
30✔
2367
    h->min_reply_i = 0;
30✔
2368
    h->total_time_i = 0;
30✔
2369
}
30✔
2370

2371
int decode_icmp_ipv4(
862✔
2372
    struct sockaddr *response_addr,
2373
    size_t response_addr_len,
2374
    char *reply_buf,
2375
    size_t reply_buf_len,
2376
    unsigned short *id,
2377
    unsigned short *seq,
2378
    int *ip_header_tos,
2379
    int *ip_header_ttl,
2380
    uint32_t *ip_header_otime_ms,
2381
    uint32_t *ip_header_rtime_ms,
2382
    uint32_t *ip_header_ttime_ms)
2383
{
2384
    struct icmp *icp;
2385
    int hlen = 0;
862✔
2386

2387
    if (!using_sock_dgram4) {
862✔
2388
        struct ip *ip = (struct ip *)reply_buf;
854✔
2389
        *ip_header_tos = ip->ip_tos;
854✔
2390
        *ip_header_ttl = ip->ip_ttl;
854✔
2391

2392
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2393
        /* The alpha headers are decidedly broken.
2394
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2395
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2396
         */
2397
        hlen = (ip->ip_vhl & 0x0F) << 2;
2398
#else
2399
        hlen = ip->ip_hl << 2;
854✔
2400
#endif
2401
    }
162✔
2402

2403
    if (reply_buf_len < hlen + ICMP_MINLEN) {
862✔
2404
        /* too short */
2405
        if (verbose_flag) {
×
2406
            char buf[INET6_ADDRSTRLEN];
2407
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2408
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2409
        }
2410
        return -1;
×
2411
    }
2412

2413
    icp = (struct icmp *)(reply_buf + hlen);
862✔
2414

2415
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
862✔
2416
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
368✔
2417
        /* Handle other ICMP packets */
2418
        struct icmp *sent_icmp;
2419
        SEQMAP_VALUE *seqmap_value;
2420
        char addr_ascii[INET6_ADDRSTRLEN];
2421
        HOST_ENTRY *h;
2422

2423
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2424
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
670✔
2425
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2426
            return -1;
×
2427
        }
2428

2429
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
346✔
2430

2431
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
346✔
2432
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
14✔
2433
            sent_icmp->icmp_id != ident4) {
×
2434
            /* not caused by us */
2435
            return -1;
346✔
2436
        }
2437

2438
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2439
        if (seqmap_value == NULL) {
×
2440
            return -1;
×
2441
        }
2442

2443
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2444

2445
        switch (icp->icmp_type) {
×
2446
        case ICMP_UNREACH:
2447
            h = table[seqmap_value->host_nr];
×
2448
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2449
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2450
                    addr_ascii, h->host);
2451
            }
2452
            else {
2453
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2454
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2455
            }
2456

2457
            print_warning("\n");
×
2458
            num_othericmprcvd++;
×
2459
            break;
×
2460

2461
        case ICMP_SOURCEQUENCH:
2462
        case ICMP_REDIRECT:
2463
        case ICMP_TIMXCEED:
2464
        case ICMP_PARAMPROB:
2465
            h = table[seqmap_value->host_nr];
×
2466
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2467
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2468
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2469
            }
2470
            else {
2471
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2472
                    icp->icmp_type, addr_ascii, h->host);
×
2473
            }
2474
            print_warning("\n");
×
2475
            num_othericmprcvd++;
×
2476
            break;
×
2477
        }
2478

2479
        return -1;
×
2480
    }
2481

2482
    *id = icp->icmp_id;
516✔
2483
    *seq = ntohs(icp->icmp_seq);
516✔
2484
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
516✔
2485

2486
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2487
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
14✔
2488
            if (verbose_flag) {
×
2489
                char buf[INET6_ADDRSTRLEN];
2490
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2491
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2492
            }
2493
            return -1;
×
2494
        }
2495

2496
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
14✔
2497
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
14✔
2498
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
14✔
2499
    }
2500

2501
    return hlen;
516✔
2502
}
162✔
2503

2504
#ifdef IPV6
2505
int decode_icmp_ipv6(
176✔
2506
    struct sockaddr *response_addr,
2507
    size_t response_addr_len,
2508
    char *reply_buf,
2509
    size_t reply_buf_len,
2510
    unsigned short *id,
2511
    unsigned short *seq)
2512
{
2513
    struct icmp6_hdr *icp;
2514

2515
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
176✔
2516
        if (verbose_flag) {
×
2517
            char buf[INET6_ADDRSTRLEN];
2518
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2519
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2520
        }
2521
        return 0; /* too short */
×
2522
    }
2523

2524
    icp = (struct icmp6_hdr *)reply_buf;
176✔
2525

2526
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
176✔
2527
        /* Handle other ICMP packets */
2528
        struct icmp6_hdr *sent_icmp;
2529
        SEQMAP_VALUE *seqmap_value;
2530
        char addr_ascii[INET6_ADDRSTRLEN];
2531
        HOST_ENTRY *h;
2532

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

2539
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
87✔
2540

2541
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
87✔
2542
            /* not caused by us */
2543
            return 0;
87✔
2544
        }
2545

2546
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2547
        if (seqmap_value == NULL) {
×
2548
            return 0;
×
2549
        }
2550

2551
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2552

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

2565
            print_warning("\n");
×
2566
            num_othericmprcvd++;
×
2567
            break;
×
2568

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

2587
        return 0;
×
2588
    }
2589

2590
    *id = icp->icmp6_id;
89✔
2591
    *seq = ntohs(icp->icmp6_seq);
89✔
2592

2593
    return 1;
89✔
2594
}
30✔
2595
#endif
2596

2597
int wait_for_reply(int64_t wait_time)
2,186✔
2598
{
2599
    int result;
2600
    static char buffer[RECV_BUFSIZE];
2601
    struct sockaddr_storage response_addr;
2602
    int n, avg;
2603
    HOST_ENTRY *h;
2604
    int64_t this_reply;
2605
    int this_count;
2606
    int64_t recv_time = 0;
2,186✔
2607
    SEQMAP_VALUE *seqmap_value;
2608
    unsigned short id;
2609
    unsigned short seq;
2610
    int ip_header_tos = -1;
2,186✔
2611
    int ip_header_ttl = -1;
2,186✔
2612
    // ICMP Timestamp
2613
    uint32_t ip_header_otime_ms = 0x80000000U;
2,186✔
2614
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,186✔
2615
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,186✔
2616

2617
    /* Receive packet */
2618
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,186✔
2619
        &recv_time, /* reply_timestamp */
2620
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2621
        sizeof(response_addr), /* reply_src_addr_len */
2622
        buffer, /* reply_buf */
2623
        sizeof(buffer) /* reply_buf_len */
2624
    );
2625

2626
    if (result <= 0) {
2,186✔
2627
        return 0;
1,148✔
2628
    }
2629

2630
    update_current_time();
1,038✔
2631
    if (recv_time == 0)
1,038✔
2632
        recv_time = current_time_ns;
192✔
2633

2634
    /* Process ICMP packet and retrieve id/seq */
2635
    if (response_addr.ss_family == AF_INET) {
1,038✔
2636
        int ip_hlen = decode_icmp_ipv4(
862✔
2637
            (struct sockaddr *)&response_addr,
2638
            sizeof(response_addr),
2639
            buffer,
2640
            sizeof(buffer),
2641
            &id,
2642
            &seq,
2643
            &ip_header_tos,
2644
            &ip_header_ttl,
2645
            &ip_header_otime_ms,
2646
            &ip_header_rtime_ms,
2647
            &ip_header_ttime_ms);
2648
        if (ip_hlen < 0) {
862✔
2649
            return 1;
346✔
2650
        }
2651
        if (id != ident4) {
516✔
2652
            return 1; /* packet received, but not the one we are looking for! */
×
2653
        }
2654
        if (!using_sock_dgram4) {
516✔
2655
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2656
             * with fping with IPv6 hosts */
2657
            result -= ip_hlen;
508✔
2658
        }
162✔
2659
    }
162✔
2660
#ifdef IPV6
2661
    else if (response_addr.ss_family == AF_INET6) {
176✔
2662
        if (!decode_icmp_ipv6(
176✔
2663
                (struct sockaddr *)&response_addr,
2664
                sizeof(response_addr),
2665
                buffer,
2666
                sizeof(buffer),
2667
                &id,
2668
                &seq)) {
2669
            return 1;
87✔
2670
        }
2671
        if (id != ident6) {
89✔
2672
            return 1; /* packet received, but not the one we are looking for! */
×
2673
        }
2674
    }
15✔
2675
#endif
2676
    else {
2677
        return 1;
×
2678
    }
2679

2680
    seqmap_value = seqmap_fetch(seq, current_time_ns);
605✔
2681
    if (seqmap_value == NULL) {
605✔
2682
        return 1;
×
2683
    }
2684

2685
    /* find corresponding host_entry */
2686
    n = seqmap_value->host_nr;
605✔
2687
    h = table[n];
605✔
2688
    this_count = seqmap_value->ping_count;
605✔
2689
    this_reply = recv_time - seqmap_value->ping_ts;
605✔
2690

2691
    /* update stats that include invalid replies */
2692
    h->num_recv_total++;
605✔
2693
    num_pingreceived++;
605✔
2694

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

2697
    /* optionally require reply source equal to target address */
2698
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
605✔
2699
        dbg_printf("discarding reply from wrong source address\n");
2700
        return 1;
12✔
2701
    }
2702

2703
    /* discard duplicates */
2704
    if (!loop_flag && h->resp_times[this_count] >= 0) {
593✔
2705
        if (!per_recv_flag) {
×
2706
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2707
                h->host, this_count, result, sprint_tm(this_reply));
2708

2709
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2710
                char buf[INET6_ADDRSTRLEN];
2711
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2712
                fprintf(stderr, " [<- %s]", buf);
×
2713
            }
2714
            fprintf(stderr, "\n");
×
2715
        }
2716
        return 1;
×
2717
    }
2718

2719
    /* discard reply if delay is larger than timeout
2720
     * (see also: github #32) */
2721
    if (this_reply > h->timeout) {
593✔
2722
        return 1;
×
2723
    }
2724

2725
    /* update stats */
2726
    stats_add(h, this_count, 1, this_reply);
593✔
2727
    // TODO: move to stats_add?
2728
    if (!max_reply || this_reply > max_reply)
593✔
2729
        max_reply = this_reply;
357✔
2730
    if (!min_reply || this_reply < min_reply)
593✔
2731
        min_reply = this_reply;
286✔
2732
    sum_replies += this_reply;
593✔
2733
    total_replies++;
593✔
2734

2735
    /* initialize timeout to initial timeout (without backoff) */
2736
    h->timeout = timeout;
593✔
2737

2738
    /* remove timeout event */
2739
    struct event *timeout_event = host_get_timeout_event(h, this_count);
593✔
2740
    if (timeout_event) {
593✔
2741
        ev_remove(&event_queue_timeout, timeout_event);
593✔
2742
    }
177✔
2743

2744
    /* print "is alive" */
2745
    if (h->num_recv == 1) {
593✔
2746
        num_alive++;
312✔
2747
        if (fast_reachable && num_alive >= min_reachable)
312✔
2748
                finish_requested = 1;
3✔
2749

2750
        if (verbose_flag || alive_flag) {
312✔
2751
            printf("%s", h->host);
181✔
2752

2753
            if (verbose_flag)
181✔
2754
                printf(" is alive");
161✔
2755
        }
47✔
2756
    }
86✔
2757

2758
    /* print received ping (unless --quiet) */
2759
    if (per_recv_flag) {
593✔
2760
        if (timestamp_flag) {
151✔
2761
            print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2762
        }
10✔
2763
        avg = h->total_time / h->num_recv;
151✔
2764
        printf("%-*s : [%d], %d bytes, %s ms",
151✔
2765
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2766
        printf(" (%s avg, ", sprint_tm(avg));
151✔
2767

2768
        if (h->num_recv <= h->num_sent) {
151✔
2769
            printf("%d%% loss)",
151✔
2770
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2771
        }
45✔
2772
        else {
2773
            printf("%d%% return)",
×
2774
                (h->num_recv_total * 100) / h->num_sent);
×
2775
        }
2776
    }
45✔
2777

2778
    if (verbose_flag || alive_flag || per_recv_flag) {
593✔
2779

2780
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
332✔
2781
            char buf[INET6_ADDRSTRLEN];
2782
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2783
            fprintf(stderr, " [<- %s]", buf);
6✔
2784
        }
2785

2786
        if (icmp_request_typ == 13) {
332✔
2787
            printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2788
                   alive_flag ? "" : ",",
14✔
2789
                   ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
2790
                   ms_since_midnight_utc(recv_time));
2791
        }
2792

2793
        if(print_tos_flag) {
332✔
2794
            if(ip_header_tos != -1) {
43✔
2795
                printf(" (TOS %d)", ip_header_tos);
39✔
2796
            }
11✔
2797
            else {
2798
                printf(" (TOS unknown)");
4✔
2799
            }
2800
        }
11✔
2801

2802
        if (print_ttl_flag) {
332✔
2803
          if(ip_header_ttl != -1) {
43✔
2804
              printf(" (TTL %d)", ip_header_ttl);
39✔
2805
          }
11✔
2806
          else {
2807
              printf(" (TTL unknown)");
4✔
2808
          }
2809
        }
11✔
2810

2811
        if (elapsed_flag && !per_recv_flag)
332✔
2812
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2813

2814
        printf("\n");
332✔
2815
    }
92✔
2816

2817
    return 1;
593✔
2818
}
527✔
2819

2820
/************************************************************
2821

2822
  Function: add_name
2823

2824
*************************************************************
2825

2826
  Inputs:  char* name
2827

2828
  Description:
2829

2830
  process input name for addition to target list
2831
  name can turn into multiple targets via multiple interfaces (-m)
2832
  or via NIS groups
2833

2834
************************************************************/
2835

2836
void add_name(char *name)
412✔
2837
{
2838
    struct addrinfo *res0, *res, hints;
2839
    int ret_ga;
2840
    char *printname;
2841
    char namebuf[256];
2842
    char addrbuf[256];
2843

2844
    /* getaddrinfo */
2845
    memset(&hints, 0, sizeof(struct addrinfo));
412✔
2846
    hints.ai_flags = AI_UNUSABLE;
412✔
2847
    hints.ai_socktype = SOCK_RAW;
412✔
2848
    hints.ai_family = hints_ai_family;
412✔
2849
    if (hints_ai_family == AF_INET) {
412✔
2850
        hints.ai_protocol = IPPROTO_ICMP;
77✔
2851
    }
21✔
2852
#ifdef IPV6
2853
    else if (hints_ai_family == AF_INET6) {
335✔
2854
        hints.ai_protocol = IPPROTO_ICMPV6;
29✔
2855
    }
1✔
2856
#endif
2857
    else {
2858
        hints.ai_socktype = SOCK_STREAM;
306✔
2859
        hints.ai_protocol = 0;
306✔
2860
    }
2861
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
412✔
2862
    if (ret_ga) {
412✔
2863
        if (!quiet_flag)
17✔
2864
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
17✔
2865
        num_noaddress++;
17✔
2866
        return;
17✔
2867
    }
2868

2869
    /* NOTE: we could/should loop with res on all addresses like this:
2870
     * for (res = res0; res; res = res->ai_next) {
2871
     * We don't do it yet, however, because is is an incompatible change
2872
     * (need to implement a separate option for this)
2873
     */
2874
    for (res = res0; res; res = res->ai_next) {
395✔
2875
        /* name_flag: addr -> name lookup requested) */
2876
        if (name_flag || rdns_flag) {
397✔
2877
            int do_rdns = rdns_flag ? 1 : 0;
3✔
2878
            if (name_flag) {
3✔
2879
                /* Was it a numerical address? Only then do a rdns-query */
2880
                struct addrinfo *nres;
2881
                hints.ai_flags = AI_NUMERICHOST;
×
2882
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
2883
                    do_rdns = 1;
×
2884
                    freeaddrinfo(nres);
×
2885
                }
2886
            }
2887

2888
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2889
                printname = namebuf;
3✔
2890
            }
1✔
2891
            else {
2892
                printname = name;
×
2893
            }
2894
        }
1✔
2895
        else {
2896
            printname = name;
392✔
2897
        }
2898

2899
        /* addr_flag: name -> addr lookup requested */
2900
        if (addr_flag) {
395✔
2901
            int ret;
2902
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2903
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2904
            if (ret) {
3✔
2905
                if (!quiet_flag) {
×
2906
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2907
                }
2908
                continue;
×
2909
            }
2910

2911
            if (name_flag || rdns_flag) {
3✔
2912
                char nameaddrbuf[512 + 3];
2913
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
2914
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2915
            }
2916
            else {
2917
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2918
            }
2919
        }
1✔
2920
        else {
2921
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
392✔
2922
        }
2923

2924
        if (!multif_flag) {
395✔
2925
            break;
395✔
2926
        }
2927
    }
2928

2929
    freeaddrinfo(res0);
395✔
2930
}
104✔
2931

2932
/************************************************************
2933

2934
  Function: add_addr
2935

2936
*************************************************************
2937

2938
  Description:
2939

2940
  add single address to list of hosts to be pinged
2941

2942
************************************************************/
2943

2944
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
395✔
2945
{
2946
    HOST_ENTRY *p;
2947
    int n;
2948
    int64_t *i;
2949

2950
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
395✔
2951
    if (!p)
395✔
2952
        crash_and_burn("can't allocate HOST_ENTRY");
×
2953

2954
    p->name = strdup(name);
395✔
2955
    p->host = strdup(host);
395✔
2956
    memcpy(&p->saddr, ipaddr, ipaddr_len);
395✔
2957
    p->saddr_len = ipaddr_len;
395✔
2958
    p->timeout = timeout;
395✔
2959
    p->min_reply = 0;
395✔
2960

2961
    if (netdata_flag) {
395✔
2962
        char *s = p->name;
3✔
2963
        while (*s) {
30✔
2964
            if (!isalnum(*s))
27✔
2965
                *s = '_';
9✔
2966
            s++;
27✔
2967
        }
2968
    }
1✔
2969

2970
    if (strlen(p->host) > max_hostname_len)
395✔
2971
        max_hostname_len = strlen(p->host);
274✔
2972

2973
    /* array for response time results */
2974
    if (!loop_flag) {
395✔
2975
        i = (int64_t *)malloc(trials * sizeof(int64_t));
395✔
2976
        if (!i)
395✔
2977
            crash_and_burn("can't allocate resp_times array");
×
2978

2979
        for (n = 1; n < trials; n++)
1,571✔
2980
            i[n] = RESP_UNUSED;
1,176✔
2981

2982
        p->resp_times = i;
395✔
2983
    }
99✔
2984

2985
    /* allocate event storage */
2986
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
395✔
2987
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
395✔
2988

2989
    /* schedule first ping */
2990
    host_add_ping_event(p, 0, current_time_ns);
395✔
2991

2992
    num_hosts++;
395✔
2993
}
395✔
2994

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

2997
  Function: crash_and_burn
2998

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

3001
  Inputs:  char* message
3002

3003
  Description:
3004

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

3007
void crash_and_burn(char *message)
×
3008
{
3009
    fprintf(stderr, "%s: %s\n", prog, message);
×
3010
    exit(4);
×
3011
}
3012

3013
/************************************************************
3014

3015
  Function: errno_crash_and_burn
3016

3017
*************************************************************
3018

3019
  Inputs:  char* message
3020

3021
  Description:
3022

3023
************************************************************/
3024

3025
void errno_crash_and_burn(char *message)
8✔
3026
{
3027
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3028
    exit(4);
8✔
3029
}
3030

3031
/************************************************************
3032

3033
  Function: print_warning
3034

3035
  Description: fprintf(stderr, ...), unless running with -q
3036

3037
*************************************************************/
3038

3039
void print_warning(char *format, ...)
43✔
3040
{
3041
    va_list args;
3042
    if (!quiet_flag) {
43✔
3043
        va_start(args, format);
43✔
3044
        vfprintf(stderr, format, args);
43✔
3045
        va_end(args);
43✔
3046
    }
5✔
3047
}
43✔
3048

3049
/************************************************************
3050

3051
  Function: sprint_tm
3052

3053
*************************************************************
3054

3055
  render nanosecond int64_t value into milliseconds string with three digits of
3056
  precision.
3057

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

3060
const char *sprint_tm(int64_t ns)
851✔
3061
{
3062
    static char buf[10];
3063
    double t = (double)ns / 1e6;
851✔
3064

3065
    if (t < 0.0) {
851✔
3066
        /* negative (unexpected) */
3067
        sprintf(buf, "%.2g", t);
×
3068
    }
3069
    else if (t < 1.0) {
851✔
3070
        /* <= 0.99 ms */
3071
        sprintf(buf, "%.3f", t);
851✔
3072
    }
261✔
3073
    else if (t < 10.0) {
×
3074
        /* 1.00 - 9.99 ms */
3075
        sprintf(buf, "%.2f", t);
×
3076
    }
3077
    else if (t < 100.0) {
×
3078
        /* 10.0 - 99.9 ms */
3079
        sprintf(buf, "%.1f", t);
×
3080
    }
3081
    else if (t < 1000000.0) {
×
3082
        /* 100 - 1'000'000 ms */
3083
        sprintf(buf, "%.0f", t);
×
3084
    }
3085
    else {
3086
        sprintf(buf, "%.3e", t);
×
3087
    }
3088

3089
    return (buf);
851✔
3090
}
3091

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

3094
  Function: addr_cmp
3095

3096
*************************************************************/
3097
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
354✔
3098
{
3099
    if (a->sa_family != b->sa_family) {
354✔
3100
        return a->sa_family - b->sa_family;
×
3101
    }
3102
    else {
3103
        if (a->sa_family == AF_INET) {
354✔
3104
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
292✔
3105
        }
3106
        else if (a->sa_family == AF_INET6) {
62✔
3107
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
70✔
3108
                &((struct sockaddr_in6 *)b)->sin6_addr,
62✔
3109
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3110
        }
3111
    }
3112

3113
    return 0;
×
3114
}
94✔
3115

3116
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
710✔
3117
{
3118
    struct event *event = &h->event_storage_ping[index % event_storage_count];
710✔
3119
    event->host = h;
710✔
3120
    event->ping_index = index;
710✔
3121
    event->ev_time = ev_time;
710✔
3122
    ev_enqueue(&event_queue_ping, event);
710✔
3123

3124
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3125
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3126
}
710✔
3127

3128
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
688✔
3129
{
3130
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
688✔
3131
    event->host = h;
688✔
3132
    event->ping_index = index;
688✔
3133
    event->ev_time = ev_time;
688✔
3134
    ev_enqueue(&event_queue_timeout, event);
688✔
3135

3136
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3137
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3138
}
688✔
3139

3140
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
593✔
3141
{
3142
    return &h->event_storage_timeout[index % event_storage_count];
593✔
3143
}
3144

3145
/************************************************************
3146

3147
  Function: ev_enqueue
3148

3149
  Enqueue an event
3150

3151
  The queue is sorted by event->ev_time, so that queue->first always points to
3152
  the earliest event.
3153

3154
  We start scanning the queue from the tail, because we assume
3155
  that new events mostly get inserted with a event time higher
3156
  than the others.
3157

3158
*************************************************************/
3159
void ev_enqueue(struct event_queue *queue, struct event *event)
1,398✔
3160
{
3161
    struct event *i;
3162
    struct event *i_prev;
3163

3164
    /* Empty list */
3165
    if (queue->last == NULL) {
1,398✔
3166
        event->ev_next = NULL;
1,075✔
3167
        event->ev_prev = NULL;
1,075✔
3168
        queue->first = event;
1,075✔
3169
        queue->last = event;
1,075✔
3170
        return;
1,075✔
3171
    }
3172

3173
    /* Insert on tail? */
3174
    if (event->ev_time - queue->last->ev_time >= 0) {
323✔
3175
        event->ev_next = NULL;
323✔
3176
        event->ev_prev = queue->last;
323✔
3177
        queue->last->ev_next = event;
323✔
3178
        queue->last = event;
323✔
3179
        return;
323✔
3180
    }
3181

3182
    /* Find insertion point */
3183
    i = queue->last;
×
3184
    while (1) {
3185
        i_prev = i->ev_prev;
×
3186
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3187
            event->ev_prev = i_prev;
×
3188
            event->ev_next = i;
×
3189
            i->ev_prev = event;
×
3190
            if (i_prev != NULL) {
×
3191
                i_prev->ev_next = event;
×
3192
            }
3193
            else {
3194
                queue->first = event;
×
3195
            }
3196
            return;
×
3197
        }
3198
        i = i_prev;
×
3199
    }
3200
}
400✔
3201

3202
/************************************************************
3203

3204
  Function: ev_dequeue
3205

3206
*************************************************************/
3207
struct event *ev_dequeue(struct event_queue *queue)
785✔
3208
{
3209
    struct event *dequeued;
3210

3211
    if (queue->first == NULL) {
785✔
3212
        return NULL;
×
3213
    }
3214
    dequeued = queue->first;
785✔
3215
    ev_remove(queue, dequeued);
785✔
3216

3217
    return dequeued;
785✔
3218
}
217✔
3219

3220
/************************************************************
3221

3222
  Function: ev_remove
3223

3224
*************************************************************/
3225
void ev_remove(struct event_queue *queue, struct event *event)
1,378✔
3226
{
3227
    if (queue->first == event) {
1,378✔
3228
        queue->first = event->ev_next;
1,369✔
3229
    }
391✔
3230
    if (queue->last == event) {
1,378✔
3231
        queue->last = event->ev_prev;
1,076✔
3232
    }
301✔
3233
    if (event->ev_prev) {
1,378✔
3234
        event->ev_prev->ev_next = event->ev_next;
9✔
3235
    }
3✔
3236
    if (event->ev_next) {
1,378✔
3237
        event->ev_next->ev_prev = event->ev_prev;
302✔
3238
    }
93✔
3239
    event->ev_prev = NULL;
1,378✔
3240
    event->ev_next = NULL;
1,378✔
3241
}
1,378✔
3242

3243
/************************************************************
3244

3245
  Function: print_human_readable_time from current_time_ns
3246

3247
*************************************************************/
3248
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
30✔
3249
{
3250
    char time_buffer[100];
3251
    time_t current_time_s;
3252
    struct tm *local_time;
3253

3254
    current_time_s = current_time_ns / 1000000000;
30✔
3255
    local_time = localtime(&current_time_s);
30✔
3256
    switch(timestamp_format) {
30✔
3257
        case 1:
4✔
3258
            // timestamp-format ctime
3259
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
6✔
3260
            printf("[%s] ", time_buffer);
6✔
3261
            break;
6✔
3262
        case 2:
4✔
3263
            // timestamp-format iso
3264
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
6✔
3265
            printf("[%s] ", time_buffer);
6✔
3266
            break;
6✔
3267
        case 3:
4✔
3268
            // timestamp-format rfc3339
3269
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
6✔
3270
            printf("[%s] ", time_buffer);
6✔
3271
            break;
6✔
3272
        default:
8✔
3273
            printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3274
    }
4✔
3275
}
30✔
3276

3277
/************************************************************
3278

3279
  Function: ms_since_midnight_utc
3280

3281
*************************************************************
3282

3283
  Input: int64_t: current UTC time in ns
3284

3285
  Output: uint32_t: current time in ms since midnight UTC
3286

3287
  Description:
3288

3289
  Return ICMP Timestamp value corresponding to the given time value.
3290
  The given time value must be in UTC.
3291

3292
*************************************************************/
3293
static uint32_t ms_since_midnight_utc(int64_t time_val)
14✔
3294
{
3295
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
14✔
3296
}
3297

3298
/************************************************************
3299

3300
  Function: usage
3301

3302
*************************************************************
3303

3304
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3305

3306
  Description:
3307

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

3310
void usage(int is_error)
115✔
3311
{
3312
    FILE *out = is_error ? stderr : stdout;
115✔
3313
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
115✔
3314
    fprintf(out, "\n");
115✔
3315
    fprintf(out, "Probing options:\n");
115✔
3316
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
115✔
3317
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
115✔
3318
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
115✔
3319
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
115✔
3320
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
115✔
3321
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
115✔
3322
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
115✔
3323
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
115✔
3324
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
115✔
3325
    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✔
3326
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
115✔
3327
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
115✔
3328
#ifdef SO_BINDTODEVICE
3329
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
78✔
3330
#endif
3331
#ifdef SO_MARK
3332
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
78✔
3333
#endif
3334
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
115✔
3335
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
115✔
3336
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
115✔
3337
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
115✔
3338
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
115✔
3339
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
115✔
3340
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
115✔
3341
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
115✔
3342
    fprintf(out, "   -S, --src=IP       set source address\n");
115✔
3343
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
115✔
3344
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
115✔
3345
    fprintf(out, "       --check-source discard replies not from target address\n");
115✔
3346
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
115✔
3347
    fprintf(out, "\n");
115✔
3348
    fprintf(out, "Output options:\n");
115✔
3349
    fprintf(out, "   -a, --alive        show targets that are alive\n");
115✔
3350
    fprintf(out, "   -A, --addr         show targets by address\n");
115✔
3351
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
115✔
3352
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
115✔
3353
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
115✔
3354
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
115✔
3355
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
115✔
3356
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
115✔
3357
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
115✔
3358
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
115✔
3359
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
115✔
3360
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
115✔
3361
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
115✔
3362
    fprintf(out, "   -s, --stats        print final stats\n");
115✔
3363
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
115✔
3364
    fprintf(out, "   -v, --version      show version\n");
115✔
3365
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
115✔
3366
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
115✔
3367
    fprintf(out, "       --print-tos    show received TOS value\n");
115✔
3368
    fprintf(out, "       --print-ttl    show IP TTL value\n");
115✔
3369
    exit(is_error);
115✔
3370
}
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