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

schweikert / fping / 12855293885

19 Jan 2025 04:31PM UTC coverage: 87.629% (+0.03%) from 87.602%
12855293885

Pull #376

github

web-flow
Merge 3c2b44edf into cb8328617
Pull Request #376: IPv6 support for -g, --generate

95 of 102 new or added lines in 1 file covered. (93.14%)

4 existing lines in 1 file now uncovered.

1360 of 1552 relevant lines covered (87.63%)

280.0 hits per line

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

87.82
/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 131071 /* 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) {
660✔
1239
            table[i] = cursor->host;
388✔
1240
            cursor->host->i = i;
388✔
1241
            i++;
388✔
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,406✔
1290
{
1291
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
4,406✔
1292
}
1293

1294
void add_cidr(char *addr)
54✔
1295
{
1296
    char *addr_end;
1297
    char *mask_str;
1298
    unsigned long mask;
1299
    int ret;
1300
    struct addrinfo addr_hints;
1301
    struct addrinfo *addr_res;
1302
    unsigned long net_addr;
1303
#ifdef IPV6
1304
    uint64_t net_upper, net_lower;
1305
    char *scope_str;
1306
#endif /* IPV6 */
1307

1308
    /* Split address from mask */
1309
    addr_end = strrchr(addr, '/');
54✔
1310
    if (addr_end == NULL) {
54✔
1311
        usage(1);
3✔
1312
    }
1✔
1313
    mask_str = addr_end + 1;
52✔
1314

1315
#ifdef IPV6
1316
    /* IPv6 addresses can have a scope */
1317
    scope_str = strchr(addr, '%');
52✔
1318
    if (scope_str && mask_str < scope_str) {
52✔
1319
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1320
        exit(1);
2✔
1321
    }
1322
#endif /*IPV6 */
1323

1324
    *addr_end = '\0';
50✔
1325
    mask = atoi(mask_str);
50✔
1326

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

1355
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
21✔
1356
{
1357
    unsigned long bitmask;
1358
    unsigned long net_last;
1359

1360
    /* check mask */
1361
    if (mask < 1 || mask > 32) {
21✔
1362
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1363
        exit(1);
6✔
1364
    }
1365

1366
    /* convert mask integer from 1 to 32 to a bitmask */
1367
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
15✔
1368

1369
    /* calculate network range */
1370
    net_addr &= bitmask;
15✔
1371
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
15✔
1372

1373
    /* exclude network and broadcast address for regular prefixes */
1374
    if (mask < 31) {
15✔
1375
        net_last--;
9✔
1376
        net_addr++;
9✔
1377
    }
3✔
1378

1379
    /* add all hosts in that network (net_addr and net_last inclusive) */
1380
    add_addr_range_ipv4(net_addr, net_last);
15✔
1381
}
13✔
1382

1383
#ifdef IPV6
1384
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1385
{
1386
    uint64_t bitmask_lower;
1387
    uint64_t last_lower;
1388

1389
    /* check mask -- 2^63 addresses should suffice for now */
1390
    if (mask < 65 || mask > 128) {
23✔
1391
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1392
        exit(1);
7✔
1393
    }
1394

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

1398
    /* calculate network range */
1399
    net_lower &= bitmask_lower;
16✔
1400
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1401

1402
    /* exclude Subnet-Router anycast address */
1403
    if (mask < 127) {
16✔
1404
        net_lower++;
6✔
1405
    }
1406

1407
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1408
}
12✔
1409
#endif /* IPV6 */
1410

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

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

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

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

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

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

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

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

1534
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
76✔
1535
{
1536
    int i;
1537
    for (i = 0; i < 8; i++) {
684✔
1538
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
608✔
1539
    }
1540
}
76✔
1541

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

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

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

1580
void main_loop()
260✔
1581
{
1582
    int64_t lt;
1583
    int64_t wait_time_ns;
1584
    struct event *event;
1585
    struct host_entry *h;
1586

1587
    while (event_queue_ping.first || event_queue_timeout.first) {
1,504✔
1588
        dbg_printf("%s", "# main_loop\n");
1589

1590
        /* timeout event ? */
1591
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,259✔
1592
            event = ev_dequeue(&event_queue_timeout);
95✔
1593
            h = event->host;
95✔
1594

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

1597
            stats_add(h, event->ping_index, 0, -1);
95✔
1598

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

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

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

1639
        /* ping event ? */
1640
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,358✔
1641
            /* Make sure that we don't ping more than once every "interval" */
1642
            lt = current_time_ns - last_send_time;
876✔
1643
            if (lt < interval)
876✔
1644
                goto wait_for_reply;
188✔
1645

1646
            /* Dequeue the event */
1647
            event = ev_dequeue(&event_queue_ping);
688✔
1648
            h = event->host;
688✔
1649

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

1652
            /* Send the ping */
1653
            send_ping(h, event->ping_index);
688✔
1654

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

1661
    wait_for_reply:
493✔
1662

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

1678
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1679
        }
263✔
1680

1681
        /* When is the next timeout event? */
1682
        if (event_queue_timeout.first) {
1,164✔
1683
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
736✔
1684
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
736✔
1685
                wait_time_ns = wait_time_timeout;
319✔
1686
                if (wait_time_ns < 0) {
319✔
1687
                    wait_time_ns = 0;
×
1688
                }
1689
            }
78✔
1690

1691
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1692
        }
