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

schweikert / fping / 20919661921

12 Jan 2026 12:38PM UTC coverage: 88.037% (-0.1%) from 88.152%
20919661921

Pull #455

github

web-flow
Merge 34bfe3c51 into 656759684
Pull Request #455: ci: coverity scan check COVERITY_SCAN_TOKEN

1619 of 1839 relevant lines covered (88.04%)

354.18 hits per line

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

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

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

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

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

51
#include "seqmap.h"
52

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

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

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

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

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

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

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

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

85
#include <sys/select.h>
86

87
/*** compatibility ***/
88

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

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

107
/*** externals ***/
108

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

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

119
/*** Constants ***/
120

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

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

137
/*** Ping packet defines ***/
138

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

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

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

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

153
#ifdef FPING_SAFE_LIMITS
154
#define MIN_INTERVAL_MS 1 /* in millisec */
155
#define MIN_PERHOST_INTERVAL_MS 10 /* in millisec */
156
#else
157
#define MIN_INTERVAL_MS 0
158
/* Set a very low limit for the per-host interval, even if safe limits are
159
 * disabled, so that the memory allocation of the event storage is not
160
 * unreasonably high. 0.001 ms would mean in theory at least 592 mbps of data
161
 * sent to a single host, which probably doesn't make sense in any scenario. */
162
#define MIN_PERHOST_INTERVAL_MS 0.001
163
#endif
164

165
/* response time array flags */
166
#define RESP_WAITING -1
167
#define RESP_UNUSED -2
168
#define RESP_ERROR -3
169
#define RESP_TIMEOUT -4
170

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

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

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

224
#define ICMP_UNREACH_MAXTYPE 15
225

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

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

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

266
typedef struct ip_header_result {
267
    int tos;
268
    int ttl;
269
    uint32_t otime_ms;
270
    uint32_t rtime_ms;
271
    uint32_t ttime_ms;
272
} IP_HEADER_RESULT;
273

274
IP_HEADER_RESULT default_ip_header_result() {
3,120✔
275
    return (IP_HEADER_RESULT){-1, -1, 0x80000000U, 0x80000000U, 0x80000000U};
3,120✔
276
}
277

278
struct event;
279
typedef struct host_entry {
280
    int i; /* index into array */
281
    char *name; /* name as given by user */
282
    char *host; /* text description of host */
283
    struct sockaddr_storage saddr; /* internet address */
284
    socklen_t saddr_len;
285
    int64_t timeout; /* time to wait for response */
286
    int64_t last_send_time; /* time of last packet sent */
287
    int num_sent; /* number of ping packets sent (for statistics) */
288
    int num_recv; /* number of pings received (duplicates ignored) */
289
    int num_recv_total; /* number of pings received, including duplicates */
290
    int64_t max_reply; /* longest response time */
291
    int64_t min_reply; /* shortest response time */
292
    int64_t total_time; /* sum of response times */
293
    /* _i -> splits (reset on every report interval) */
294
    int num_sent_i; /* number of ping packets sent */
295
    int num_recv_i; /* number of pings received */
296
    int64_t max_reply_i; /* longest response time */
297
    int64_t min_reply_i; /* shortest response time */
298
    int64_t total_time_i; /* sum of response times */
299
    int64_t *resp_times; /* individual response times */
300

301
    /* to avoid allocating two struct events each time that we send a ping, we
302
     * preallocate here two struct events for each ping that we might send for
303
     * this host. */
304
    struct event *event_storage_ping;
305
    struct event *event_storage_timeout;
306
} HOST_ENTRY;
307

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

310
/* basic algorithm to ensure that we have correct data at all times:
311
 *
312
 * 1. when a ping is sent:
313
 *    - two events get added into event_queue:
314
 *      - t+PERIOD: ping event
315
 *      - t+TIMEOUT: timeout event
316
 *
317
 * 2. when a ping is received:
318
 *    - record statistics (increase num_sent and num_received)
319
 *    - remove timeout event (we store the event in seqmap, so that we can retrieve it when the response is received)
320
 *
321
 * 3. when a timeout happens:
322
 *    - record statistics (increase num_sent only)
323
 */
324

325
#define EV_TYPE_PING 1
326
#define EV_TYPE_TIMEOUT 2
327

328
struct event {
329
    struct event *ev_prev;
330
    struct event *ev_next;
331
    int64_t ev_time;
332
    struct host_entry *host;
333
    int ping_index;
334
};
335

336
struct event_queue {
337
    struct event *first;
338
    struct event *last;
339
};
340

341
/*** globals ***/
342

343
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
344

345
/* we keep two separate queues: a ping queue, for when the next ping should be
346
 * sent, and a timeout queue. the reason for having two separate queues is that
347
 * the ping period and the timeout value are different, so if we put them in
348
 * the same event queue, we would need to scan many more entries when inserting
349
 * into the sorted list.
350
 */
351
struct event_queue event_queue_ping;
352
struct event_queue event_queue_timeout;
353

354
char *prog;
355
int ident4 = 0; /* our icmp identity field */
356
int ident6 = 0;
357
const int sock_opt_on = 1; /* to activate a socket option */
358
int socket4 = -1;
359
int socktype4 = -1;
360
int using_sock_dgram4 = 0;
361
#ifndef IPV6
362
int hints_ai_family = AF_INET;
363
#else
364
int socket6 = -1;
365
int socktype6 = -1;
366
int hints_ai_family = AF_UNSPEC;
367
#endif
368

369
volatile sig_atomic_t status_snapshot = 0;
370
volatile sig_atomic_t finish_requested = 0;
371

372
unsigned int debugging = 0;
373

374
/* all time-related values are int64_t nanoseconds */
375
unsigned int retry = DEFAULT_RETRY;
376
int64_t timeout = (int64_t)DEFAULT_TIMEOUT * 1000000;
377
int64_t seqmap_timeout = (int64_t)DEFAULT_SEQMAP_TIMEOUT * 1000000;
378
int64_t interval = (int64_t)DEFAULT_INTERVAL * 1000000;
379
int64_t perhost_interval = (int64_t)DEFAULT_PERHOST_INTERVAL * 1000000;
380
float backoff = DEFAULT_BACKOFF_FACTOR;
381
unsigned int ping_data_size = DEFAULT_PING_DATA_SIZE;
382
unsigned int count = 1, min_reachable = 0;
383
unsigned int trials;
384
int64_t report_interval = 0;
385
unsigned int ttl = 0;
386
int src_addr_set = 0;
387
struct in_addr src_addr;
388
#ifdef IPV6
389
int src_addr6_set = 0;
390
struct in6_addr src_addr6;
391
#endif
392

393
/* global stats */
394
int64_t max_reply = 0;
395
int64_t min_reply = 0;
396
int64_t total_replies = 0;
397
int64_t sum_replies = 0;
398
int max_hostname_len = 0;
399
int num_hosts = 0; /* total number of hosts */
400
int num_alive = 0, /* total number alive */
401
    num_unreachable = 0, /* total number unreachable */
402
    num_noaddress = 0; /* total number of addresses not found */
403
int num_timeout = 0, /* number of times select timed out */
404
    num_pingsent = 0, /* total pings sent */
405
    num_pingreceived = 0, /* total pings received */
406
    num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
407

408
struct timespec current_time; /* current time (pseudo) */
409
int64_t current_time_ns;
410
int64_t start_time;
411
int64_t end_time;
412
int64_t last_send_time; /* time last ping was sent */
413
int64_t next_report_time; /* time next -Q report is expected */
414

415
/* switches */
416
int generate_flag = 0; /* flag for IP list generation */
417
int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
418
int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag, json_flag;
419
int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
420
int multif_flag, timeout_flag, fast_reachable;
421
int outage_flag = 0;
422
int timestamp_flag = 0;
423
int timestamp_format_flag = 0;
424
int random_data_flag = 0;
425
int cumulative_stats_flag = 0;
426
int check_source_flag = 0;
427
int icmp_request_typ = 0;
428
int print_tos_flag = 0;
429
int print_ttl_flag = 0;
430
int size_flag = 0;
431
#if defined(DEBUG) || defined(_DEBUG)
432
int randomly_lose_flag, trace_flag, print_per_system_flag;
433
int lose_factor;
434
#endif /* DEBUG || _DEBUG */
435

436
unsigned int fwmark = 0;
437

438
char *filename = NULL; /* file containing hosts to ping */
439

440
/*** forward declarations ***/
441

442
void add_name(char *name);
443
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len);
444
char *na_cat(char *name, struct in_addr ipaddr);
445
void crash_and_burn(char *message);
446
void errno_crash_and_burn(char *message);
447
char *get_host_by_address(struct in_addr in);
448
int send_ping(HOST_ENTRY *h, int index);
449
void usage(int);
450
int wait_for_reply(int64_t);
451
void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg);
452
void print_timeout(HOST_ENTRY *h, int ping_index);
453
void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply);
454
void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply);
455
void print_per_system_stats(void);
456
void print_per_system_stats_json(void);
457
void print_per_system_splits(void);
458
void print_per_system_splits_json(void);
459
void stats_reset_interval(HOST_ENTRY *h);
460
void print_netdata(void);
461
void print_global_stats(void);
462
void print_global_stats_json(void);
463
void main_loop();
464
void signal_handler(int);
465
void finish();
466
const char *sprint_tm(int64_t t);
467
void ev_enqueue(struct event_queue *queue, struct event *event);
468
struct event *ev_dequeue(struct event_queue *queue);
469
void ev_remove(struct event_queue *queue, struct event *event);
470
void add_cidr(char *);
471
void add_cidr_ipv4(unsigned long, unsigned long);
472
void add_range(char *, char *);
473
void add_addr_range_ipv4(unsigned long, unsigned long);
474
#ifdef IPV6
475
uint64_t be_octets_to_uint64(uint8_t*);
476
void uint64_to_be_octets(uint64_t, uint8_t*);
477
void add_cidr_ipv6(uint64_t, uint64_t, unsigned long, const char *);
478
void add_addr_range_ipv6(uint64_t, uint64_t, uint64_t, uint64_t, const char *);
479
#endif
480
void print_warning(char *fmt, ...);
481
int addr_cmp(struct sockaddr *a, struct sockaddr *b);
482
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
483
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
484
struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
485
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
486
void update_current_time();
487
void print_timestamp_format(int64_t current_time_ns, int timestamp_format);
488
static uint32_t ms_since_midnight_utc(int64_t time_val);
489

490
/************************************************************
491

492
  Function: p_setsockopt
493

494
*************************************************************
495

496
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
497

498
  Description:
499

500
  Elevates privileges to p_uid when required, calls
501
  setsockopt, and drops privileges back.
502

503
************************************************************/
504

505
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
22✔
506
    const void *optval, socklen_t optlen)
507
{
508
    const uid_t saved_uid = geteuid();
22✔
509
    int res;
510

511
    if (p_uid != saved_uid && seteuid(p_uid)) {
22✔
512
        perror("cannot elevate privileges for setsockopt");
×
513
    }
514

515
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
516

517
    if (p_uid != saved_uid && seteuid(saved_uid)) {
22✔
518
        perror("fatal error: could not drop privileges after setsockopt");
×
519
        /* continuing would be a security hole */
520
        exit(4);
×
521
    }
522

523
    return res;
22✔
524
}
525

526
unsigned long strtoul_strict(const char *arg, int base)
454✔
527
{
528
    char *endptr;
529
    unsigned long val;
530

531
    while (isspace(*arg))
480✔
532
        arg++;
26✔
533

534
    if (arg[0] == '-')
454✔
535
        usage(1);
52✔
536

537
    errno = 0;
418✔
538
    val = strtoul(arg, &endptr, base);
418✔
539
    if (errno != 0 || arg == endptr || *endptr != '\0')
418✔
540
        usage(1);
100✔
541

542
    return val;
344✔
543
}
544

545
double strtod_strict(const char *arg)
290✔
546
{
547
    char *endptr;
548
    double val;
549

550
    errno = 0;
290✔
551
    val = strtod(arg, &endptr);
290✔
552
    if (errno != 0 || arg == endptr || *endptr != '\0' || val < 0)
290✔
553
        usage(1);
102✔
554

555
    return val;
222✔
556
}
557

558
/************************************************************
559

560
  Function: main
561

562
*************************************************************
563

564
  Inputs:  int argc, char** argv
565

566
  Description:
567

568
  Main program entry point
569

570
************************************************************/
571

572
int main(int argc, char **argv)
968✔
573
{
574
/* Debug: CPU Performance */
575
#if defined(DEBUG) || defined(_DEBUG)
576
    clock_t perf_cpu_start, perf_cpu_end;
577
    double perf_cpu_time_used;
578
    perf_cpu_start = clock();
579
#endif /* DEBUG || _DEBUG */
580

581
    int c;
582
    char *endptr;
583
    const uid_t suid = geteuid();
968✔
584
    int tos = 0;
968✔
585
    struct optparse optparse_state;
586
#ifdef USE_SIGACTION
587
    struct sigaction act;
588
#endif
589

590
    /* pre-parse -h/--help, so that we also can output help information
591
     * without trying to open the socket, which might fail */
592
    prog = argv[0];
968✔
593
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
968✔
594
        usage(0);
3✔
595
    }
1✔
596

597
    socket4 = open_ping_socket_ipv4(&socktype4);
966✔
598
#ifdef __linux__
599
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
600
     * structure is missing in the message.
601
     */
602
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
638✔
603
#endif
604

605
#ifdef IPV6
606
    socket6 = open_ping_socket_ipv6(&socktype6);
966✔
607
    /* if called (sym-linked) via 'fping6', imply '-6'
608
     * for backward compatibility */
609
    if (strstr(prog, "fping6")) {
966✔
610
        hints_ai_family = AF_INET6;
2✔
611
    }
612
#endif
613

614
    memset(&src_addr, 0, sizeof(src_addr));
966✔
615
#ifdef IPV6
616
    memset(&src_addr6, 0, sizeof(src_addr6));
966✔
617
#endif
618

619
    if (!suid && suid != getuid()) {
966✔
620
        /* *temporarily* drop privileges */
621
        if (seteuid(getuid()) == -1)
865✔
622
            perror("cannot setuid");
×
623
    }
239✔
624

625
    optparse_init(&optparse_state, argv);
966✔
626
    ident4 = ident6 = htons(getpid() & 0xFFFF);
966✔
627
    verbose_flag = 1;
966✔
628
    backoff_flag = 1;
966✔
629
    opterr = 1;
966✔
630

631
    /* get command line options */
632