211✔
1693

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

1704
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1705
        }
68✔
1706

1707
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1708
        if (wait_time_ns == -1) {
1,164✔
1709
            break;
12✔
1710
        }
1711

1712
        /* end of loop was requested by interrupt signal handler */
1713
        if (finish_requested) {
1,152✔
1714
            break;
3✔
1715
        }
1716

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

1725
        update_current_time();
1,149✔
1726

1727
        if (status_snapshot) {
1,149✔
1728
            status_snapshot = 0;
×
1729
            print_per_system_splits();
×
1730
        }
1731

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

1739
            while (current_time_ns >= next_report_time) {
84✔
1740
                next_report_time += report_interval;
42✔
1741
            }
1742
        }
14✔
1743
    }
1744
}
260✔
1745

1746
/************************************************************
1747

1748
  Function: signal_handler
1749

1750
*************************************************************
1751

1752
  Inputs:  int signum
1753

1754
  Description:
1755

1756
  SIGQUIT signal handler - set flag and return
1757
  SIGINT signal handler - set flag and return
1758

1759
************************************************************/
1760

1761
void signal_handler(int signum)
×
1762
{
1763
    switch (signum) {
×
1764
    case SIGINT:
1765
        finish_requested = 1;
×
1766
        break;
×
1767

1768
    case SIGQUIT:
1769
        status_snapshot = 1;
×
1770
        break;
×
1771
    }
1772
}
×
1773

1774
/************************************************************
1775

1776
  Function: update_current_time
1777

1778
*************************************************************/
1779

1780
void update_current_time()
3,560✔
1781
{
1782
    clock_gettime(CLOCKID, &current_time);
3,560✔
1783
    current_time_ns = timespec_ns(&current_time);
3,560✔
1784
}
3,560✔
1785

1786
/************************************************************
1787

1788
  Function: finish
1789

1790
*************************************************************
1791

1792
  Inputs:  void (none)
1793

1794
  Description:
1795

1796
  Main program clean up and exit point
1797

1798
************************************************************/
1799

1800
void finish()
260✔
1801
{
1802
    int i;
1803
    HOST_ENTRY *h;
1804

1805
    update_current_time();
260✔
1806
    end_time = current_time_ns;
260✔
1807

1808
    /* tot up unreachables */
1809
    for (i = 0; i < num_hosts; i++) {
648✔
1810
        h = table[i];
388✔
1811

1812
        if (!h->num_recv) {
388✔
1813
            num_unreachable++;
76✔
1814

1815
            if (verbose_flag || unreachable_flag) {
76✔
1816
                printf("%s", h->host);
40✔
1817

1818
                if (verbose_flag)
40✔
1819
                    printf(" is unreachable");
37✔
1820

1821
                printf("\n");
40✔
1822
            }
2✔
1823
        }
12✔
1824
    }
98✔
1825

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

1833
    if (stats_flag)
260✔
1834
        print_global_stats();
9✔
1835

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

1847
    if (num_noaddress)
251✔
1848
        exit(2);
3✔
1849
    else if (num_alive != num_hosts)
248✔
1850
        exit(1);
39✔
1851

1852
    exit(0);
209✔
1853
}
1854

1855
/************************************************************
1856

1857
  Function: print_per_system_stats
1858

1859
*************************************************************
1860

1861
  Inputs:  void (none)
1862

1863
  Description:
1864

1865

1866
************************************************************/
1867

1868
void print_per_system_stats(void)
100✔
1869
{
1870
    int i, j, avg, outage_ms;
1871
    HOST_ENTRY *h;
1872
    int64_t resp;
1873

1874
    if (verbose_flag || per_recv_flag)
100✔
1875
        fprintf(stderr, "\n");
59✔
1876

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

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

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

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

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

1915
            fprintf(stderr, "\n");
90✔
1916
        }
1917
    }
38✔
1918
}
100✔
1919

1920
/************************************************************
1921

1922
  Function: print_netdata
1923

1924
*************************************************************
1925

1926
  Inputs:  void (none)
1927

1928
  Description:
1929

1930

1931
************************************************************/
1932

1933
void print_netdata(void)
3✔
1934
{
1935
    static int sent_charts = 0;
1936

1937
    int i;
1938
    int64_t avg;
1939
    HOST_ENTRY *h;
1940

1941
    for (i = 0; i < num_hosts; i++) {
6✔
1942
        h = table[i];
3✔
1943

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

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

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

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

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

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

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

1988
        stats_reset_interval(h);
3✔
1989
    }
1✔
1990

1991
    sent_charts = 1;
3✔
1992
}
3✔
1993

1994
/************************************************************
1995

1996
  Function: print_per_system_splits
1997

1998
*************************************************************
1999

2000
  Inputs:  void (none)
2001

2002
  Description:
2003

2004

2005
************************************************************/
2006

2007
void print_per_system_splits(void)
39✔
2008
{
2009
    int i, avg, outage_ms_i;
2010
    HOST_ENTRY *h;
2011
    struct tm *curr_tm;
2012

2013
    if (verbose_flag || per_recv_flag)
39✔
2014
        fprintf(stderr, "\n");
×
2015

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

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

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

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

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

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

2054
/************************************************************
2055

2056
  Function: print_global_stats
2057

2058
*************************************************************
2059

2060
  Inputs:  void (none)
2061

2062
  Description:
2063

2064

2065
************************************************************/
2066

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

2081
    if (total_replies == 0) {
9✔
2082
        min_reply = 0;
3✔
2083
        max_reply = 0;
3✔
2084
        total_replies = 1;
3✔
2085
        sum_replies = 0;
3✔
2086
    }
1✔
2087

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

2097
/************************************************************
2098

2099
  Function: send_ping
2100

2101
*************************************************************
2102

2103
  Inputs:  int s, HOST_ENTRY *h
2104

2105
  Description:
2106

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

2113
************************************************************/
2114

2115
int send_ping(HOST_ENTRY *h, int index)
718✔
2116
{
2117
    int n;
2118
    int myseq;
2119
    int ret = 1;
718✔
2120
    uint8_t proto = ICMP_ECHO;
718✔
2121

2122
    update_current_time();
718✔
2123
    h->last_send_time = current_time_ns;
718✔
2124
    myseq = seqmap_add(h->i, index, current_time_ns);
718✔
2125

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

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

2142
    /* error sending? */
2143
    if (
518✔
2144
        (n < 0)
200✔
2145
#if defined(EHOSTDOWN)
2146
        && errno != EHOSTDOWN
230✔
2147
#endif
2148
    ) {
2149
        if (verbose_flag) {
30✔
2150
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
24✔
2151
        }
2152
        else {
2153
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2154
        }
2155

2156
        h->num_sent++;
30✔
2157
        h->num_sent_i++;
30✔
2158
        if (!loop_flag)
30✔
2159
            h->resp_times[index] = RESP_ERROR;
30✔
2160

2161
        ret = 0;
30✔
2162
    }
2163
    else {
2164
        /* schedule timeout */
2165
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
688✔
2166

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

2173
    num_pingsent++;
718✔
2174
    last_send_time = h->last_send_time;
718✔
2175

2176
    return (ret);
718✔
2177
}
200✔
2178

2179
int socket_can_read(struct timeval *timeout)
2,187✔
2180
{
2181
    int nfound;
2182
    fd_set readset;
2183
    int socketmax;
2184

2185
#ifndef IPV6
2186
    socketmax = socket4;
2187
#else
2188
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,187✔
2189
#endif
2190

2191
select_again:
1,657✔
2192
    FD_ZERO(&readset);
15,435✔
2193
    if (socket4 >= 0)
2,187✔
2194
        FD_SET(socket4, &readset);
2,187✔
2195
#ifdef IPV6
2196
    if (socket6 >= 0)
2,187✔
2197
        FD_SET(socket6, &readset);
2,187✔
2198
#endif
2199

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

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

2222
    return -1;
1,149✔
2223
}
530✔
2224

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

2255
    /* Wait for a socket to become ready */
2256
    if (wait_time) {
2,187✔
2257
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,149✔
2258
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,149✔
2259
    }
338✔
2260
    else {
2261
        to.tv_sec = 0;
1,038✔
2262
        to.tv_usec = 0;
1,038✔
2263
    }
2264
    s = socket_can_read(&to);
2,187✔
2265
    if (s == -1) {
2,187✔
2266
        return 0; /* timeout */
1,149✔
2267
    }
2268

2269
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,038✔
2270
    if (recv_len <= 0) {
1,038✔
2271
        return 0;
×
2272
    }
2273

2274
#if HAVE_SO_TIMESTAMPNS
2275
    /* ancilliary data */
2276
    {
2277
        struct timespec reply_timestamp_ts;
2278
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,269✔
2279
             cmsg != NULL;
846✔
2280
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
846✔
2281
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
846✔
2282
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
846✔
2283
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
846✔
2284
            }
2285
        }
2286
    }
2287
#endif
2288

2289
#if defined(DEBUG) || defined(_DEBUG)
2290
    if (randomly_lose_flag) {
2291
        if ((random() & 0x07) <= lose_factor)
2292
            return 0;
2293
    }
2294
#endif
2295

2296
    return recv_len;
1,038✔
2297
}
530✔
2298

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

2313
    if (!success) {
688✔
2314
        if (!loop_flag && index >= 0) {
95✔
2315
            h->resp_times[index] = RESP_TIMEOUT;
95✔
2316
        }
23✔
2317
        num_timeout++;
95✔
2318
        return;
95✔
2319
    }
2320

2321
    /* received count */
2322
    h->num_recv++;
593✔
2323
    h->num_recv_i++;
593✔
2324

2325
    /* maximum */
2326
    if (!h->max_reply || latency > h->max_reply) {
593✔
2327
        h->max_reply = latency;
427✔
2328
    }
118✔
2329
    if (!h->max_reply_i || latency > h->max_reply_i) {
593✔
2330
        h->max_reply_i = latency;
442✔
2331
    }
123✔
2332

2333
    /* minimum */