633
    struct optparse_long longopts[] = {
966✔
634
        { "ipv4", '4', OPTPARSE_NONE },
635
        { "ipv6", '6', OPTPARSE_NONE },
636
        { "alive", 'a', OPTPARSE_NONE },
637
        { "addr", 'A', OPTPARSE_NONE },
638
        { "size", 'b', OPTPARSE_REQUIRED },
639
        { "backoff", 'B', OPTPARSE_REQUIRED },
640
        { "count", 'c', OPTPARSE_REQUIRED },
641
        { "vcount", 'C', OPTPARSE_REQUIRED },
642
        { "rdns", 'd', OPTPARSE_NONE },
643
        { "timestamp", 'D', OPTPARSE_NONE },
644
        { "timestamp-format", '0', OPTPARSE_REQUIRED },
645
        { "elapsed", 'e', OPTPARSE_NONE },
646
        { "file", 'f', OPTPARSE_REQUIRED },
647
        { "generate", 'g', OPTPARSE_NONE },
648
        { "help", 'h', OPTPARSE_NONE },
649
        { "ttl", 'H', OPTPARSE_REQUIRED },
650
        { "interval", 'i', OPTPARSE_REQUIRED },
651
        { "iface", 'I', OPTPARSE_REQUIRED },
652
        { "json", 'J', OPTPARSE_NONE },
653
        { "icmp-timestamp", '0', OPTPARSE_NONE },
654
#ifdef SO_MARK
655
        { "fwmark", 'k', OPTPARSE_REQUIRED },
656
#endif
657
        { "loop", 'l', OPTPARSE_NONE },
658
        { "all", 'm', OPTPARSE_NONE },
659
        { "dontfrag", 'M', OPTPARSE_NONE },
660
        { "name", 'n', OPTPARSE_NONE },
661
        { "netdata", 'N', OPTPARSE_NONE },
662
        { "outage", 'o', OPTPARSE_NONE },
663
        { "tos", 'O', OPTPARSE_REQUIRED },
664
        { "period", 'p', OPTPARSE_REQUIRED },
665
        { "quiet", 'q', OPTPARSE_NONE },
666
        { "squiet", 'Q', OPTPARSE_REQUIRED },
667
        { "retry", 'r', OPTPARSE_REQUIRED },
668
        { "random", 'R', OPTPARSE_NONE },
669
        { "stats", 's', OPTPARSE_NONE },
670
        { "src", 'S', OPTPARSE_REQUIRED },
671
        { "timeout", 't', OPTPARSE_REQUIRED },
672
        { NULL, 'T', OPTPARSE_REQUIRED },
673
        { "unreach", 'u', OPTPARSE_NONE },
674
        { "version", 'v', OPTPARSE_NONE },
675
        { "reachable", 'x', OPTPARSE_REQUIRED },
676
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
677
        { "check-source", '0', OPTPARSE_NONE },
678
        { "print-tos", '0', OPTPARSE_NONE },
679
        { "print-ttl", '0', OPTPARSE_NONE },
680
        { "seqmap-timeout", '0', OPTPARSE_REQUIRED },
681
#if defined(DEBUG) || defined(_DEBUG)
682
        { NULL, 'z', OPTPARSE_REQUIRED },
683
#endif
684
        { 0, 0, 0 }
685
    };
686

687
    double opt_val_double;
688
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
2,193✔
689
        switch (c) {
1,448✔
690
        case '0':
116✔
691
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
153✔
692
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
24✔
693
                  timestamp_format_flag = 1;
6✔
694
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
20✔
695
                  timestamp_format_flag = 2;
6✔
696
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
14✔
697
                  timestamp_format_flag = 3;
6✔
698
                }else{
2✔
699
                  usage(1);
6✔
700
                }
701
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
137✔
702
                check_source_flag = 1;
9✔
703
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
121✔
704
#ifdef IPV6
705
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
29✔
706
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
707
                    exit(1);
2✔
708
                }
709
                hints_ai_family = AF_INET;
27✔
710
#endif
711
                icmp_request_typ = 13;
27✔
712
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
27✔
713
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
94✔
714
                print_tos_flag = 1;
35✔
715
#if defined(IP_RECVTOS)
716
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
35✔
717
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTOS, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
718
                        perror("setsockopt IP_RECVTOS");
×
719
                    }
720
                }
721
#endif
722
#if defined(IPV6) && defined(IPV6_RECVTCLASS)
723
                if (socket6 >= 0) {
35✔
724
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVTCLASS, &sock_opt_on, sizeof(sock_opt_on))) {
35✔
725
                        perror("setsockopt IPV6_RECVTCLASS");
×
726
                    }
727
                }
9✔
728
#endif
729
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
65✔
730
                print_ttl_flag = 1;
29✔
731
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
29✔
732
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTTL, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
733
                        perror("setsockopt IP_RECVTTL");
×
734
                    }
735
                }
736
#if defined(IPV6) && defined(IPV6_RECVHOPLIMIT)
737
                if (socket6 >= 0) {
29✔
738
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &sock_opt_on, sizeof(sock_opt_on))) {
29✔
739
                        perror("setsockopt IPV6_RECVHOPLIMIT");
×
740
                    }
741
                }
7✔
742
#endif
743
            } else if (strstr(optparse_state.optlongname, "seqmap-timeout") != NULL) {
34✔
744
                seqmap_timeout = strtod_strict(optparse_state.optarg) * 1000000;
27✔
745
            } else {
9✔
746
                usage(1);
×
747
            }
748
            break;
133✔
749
        case '4':
32✔
750
#ifdef IPV6
751
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
752
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
753
                exit(1);
3✔
754
            }
755
            hints_ai_family = AF_INET;
40✔
756
#endif
757
            break;
40✔
758
        case '6':
28✔
759
#ifdef IPV6
760
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
31✔
761
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
762
                exit(1);
5✔
763
            }
764
            hints_ai_family = AF_INET6;
26✔
765
#else
766
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
767
            exit(1);
768
#endif
769
            break;
26✔
770
        case 'M':
2✔
771
#ifdef IP_MTU_DISCOVER
772
            if (socket4 >= 0) {
2✔
773
                int val = IP_PMTUDISC_DO;
2✔
774
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
775
                    perror("setsockopt IP_MTU_DISCOVER");
776
                }
777
            }
778
#ifdef IPV6
779
            if (socket6 >= 0) {
2✔
780
                int val = IPV6_PMTUDISC_DO;
2✔
781
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
782
                    perror("setsockopt IPV6_MTU_DISCOVER");
783
                }
784
            }
785
#endif
786
#else
787
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
788
            exit(1);
789
#endif
790
            break;
2✔
791

792
        case 't':
56✔
793
            timeout = strtod_strict(optparse_state.optarg) * 1000000;
66✔
794
            timeout_flag = 1;
52✔
795
            break;
52✔
796

797
        case 'r':
44✔
798
            retry = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
51✔
799
            break;
41✔
800

801
        case 'i':
38✔
802
            interval = strtod_strict(optparse_state.optarg) * 1000000;
48✔
803
            break;
34✔
804

805
        case 'p':
90✔
806
            perhost_interval = strtod_strict(optparse_state.optarg) * 1000000;
123✔
807
            break;
109✔
808

809
        case 'c':
102✔
810
            count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
139✔
811
            if (!count)
129✔
812
                usage(1);
3✔
813

814
            count_flag = 1;
127✔
815
            break;
127✔
816

817
        case 'C':
42✔
818
            count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
60✔
819
            if (!count)
50✔
820
                usage(1);
3✔
821

822
            count_flag = 1;
48✔
823
            report_all_rtts_flag = 1;
48✔
824
            break;
48✔
825

826
        case 'b':
20✔
827
            ping_data_size = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
30✔
828
            size_flag = 1;
20✔
829
            break;
20✔
830

831
        case 'h':
2✔
832
            usage(0);
3✔
833
            break;
1✔
834

835
        case 'q':
26✔
836
            verbose_flag = 0;
38✔
837
            quiet_flag = 1;
38✔
838
            break;
38✔
839

840
        case 'Q':
34✔
841
            verbose_flag = 0;
43✔
842
            quiet_flag = 1;
43✔
843
            errno = 0;
43✔
844
            opt_val_double = strtod(optparse_state.optarg, &endptr);
43✔
845
            if (errno != 0 || optparse_state.optarg == endptr || (*endptr != '\0' && *endptr != ','))
43✔
846
                usage(1);
12✔
847
            if (opt_val_double < 0) {
35✔
848
                usage(1);
9✔
849
            }
3✔
850
            report_interval = opt_val_double * 1e9;
29✔
851

852
            /* recognize keyword(s) after number, ignore everything else */
853
            {
854
                char *comma = strchr(optparse_state.optarg, ',');
29✔
855
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
29✔
856
                    cumulative_stats_flag = 1;
4✔
857
                }
858
            }
859

860
            break;
29✔
861

862
        case 'e':
6✔
863
            elapsed_flag = 1;
8✔
864
            break;
8✔
865

866
        case 'm':
867
            multif_flag = 1;
×
868
            break;
×
869

870
        case 'N':
2✔
871
            netdata_flag = 1;
3✔
872
            break;
3✔
873

874
        case 'n':
4✔
875
            name_flag = 1;
6✔
876
            if (rdns_flag) {
6✔
877
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
878
                exit(1);
3✔
879
            }
880
            break;
3✔
881

882
        case 'd':
6✔
883
            rdns_flag = 1;
9✔
884
            if (name_flag) {
9✔
885
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
886
                exit(1);
3✔
887
            }
888
            break;
6✔
889

890
        case 'A':
2✔
891
            addr_flag = 1;
3✔
892
            break;
3✔
893

894
        case 'B':
18✔
895
            backoff = strtod_strict(optparse_state.optarg);
26✔
896
            break;
14✔
897

898
        case 's':
8✔
899
            stats_flag = 1;
12✔
900
            break;
12✔
901

902
        case 'D':
22✔
903
            timestamp_flag = 1;
33✔
904
            break;
33✔
905

906
        case 'R':
4✔
907
            random_data_flag = 1;
5✔
908
            break;
5✔
909

910
        case 'l':
12✔
911
            loop_flag = 1;
17✔
912
            backoff_flag = 0;
17✔
913
            break;
17✔
914

915
        case 'u':
8✔
916
            unreachable_flag = 1;
12✔
917
            break;
12✔
918

919
        case 'a':
18✔
920
            alive_flag = 1;
26✔
921
            break;
26✔
922

923
        case 'H':
16✔
924
            ttl = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
925
            if (!ttl)
14✔
926
                usage(1);
3✔
927
            break;
12✔
928

929
#if defined(DEBUG) || defined(_DEBUG)
930
        case 'z':
931
            debugging = (unsigned int)strtoul_strict(optparse_state.optarg, 0);
932
            break;
933
#endif /* DEBUG || _DEBUG */
934

935
        case 'v':
4✔
936
            printf("%s: Version %s\n", prog, VERSION);
6✔
937
            exit(0);
6✔
938

939
        case 'x':
16✔
940
            min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
941
            if (!min_reachable)
14✔
942
                usage(1);
3✔
943
            break;
12✔
944

945
        case 'X':
14✔
946
            min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
21✔
947
            if (!min_reachable)
11✔
948
                usage(1);
3✔
949
            fast_reachable = 1;
9✔
950
            break;
9✔
951

952
        case 'f':
18✔
953
            filename = optparse_state.optarg;
27✔
954
            break;
27✔
955
#ifdef SO_MARK
956
        case 'k':
18✔
957
            fwmark = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
18✔
958
            if (!fwmark)
8✔
959
                usage(1);
2✔
960

961
            if (socket4 >= 0)
6✔
962
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
963
                    perror("fwmark ipv4");
4✔
964

965
#ifdef IPV6
966
            if (socket6 >= 0)
6✔
967
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
968
                    perror("fwmark ipv6");
4✔
969
#endif
970

971
            break;
6✔
972
#endif
973

974
        case 'g':
106✔
975
            /* use IP list generation */
976
            /* mutually exclusive with using file input or command line targets */
977
            generate_flag = 1;
129✔
978
            break;
129✔
979

980
        case 'S':
10✔
981
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
982
                src_addr_set = 1;
6✔
983
                break;
6✔
984
            }
985
#ifdef IPV6
986
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
987
                src_addr6_set = 1;
4✔
988
                break;
4✔
989
            }
990
#endif
991
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
992
            exit(1);
3✔
993

994
        case 'I':
6✔
995
#ifdef SO_BINDTODEVICE
996
            if (socket4 >= 0) {
6✔
997
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
998
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
999
                    exit(1);
2✔
1000
                }
1001
            }
1002
#ifdef IPV6
1003
            if (socket6 >= 0) {
4✔
1004
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
1005
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
×
1006
                    exit(1);
×
1007
                }
1008
            }
1009
#endif
1010
#else
1011
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
1012
            exit(3);
1013
            ;
1014
#endif
1015
            break;
4✔
1016

1017
        case 'J':
42✔
1018
            json_flag = 1;
62✔
1019
            break;
62✔
1020

1021
        case 'T':
2✔
1022
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
1023
            break;
3✔
1024

1025
        case 'O':
16✔
1026
            {
1027
                unsigned long val = strtoul_strict(optparse_state.optarg, 0);
24✔
1028
                if (val > 255)
16✔
1029
                    usage(1);
×
1030
                tos = (int)val;
16✔
1031
            }
1032
            if (socket4 >= 0) {
16✔
1033
                if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
12✔
1034
                    perror("setting type of service octet IP_TOS");
×
1035
                }
1036
            }
4✔
1037
#if defined(IPV6) && defined(IPV6_TCLASS)
1038
            if (socket6 >= 0) {
16✔
1039
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
12✔
1040
                    perror("setting type of service octet IPV6_TCLASS");
×
1041
                }
1042
            }
4✔
1043
#endif
1044
            break;
16✔
1045

1046
        case 'o':
6✔
1047
            outage_flag = 1;
7✔
1048
            break;
7✔
1049

1050
        case '?':
4✔
1051
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
1052
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
1053
            exit(1);
6✔
1054
            break;
1055
        }
1056
    }
1057

1058
    /* permanently drop privileges */
1059
    if (suid != getuid() && setuid(getuid())) {
569✔
1060
        perror("fatal: failed to permanently drop privileges");
×
1061
        /* continuing would be a security hole */
1062
        exit(4);
×
1063
    }
1064

1065
    /* validate various option settings */
1066

1067
#ifndef IPV6
1068
    if (socket4 < 0) {
1069
        crash_and_burn("can't create socket (must run as root?)");
1070
    }
1071
#else
1072
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
569✔
1073
        crash_and_burn("can't create socket (must run as root?)");
×
1074
    }
1075
#endif
1076

1077
    if (ttl > 255) {
567✔
1078
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
1079
        exit(1);
3✔
1080
    }
1081

1082
    if (unreachable_flag && alive_flag) {
564✔
1083
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
1084
        exit(1);
3✔
1085
    }
1086

1087
    if (count_flag && loop_flag) {
561✔
1088
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
1089
        exit(1);
3✔
1090
    }
1091

1092
    if (json_flag && !count_flag && !loop_flag) {
558✔
1093
        fprintf(stderr, "%s: option -J, --json requires -c, -C, or -l\n", prog);
3✔
1094
        exit(1);
3✔
1095
    }
1096

1097
    if (interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
555✔
1098
        fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
3✔
1099
        exit(1);
3✔
1100
    }