2334
    if (!h->min_reply || latency < h->min_reply) {
593✔
2335
        h->min_reply = latency;
361✔
2336
    }
103✔
2337
    if (!h->min_reply_i || latency < h->min_reply_i) {
593✔
2338
        h->min_reply_i = latency;
388✔
2339
    }
112✔
2340

2341
    /* total time (for average) */
2342
    h->total_time += latency;
593✔
2343
    h->total_time_i += latency;
593✔
2344

2345
    /* response time per-packet (count mode) */
2346
    if (!loop_flag && index >= 0) {
593✔
2347
        h->resp_times[index] = latency;
593✔
2348
    }
177✔
2349
}
200✔
2350

2351
/* stats_reset_interval: reset interval statistics
2352
 * h: host entry to update
2353
 */
2354
void stats_reset_interval(HOST_ENTRY *h)
30✔
2355
{
2356
    h->num_sent_i = 0;
30✔
2357
    h->num_recv_i = 0;
30✔
2358
    h->max_reply_i = 0;
30✔
2359
    h->min_reply_i = 0;
30✔
2360
    h->total_time_i = 0;
30✔
2361
}
30✔
2362

2363
int decode_icmp_ipv4(
862✔
2364
    struct sockaddr *response_addr,
2365
    size_t response_addr_len,
2366
    char *reply_buf,
2367
    size_t reply_buf_len,
2368
    unsigned short *id,
2369
    unsigned short *seq,
2370
    int *ip_header_tos,
2371
    int *ip_header_ttl,
2372
    uint32_t *ip_header_otime_ms,
2373
    uint32_t *ip_header_rtime_ms,
2374
    uint32_t *ip_header_ttime_ms)
2375
{
2376
    struct icmp *icp;
2377
    int hlen = 0;
862✔
2378

2379
    if (!using_sock_dgram4) {
862✔
2380
        struct ip *ip = (struct ip *)reply_buf;
854✔
2381
        *ip_header_tos = ip->ip_tos;
854✔
2382
        *ip_header_ttl = ip->ip_ttl;
854✔
2383

2384
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2385
        /* The alpha headers are decidedly broken.
2386
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2387
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2388
         */
2389
        hlen = (ip->ip_vhl & 0x0F) << 2;
2390
#else
2391
        hlen = ip->ip_hl << 2;
854✔
2392
#endif
2393
    }
162✔
2394

2395
    if (reply_buf_len < hlen + ICMP_MINLEN) {
862✔
2396
        /* too short */
2397
        if (verbose_flag) {
×
2398
            char buf[INET6_ADDRSTRLEN];
2399
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2400
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2401
        }
2402
        return -1;
×
2403
    }
2404

2405
    icp = (struct icmp *)(reply_buf + hlen);
862✔
2406

2407
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
862✔
2408
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
368✔
2409
        /* Handle other ICMP packets */
2410
        struct icmp *sent_icmp;
2411
        SEQMAP_VALUE *seqmap_value;
2412
        char addr_ascii[INET6_ADDRSTRLEN];
2413
        HOST_ENTRY *h;
2414

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

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

2423
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
346✔
2424
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
14✔
2425
            sent_icmp->icmp_id != ident4) {
×
2426
            /* not caused by us */
2427
            return -1;
346✔
2428
        }
2429

2430
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2431
        if (seqmap_value == NULL) {
×
2432
            return -1;
×
2433
        }
2434

2435
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2436

2437
        switch (icp->icmp_type) {
×
2438
        case ICMP_UNREACH:
2439
            h = table[seqmap_value->host_nr];
×
2440
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2441
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2442
                    addr_ascii, h->host);
2443
            }
2444
            else {
2445
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2446
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2447
            }
2448

2449
            print_warning("\n");
×
2450
            num_othericmprcvd++;
×
2451
            break;
×
2452

2453
        case ICMP_SOURCEQUENCH:
2454
        case ICMP_REDIRECT:
2455
        case ICMP_TIMXCEED:
2456
        case ICMP_PARAMPROB:
2457
            h = table[seqmap_value->host_nr];
×
2458
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2459
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2460
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2461
            }
2462
            else {
2463
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2464
                    icp->icmp_type, addr_ascii, h->host);
×
2465
            }
2466
            print_warning("\n");
×
2467
            num_othericmprcvd++;
×
2468
            break;
×
2469
        }
2470

2471
        return -1;
×
2472
    }
2473

2474
    *id = icp->icmp_id;
516✔
2475
    *seq = ntohs(icp->icmp_seq);
516✔
2476
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
516✔
2477

2478
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2479
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
14✔
2480
            if (verbose_flag) {
×
2481
                char buf[INET6_ADDRSTRLEN];
2482
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2483
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2484
            }
2485
            return -1;
×
2486
        }
2487

2488
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
14✔
2489
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
14✔
2490
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
14✔
2491
    }
2492

2493
    return hlen;
516✔
2494
}
162✔
2495

2496
#ifdef IPV6
2497
int decode_icmp_ipv6(
176✔
2498
    struct sockaddr *response_addr,
2499
    size_t response_addr_len,
2500
    char *reply_buf,
2501
    size_t reply_buf_len,
2502
    unsigned short *id,
2503
    unsigned short *seq)
2504
{
2505
    struct icmp6_hdr *icp;
2506

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

2516
    icp = (struct icmp6_hdr *)reply_buf;
176✔
2517

2518
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
176✔
2519
        /* Handle other ICMP packets */
2520
        struct icmp6_hdr *sent_icmp;
2521
        SEQMAP_VALUE *seqmap_value;
2522
        char addr_ascii[INET6_ADDRSTRLEN];
2523
        HOST_ENTRY *h;
2524

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

2531
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
87✔
2532

2533
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
87✔
2534
            /* not caused by us */
2535
            return 0;
87✔
2536
        }
2537

2538
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2539
        if (seqmap_value == NULL) {
×
2540
            return 0;
×
2541
        }
2542

2543
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2544

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

2557
            print_warning("\n");
×
2558
            num_othericmprcvd++;
×
2559
            break;
×
2560

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

2579
        return 0;
×
2580
    }
2581

2582
    *id = icp->icmp6_id;
89✔
2583
    *seq = ntohs(icp->icmp6_seq);
89✔
2584

2585
    return 1;
89✔
2586
}
30✔
2587
#endif
2588

2589
int wait_for_reply(int64_t wait_time)
2,187✔
2590
{
2591
    int result;
2592
    static char buffer[RECV_BUFSIZE];
2593
    struct sockaddr_storage response_addr;
2594
    int n, avg;
2595
    HOST_ENTRY *h;
2596
    int64_t this_reply;
2597
    int this_count;
2598
    int64_t recv_time = 0;
2,187✔
2599
    SEQMAP_VALUE *seqmap_value;
2600
    unsigned short id;
2601
    unsigned short seq;
2602
    int ip_header_tos = -1;
2,187✔
2603
    int ip_header_ttl = -1;
2,187✔
2604
    // ICMP Timestamp
2605
    uint32_t ip_header_otime_ms = 0x80000000U;
2,187✔
2606
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,187✔
2607
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,187✔
2608

2609
    /* Receive packet */
2610
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,187✔
2611
        &recv_time, /* reply_timestamp */
2612
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2613
        sizeof(response_addr), /* reply_src_addr_len */
2614
        buffer, /* reply_buf */
2615
        sizeof(buffer) /* reply_buf_len */
2616
    );
2617

2618
    if (result <= 0) {
2,187✔
2619
        return 0;
1,149✔
2620
    }
2621

2622
    update_current_time();
1,038✔
2623
    if (recv_time == 0)
1,038✔
2624
        recv_time = current_time_ns;
192✔
2625

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

2672
    seqmap_value = seqmap_fetch(seq, current_time_ns);
605✔
2673
    if (seqmap_value == NULL) {
605✔
2674
        return 1;
×
2675
    }
2676

2677
    /* find corresponding host_entry */
2678
    n = seqmap_value->host_nr;
605✔
2679
    h = table[n];
605✔
2680
    this_count = seqmap_value->ping_count;
605✔
2681
    this_reply = recv_time - seqmap_value->ping_ts;
605✔
2682

2683
    /* update stats that include invalid replies */
2684
    h->num_recv_total++;
605✔
2685
    num_pingreceived++;
605✔
2686

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

2689
    /* optionally require reply source equal to target address */
2690
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
605✔
2691
        dbg_printf("discarding reply from wrong source address\n");
2692
        return 1;
12✔
2693
    }
2694

2695
    /* discard duplicates */
2696
    if (!loop_flag && h->resp_times[this_count] >= 0) {
593✔
2697
        if (!per_recv_flag) {
×
2698
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2699
                h->host, this_count, result, sprint_tm(this_reply));
2700

2701
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2702
                char buf[INET6_ADDRSTRLEN];
2703
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2704
                fprintf(stderr, " [<- %s]", buf);
×
2705
            }
2706
            fprintf(stderr, "\n");
×
2707
        }
2708
        return 1;
×
2709
    }
2710

2711
    /* discard reply if delay is larger than timeout
2712
     * (see also: github #32) */
2713
    if (this_reply > h->timeout) {
593✔
2714
        return 1;
×
2715
    }
2716

2717
    /* update stats */
2718
    stats_add(h, this_count, 1, this_reply);
593✔
2719
    // TODO: move to stats_add?
2720
    if (!max_reply || this_reply > max_reply)
593✔
2721
        max_reply = this_reply;
356✔
2722
    if (!min_reply || this_reply < min_reply)
593✔
2723
        min_reply = this_reply;
301✔
2724
    sum_replies += this_reply;
593✔
2725
    total_replies++;
593✔
2726

2727
    /* initialize timeout to initial timeout (without backoff) */
2728
    h->timeout = timeout;
593✔
2729

2730
    /* remove timeout event */
2731
    struct event *timeout_event = host_get_timeout_event(h, this_count);
593✔
2732
    if (timeout_event) {
593✔
2733
        ev_remove(&event_queue_timeout, timeout_event);
593✔
2734
    }
177✔
2735

2736
    /* print "is alive" */