1101

1102
    if (perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
552✔
1103
        fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
3✔
1104
        exit(1);
3✔
1105
    }
1106

1107
    if (ping_data_size > MAX_PING_DATA) {
549✔
1108
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1109
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1110
        exit(1);
3✔
1111
    }
1112

1113
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
546✔
1114
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1115
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1116
        exit(1);
6✔
1117
    }
1118

1119
    if (icmp_request_typ == 13 && size_flag != 0) {
540✔
1120
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1121
        exit(1);
9✔
1122
    }
1123

1124
    if (count_flag) {
531✔
1125
        if (verbose_flag)
157✔
1126
            per_recv_flag = 1;
99✔
1127

1128
        alive_flag = unreachable_flag = verbose_flag = 0;
157✔
1129
    }
41✔
1130

1131
    if (loop_flag) {
531✔
1132
        if (!report_interval)
14✔
1133
            per_recv_flag = 1;
12✔
1134

1135
        alive_flag = unreachable_flag = verbose_flag = 0;
14✔
1136
    }
4✔
1137

1138
    if (alive_flag || unreachable_flag || min_reachable)
531✔
1139
        verbose_flag = 0;
37✔
1140

1141
    trials = (count > retry + 1) ? count : retry + 1;
547✔
1142

1143
    /* auto-tune default timeout for count/loop modes
1144
     * see also github #32 */
1145
    if (loop_flag || count_flag) {
547✔
1146
        if (!timeout_flag) {
171✔
1147
            timeout = perhost_interval;
159✔
1148
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
159✔
1149
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1150
            }
1151
        }
43✔
1152
    }
45✔
1153

1154
#if defined(DEBUG) || defined(_DEBUG)
1155
    if (debugging & DBG_TRACE)
1156
        trace_flag = 1;
1157

1158
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1159
        randomly_lose_flag = 1;
1160
        lose_factor = 1; /* ie, 1/4 */
1161
    }
1162

1163
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1164
        randomly_lose_flag = 1;
1165
        lose_factor = 5; /* ie, 3/4 */
1166
    }
1167

1168
    if (debugging & DBG_PRINT_PER_SYSTEM)
1169
        print_per_system_flag = 1;
1170

1171
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1172
        report_all_rtts_flag = 1;
1173

1174
    if (trace_flag) {
1175
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1176
            prog, count, retry, interval / 1e6);
1177
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1178
            perhost_interval / 1e6, timeout / 1e6);
1179
        fprintf(stderr, "  seqmap_timeout: %.0f\n", seqmap_timeout / 1e6);
1180
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1181
            ping_data_size, trials);
1182

1183
        if (verbose_flag)
1184
            fprintf(stderr, "  verbose_flag set\n");
1185
        if (multif_flag)
1186
            fprintf(stderr, "  multif_flag set\n");
1187
        if (name_flag)
1188
            fprintf(stderr, "  name_flag set\n");
1189
        if (addr_flag)
1190
            fprintf(stderr, "  addr_flag set\n");
1191
        if (stats_flag)
1192
            fprintf(stderr, "  stats_flag set\n");
1193
        if (unreachable_flag)
1194
            fprintf(stderr, "  unreachable_flag set\n");
1195
        if (alive_flag)
1196
            fprintf(stderr, "  alive_flag set\n");
1197
        if (elapsed_flag)
1198
            fprintf(stderr, "  elapsed_flag set\n");
1199
        if (version_flag)
1200
            fprintf(stderr, "  version_flag set\n");
1201
        if (count_flag)
1202
            fprintf(stderr, "  count_flag set\n");
1203
        if (loop_flag)
1204
            fprintf(stderr, "  loop_flag set\n");
1205
        if (backoff_flag)
1206
            fprintf(stderr, "  backoff_flag set\n");
1207
        if (per_recv_flag)
1208
            fprintf(stderr, "  per_recv_flag set\n");
1209
        if (report_all_rtts_flag)
1210
            fprintf(stderr, "  report_all_rtts_flag set\n");
1211
        if (randomly_lose_flag)
1212
            fprintf(stderr, "  randomly_lose_flag set\n");
1213
        if (print_per_system_flag)
1214
            fprintf(stderr, "  print_per_system_flag set\n");
1215
        if (outage_flag)
1216
            fprintf(stderr, "  outage_flag set\n");
1217
        if (netdata_flag)
1218
            fprintf(stderr, "  netdata_flag set\n");
1219
        if (json_flag)
1220
            fprintf(stderr, "  json_flag set\n");
1221
    }
1222
#endif /* DEBUG || _DEBUG */
1223

1224
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1225
    if (ttl > 0) {
547✔
1226
        if (socket4 >= 0) {
3✔
1227
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1228
                perror("setting time to live");
×
1229
            }
1230
        }
1✔
1231
#ifdef IPV6
1232
        if (socket6 >= 0) {
3✔
1233
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1234
                perror("setting time to live");
×
1235
            }
1236
        }
1✔
1237
#endif
1238
    }
1✔
1239

1240
#if HAVE_SO_TIMESTAMPNS
1241
    {
1242
        int opt = 1;
402✔
1243
        if (socket4 >= 0) {
402✔
1244
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
402✔
1245
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1246
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1247
                }
1248
            }
1249
        }
1250
#ifdef IPV6
1251
        if (socket6 >= 0) {
402✔
1252
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
402✔
1253
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1254
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1255
                }
1256
            }
1257
        }
1258
#endif
1259
    }
1260
#endif
1261

1262
    update_current_time();
547✔
1263
    start_time = current_time_ns;
547✔
1264

1265
    /* handle host names supplied on command line or in a file */
1266
    /* if the generate_flag is on, then generate the IP list */
1267

1268
    argv = &argv[optparse_state.optind];
547✔
1269
    argc -= optparse_state.optind;
547✔
1270

1271
    /* calculate how many ping can be in-flight per host */
1272
    if (count_flag) {
547✔
1273
        event_storage_count = count;
172✔
1274
    }
56✔
1275
    else if (loop_flag) {
375✔
1276
        if (perhost_interval > timeout) {
14✔
1277
            event_storage_count = 1;
×
1278
        }
1279
        else {
1280
            event_storage_count = 1 + timeout / perhost_interval;
14✔
1281
        }
1282
    }
4✔
1283
    else {
1284
        event_storage_count = 1;
361✔
1285
    }
1286

1287
    /* file and generate are mutually exclusive */
1288
    /* file and command line are mutually exclusive */
1289
    /* generate requires command line parameters beyond the switches */
1290
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
547✔
1291
        usage(1);
19✔
1292

1293
    /* if no conditions are specified, then assume input from stdin */
1294
    if (!*argv && !filename && !generate_flag)
529✔
1295
        filename = "-";
69✔
1296

1297
    if (*argv && !generate_flag) {
529✔
1298
        while (*argv) {
681✔
1299
            add_name(*argv);
371✔
1300
            ++argv;
371✔
1301
        }
1302
    }
76✔
1303
    else if (filename) {
219✔
1304
        FILE *ping_file;
1305
        char line[MAX_TARGET_NAME_LEN + 1];
1306
        char host[MAX_TARGET_NAME_LEN + 1];
1307
        char scratch[MAX_TARGET_NAME_LEN + 1];
1308
        int skip, non_empty;
1309

1310
        if (strcmp(filename, "-") == 0)
96✔
1311
            ping_file = fdopen(0, "r");
72✔
1312
        else
1313
            ping_file = fopen(filename, "r");
24✔
1314

1315
        if (!ping_file)
96✔
1316
            errno_crash_and_burn("fopen");
3✔
1317

1318
        /*
1319
         * Read the first word of every non-comment line, skip everything else.
1320
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1321
         * character is a '#' are interpreted as comments and ignored.)
1322
        */
1323
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1324
            skip = non_empty = 0;
309✔
1325

1326
            /* skip over a prefix of the line where sscanf finds nothing */
1327
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1328
                continue;
99✔
1329
            }
1330

1331
            /* the first word of the line can indicate a comment line */
1332
            if (host[0] == '#') {
210✔
1333
                skip = 1; /* skip remainder of line */
42✔
1334
            } else {
14✔
1335
                non_empty = 1; /* we have something to add as a target name */
168✔
1336
                /*
1337
                 * We have found the start of a word.
1338
                 * This part of the line may contain all of the first word.
1339
                 */
1340
                if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
168✔
1341
                    char discard1[MAX_TARGET_NAME_LEN + 1];
1342
                    char discard2[MAX_TARGET_NAME_LEN + 1];
1343
                    if (sscanf(line, "%s%s", discard1, discard2) == 2) {
102✔
1344
                        skip = 1; /* a second word starts in this part */
6✔
1345
                    }
2✔
1346
                    if (isspace(line[sizeof(line) - 2])) {
102✔
1347
                        skip = 1; /* the first word ends in this part */
42✔
1348
                    }
14✔
1349
                }
34✔
1350
            }
1351
            /* read remainder of this input line */
1352
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
336✔
1353
                if (skip) {
150✔
1354
                    continue; /* skip rest of data in this input line */
102✔
1355
                }
1356
                if (isspace(line[0])) {
48✔
1357
                    skip = 1; /* first word ended in previous part */
6✔
1358
                    continue;
6✔
1359
                }
1360
                if ((sscanf(line, "%s", scratch) != 1) || (!*scratch)) {
42✔
1361
                    skip = 1; /* empty or blank part of line, skip the rest */
×
1362
                    continue;
×
1363
                }
1364
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
42✔
1365
                    fprintf(stderr, "%s: target name too long\n", prog);
24✔
1366
                    exit(1);
24✔
1367
                }
1368
                /* append remainder of word started in previous line part */
1369
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
18✔
1370
                /*
1371
                 * Since the "host" buffer is the same size as the "line"
1372
                 * buffer, a target name that fits into the "host" buffer
1373
                 * cannot use more than two consecutive line parts.
1374
                 * A target name that uses two consecutive line parts
1375
                 * and fits into the "host" buffer must end before the
1376
                 * end of the second "line" buffer.  Thus the rest of
1377
                 * the line can be skipped.
1378
                 */
1379
                skip = 1;
18✔
1380
            }
1381

1382
            if (non_empty)
186✔
1383
                add_name(host);
144✔
1384
        }
1385

1386
        fclose(ping_file);
70✔
1387
    }
24✔
1388
    else if (*argv && generate_flag) {
123✔
1389
        if (argc == 1) {
123✔
1390
            /* one target: we expect a cidr range (n.n.n.n/m) */
1391
            add_cidr(argv[0]);
68✔
1392
        }
14✔
1393
        else if (argc == 2) {
55✔
1394
            add_range(argv[0], argv[1]);
52✔
1395
        }
6✔
1396
        else {
1397
            usage(1);
3✔
1398
        }
1399
    }
21✔
1400
    else {
1401
        usage(1);
×
1402
    }
1403

1404
    if (!num_hosts) {
443✔
1405
        exit(num_noaddress ? 2 : 1);
36✔
1406
    }
1407

1408
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
407✔
1409
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1410
    }
2✔
1411
#ifdef IPV6
1412
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
405✔
1413
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1414
    }
1415
#endif
1416

1417
    /* allocate and initialize array to map host nr to host_entry */
1418
    {
1419
        struct event *cursor = event_queue_ping.first;
403✔
1420
        int i = 0;
403✔
1421
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
403✔
1422
        if (!table)
403✔
1423
            crash_and_burn("Can't malloc array of hosts");
×
1424
        /* initialize table of hosts. we know that we have ping events scheduled
1425
         * for each of them */
1426
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
993✔
1427
            table[i] = cursor->host;
590✔
1428
            cursor->host->i = i;
590✔
1429
            i++;
590✔
1430
        }
156✔
1431
    }
1432

1433
    init_ping_buffer_ipv4(ping_data_size);
403✔
1434
#ifdef IPV6
1435
    init_ping_buffer_ipv6(ping_data_size);
403✔
1436
#endif
1437

1438
#ifdef USE_SIGACTION
1439
    memset(&act, 0, sizeof(act));
403✔
1440
    act.sa_handler = signal_handler;
403✔
1441
    sigemptyset(&act.sa_mask);
403✔
1442
    sigaddset(&act.sa_mask, SIGINT);
403✔
1443
    sigaddset(&act.sa_mask, SIGQUIT);
403✔
1444
    act.sa_flags = SA_RESTART;
403✔
1445
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
403✔
1446
        crash_and_burn("failure to set signal handler");
30✔
1447
    }
30✔
1448
#else
1449
    signal(SIGINT, signal_handler);
1450
    signal(SIGQUIT, signal_handler);
1451
#endif
1452
    setlinebuf(stdout);
373✔
1453

1454
    // Last time we updated current_time_ns was before adding the hosts and
1455
    // possibly doing DNS resolution, which means that it isn't accurate
1456
    // anymore.
1457
    update_current_time();
373✔
1458
    if (report_interval) {
373✔
1459
        next_report_time = current_time_ns + report_interval;
22✔
1460
    }
2✔
1461

1462
    last_send_time = 0;
373✔
1463

1464
    seqmap_init(seqmap_timeout);
373✔
1465

1466
    /* main loop */
1467
    main_loop();
373✔
1468

1469
/* Debug: CPU Performance */
1470
#if defined(DEBUG) || defined(_DEBUG)
1471
    perf_cpu_end = clock();
1472
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1473
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1474
#endif /* DEBUG || _DEBUG */
1475

1476
    finish();
373✔
1477

1478
    return 0;
81✔
1479
}
1480

1481
static inline int64_t timespec_ns(struct timespec *a)
6,736✔
1482
{
1483
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
6,736✔
1484
}
1485

1486
#if HAVE_SO_TIMESTAMPNS
1487
/* convert a struct timeval to nanoseconds */
1488
static inline int64_t timeval_ns(struct timeval *a)
1489
{
1490
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1491
}
1492
#endif /* HAVE_SO_TIMESTAMPNS */
1493

1494
void add_cidr(char *addr)
63✔
1495
{
1496
    char *addr_end;
1497
    char *mask_str;
1498
    unsigned long mask;
1499
    int ret;
1500
    struct addrinfo addr_hints;
1501
    struct addrinfo *addr_res;
1502
    unsigned long net_addr;
1503
#ifdef IPV6
1504
    uint64_t net_upper, net_lower;
1505
    char *scope_str;
1506
#endif /* IPV6 */
1507

1508
    /* Split address from mask */
1509
    addr_end = strrchr(addr, '/');
63✔
1510
    if (addr_end == NULL) {
63✔
1511
        usage(1);
3✔
1512
    }
1✔
1513
    mask_str = addr_end + 1;
61✔
1514

1515
#ifdef IPV6
1516
    /* IPv6 addresses can have a scope */
1517
    scope_str = strchr(addr, '%');
61✔
1518
    if (scope_str && mask_str < scope_str) {
61✔
1519
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1520
        exit(1);
2✔
1521
    }
1522
#endif /*IPV6 */
1523

1524
    *addr_end = '\0';
59✔
1525
    mask = strtoul_strict(mask_str, 10);
59✔
1526

1527
    /* parse address */
1528
    memset(&addr_hints, 0, sizeof(struct addrinfo));
53✔
1529
    addr_hints.ai_family = hints_ai_family;
53✔
1530
    addr_hints.ai_flags = AI_NUMERICHOST;
53✔
1531
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
53✔
1532
    if (ret) {
53✔
1533
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1534
        exit(1);
7✔
1535
    }
1536
    if (addr_res->ai_family == AF_INET) {
46✔
1537
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
24✔
1538
        freeaddrinfo(addr_res);
24✔
1539
        add_cidr_ipv4(net_addr, mask);
24✔
1540
#ifdef IPV6
1541
    } else if (addr_res->ai_family == AF_INET6) {
30✔
1542
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1543
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1544
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1545
        freeaddrinfo(addr_res);
22✔
1546
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1547
#endif /* IPV6 */
1548
    } else {
1549
        freeaddrinfo(addr_res);
×
1550
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1551
        exit(1);
×
1552
    }
1553
}
30✔
1554

1555
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1556
{
1557
    unsigned long bitmask;
1558
    unsigned long net_last;
1559

1560
    /* check mask */
1561
    if (mask < 1 || mask > 32) {
24✔
1562
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1563
        exit(1);
6✔
1564
    }
1565

1566
    /* convert mask integer from 1 to 32 to a bitmask */
1567
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1568

1569
    /* calculate network range */
1570
    net_addr &= bitmask;
18✔
1571
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1572

1573
    /* exclude network and broadcast address for regular prefixes */
1574
    if (mask < 31) {
18✔
1575
        net_last--;
12✔
1576
        net_addr++;
12✔
1577
    }
4✔
1578

1579
    /* add all hosts in that network (net_addr and net_last inclusive) */
1580
    add_addr_range_ipv4(net_addr, net_last);
18✔
1581
}
16✔
1582

1583
#ifdef IPV6
1584
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1585
{
1586
    uint64_t bitmask_lower;
1587
    uint64_t last_lower;
1588

1589
    /* check mask -- 2^63 addresses should suffice for now */
1590
    if (mask < 65 || mask > 128) {
23✔
1591
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1592
        exit(1);
7✔
1593
    }
1594

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

1598
    /* calculate network range */
1599
    net_lower &= bitmask_lower;
16✔
1600
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1601

1602
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1603
}
12✔
1604
#endif /* IPV6 */
1605

1606
void add_range(char *start, char *end)
52✔
1607
{
1608
    struct addrinfo addr_hints;
1609
    struct addrinfo *addr_res;
1610
    unsigned long start_long;
1611
    unsigned long end_long;
1612
    int ret;
1613
#ifdef IPV6
1614
    uint64_t start_upper, start_lower;
1615
    uint64_t end_upper, end_lower;
1616
    char *start_scope_str, *end_scope_str;
1617

1618
    /*
1619
     * The compiler does not know that setting the address family hint to
1620
     * ensure that start and end are from the same address family also
1621
     * ensures that either start_long and end_long are initialized and used,
1622
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1623
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1624
     * supported to suppress compiler warnings.
1625
     */
1626
    start_long = -1;
52✔
1627
    end_long = 0;
52✔
1628
    start_upper = start_lower = -1;
52✔
1629
    end_upper = end_lower = 0;
52✔
1630
    start_scope_str = end_scope_str = NULL;
52✔
1631
#endif /* IPV6 */
1632

1633
    /* parse start address */
1634
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1635
    addr_hints.ai_family = hints_ai_family;
52✔
1636
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1637
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1638
    if (ret) {
52✔
1639
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1640
        exit(1);
9✔
1641
    }
1642
    /* start and end must be from the same address family */
1643
    hints_ai_family = addr_res->ai_family;
43✔
1644
    if (addr_res->ai_family == AF_INET) {
43✔
1645
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1646
        freeaddrinfo(addr_res);
17✔
1647
#ifdef IPV6
1648
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1649
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1650
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1651
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1652
        freeaddrinfo(addr_res);
26✔
1653
#endif /* IPV6 */
1654
    } else {
1655
        freeaddrinfo(addr_res);
×
1656
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1657
        exit(1);
×
1658
    }
1659

1660
#ifdef IPV6
1661
    /* IPv6 addresses can have a scope */
1662
    if (hints_ai_family == AF_INET6) {
43✔
1663
        start_scope_str = strchr(start, '%');
26✔
1664
        end_scope_str = strchr(end, '%');
26✔
1665
        if ((!start_scope_str && end_scope_str) ||
26✔
1666
            (start_scope_str && !end_scope_str) ||
24✔
1667
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1668
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1669
                exit(1);
6✔
1670
        }
1671
    }
1672
#endif
1673

1674
    /* parse end address */
1675
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1676
    addr_hints.ai_family = hints_ai_family;
37✔
1677
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1678
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1679
    if (ret) {
37✔
1680
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1681
        exit(1);
7✔
1682
    }
1683
    if (addr_res->ai_family == AF_INET) {
30✔
1684
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1685
        freeaddrinfo(addr_res);
12✔
1686
        add_addr_range_ipv4(start_long, end_long);
12✔
1687
#ifdef IPV6
1688
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1689
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1690
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1691
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1692
        freeaddrinfo(addr_res);
18✔
1693
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1694
#endif /* IPV6 */
1695
    } else {
1696
        freeaddrinfo(addr_res);
×
1697
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1698
        exit(1);
×
1699
    }
1700
}
24✔
1701

1702
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1703
{
1704
    /* check if generator limit is exceeded */
1705
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1706
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1707
        exit(1);
6✔
1708
    }
1709

1710
    /* generate */
1711
    for (; start_long <= end_long; start_long++) {
93✔
1712
        struct in_addr in_addr_tmp;
1713
        char buffer[20];
1714
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1715
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1716
        add_name(buffer);
69✔
1717
    }
23✔
1718
}
24✔
1719

1720
#ifdef IPV6
1721
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1722
{
1723
    int i;
1724
    uint64_t ret = 0;
134✔
1725
    for (i = 0; i < 8; i++) {
1,206✔
1726
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1727
    }
16✔
1728
    return ret;
134✔
1729
}
1730

1731
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1732
{
1733
    int i;
1734
    for (i = 0; i < 8; i++) {
720✔
1735
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1736
    }
1737
}
80✔
1738

1739
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1740
                         uint64_t end_upper, uint64_t end_lower,
1741
                         const char *scope_str)
1742
{
1743
    struct in6_addr in6_addr_tmp;
1744
    char buffer[100];
1745

1746
    /* prevent generating too many addresses */
1747
    if ((start_upper + 1 < end_upper) ||
34✔
1748
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1749
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1750
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1751
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1752
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1753
        exit(1);
8✔
1754
    }
1755

1756
    while ((start_upper < end_upper) ||
66✔
1757
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1758
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1759
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1760
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1761
        if (scope_str) {
40✔
1762
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1763
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1764
                exit(1);
×
1765
            }
1766
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1767
        }
1768
        add_name(buffer);
40✔
1769
        start_lower++;
40✔
1770
        if (start_lower == 0) {
40✔
1771
            start_upper++;
2✔
1772
        }
1773
    }
1774
}
26✔
1775
#endif /* IPv6 */
1776

1777
void main_loop()
388✔
1778
{
1779
    int64_t lt;
1780
    int64_t wait_time_ns;
1781
    struct event *event;
1782
    struct host_entry *h;
1783

1784
    while (event_queue_ping.first || event_queue_timeout.first) {
2,122✔
1785
        dbg_printf("%s", "# main_loop\n");
1786

1787
        /* timeout event ? */
1788
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,765✔
1789
            event = ev_dequeue(&event_queue_timeout);
101✔
1790
            h = event->host;
101✔
1791

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

1794
            stats_add(h, event->ping_index, 0, -1);
101✔
1795

1796
            if (per_recv_flag) {
101✔
1797
                print_timeout(h, event->ping_index);
25✔
1798
            }
7✔
1799

1800
            /* do we need to send a retry? */
1801
            if (!loop_flag && !count_flag) {
101✔
1802
                if (h->num_sent < retry + 1) {
60✔
1803
                    if (backoff_flag) {
35✔
1804
                        h->timeout *= backoff;
35✔
1805
                    }
9✔
1806
                    send_ping(h, event->ping_index);
35✔
1807
                }
9✔
1808
            }
14✔
1809

1810
            /* note: we process first timeout events, because we might need to
1811
             * wait to process ping events, while we for sure never need to
1812
             * wait for timeout events.
1813
             */
1814
            continue;
101✔
1815
        }
1816

1817
        /* ping event ? */
1818
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,927✔
1819
            /* Make sure that we don't ping more than once every "interval" */
1820
            lt = current_time_ns - last_send_time;
1,272✔
1821
            if (lt < interval)
1,272✔
1822
                goto wait_for_reply;
271✔
1823

1824
            /* Dequeue the event */
1825
            event = ev_dequeue(&event_queue_ping);
1,001✔
1826
            h = event->host;
1,001✔
1827

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

1830
            /* Send the ping */
1831
            send_ping(h, event->ping_index);
1,001✔
1832

1833
            /* Loop and count mode: schedule next ping */
1834
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
1,001✔
1835
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
440✔
1836
            }
116✔
1837
        }
263✔
1838

1839
    wait_for_reply:
734✔
1840

1841
        /* When is the next ping next event? */
1842
        wait_time_ns = -1;
1,664✔
1843
        if (event_queue_ping.first) {
1,664✔
1844
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,222✔
1845
            if (wait_time_ns < 0)
1,222✔
1846
                wait_time_ns = 0;
571✔
1847
            /* make sure that we wait enough, so that the inter-ping delay is
1848
             * bigger than 'interval' */
1849
            if (wait_time_ns < interval) {
1,222✔
1850
                lt = current_time_ns - last_send_time;
576✔
1851
                if (lt < interval) {
576✔
1852
                    wait_time_ns = interval - lt;
576✔
1853
                }
220✔
1854
            }
220✔
1855

1856
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1857
        }
333✔
1858

1859
        /* When is the next timeout event? */
1860
        if (event_queue_timeout.first) {
1,664✔
1861
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
1,049✔
1862
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
1,049✔
1863
                wait_time_ns = wait_time_timeout;
436✔
1864
                if (wait_time_ns < 0) {
436✔
1865
                    wait_time_ns = 0;
×
1866
                }
1867
            }
108✔
1868

1869
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1870
        }
279✔
1871

1872
        /* When is the next report due? */
1873
        if (report_interval && (loop_flag || count_flag)) {
1,664✔
1874
            int64_t wait_time_next_report = next_report_time - current_time_ns;
214✔
1875
            if (wait_time_next_report < wait_time_ns) {
214✔
1876
                wait_time_ns = wait_time_next_report;
50✔
1877
                if (wait_time_ns < 0) {
50✔
1878
                    wait_time_ns = 0;
×
1879
                }
1880
            }
2✔
1881

1882
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1883
        }
6✔
1884

1885
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1886
        if (wait_time_ns == -1) {
1,664✔
1887
            break;
14✔
1888
        }
1889

1890
        /* end of loop was requested by interrupt signal handler */
1891
        if (finish_requested) {
1,650✔
1892
            break;
17✔
1893
        }
1894

1895
        /* Receive replies */
1896
        /* (this is what sleeps during each loop iteration) */
1897
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1898
        if (wait_for_reply(wait_time_ns)) {
1,633✔
1899
            while (wait_for_reply(0))
1,487✔
1900
                ; /* process other replies in the queue */
1901
        }
251✔
1902

1903
        update_current_time();
1,633✔
1904

1905
        if (status_snapshot) {
1,633✔
1906
            status_snapshot = 0;
3✔
1907
            if (json_flag)
3✔
1908
                print_per_system_splits_json();
×
1909
            else
1910
                print_per_system_splits();
3✔
1911
        }
1✔
1912

1913
        /* Print report */
1914
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,633✔
1915
            if (netdata_flag) {
36✔
1916
                print_netdata();
3✔
1917
            }
1✔
1918
            else if (json_flag) {
33✔
1919
                print_per_system_splits_json();
3✔
1920
            }
1✔
1921
            else {
1922
                print_per_system_splits();
30✔
1923
            }
1924

1925
            while (current_time_ns >= next_report_time) {
72✔
1926
                next_report_time += report_interval;
36✔
1927
            }
1928
        }
2✔
1929
    }
1930
}
388✔
1931

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

1934
  Function: signal_handler
1935

1936
*************************************************************
1937

1938
  Inputs:  int signum
1939

1940
  Description:
1941

1942
  SIGQUIT signal handler - set flag and return
1943
  SIGINT signal handler - set flag and return
1944

1945
************************************************************/
1946

1947
void signal_handler(int signum)
17✔
1948
{
1949
    switch (signum) {
17✔
1950
    case SIGINT:
10✔
1951
        finish_requested = 1;
14✔
1952
        break;
14✔
1953

1954
    case SIGQUIT:
2✔
1955
        status_snapshot = 1;
3✔
1956
        break;
3✔
1957
    }
1958
}
17✔
1959

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

1962
  Function: update_current_time
1963

1964
*************************************************************/
1965

1966
void update_current_time()
5,500✔
1967
{
1968
    clock_gettime(CLOCKID, &current_time);
5,500✔
1969
    current_time_ns = timespec_ns(&current_time);
5,500✔
1970
}
5,500✔
1971

1972
/************************************************************
1973

1974
  Function: finish
1975

1976
*************************************************************
1977

1978
  Inputs:  void (none)
1979

1980
  Description:
1981

1982
  Main program clean up and exit point
1983

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

1986
void finish()
388✔
1987
{
1988
    int i;
1989
    HOST_ENTRY *h;
1990

1991
    update_current_time();
388✔
1992
    end_time = current_time_ns;
388✔
1993

1994
    /* tot up unreachables */
1995
    for (i = 0; i < num_hosts; i++) {
978✔
1996
        h = table[i];
590✔
1997

1998
        if (!h->num_recv) {
590✔
1999
            num_unreachable++;
87✔
2000

2001
            if (verbose_flag || unreachable_flag) {
87✔
2002
                printf("%s", h->host);
47✔
2003

2004
                if (verbose_flag)
47✔
2005
                    printf(" is unreachable");
44✔
2006

2007
                printf("\n");
47✔
2008
            }
3✔
2009
        }
13✔
2010
    }
156✔
2011

2012
    if (count_flag || loop_flag) {
388✔
2013
        if (json_flag)
168✔
2014
            print_per_system_stats_json();
56✔
2015
        else
2016
            print_per_system_stats();
112✔
2017
    }
44✔
2018
#if defined(DEBUG) || defined(_DEBUG)
2019
    else if (print_per_system_flag) {
2020
        if (json_flag)
2021
            print_per_system_stats_json();
2022
        else
2023
            print_per_system_stats();
2024
    }