2737
    if (h->num_recv == 1) {
593✔
2738
        num_alive++;
312✔
2739
        if (fast_reachable && num_alive >= min_reachable)
312✔
2740
                finish_requested = 1;
3✔
2741

2742
        if (verbose_flag || alive_flag) {
312✔
2743
            printf("%s", h->host);
181✔
2744

2745
            if (verbose_flag)
181✔
2746
                printf(" is alive");
161✔
2747
        }
47✔
2748
    }
86✔
2749

2750
    /* print received ping (unless --quiet) */
2751
    if (per_recv_flag) {
593✔
2752
        if (timestamp_flag) {
151✔
2753
            print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2754
        }
10✔
2755
        avg = h->total_time / h->num_recv;
151✔
2756
        printf("%-*s : [%d], %d bytes, %s ms",
151✔
2757
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2758
        printf(" (%s avg, ", sprint_tm(avg));
151✔
2759

2760
        if (h->num_recv <= h->num_sent) {
151✔
2761
            printf("%d%% loss)",
151✔
2762
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2763
        }
45✔
2764
        else {
2765
            printf("%d%% return)",
×
2766
                (h->num_recv_total * 100) / h->num_sent);
×
2767
        }
2768
    }
45✔
2769

2770
    if (verbose_flag || alive_flag || per_recv_flag) {
593✔
2771

2772
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
332✔
2773
            char buf[INET6_ADDRSTRLEN];
2774
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2775
            fprintf(stderr, " [<- %s]", buf);
6✔
2776
        }
2777

2778
        if (icmp_request_typ == 13) {
332✔
2779
            printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2780
                   alive_flag ? "" : ",",
14✔
2781
                   ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
2782
                   ms_since_midnight_utc(recv_time));
2783
        }
2784

2785
        if(print_tos_flag) {
332✔
2786
            if(ip_header_tos != -1) {
43✔
2787
                printf(" (TOS %d)", ip_header_tos);
39✔
2788
            }
11✔
2789
            else {
2790
                printf(" (TOS unknown)");
4✔
2791
            }
2792
        }
11✔
2793

2794
        if (print_ttl_flag) {
332✔
2795
          if(ip_header_ttl != -1) {
43✔
2796
              printf(" (TTL %d)", ip_header_ttl);
39✔
2797
          }
11✔
2798
          else {
2799
              printf(" (TTL unknown)");
4✔
2800
          }
2801
        }
11✔
2802

2803
        if (elapsed_flag && !per_recv_flag)
332✔
2804
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2805

2806
        printf("\n");
332✔
2807
    }
92✔
2808

2809
    return 1;
593✔
2810
}
530✔
2811

2812
/************************************************************
2813

2814
  Function: add_name
2815

2816
*************************************************************
2817

2818
  Inputs:  char* name
2819

2820
  Description:
2821

2822
  process input name for addition to target list
2823
  name can turn into multiple targets via multiple interfaces (-m)
2824
  or via NIS groups
2825

2826
************************************************************/
2827

2828
void add_name(char *name)
410✔
2829
{
2830
    struct addrinfo *res0, *res, hints;
2831
    int ret_ga;
2832
    char *printname;
2833
    char namebuf[256];
2834
    char addrbuf[256];
2835

2836
    /* getaddrinfo */
2837
    memset(&hints, 0, sizeof(struct addrinfo));
410✔
2838
    hints.ai_flags = AI_UNUSABLE;
410✔
2839
    hints.ai_socktype = SOCK_RAW;
410✔
2840
    hints.ai_family = hints_ai_family;
410✔
2841
    if (hints_ai_family == AF_INET) {
410✔
2842
        hints.ai_protocol = IPPROTO_ICMP;
77✔
2843
    }
21✔
2844
#ifdef IPV6
2845
    else if (hints_ai_family == AF_INET6) {
333✔
2846
        hints.ai_protocol = IPPROTO_ICMPV6;
29✔
2847
    }
1✔
2848
#endif
2849
    else {
2850
        hints.ai_socktype = SOCK_STREAM;
304✔
2851
        hints.ai_protocol = 0;
304✔
2852
    }
2853
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
410✔
2854
    if (ret_ga) {
410✔
2855
        if (!quiet_flag)
17✔
2856
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
17✔
2857
        num_noaddress++;
17✔
2858
        return;
17✔
2859
    }
2860

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

2880
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2881
                printname = namebuf;
3✔
2882
            }
1✔
2883
            else {
2884
                printname = name;
×
2885
            }
2886
        }
1✔
2887
        else {
2888
            printname = name;
390✔
2889
        }
2890

2891
        /* addr_flag: name -> addr lookup requested */
2892
        if (addr_flag) {
393✔
2893
            int ret;
2894
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2895
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2896
            if (ret) {
3✔
2897
                if (!quiet_flag) {
×
2898
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2899
                }
2900
                continue;
×
2901
            }
2902

2903
            if (name_flag || rdns_flag) {
3✔
2904
                char nameaddrbuf[512 + 3];
2905
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
2906
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2907
            }
2908
            else {
2909
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2910
            }
2911
        }
1✔
2912
        else {
2913
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
390✔
2914
        }
2915

2916
        if (!multif_flag) {
393✔
2917
            break;
393✔
2918
        }
2919
    }
2920

2921
    freeaddrinfo(res0);
393✔
2922
}
104✔
2923