2025
#endif /* DEBUG || _DEBUG */
2026

2027
    if (stats_flag) {
388✔
2028
        if (json_flag)
12✔
2029
            print_global_stats_json();
3✔
2030
        else
2031
            print_global_stats();
9✔
2032
    }
4✔
2033

2034
    if (min_reachable) {
388✔
2035
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
2036
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
2037
            exit(0);
6✔
2038
        }
2039
        else {
2040
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
2041
            exit(1);
3✔
2042
        }
2043
    }
2044

2045
    if (num_noaddress)
379✔
2046
        exit(2);
3✔
2047
    else if (num_alive != num_hosts)
376✔
2048
        exit(1);
48✔
2049

2050
    exit(0);
328✔
2051
}
2052

2053
/************************************************************
2054

2055
  Function: print_recv
2056

2057
*************************************************************
2058

2059
  Inputs: HOST_ENTRY *h, int64_t recv_time, int result,
2060
          int this_count, int64_t this_reply, int avg
2061

2062
  Description:
2063

2064
************************************************************/
2065

2066
void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) {
279✔
2067
    if (json_flag) {
279✔
2068
        printf("{\"resp\": {");
35✔
2069

2070
        if (timestamp_flag)
35✔
2071
            print_timestamp_format(recv_time, timestamp_format_flag);
15✔
2072

2073
        printf("\"host\": \"%s\", ", h->host);
35✔
2074
        printf("\"seq\": %d, ", this_count);
35✔
2075
        printf("\"size\": %d, ", result);
35✔
2076
        printf("\"rtt\": %s", sprint_tm(this_reply));
35✔
2077
        return;
35✔
2078
    }
2079

2080
    /* Normal Output */
2081
    if (timestamp_flag)
244✔
2082
        print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2083

2084
    printf("%-*s : [%d], %d bytes, %s ms",
244✔
2085
        max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
76✔
2086

2087
    printf(" (%s avg, ", sprint_tm(avg));
244✔
2088

2089
    if (h->num_recv <= h->num_sent) {
244✔
2090
        printf("%d%% loss)",
244✔
2091
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
244✔
2092
    }
76✔
2093
    else {
2094
        printf("%d%% return)",
×
2095
            (h->num_recv_total * 100) / h->num_sent);
×
2096
    }
2097
}
87✔
2098

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

2101
  Function: print_timeout
2102

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

2105
  Inputs: HOST_ENTRY *h, int ping_index
2106

2107
  Description:
2108

2109
************************************************************/
2110

2111
void print_timeout(HOST_ENTRY *h, int ping_index) {
25✔
2112
    if (json_flag) {
25✔
2113
        printf("{\"timeout\": {");
6✔
2114
        if (timestamp_flag)
6✔
2115
            print_timestamp_format(current_time_ns, timestamp_format_flag);
×
2116

2117
        printf("\"host\": \"%s\", ", h->host);
6✔
2118
        printf("\"seq\": %d", ping_index);
6✔
2119
        printf("}}\n");
6✔
2120
        return;
6✔
2121
    }
2122

2123
    /* Normal Output */
2124
    if (timestamp_flag)
19✔
2125
        print_timestamp_format(current_time_ns, timestamp_format_flag);
×
2126

2127
    printf("%-*s : [%d], timed out",
19✔
2128
        max_hostname_len, h->host, ping_index);
5✔
2129

2130
    if (h->num_recv > 0) {
19✔
2131
        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
2132
    }
2133
    else {
2134
        printf(" (NaN avg, ");
19✔
2135
    }
2136

2137
    if (h->num_recv <= h->num_sent) {
19✔
2138
        printf("%d%% loss)",
19✔
2139
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
2140
    }
5✔
2141
    else {
2142
        printf("%d%% return)",
×
2143
            (h->num_recv_total * 100) / h->num_sent);
×
2144
    }
2145
    printf("\n");
19✔
2146
}
7✔
2147

2148
/************************************************************
2149

2150
  Function: print_recv_ext
2151

2152
*************************************************************
2153

2154
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2155
           int64_t recv_time, int64_t this_reply
2156

2157
  Description:
2158

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

2161
void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { 
546✔
2162
    if (icmp_request_typ == 13) {
546✔
2163
        printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2164
            alive_flag ? "" : ",",
14✔
2165
            ip_header_res->otime_ms, ip_header_res->rtime_ms, ip_header_res->ttime_ms,
2166
            ms_since_midnight_utc(recv_time));
2167
    }
2168

2169
    if(print_tos_flag) {
546✔
2170
        if(ip_header_res->tos != -1) {
63✔
2171
            printf(" (TOS %d)", ip_header_res->tos);
63✔
2172
        }
17✔
2173
        else {
2174
            printf(" (TOS unknown)");
×
2175
        }
2176
    }
17✔
2177

2178
    if (print_ttl_flag) {
546✔
2179
        if(ip_header_res->ttl != -1) {
60✔
2180
            printf(" (TTL %d)", ip_header_res->ttl);
60✔
2181
        }
16✔
2182
        else {
2183
            printf(" (TTL unknown)");
×
2184
        }
2185
    }
16✔
2186

2187
    if (elapsed_flag && !per_recv_flag)
546✔
2188
        printf(" (%s ms)", sprint_tm(this_reply));
8✔
2189
    
2190
    printf("\n");
546✔
2191
}
546✔
2192

2193
/************************************************************
2194

2195
  Function: print_recv_ext_json
2196

2197
*************************************************************
2198

2199
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2200
           int64_t recv_time, int64_t this_reply
2201

2202
  Description:
2203

2204
************************************************************/
2205

2206
void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) {
35✔
2207
    if (icmp_request_typ == 13) {
35✔
2208
        printf(", \"timestamps\": {");
2✔
2209
        printf("\"originate\": %u, ", ip_header_res->otime_ms);
2✔
2210
        printf("\"receive\": %u, ", ip_header_res->rtime_ms);
2✔
2211
        printf("\"transmit\": %u, ", ip_header_res->ttime_ms);
2✔
2212
        printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
2213
    }
2214

2215
    if(print_tos_flag) {
35✔
2216
        if(ip_header_res->tos != -1) {
6✔
2217
            printf(", \"tos\": %d", ip_header_res->tos);
6✔
2218
        }
2✔
2219
        else {
2220
            printf(", \"tos\": -1");
×
2221
        }
2222
    }
2✔
2223

2224
    if (print_ttl_flag) {
35✔
2225
        if(ip_header_res->ttl != -1) {
6✔
2226
            printf(", \"ttl\": %d", ip_header_res->ttl);
6✔
2227
        }
2✔
2228
        else {
2229
            printf(", \"ttl\": -1");
×
2230
        }
2231
    }
2✔
2232

2233
    if (elapsed_flag && !per_recv_flag)
35✔
2234
        printf(" (%s ms)", sprint_tm(this_reply));
×
2235

2236
    printf("}}");
35✔
2237
    printf("\n");
35✔
2238
}
35✔
2239

2240
/************************************************************
2241

2242
  Function: print_per_system_stats
2243

2244
*************************************************************
2245

2246
  Inputs:  void (none)
2247

2248
  Description:
2249

2250

2251
************************************************************/
2252

2253
void print_per_system_stats(void)
112✔
2254
{
2255
    int i, j, avg, outage_ms;
2256
    HOST_ENTRY *h;
2257
    int64_t resp;
2258

2259
    if (verbose_flag || per_recv_flag)
112✔
2260
        fprintf(stderr, "\n");
76✔
2261

2262
    for (i = 0; i < num_hosts; i++) {
258✔
2263
        h = table[i];
146✔
2264
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
146✔
2265

2266
        if (report_all_rtts_flag) {
146✔
2267
            for (j = 0; j < h->num_sent; j++) {
248✔
2268
                if ((resp = h->resp_times[j]) >= 0)
204✔
2269
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2270
                else
2271
                    fprintf(stderr, " -");
8✔
2272
            }
64✔
2273

2274
            fprintf(stderr, "\n");
44✔
2275
        }
12✔
2276
        else {
2277
            if (h->num_recv <= h->num_sent) {
102✔
2278
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
126✔
2279
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
102✔
2280

2281
                if (outage_flag) {
102✔
2282
                    /* Time outage total */
2283
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
7✔
2284
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
7✔
2285
                }
1✔
2286
            }
24✔
2287
            else {
2288
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2289
                    h->num_sent, h->num_recv,
2290
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2291
            }
2292

2293
            if (h->num_recv) {
102✔
2294
                avg = h->total_time / h->num_recv;
93✔
2295
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
93✔
2296
                fprintf(stderr, "/%s", sprint_tm(avg));
93✔
2297
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
93✔
2298
            }
23✔
2299

2300
            fprintf(stderr, "\n");
102✔
2301
        }
2302
    }
36✔
2303
}
112✔
2304

2305
/************************************************************
2306

2307
  Function: print_per_system_stats_json
2308

2309
*************************************************************
2310

2311
  Inputs:  void (none)
2312

2313
  Description:
2314

2315

2316
************************************************************/
2317

2318
void print_per_system_stats_json(void)
56✔
2319
{
2320
    int i, j, avg, outage_ms;
2321
    HOST_ENTRY *h;
2322
    int64_t resp;
2323

2324
    for (i = 0; i < num_hosts; i++) {
118✔
2325
        h = table[i];
62✔
2326

2327
        if (report_all_rtts_flag)
62✔
2328
            fprintf(stdout, "{\"vSum\": {");
18✔
2329
        else
2330
            fprintf(stdout, "{\"summary\": {");
44✔
2331

2332
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
62✔
2333

2334
        if (report_all_rtts_flag) {
62✔
2335
            fprintf(stdout, "\"values\": [");
18✔
2336
            for (j = 0; j < h->num_sent; j++) {
42✔
2337
                if (j > 0)
24✔
2338
                  fprintf(stdout, ", ");
6✔
2339
                
2340
                if ((resp = h->resp_times[j]) >= 0)
24✔
2341
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2342
                else
2343
                    fprintf(stdout, "null");
3✔
2344
            }
8✔
2345

2346
            fprintf(stdout, "]}");
18✔
2347
        }
6✔
2348
        else {
2349
            if (h->num_recv <= h->num_sent) {
44✔
2350
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
44✔
2351
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
44✔
2352
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
44✔
2353

2354
                if (outage_flag) {
44✔
2355
                    /* Time outage total */
2356
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
2357
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2358
                }
2359
            }
14✔
2360
            else {
2361
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
2362
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
2363
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2364
            }
2365

2366
            if (h->num_recv) {
44✔
2367
                avg = h->total_time / h->num_recv;
41✔
2368
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
41✔
2369
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
41✔
2370
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
41✔
2371
            }
13✔
2372

2373
            fprintf(stdout, "}");
44✔
2374
        }
2375
        fprintf(stdout, "}\n");
62✔
2376
    }
20✔
2377
}
56✔
2378

2379
/************************************************************
2380

2381
  Function: print_netdata
2382

2383
*************************************************************
2384

2385
  Inputs:  void (none)
2386

2387
  Description:
2388

2389

2390
************************************************************/
2391

2392
void print_netdata(void)
3✔
2393
{
2394
    static int sent_charts = 0;
2395

2396
    int i;
2397
    int64_t avg;
2398
    HOST_ENTRY *h;
2399

2400
    for (i = 0; i < num_hosts; i++) {
6✔
2401
        h = table[i];
3✔
2402

2403
        if (!sent_charts) {
3✔
2404
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2405
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2406
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2407
        }
1✔
2408

2409
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2410
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2411
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2412
        printf("END\n");
3✔
2413

2414
        if (!sent_charts) {
3✔
2415
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2416
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2417
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2418
        }
1✔
2419

2420
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2421
        /*
2422
        if( h->num_recv_i <= h->num_sent_i )
2423
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2424
        else
2425
            printf("SET lost = 0\n");
2426
*/
2427

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

2431
        if (!sent_charts) {
3✔
2432
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2433
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2434
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2435
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2436
        }
1✔
2437

2438
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2439
        if (h->num_recv_i) {
3✔
2440
            avg = h->total_time_i / h->num_recv_i;
3✔
2441
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2442
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2443
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2444
        }
1✔
2445
        printf("END\n");
3✔
2446

2447
        stats_reset_interval(h);
3✔
2448
    }
1✔
2449

2450
    sent_charts = 1;
3✔
2451
}
3✔
2452

2453
/************************************************************
2454

2455
  Function: print_per_system_splits
2456

2457
*************************************************************
2458

2459
  Inputs:  void (none)
2460

2461
  Description:
2462

2463

2464
************************************************************/
2465

2466
void print_per_system_splits(void)
33✔
2467
{
2468
    int i, avg, outage_ms_i;
2469
    HOST_ENTRY *h;
2470
    struct tm *curr_tm;
2471

2472
    if (verbose_flag || per_recv_flag)
33✔
2473
        fprintf(stderr, "\n");
3✔
2474

2475
    update_current_time();
33✔
2476
    curr_tm = localtime((time_t *)&current_time.tv_sec);
33✔
2477
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
34✔
2478
        curr_tm->tm_min, curr_tm->tm_sec);
1✔
2479

2480
    for (i = 0; i < num_hosts; i++) {
66✔
2481
        h = table[i];
33✔
2482
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
33✔
2483

2484
        if (h->num_recv_i <= h->num_sent_i) {
33✔
2485
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
34✔
2486
                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);
33✔
2487

2488
            if (outage_flag) {
33✔
2489
                /* Time outage  */
2490
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
8✔
2491
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
8✔
2492
            }
2493
        }
1✔
2494
        else {
2495
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2496
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2497
        }
2498

2499
        if (h->num_recv_i) {
33✔
2500
            avg = h->total_time_i / h->num_recv_i;
25✔
2501
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
25✔
2502
            fprintf(stderr, "/%s", sprint_tm(avg));
25✔
2503
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
25✔
2504
        }
1✔
2505

2506
        fprintf(stderr, "\n");
33✔
2507
        if (!cumulative_stats_flag) {
33✔
2508
            stats_reset_interval(h);
25✔
2509
        }
1✔
2510
    }
1✔
2511
}
33✔
2512

2513
/************************************************************
2514

2515
  Function: print_per_system_splits_json
2516

2517
*************************************************************
2518

2519
  Inputs:  void (none)
2520

2521
  Description:
2522

2523

2524
************************************************************/
2525

2526
void print_per_system_splits_json(void)
3✔
2527
{
2528
    int i, avg, outage_ms_i;
2529
    HOST_ENTRY *h;
2530

2531
    update_current_time();
3✔
2532

2533
    for (i = 0; i < num_hosts; i++) {
6✔
2534
        h = table[i];
3✔
2535
        fprintf(stdout, "{\"intSum\": {");
3✔
2536
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2537
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2538

2539
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2540
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2541
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2542
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2543

2544
            if (outage_flag) {
3✔
2545
                /* Time outage  */
2546
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
2547
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2548
            }
2549
        }
1✔
2550
        else {
2551
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
2552
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
2553
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2554
        }
2555

2556
        if (h->num_recv_i) {
3✔
2557
            avg = h->total_time_i / h->num_recv_i;
3✔
2558
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2559
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2560
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2561
        }
1✔
2562

2563
        fprintf(stdout, "}}\n");
3✔
2564
        if (!cumulative_stats_flag) {
3✔
2565
            stats_reset_interval(h);
3✔
2566
        }
1✔
2567
    }
1✔
2568
}
3✔
2569

2570
/************************************************************
2571

2572
  Function: print_global_stats
2573

2574
*************************************************************
2575

2576
  Inputs:  void (none)
2577

2578
  Description:
2579

2580

2581
************************************************************/
2582

2583
void print_global_stats(void)
9✔
2584
{
2585
    fprintf(stderr, "\n");
9✔
2586
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2587
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2588
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2589
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2590
    fprintf(stderr, "\n");
9✔
2591
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2592
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2593
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2594
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2595
    fprintf(stderr, "\n");
9✔
2596

2597
    if (total_replies == 0) {
9✔
2598
        min_reply = 0;
3✔
2599
        max_reply = 0;
3✔
2600
        total_replies = 1;
3✔
2601
        sum_replies = 0;
3✔
2602
    }
1✔
2603

2604
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2605
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2606
        sprint_tm(sum_replies / total_replies));
3✔
2607
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2608
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2609
        (end_time - start_time) / 1e9);
9✔
2610
    fprintf(stderr, "\n");
9✔
2611
}
9✔
2612

2613
/************************************************************
2614

2615
  Function: print_global_stats_json
2616

2617
*************************************************************
2618

2619
  Inputs:  void (none)
2620

2621
  Description:
2622

2623

2624
************************************************************/
2625

2626
void print_global_stats_json(void)
3✔
2627
{
2628
    fprintf(stdout, "{\"stats\": {");
3✔
2629
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2630
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2631
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2632
    fprintf(stdout, "\"unknownAddresses\": %d, ", num_noaddress);
3✔
2633
    fprintf(stdout, "\"timeouts\": %d, ", num_timeout);
3✔
2634
    fprintf(stdout, "\"icmpEchosSent\": %d, ", num_pingsent);
3✔
2635
    fprintf(stdout, "\"icmpEchoRepliesReceived\": %d, ", num_pingreceived);
3✔
2636
    fprintf(stdout, "\"otherIcmpReceived\": %d, ", num_othericmprcvd);
3✔
2637

2638
    if (total_replies == 0) {
3✔
2639
        min_reply = 0;
×
2640
        max_reply = 0;
×
2641
        total_replies = 1;
×
2642
        sum_replies = 0;
×
2643
    }
2644

2645
    fprintf(stdout, "\"rttMin\": %s, ", sprint_tm(min_reply));
3✔
2646
    fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2647
    fprintf(stdout, "\"rttMax\": %s, ", sprint_tm(max_reply));
3✔
2648
    fprintf(stdout, "\"elapsed\": %.3f", (end_time - start_time) / 1e9);
3✔
2649
    fprintf(stdout, "}}\n");
3✔
2650
}
3✔
2651

2652
/************************************************************
2653

2654
  Function: send_ping
2655

2656
*************************************************************
2657

2658
  Inputs:  int s, HOST_ENTRY *h
2659

2660
  Description:
2661

2662
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2663
  will be added on by the kernel.  The ID field is our UNIX process ID,
2664
  and the sequence number is an index into an array of outstanding
2665
  ping requests. The sequence number will later be used to quickly
2666
  figure out who the ping reply came from.
2667

2668
************************************************************/
2669

2670
int send_ping(HOST_ENTRY *h, int index)
1,036✔
2671
{
2672
    int n;
2673
    int myseq;
2674
    int ret = 1;
1,036✔
2675
    uint8_t proto = ICMP_ECHO;
1,036✔
2676

2677
    update_current_time();
1,036✔
2678
    h->last_send_time = current_time_ns;
1,036✔
2679
    myseq = seqmap_add(h->i, index, current_time_ns);
1,036✔
2680

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

2683
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
1,036✔
2684
        if(icmp_request_typ == 13)
915✔
2685
            proto = ICMP_TSTAMP;
18✔
2686
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
915✔
2687
    }
257✔
2688
#ifdef IPV6
2689
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
121✔
2690
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
121✔
2691
    }
15✔
2692
#endif
2693
    else {
2694
        return 0;
×
2695
    }
2696

2697
    /* error sending? */
2698
    if (
764✔
2699
        (n < 0)
272✔
2700
#if defined(EHOSTDOWN)
2701
        && errno != EHOSTDOWN
306✔
2702
#endif
2703
    ) {
2704
        if (verbose_flag) {
34✔
2705
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2706
        }
2707
        else {
2708
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2709
        }
2710

2711
        h->num_sent++;
34✔
2712
        h->num_sent_i++;
34✔
2713
        if (!loop_flag)
34✔
2714
            h->resp_times[index] = RESP_ERROR;
34✔
2715

2716
        ret = 0;
34✔
2717
    }
2718
    else {
2719
        /* schedule timeout */
2720
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
1,002✔
2721

2722
        /* mark this trial as outstanding */
2723
        if (!loop_flag) {
1,002✔
2724
            h->resp_times[index] = RESP_WAITING;
868✔
2725
        }
238✔
2726
    }
2727

2728
    num_pingsent++;
1,036✔
2729
    last_send_time = h->last_send_time;
1,036✔
2730

2731
    return (ret);
1,036✔
2732
}
272✔
2733

2734
int socket_can_read(struct timeval *timeout)
1,633✔
2735
{
2736
    int nfound;
2737
    fd_set readset;
2738
    int socketmax;
2739

2740
#ifndef IPV6
2741
    socketmax = socket4;
2742
#else
2743
    socketmax = socket4 > socket6 ? socket4 : socket6;
1,633✔
2744
#endif
2745

2746
select_again:
1,210✔
2747
    FD_ZERO(&readset);
21,010✔
2748
    if (socket4 >= 0)
1,650✔
2749
        FD_SET(socket4, &readset);
1,650✔
2750
#ifdef IPV6
2751
    if (socket6 >= 0)
1,650✔
2752
        FD_SET(socket6, &readset);
1,650✔
2753
#endif
2754

2755
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
1,650✔
2756
    if (nfound < 0) {
1,650✔
2757
        if (errno == EINTR) {
17✔
2758
            /* interrupted system call: redo the select */
2759
            goto select_again;
17✔
2760
        }
2761
        else {
2762
            perror("select");
×
2763
        }
2764
    }
2765

2766
    if (nfound > 0) {
1,633✔
2767
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
911✔
2768
            return socket4;
818✔
2769
        }
2770
#ifdef IPV6
2771
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
93✔
2772
            return socket6;
93✔
2773
        }
2774
#endif
2775
    }
2776

2777
    return -1;
722✔
2778
}
435✔
2779

2780
int receive_packet(int64_t wait_time,
3,120✔
2781
#if HAVE_SO_TIMESTAMPNS
2782
    int64_t *reply_timestamp,
2783
#else
2784
    int64_t *reply_timestamp __attribute__((unused)),
2785
#endif
2786
    struct sockaddr *reply_src_addr,
2787
    size_t reply_src_addr_len,
2788
    char *reply_buf,
2789
    size_t reply_buf_len,
2790
    int *ip_header_tos,
2791
    int *ip_header_ttl)
2792
{
2793
    struct timeval to;
2794
    int s = 0;
3,120✔
2795
    int recv_len;
2796
    static unsigned char msg_control[128];
2797
    struct iovec msg_iov = {
4,492✔
2798
        reply_buf,
686✔
2799
        reply_buf_len
686✔
2800
    };
2801
    struct msghdr recv_msghdr = {0};
3,120✔
2802
    recv_msghdr.msg_name = reply_src_addr;
3,120✔
2803
    recv_msghdr.msg_namelen = reply_src_addr_len;
3,120✔
2804
    recv_msghdr.msg_iov = &msg_iov;
3,120✔
2805
    recv_msghdr.msg_iovlen = 1;
3,120✔
2806
    recv_msghdr.msg_control = &msg_control;
3,120✔
2807
    recv_msghdr.msg_controllen = sizeof(msg_control);
3,120✔
2808
    struct cmsghdr *cmsg;
2809

2810
    /* Wait for a socket to become ready */
2811
#if HAVE_MSG_DONTWAIT
2812
    if (wait_time == 0) {
3,120✔
2813
        /* Optimization: if wait_time is 0, we can skip select() and just try to
2814
         * read from the sockets with MSG_DONTWAIT */
2815
        recv_len = (socket4 >= 0) ? recvmsg(socket4, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT) : -1;
1,487✔
2816
#ifdef IPV6
2817
        if (recv_len <= 0 && socket6 >= 0) {
1,487✔
2818
            /* Reset fields potentially modified by failed recvmsg */
2819
            recv_msghdr.msg_namelen = reply_src_addr_len;
913✔
2820
            recv_msghdr.msg_controllen = sizeof(msg_control);
913✔
2821
            recv_len = recvmsg(socket6, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT);
913✔
2822
        }
251✔
2823
#endif
2824
        if (recv_len > 0) {
1,487✔
2825
            goto packet_received;
576✔
2826
        }
2827
        return 0;
911✔
2828
    }
2829
#endif
2830

2831
    int64_t wait_us = (wait_time + 999) / 1000; // round up (1 ns -> 1 us)
1,633✔
2832
    to.tv_sec = wait_us / 1000000;
1,633✔
2833
    to.tv_usec = wait_us % 1000000;
1,633✔
2834

2835
    s = socket_can_read(&to);
1,633✔
2836
    if (s == -1 || (recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC)) <= 0) {
1,633✔
2837
        return 0;
722✔
2838
    }
2839

2840
packet_received:
660✔
2841
    /* ancilliary data */
2842
    {
2843
#if HAVE_SO_TIMESTAMPNS
2844
        struct timespec reply_timestamp_ts;
2845
        struct timeval reply_timestamp_tv;
2846
#endif
2847
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,487✔
2848
             cmsg != NULL;
2,731✔
2849
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,244✔
2850
#if HAVE_SO_TIMESTAMPNS
2851
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,244✔
2852
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,236✔
2853
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,236✔
2854
            }
2855
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,244✔
2856
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2857
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2858
            }
2859
#endif
2860
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,244✔
2861
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2862
            }
2863
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,244✔
2864
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2865
            }
2866
#ifdef IPV6
2867
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,244✔
2868
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2869
            }
2870
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,244✔
2871
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2872
            }
2873
#endif
2874
        }
2875
    }
2876

2877
#if defined(DEBUG) || defined(_DEBUG)
2878
    if (randomly_lose_flag) {
2879
        if ((random() & 0x07) <= lose_factor)
2880
            return 0;
2881
    }
2882
#endif
2883

2884
    return recv_len;
1,487✔
2885
}
686✔
2886

2887
/* stats_add: update host statistics for a single packet that was received (or timed out)
2888
 * h: host entry to update
2889
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2890
 * success: 1 if response received, 0 otherwise
2891
 * latency: response time, in ns
2892
 */
2893
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
988✔
2894
{
2895
    /* sent count - we update only on receive/timeout, so that we don't get
2896
     * weird loss percentage, just because a packet was note recived yet.
2897
     */
2898
    h->num_sent++;
988✔
2899
    h->num_sent_i++;
988✔
2900

2901
    if (!success) {
988✔
2902
        if (!loop_flag && index >= 0) {
101✔
2903
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2904
        }
21✔
2905
        num_timeout++;
101✔
2906
        return;
101✔
2907
    }
2908

2909
    /* received count */
2910
    h->num_recv++;
887✔
2911
    h->num_recv_i++;
887✔
2912

2913
    /* maximum */
2914
    if (!h->max_reply || latency > h->max_reply) {
887✔
2915
        h->max_reply = latency;
663✔
2916
    }
180✔
2917
    if (!h->max_reply_i || latency > h->max_reply_i) {
887✔
2918
        h->max_reply_i = latency;
683✔
2919
    }
181✔
2920

2921
    /* minimum */
2922
    if (!h->min_reply || latency < h->min_reply) {
887✔
2923
        h->min_reply = latency;
559✔
2924
    }
164✔
2925
    if (!h->min_reply_i || latency < h->min_reply_i) {
887✔
2926
        h->min_reply_i = latency;
595✔
2927
    }
168✔
2928

2929
    /* total time (for average) */
2930
    h->total_time += latency;
887✔
2931
    h->total_time_i += latency;
887✔
2932

2933
    /* response time per-packet (count mode) */
2934
    if (!loop_flag && index >= 0) {
887✔
2935
        h->resp_times[index] = latency;
767✔
2936
    }
217✔
2937
}
268✔
2938

2939
/* stats_reset_interval: reset interval statistics
2940
 * h: host entry to update
2941
 */
2942
void stats_reset_interval(HOST_ENTRY *h)
31✔
2943
{
2944
    h->num_sent_i = 0;
31✔
2945
    h->num_recv_i = 0;
31✔
2946
    h->max_reply_i = 0;
31✔
2947
    h->min_reply_i = 0;
31✔
2948
    h->total_time_i = 0;
31✔
2949
}
31✔
2950

2951
int decode_icmp_ipv4(
1,392✔
2952
    struct sockaddr *response_addr,
2953
    size_t response_addr_len,
2954
    char *reply_buf,
2955
    size_t reply_buf_len,
2956
    unsigned short *id,
2957
    unsigned short *seq,
2958
    IP_HEADER_RESULT *ip_header_res)
2959
{
2960
    struct icmp *icp;
2961
    int hlen = 0;
1,392✔
2962

2963
    if (!using_sock_dgram4) {
1,392✔
2964
        struct ip *ip = (struct ip *)reply_buf;
1,384✔
2965
        ip_header_res->tos = ip->ip_tos;
1,384✔
2966
        ip_header_res->ttl = ip->ip_ttl;
1,384✔
2967

2968
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2969
        /* The alpha headers are decidedly broken.
2970
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2971
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2972
         */
2973
        hlen = (ip->ip_vhl & 0x0F) << 2;
2974
#else
2975
        hlen = ip->ip_hl << 2;
1,384✔
2976
#endif
2977
    }
236✔
2978

2979
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,392✔
2980
        /* too short */
2981
        if (verbose_flag) {
×
2982
            char buf[INET6_ADDRSTRLEN];
2983
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2984
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2985
        }
2986
        return -1;
×
2987
    }
2988

2989
    icp = (struct icmp *)(reply_buf + hlen);
1,392✔
2990

2991
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,392✔
2992
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
598✔
2993
        /* Handle other ICMP packets */
2994
        struct icmp *sent_icmp;
2995
        SEQMAP_VALUE *seqmap_value;
2996
        char addr_ascii[INET6_ADDRSTRLEN];
2997
        HOST_ENTRY *h;
2998

2999
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
3000
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
1,046✔
3001
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
3002
            return -1;