2924
/************************************************************
2925

2926
  Function: add_addr
2927

2928
*************************************************************
2929

2930
  Description:
2931

2932
  add single address to list of hosts to be pinged
2933

2934
************************************************************/
2935

2936
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
393✔
2937
{
2938
    HOST_ENTRY *p;
2939
    int n;
2940
    int64_t *i;
2941

2942
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
393✔
2943
    if (!p)
393✔
2944
        crash_and_burn("can't allocate HOST_ENTRY");
×
2945

2946
    p->name = strdup(name);
393✔
2947
    p->host = strdup(host);
393✔
2948
    memcpy(&p->saddr, ipaddr, ipaddr_len);
393✔
2949
    p->saddr_len = ipaddr_len;
393✔
2950
    p->timeout = timeout;
393✔
2951
    p->min_reply = 0;
393✔
2952

2953
    if (netdata_flag) {
393✔
2954
        char *s = p->name;
3✔
2955
        while (*s) {
30✔
2956
            if (!isalnum(*s))
27✔
2957
                *s = '_';
9✔
2958
            s++;
27✔
2959
        }
2960
    }
1✔
2961

2962
    if (strlen(p->host) > max_hostname_len)
393✔
2963
        max_hostname_len = strlen(p->host);
274✔
2964

2965
    /* array for response time results */
2966
    if (!loop_flag) {
393✔
2967
        i = (int64_t *)malloc(trials * sizeof(int64_t));
393✔
2968
        if (!i)
393✔
2969
            crash_and_burn("can't allocate resp_times array");
×
2970

2971
        for (n = 1; n < trials; n++)
1,569✔
2972
            i[n] = RESP_UNUSED;
1,176✔
2973

2974
        p->resp_times = i;
393✔
2975
    }
99✔
2976

2977
    /* allocate event storage */
2978
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
393✔
2979
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
393✔
2980

2981
    /* schedule first ping */
2982
    host_add_ping_event(p, 0, current_time_ns);
393✔
2983

2984
    num_hosts++;
393✔
2985
}
393✔
2986

2987
/************************************************************
2988

2989
  Function: crash_and_burn
2990

2991
*************************************************************
2992

2993
  Inputs:  char* message
2994

2995
  Description:
2996

2997
************************************************************/
2998

2999
void crash_and_burn(char *message)
×
3000
{
3001
    fprintf(stderr, "%s: %s\n", prog, message);
×
3002
    exit(4);
×
3003
}
3004

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

3007
  Function: errno_crash_and_burn
3008

3009
*************************************************************
3010

3011
  Inputs:  char* message
3012

3013
  Description:
3014

3015
************************************************************/
3016

3017
void errno_crash_and_burn(char *message)
8✔
3018
{
3019
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3020
    exit(4);
8✔
3021
}
3022

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

3025
  Function: print_warning
3026

3027
  Description: fprintf(stderr, ...), unless running with -q
3028

3029
*************************************************************/
3030

3031
void print_warning(char *format, ...)
41✔
3032
{
3033
    va_list args;
3034
    if (!quiet_flag) {
41✔
3035
        va_start(args, format);
41✔
3036
        vfprintf(stderr, format, args);
41✔
3037
        va_end(args);
41✔
3038
    }
5✔
3039
}
41✔
3040

3041
/************************************************************
3042

3043
  Function: sprint_tm
3044

3045
*************************************************************
3046

3047
  render nanosecond int64_t value into milliseconds string with three digits of
3048
  precision.
3049

3050
************************************************************/
3051

3052
const char *sprint_tm(int64_t ns)
851✔
3053
{
3054
    static char buf[10];
3055
    double t = (double)ns / 1e6;
851✔
3056

3057
    if (t < 0.0) {
851✔
3058
        /* negative (unexpected) */
3059
        sprintf(buf, "%.2g", t);
×
3060
    }
3061
    else if (t < 1.0) {
851✔
3062
        /* <= 0.99 ms */
3063
        sprintf(buf, "%.3f", t);
851✔
3064
    }
261✔
3065
    else if (t < 10.0) {
×
3066
        /* 1.00 - 9.99 ms */
3067
        sprintf(buf, "%.2f", t);
×
3068
    }
3069
    else if (t < 100.0) {
×
3070
        /* 10.0 - 99.9 ms */
3071
        sprintf(buf, "%.1f", t);
×
3072
    }
3073
    else if (t < 1000000.0) {
×
3074
        /* 100 - 1'000'000 ms */
3075
        sprintf(buf, "%.0f", t);
×
3076
    }
3077
    else {
3078
        sprintf(buf, "%.3e", t);
×
3079
    }
3080

3081
    return (buf);
851✔
3082
}
3083

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

3086
  Function: addr_cmp
3087

3088
*************************************************************/
3089
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
354✔
3090
{
3091
    if (a->sa_family != b->sa_family) {
354✔
3092
        return a->sa_family - b->sa_family;
×
3093
    }
3094
    else {
3095
        if (a->sa_family == AF_INET) {
354✔
3096
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
292✔
3097
        }
3098
        else if (a->sa_family == AF_INET6) {
62✔
3099
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
70✔
3100
                &((struct sockaddr_in6 *)b)->sin6_addr,
62✔
3101
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3102
        }
3103
    }
3104

3105
    return 0;
×
3106
}
94✔
3107