×
3003
        }
3004

3005
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
574✔
3006

3007
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
574✔
3008
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
3009
            sent_icmp->icmp_id != ident4) {
×
3010
            /* not caused by us */
3011
            return -1;
574✔
3012
        }
3013

3014
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
3015
        if (seqmap_value == NULL) {
×
3016
            return -1;
×
3017
        }
3018

3019
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3020

3021
        switch (icp->icmp_type) {
×
3022
        case ICMP_UNREACH:
3023
            h = table[seqmap_value->host_nr];
×
3024
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
3025
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
3026
                    addr_ascii, h->host);
3027
            }
3028
            else {
3029
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3030
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
3031
            }
3032

3033
            print_warning("\n");
×
3034
            num_othericmprcvd++;
×
3035
            break;
×
3036

3037
        case ICMP_SOURCEQUENCH:
3038
        case ICMP_REDIRECT:
3039
        case ICMP_TIMXCEED:
3040
        case ICMP_PARAMPROB:
3041
            h = table[seqmap_value->host_nr];
×
3042
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
3043
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3044
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
3045
            }
3046
            else {
3047
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
3048
                    icp->icmp_type, addr_ascii, h->host);
×
3049
            }
3050
            print_warning("\n");
×
3051
            num_othericmprcvd++;
×
3052
            break;
×
3053
        }
3054

3055
        return -1;
×
3056
    }
3057

3058
    *id = icp->icmp_id;
818✔
3059
    *seq = ntohs(icp->icmp_seq);
818✔
3060
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
818✔
3061

3062
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
3063
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
3064
            if (verbose_flag) {
×
3065
                char buf[INET6_ADDRSTRLEN];
3066
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3067
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3068
            }
3069
            return -1;
×
3070
        }
3071

3072
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
3073
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
3074
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
3075
    }
3076

3077
    return hlen;
818✔
3078
}
236✔
3079

3080
#ifdef IPV6
3081
int decode_icmp_ipv6(
95✔
3082
    struct sockaddr *response_addr,
3083
    size_t response_addr_len,
3084
    char *reply_buf,
3085
    size_t reply_buf_len,
3086
    unsigned short *id,
3087
    unsigned short *seq)
3088
{
3089
    struct icmp6_hdr *icp;
3090

3091
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
95✔
3092
        if (verbose_flag) {
×
3093
            char buf[INET6_ADDRSTRLEN];
3094
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3095
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3096
        }
3097
        return 0; /* too short */
×
3098
    }
3099

3100
    icp = (struct icmp6_hdr *)reply_buf;
95✔
3101

3102
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
95✔
3103
        /* Handle other ICMPv6 packets */
3104
        struct ip6_hdr *sent_ipv6;
3105
        struct icmp6_hdr *sent_icmp;
3106
        SEQMAP_VALUE *seqmap_value;
3107
        char addr_ascii[INET6_ADDRSTRLEN];
3108
        HOST_ENTRY *h;
3109

3110
        /* reply icmp packet (ICMPv6 header) followed by "sent packet" (IPv6 + ICMPv6 header) */
3111
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
6✔
3112
            /* discard ICMPv6 message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
3113
            return 0;
×
3114
        }
3115

3116
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
6✔
3117
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
6✔
3118
            /* discard ICMPv6 message if we can't tell that it was caused by
3119
             * us, because the IPv6 header is not directly followed by an
3120
             * ICMPv6 header
3121
             */
3122
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
3123
            return 0;
×
3124
        }
3125
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
6✔
3126

3127
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
6✔
3128
            /* not caused by us */
3129
            return 0;
6✔
3130
        }
3131

3132
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
3133
        if (seqmap_value == NULL) {
×
3134
            return 0;
×
3135
        }
3136

3137
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3138
        h = table[seqmap_value->host_nr];
×
3139

3140
        switch (icp->icmp6_type) {
×
3141
        case ICMP6_DST_UNREACH:
3142
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
3143
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3144
                    icp->icmp6_code, addr_ascii, h->host);
×
3145
            } else {
3146
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3147
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
3148
            }
3149
            print_warning("\n");
×
3150
            num_othericmprcvd++;
×
3151
            break;
×
3152

3153
        case ICMP6_PACKET_TOO_BIG:
3154
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
3155
                addr_ascii, h->host);
3156
            num_othericmprcvd++;
×
3157
            break;
×
3158

3159
        case ICMP6_TIME_EXCEEDED:
3160
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
3161
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3162
                    icp->icmp6_code, addr_ascii, h->host);
×
3163
            } else {
3164
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3165
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
3166
            }
3167
            print_warning("\n");
×
3168
            num_othericmprcvd++;
×
3169
            break;
×
3170

3171
        case ICMP6_PARAM_PROB:
3172
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
3173
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3174
                    icp->icmp6_code, addr_ascii, h->host);
×
3175
            } else {
3176
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3177
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
3178
            }
3179
            print_warning("\n");
×
3180
            num_othericmprcvd++;
×
3181
            break;
×
3182

3183
        default:
3184
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
3185
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
3186
            num_othericmprcvd++;
×
3187
            break;
×
3188
        }
3189

3190
        return 0;
×
3191
    }
3192

3193
    *id = icp->icmp6_id;
89✔
3194
    *seq = ntohs(icp->icmp6_seq);
89✔
3195

3196
    return 1;
89✔
3197
}
15✔
3198
#endif
3199

3200
int wait_for_reply(int64_t wait_time)
3,120✔
3201
{
3202
    int result;
3203
    static char buffer[RECV_BUFSIZE];
3204
    struct sockaddr_storage response_addr;
3205
    int n, avg;
3206
    HOST_ENTRY *h;
3207
    int64_t this_reply;
3208
    int this_count;
3209
    int64_t recv_time = 0;
3,120✔
3210
    SEQMAP_VALUE *seqmap_value;
3211
    unsigned short id;
3212
    unsigned short seq;
3213
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
3,120✔
3214

3215
    /* Receive packet */
3216
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,806✔
3217
        &recv_time, /* reply_timestamp */
3218
        (struct sockaddr *)&response_addr, /* reply_src_addr */
3219
        sizeof(response_addr), /* reply_src_addr_len */
3220
        buffer, /* reply_buf */
3221
        sizeof(buffer), /* reply_buf_len */
3222
        &ip_header_res.tos, /* TOS resp. TC byte */
686✔
3223
        &ip_header_res.ttl /* TTL resp. hop limit */
686✔
3224
    );
3225

3226
    if (result <= 0) {
3,120✔
3227
        return 0;
1,633✔
3228
    }
3229

3230
    update_current_time();
1,487✔
3231
    if (recv_time == 0)
1,487✔
3232
        recv_time = current_time_ns;
251✔
3233

3234
    /* Process ICMP packet and retrieve id/seq */
3235
    if (response_addr.ss_family == AF_INET) {
1,487✔
3236
        int ip_hlen = decode_icmp_ipv4(
1,392✔
3237
            (struct sockaddr *)&response_addr,
3238
            sizeof(response_addr),
3239
            buffer,
3240
            sizeof(buffer),
3241
            &id,
3242
            &seq,
3243
            &ip_header_res);
3244
        if (ip_hlen < 0) {
1,392✔
3245
            return 1;
574✔
3246
        }
3247
        if (id != ident4) {
818✔
3248
            return 1; /* packet received, but not the one we are looking for! */
×
3249
        }
3250
        if (!using_sock_dgram4) {
818✔
3251
            /* do not include IP header in returned size, to be consistent with ping(8) and also
3252
             * with fping with IPv6 hosts */
3253
            result -= ip_hlen;
810✔
3254
        }
236✔
3255
    }
236✔
3256
#ifdef IPV6
3257
    else if (response_addr.ss_family == AF_INET6) {
95✔
3258
        if (!decode_icmp_ipv6(
95✔
3259
                (struct sockaddr *)&response_addr,
3260
                sizeof(response_addr),
3261
                buffer,
3262
                sizeof(buffer),
3263
                &id,
3264
                &seq)) {
3265
            return 1;
6✔
3266
        }
3267
        if (id != ident6) {
89✔
3268
            return 1; /* packet received, but not the one we are looking for! */
×
3269
        }
3270
    }
15✔
3271
#endif
3272
    else {
3273
        return 1;
×
3274
    }
3275

3276
    seqmap_value = seqmap_fetch(seq, current_time_ns);
907✔
3277
    if (seqmap_value == NULL) {
907✔
3278
        return 1;
12✔
3279
    }
3280

3281
    /* find corresponding host_entry */
3282
    n = seqmap_value->host_nr;
895✔
3283
    h = table[n];
895✔
3284
    this_count = seqmap_value->ping_count;
895✔
3285
    this_reply = recv_time - seqmap_value->ping_ts;
895✔
3286

3287
    /* update stats that include invalid replies */
3288
    h->num_recv_total++;
895✔
3289
    num_pingreceived++;
895✔
3290

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

3293
    /* optionally require reply source equal to target address */
3294
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
895✔
3295
        dbg_printf("%s\n", "discarding reply from wrong source address");
3296
        return 1;
8✔
3297
    }
3298

3299
    /* discard duplicates */
3300
    if (!loop_flag && !(count_flag && quiet_flag) && h->resp_times[this_count] >= 0) {
887✔
3301
        if (!per_recv_flag) {
×
3302
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3303
                h->host, this_count, result, sprint_tm(this_reply));
3304

3305
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3306
                char buf[INET6_ADDRSTRLEN];
3307
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3308
                fprintf(stderr, " [<- %s]", buf);
×
3309
            }
3310
            fprintf(stderr, "\n");
×
3311
        }
3312
        return 1;
×
3313
    }
3314

3315
    /* discard reply if delay is larger than timeout
3316
     * (see also: github #32) */
3317
    if (this_reply > h->timeout) {
887✔
3318
        return 1;
×
3319
    }
3320

3321
    /* update stats */
3322
    stats_add(h, this_count, 1, this_reply);
887✔
3323
    // TODO: move to stats_add?
3324
    if (!max_reply || this_reply > max_reply)
887✔
3325
        max_reply = this_reply;
535✔
3326
    if (!min_reply || this_reply < min_reply)
887✔
3327
        min_reply = this_reply;
468✔
3328
    sum_replies += this_reply;
887✔
3329
    total_replies++;
887✔
3330

3331
    /* initialize timeout to initial timeout (without backoff) */
3332
    h->timeout = timeout;
887✔
3333

3334
    /* remove timeout event */
3335
    struct event *timeout_event = host_get_timeout_event(h, this_count);
887✔
3336
    if (timeout_event) {
887✔
3337
        ev_remove(&event_queue_timeout, timeout_event);
887✔
3338
    }
247✔
3339

3340
    /* print "is alive" */
3341
    if (h->num_recv == 1) {
887✔
3342
        num_alive++;
503✔
3343
        if (fast_reachable && num_alive >= min_reachable)
503✔
3344
            finish_requested = 1;
3✔
3345

3346
        if (verbose_flag || alive_flag) {
503✔
3347
            printf("%s", h->host);
302✔
3348

3349
            if (verbose_flag)
302✔
3350
                printf(" is alive");
282✔
3351
        }
86✔
3352
    }
143✔
3353

3354
    /* print received ping (unless --quiet) */
3355
    if (per_recv_flag) {
887✔
3356
        avg = h->total_time / h->num_recv;
279✔
3357
        print_recv(h,
366✔
3358
            recv_time,
87✔
3359
            result,
87✔
3360
            this_count,
87✔
3361
            this_reply,
87✔
3362
            avg);
87✔
3363
    }
87✔
3364

3365
    if (verbose_flag || alive_flag || per_recv_flag) {
887✔
3366
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
581✔
3367
            char buf[INET6_ADDRSTRLEN];
3368
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3369
            fprintf(stderr, " [<- %s]", buf);
6✔
3370
        }
3371
        if (json_flag) {
581✔
3372
            print_recv_ext_json(&ip_header_res,
35✔
3373
                recv_time,
11✔
3374
                this_reply);
11✔
3375
        }
11✔
3376
        else {
3377
            print_recv_ext(&ip_header_res,
546✔
3378
                recv_time,
162✔
3379
                this_reply);
162✔
3380
        }
3381
    }
173✔
3382
    return 1;
887✔
3383
}
686✔
3384

3385
/************************************************************
3386

3387
  Function: add_name
3388

3389
*************************************************************
3390

3391
  Inputs:  char* name
3392

3393
  Description:
3394

3395
  process input name for addition to target list
3396
  name can turn into multiple targets via multiple interfaces (-m)
3397
  or via NIS groups
3398

3399
************************************************************/
3400

3401
void add_name(char *name)
624✔
3402
{
3403
    struct addrinfo *res0, *res, hints;
3404
    int ret_ga;
3405
    char *printname;
3406
    char namebuf[256];
3407
    char addrbuf[256];
3408

3409
    /* getaddrinfo */
3410
    memset(&hints, 0, sizeof(struct addrinfo));
624✔
3411
    hints.ai_flags = AI_UNUSABLE;
624✔
3412
    hints.ai_socktype = SOCK_RAW;
624✔
3413
    hints.ai_family = hints_ai_family;
624✔
3414
    if (hints_ai_family == AF_INET) {
624✔
3415
        hints.ai_protocol = IPPROTO_ICMP;
81✔
3416
    }
21✔
3417
#ifdef IPV6
3418
    else if (hints_ai_family == AF_INET6) {
543✔
3419
        hints.ai_protocol = IPPROTO_ICMPV6;
33✔
3420
    }
1✔
3421
#endif
3422
    else {
3423
        hints.ai_socktype = SOCK_STREAM;
510✔
3424
        hints.ai_protocol = 0;
510✔
3425
    }
3426
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
624✔
3427
    if (ret_ga) {
624✔
3428
        if (!quiet_flag)
29✔
3429
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3430
        num_noaddress++;
29✔
3431

3432
        // Handle JSON output for invalid hosts
3433
        if (json_flag) {
29✔
3434
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
3435
            return;
21✔
3436
        }
3437

3438
        return;
26✔
3439
    }
3440

3441
    /* NOTE: we could/should loop with res on all addresses like this:
3442
     * for (res = res0; res; res = res->ai_next) {
3443
     * We don't do it yet, however, because is is an incompatible change
3444
     * (need to implement a separate option for this)
3445
     */
3446
    for (res = res0; res; res = res->ai_next) {
595✔
3447
        /* name_flag: addr -> name lookup requested) */
3448
        if (name_flag || rdns_flag) {
597✔
3449
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3450
            if (name_flag) {
3✔
3451
                /* Was it a numerical address? Only then do a rdns-query */
3452
                struct addrinfo *nres;
3453
                hints.ai_flags = AI_NUMERICHOST;
×
3454
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3455
                    do_rdns = 1;
×
3456
                    freeaddrinfo(nres);
×
3457
                }
3458
            }
3459

3460
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3461
                printname = namebuf;
3✔
3462
            }
1✔
3463
            else {
3464
                printname = name;
×
3465
            }
3466
        }
1✔
3467
        else {
3468
            printname = name;
592✔
3469
        }
3470

3471
        /* addr_flag: name -> addr lookup requested */
3472
        if (addr_flag) {
595✔
3473
            int ret;
3474
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3475
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3476
            if (ret) {
3✔
3477
                if (!quiet_flag) {
×
3478
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3479
                }
3480
                continue;
×
3481
            }
3482

3483
            if (name_flag || rdns_flag) {
3✔
3484
                char nameaddrbuf[512 + 3];
3485
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3486
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3487
            }
3488
            else {
3489
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3490
            }
3491
        }
1✔
3492
        else {
3493
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
592✔
3494
        }
3495

3496
        if (!multif_flag) {
595✔
3497
            break;
595✔
3498
        }
3499
    }
3500

3501
    freeaddrinfo(res0);
595✔
3502
}
166✔
3503

3504
/************************************************************
3505

3506
  Function: add_addr
3507

3508
*************************************************************
3509

3510
  Description:
3511

3512
  add single address to list of hosts to be pinged
3513

3514
************************************************************/
3515

3516
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
595✔
3517
{
3518
    HOST_ENTRY *p;
3519
    int n;
3520
    int64_t *i;
3521

3522
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
595✔
3523
    if (!p)
595✔
3524
        crash_and_burn("can't allocate HOST_ENTRY");
×
3525

3526
    p->name = strdup(name);
595✔
3527
    p->host = strdup(host);
595✔
3528
    memcpy(&p->saddr, ipaddr, ipaddr_len);
595✔
3529
    p->saddr_len = ipaddr_len;
595✔
3530
    p->timeout = timeout;
595✔
3531
    p->min_reply = 0;
595✔
3532

3533
    if (netdata_flag) {
595✔
3534
        char *s = p->name;
3✔
3535
        while (*s) {
30✔
3536
            if (!isalnum(*s))
27✔
3537
                *s = '_';
9✔
3538
            s++;
27✔
3539
        }
3540
    }
1✔
3541

3542
    if (strlen(p->host) > max_hostname_len)
595✔
3543
        max_hostname_len = strlen(p->host);
402✔
3544

3545
    /* array for response time results */
3546
    if (!loop_flag) {
595✔
3547
#if SIZE_MAX <= UINT_MAX
3548
        if (trials > (SIZE_MAX / sizeof(int64_t)))
3549
            crash_and_burn("resp_times array too large for memory");
3550
#endif
3551
        i = (int64_t *)malloc(trials * sizeof(int64_t));
581✔
3552
        if (!i)
581✔
3553
            crash_and_burn("can't allocate resp_times array");
×
3554

3555
        for (n = 0; n < trials; n++)
2,886✔
3556
            i[n] = RESP_UNUSED;
2,305✔
3557

3558
        p->resp_times = i;
581✔
3559
    }
153✔
3560

3561
    /* allocate event storage */
3562
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
595✔
3563
    if (!p->event_storage_ping) {
595✔
3564
        errno_crash_and_burn("can't allocate event_storage_ping");
×
3565
    }
3566
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
595✔
3567
    if (!p->event_storage_timeout) {
595✔
3568
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
3569
    }
3570

3571
    /* schedule first ping */
3572
    host_add_ping_event(p, 0, current_time_ns);
595✔
3573

3574
    num_hosts++;
595✔
3575
}
595✔
3576

3577
/************************************************************
3578

3579
  Function: crash_and_burn
3580

3581
*************************************************************
3582

3583
  Inputs:  char* message
3584

3585
  Description:
3586

3587
************************************************************/
3588

3589
void crash_and_burn(char *message)
×
3590
{
3591
    fprintf(stderr, "%s: %s\n", prog, message);
×
3592
    exit(4);
×
3593
}
3594

3595
/************************************************************
3596

3597
  Function: errno_crash_and_burn
3598

3599
*************************************************************
3600

3601
  Inputs:  char* message
3602

3603
  Description:
3604

3605
************************************************************/
3606

3607
void errno_crash_and_burn(char *message)
8✔
3608
{
3609
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3610
    exit(4);
8✔
3611
}
3612

3613
/************************************************************
3614

3615
  Function: print_warning
3616

3617
  Description: fprintf(stderr, ...), unless running with -q
3618

3619
*************************************************************/
3620

3621
void print_warning(char *format, ...)
54✔
3622
{
3623
    va_list args;
3624
    if (!quiet_flag) {
54✔
3625
        va_start(args, format);
54✔
3626
        vfprintf(stderr, format, args);
54✔
3627
        va_end(args);
54✔
3628
    }
8✔
3629
}
54✔
3630

3631
/************************************************************
3632

3633
  Function: sprint_tm
3634

3635
*************************************************************
3636

3637
  render nanosecond int64_t value into milliseconds string with three digits of
3638
  precision.
3639

3640
************************************************************/
3641

3642
const char *sprint_tm(int64_t ns)
1,270✔
3643
{
3644
    static char buf[10];
3645
    double t = (double)ns / 1e6;
1,270✔
3646

3647
    if (t < 0.0) {
1,270✔
3648
        /* negative (unexpected) */
3649
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3650
    }
3651
    else if (t < 1.0) {
1,270✔
3652
        /* <= 0.99 ms */
3653
        snprintf(buf, sizeof(buf), "%.3f", t);
1,270✔
3654
    }
362✔
3655
    else if (t < 10.0) {
×
3656
        /* 1.00 - 9.99 ms */
3657
        snprintf(buf, sizeof(buf), "%.2f", t);
×
3658
    }
3659
    else if (t < 100.0) {
×
3660
        /* 10.0 - 99.9 ms */
3661
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3662
    }
3663
    else if (t < 1000000.0) {
×
3664
        /* 100 - 1'000'000 ms */
3665
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3666
    }
3667
    else {
3668
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3669
    }
3670

3671
    return (buf);
1,270✔
3672
}
3673

3674
/************************************************************
3675

3676
  Function: addr_cmp
3677

3678
*************************************************************/
3679
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
599✔
3680
{
3681
    if (a->sa_family != b->sa_family) {
599✔
3682
        return a->sa_family - b->sa_family;
×
3683
    }
3684
    else {
3685
        if (a->sa_family == AF_INET) {
599✔
3686
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
537✔
3687
        }
3688
        else if (a->sa_family == AF_INET6) {
62✔
3689
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
70✔
3690
                &((struct sockaddr_in6 *)b)->sin6_addr,
62✔
3691
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3692
        }
3693
    }
3694

3695
    return 0;
×
3696
}
175✔
3697

3698
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,035✔
3699
{
3700
    struct event *event = &h->event_storage_ping[index % event_storage_count];
1,035✔
3701
    event->host = h;
1,035✔
3702
    event->ping_index = index;
1,035✔
3703
    event->ev_time = ev_time;
1,035✔
3704
    ev_enqueue(&event_queue_ping, event);
1,035✔
3705

3706
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3707
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3708
}
1,035✔
3709

3710
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,002✔
3711
{
3712
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
1,002✔
3713
    event->host = h;
1,002✔
3714
    event->ping_index = index;
1,002✔
3715
    event->ev_time = ev_time;
1,002✔
3716
    ev_enqueue(&event_queue_timeout, event);
1,002✔
3717

3718
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3719
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3720
}
1,002✔
3721

3722
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
887✔
3723
{
3724
    return &h->event_storage_timeout[index % event_storage_count];
887✔
3725
}
3726

3727
/************************************************************
3728

3729
  Function: ev_enqueue
3730

3731
  Enqueue an event
3732

3733
  The queue is sorted by event->ev_time, so that queue->first always points to
3734
  the earliest event.
3735

3736
  We start scanning the queue from the tail, because we assume
3737
  that new events mostly get inserted with a event time higher
3738
  than the others.
3739

3740
*************************************************************/
3741
void ev_enqueue(struct event_queue *queue, struct event *event)
2,037✔
3742
{
3743
    struct event *i;
3744
    struct event *i_prev;
3745

3746
    /* Empty list */
3747
    if (queue->last == NULL) {
2,037✔
3748
        event->ev_next = NULL;
1,647✔
3749
        event->ev_prev = NULL;
1,647✔
3750
        queue->first = event;
1,647✔
3751
        queue->last = event;
1,647✔
3752
        return;
1,647✔
3753
    }
3754

3755
    /* Insert on tail? */
3756
    if (event->ev_time - queue->last->ev_time >= 0) {
390✔
3757
        event->ev_next = NULL;
390✔
3758
        event->ev_prev = queue->last;
390✔
3759
        queue->last->ev_next = event;
390✔
3760
        queue->last = event;
390✔
3761
        return;
390✔
3762
    }
3763

3764
    /* Find insertion point */
3765
    i = queue->last;
×
3766
    while (1) {
3767
        i_prev = i->ev_prev;
×
3768
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3769
            event->ev_prev = i_prev;
×
3770
            event->ev_next = i;
×
3771
            i->ev_prev = event;
×
3772
            if (i_prev != NULL) {
×
3773
                i_prev->ev_next = event;
×
3774
            }
3775
            else {
3776
                queue->first = event;
×
3777
            }
3778
            return;
×
3779
        }
3780
        i = i_prev;
×
3781
    }
3782
}
545✔
3783

3784
/************************************************************
3785

3786
  Function: ev_dequeue
3787

3788
*************************************************************/
3789
struct event *ev_dequeue(struct event_queue *queue)
1,102✔
3790
{
3791
    struct event *dequeued;
3792

3793
    if (queue->first == NULL) {
1,102✔
3794
        return NULL;
×
3795
    }
3796
    dequeued = queue->first;
1,102✔
3797
    ev_remove(queue, dequeued);
1,102✔
3798

3799
    return dequeued;
1,102✔
3800
}
284✔
3801

3802
/************************************************************
3803

3804
  Function: ev_remove
3805

3806
*************************************************************/
3807
void ev_remove(struct event_queue *queue, struct event *event)
1,989✔
3808
{
3809
    if (queue->first == event) {
1,989✔
3810
        queue->first = event->ev_next;
1,980✔
3811
    }
528✔
3812
    if (queue->last == event) {
1,989✔
3813
        queue->last = event->ev_prev;
1,620✔
3814
    }
418✔
3815
    if (event->ev_prev) {
1,989✔
3816
        event->ev_prev->ev_next = event->ev_next;
9✔
3817
    }
3✔
3818
    if (event->ev_next) {
1,989✔
3819
        event->ev_next->ev_prev = event->ev_prev;
369✔
3820
    }
113✔
3821
    event->ev_prev = NULL;
1,989✔
3822
    event->ev_next = NULL;
1,989✔
3823
}
1,989✔
3824

3825
/************************************************************
3826

3827
  Function: print_human_readable_time from current_time_ns
3828

3829
*************************************************************/
3830
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
45✔
3831
{
3832
    char time_buffer[100];
3833
    time_t current_time_s;
3834
    struct tm *local_time;
3835

3836
    current_time_s = current_time_ns / 1000000000;
45✔
3837
    local_time = localtime(&current_time_s);
45✔
3838
    switch(timestamp_format) {
45✔
3839
        case 1:
6✔
3840
            // timestamp-format ctime
3841
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
9✔
3842
            if (json_flag)
9✔
3843
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3844
            else
3845
                printf("[%s] ", time_buffer);
6✔
3846
            break;
9✔
3847
        case 2:
6✔
3848
            // timestamp-format iso
3849
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
9✔
3850
            if (json_flag)
9✔
3851
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3852
            else
3853
                printf("[%s] ", time_buffer);
6✔
3854
            break;
9✔
3855
        case 3:
6✔
3856
            // timestamp-format rfc3339
3857
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
9✔
3858
            if (json_flag)
9✔
3859
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3860
            else
3861
                printf("[%s] ", time_buffer);
6✔
3862
            break;
9✔
3863
        default:
12✔
3864
            if (json_flag)
18✔
3865
                printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9);
6✔
3866
            else
3867
                printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3868
    }
6✔
3869
}
45✔
3870

3871
/************************************************************
3872

3873
  Function: ms_since_midnight_utc
3874

3875
*************************************************************
3876

3877
  Input: int64_t: current UTC time in ns
3878

3879
  Output: uint32_t: current time in ms since midnight UTC
3880

3881
  Description:
3882

3883
  Return ICMP Timestamp value corresponding to the given time value.
3884
  The given time value must be in UTC.
3885

3886
*************************************************************/
3887
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
3888
{
3889
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
3890
}
3891

3892
/************************************************************
3893

3894
  Function: usage
3895

3896
*************************************************************
3897

3898
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3899

3900
  Description:
3901

3902
************************************************************/
3903

3904
void usage(int is_error)
297✔
3905
{
3906
    FILE *out = is_error ? stderr : stdout;
297✔
3907
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
297✔
3908
    fprintf(out, "\n");
297✔
3909
    fprintf(out, "Probing options:\n");
297✔
3910
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
297✔
3911
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
297✔
3912
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
297✔
3913
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
297✔
3914
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
297✔
3915
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
297✔
3916
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
297✔
3917
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
297✔
3918
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
297✔
3919
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
297✔
3920
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
297✔
3921
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
297✔
3922
#ifdef SO_BINDTODEVICE
3923
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
297✔
3924
#endif
3925
#ifdef SO_MARK
3926
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
202✔
3927
#endif
3928
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
297✔
3929
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
297✔
3930
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
297✔
3931
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
297✔
3932
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
297✔
3933
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
297✔
3934
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
297✔
3935
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
297✔
3936
    fprintf(out, "   -S, --src=IP       set source address\n");
297✔
3937
    fprintf(out, "       --seqmap-timeout=MSEC sequence number mapping timeout (default: %.0f ms)\n", seqmap_timeout / 1e6);
297✔
3938
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
297✔
3939
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
297✔
3940
    fprintf(out, "       --check-source discard replies not from target address\n");
297✔
3941
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
297✔
3942
    fprintf(out, "\n");
297✔
3943
    fprintf(out, "Output options:\n");
297✔
3944
    fprintf(out, "   -a, --alive        show targets that are alive\n");
297✔
3945
    fprintf(out, "   -A, --addr         show targets by address\n");
297✔
3946
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
297✔
3947
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
297✔
3948
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
297✔
3949
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
297✔
3950
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
297✔
3951
    fprintf(out, "   -J, --json         output in JSON format (-c, -C, or -l required)\n");
297✔
3952
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
297✔
3953
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
297✔
3954
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
297✔
3955
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
297✔
3956
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
297✔
3957
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
297✔
3958
    fprintf(out, "   -s, --stats        print final stats\n");
297✔
3959
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
297✔
3960
    fprintf(out, "   -v, --version      show version\n");
297✔
3961
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
297✔
3962
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
297✔
3963
    fprintf(out, "       --print-tos    show received TOS value\n");
297✔
3964
    fprintf(out, "       --print-ttl    show IP TTL value\n");
297✔
3965
    exit(is_error);
297✔
3966
}
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