3108
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
708✔
3109
{
3110
    struct event *event = &h->event_storage_ping[index % event_storage_count];
708✔
3111
    event->host = h;
708✔
3112
    event->ping_index = index;
708✔
3113
    event->ev_time = ev_time;
708✔
3114
    ev_enqueue(&event_queue_ping, event);
708✔
3115

3116
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3117
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3118
}
708✔
3119

3120
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
688✔
3121
{
3122
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
688✔
3123
    event->host = h;
688✔
3124
    event->ping_index = index;
688✔
3125
    event->ev_time = ev_time;
688✔
3126
    ev_enqueue(&event_queue_timeout, event);
688✔
3127

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

3132
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
593✔
3133
{
3134
    return &h->event_storage_timeout[index % event_storage_count];
593✔
3135
}
3136

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

3139
  Function: ev_enqueue
3140

3141
  Enqueue an event
3142

3143
  The queue is sorted by event->ev_time, so that queue->first always points to
3144
  the earliest event.
3145

3146
  We start scanning the queue from the tail, because we assume
3147
  that new events mostly get inserted with a event time higher
3148
  than the others.
3149

3150
*************************************************************/
3151
void ev_enqueue(struct event_queue *queue, struct event *event)
1,396✔
3152
{
3153
    struct event *i;
3154
    struct event *i_prev;
3155

3156
    /* Empty list */
3157
    if (queue->last == NULL) {
1,396✔
3158
        event->ev_next = NULL;
1,074✔
3159
        event->ev_prev = NULL;
1,074✔
3160
        queue->first = event;
1,074✔
3161
        queue->last = event;
1,074✔
3162
        return;
1,074✔
3163
    }
3164

3165
    /* Insert on tail? */
3166
    if (event->ev_time - queue->last->ev_time >= 0) {
322✔
3167
        event->ev_next = NULL;
322✔
3168
        event->ev_prev = queue->last;
322✔
3169
        queue->last->ev_next = event;
322✔
3170
        queue->last = event;
322✔
3171
        return;
322✔
3172
    }
3173

3174
    /* Find insertion point */
3175
    i = queue->last;
×
3176
    while (1) {
3177
        i_prev = i->ev_prev;
×
3178
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3179
            event->ev_prev = i_prev;
×
3180
            event->ev_next = i;
×
3181
            i->ev_prev = event;
×
3182
            if (i_prev != NULL) {
×
3183
                i_prev->ev_next = event;
×
3184
            }
3185
            else {
3186
                queue->first = event;
×
3187
            }
3188
            return;
×
3189
        }
3190
        i = i_prev;
×
3191
    }
3192
}
400✔
3193

3194
/************************************************************
3195

3196
  Function: ev_dequeue
3197

3198
*************************************************************/
3199
struct event *ev_dequeue(struct event_queue *queue)
783✔
3200
{
3201
    struct event *dequeued;
3202

3203
    if (queue->first == NULL) {
783✔
3204
        return NULL;
×
3205
    }
3206
    dequeued = queue->first;
783✔
3207
    ev_remove(queue, dequeued);
783✔
3208

3209
    return dequeued;
783✔
3210
}
217✔
3211

3212
/************************************************************
3213

3214
  Function: ev_remove
3215

3216
*************************************************************/
3217
void ev_remove(struct event_queue *queue, struct event *event)
1,376✔
3218
{
3219
    if (queue->first == event) {
1,376✔
3220
        queue->first = event->ev_next;
1,367✔
3221
    }
391✔
3222
    if (queue->last == event) {
1,376✔
3223
        queue->last = event->ev_prev;
1,075✔
3224
    }
300✔
3225
    if (event->ev_prev) {
1,376✔
3226
        event->ev_prev->ev_next = event->ev_next;
9✔
3227
    }
3✔
3228
    if (event->ev_next) {
1,376✔
3229
        event->ev_next->ev_prev = event->ev_prev;
301✔
3230
    }
94✔
3231
    event->ev_prev = NULL;
1,376✔
3232
    event->ev_next = NULL;
1,376✔
3233
}
1,376✔
3234

3235
/************************************************************
3236

3237
  Function: print_human_readable_time from current_time_ns
3238

3239
*************************************************************/
3240
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
30✔
3241
{
3242
    char time_buffer[100];
3243
    time_t current_time_s;
3244
    struct tm *local_time;
3245

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

3269
/************************************************************
3270

3271
  Function: ms_since_midnight_utc
3272

3273
*************************************************************
3274

3275
  Input: int64_t: current UTC time in ns
3276

3277
  Output: uint32_t: current time in ms since midnight UTC
3278

3279
  Description:
3280

3281
  Return ICMP Timestamp value corresponding to the given time value.
3282
  The given time value must be in UTC.
3283

3284
*************************************************************/
3285
static uint32_t ms_since_midnight_utc(int64_t time_val)
14✔
3286
{
3287
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
14✔
3288
}
3289

3290
/************************************************************
3291

3292
  Function: usage
3293

3294
*************************************************************
3295

3296
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3297

3298
  Description:
3299

3300
************************************************************/
3301

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