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

schweikert / fping / 20653892668

02 Jan 2026 08:18AM UTC coverage: 88.082% (-0.04%) from 88.12%
20653892668

Pull #449

github

web-flow
Merge 39ec392ef into 685edae53
Pull Request #449: Performance: use MSG_DONTWAIT instead of select

18 of 20 new or added lines in 1 file covered. (90.0%)

1 existing line in 1 file now uncovered.

1626 of 1846 relevant lines covered (88.08%)

354.25 hits per line

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

88.21
/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,109✔
275
    return (IP_HEADER_RESULT){-1, -1, 0x80000000U, 0x80000000U, 0x80000000U};
3,109✔
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 (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
35✔
716
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTOS, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
717
                        perror("setsockopt IP_RECVTOS");
×
718
                    }
719
                }
720
#if defined(IPV6) && defined(IPV6_RECVTCLASS)
721
                if (socket6 >= 0) {
35✔
722
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVTCLASS, &sock_opt_on, sizeof(sock_opt_on))) {
35✔
723
                        perror("setsockopt IPV6_RECVTCLASS");
×
724
                    }
725
                }
9✔
726
#endif
727
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
65✔
728
                print_ttl_flag = 1;
29✔
729
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
29✔
730
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTTL, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
731
                        perror("setsockopt IP_RECVTTL");
×
732
                    }
733
                }
734
#if defined(IPV6) && defined(IPV6_RECVHOPLIMIT)
735
                if (socket6 >= 0) {
29✔
736
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &sock_opt_on, sizeof(sock_opt_on))) {
29✔
737
                        perror("setsockopt IPV6_RECVHOPLIMIT");
×
738
                    }
739
                }
7✔
740
#endif
741
            } else if (strstr(optparse_state.optlongname, "seqmap-timeout") != NULL) {
34✔
742
                seqmap_timeout = strtod_strict(optparse_state.optarg) * 1000000;
27✔
743
            } else {
9✔
744
                usage(1);
×
745
            }
746
            break;
133✔
747
        case '4':
32✔
748
#ifdef IPV6
749
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
750
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
751
                exit(1);
3✔
752
            }
753
            hints_ai_family = AF_INET;
40✔
754
#endif
755
            break;
40✔
756
        case '6':
28✔
757
#ifdef IPV6
758
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
31✔
759
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
760
                exit(1);
5✔
761
            }
762
            hints_ai_family = AF_INET6;
26✔
763
#else
764
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
765
            exit(1);
766
#endif
767
            break;
26✔
768
        case 'M':
2✔
769
#ifdef IP_MTU_DISCOVER
770
            if (socket4 >= 0) {
2✔
771
                int val = IP_PMTUDISC_DO;
2✔
772
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
773
                    perror("setsockopt IP_MTU_DISCOVER");
774
                }
775
            }
776
#ifdef IPV6
777
            if (socket6 >= 0) {
2✔
778
                int val = IPV6_PMTUDISC_DO;
2✔
779
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
780
                    perror("setsockopt IPV6_MTU_DISCOVER");
781
                }
782
            }
783
#endif
784
#else
785
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
786
            exit(1);
787
#endif
788
            break;
2✔
789

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

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

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

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

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

812
            count_flag = 1;
127✔
813
            break;
127✔
814

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

820
            count_flag = 1;
48✔
821
            report_all_rtts_flag = 1;
48✔
822
            break;
48✔
823

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

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

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

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

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

858
            break;
29✔
859

860
        case 'e':
6✔
861
            elapsed_flag = 1;
8✔
862
            break;
8✔
863

864
        case 'm':
865
            multif_flag = 1;
×
866
            break;
×
867

868
        case 'N':
2✔
869
            netdata_flag = 1;
3✔
870
            break;
3✔
871

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

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

888
        case 'A':
2✔
889
            addr_flag = 1;
3✔
890
            break;
3✔
891

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

896
        case 's':
8✔
897
            stats_flag = 1;
12✔
898
            break;
12✔
899

900
        case 'D':
22✔
901
            timestamp_flag = 1;
33✔
902
            break;
33✔
903

904
        case 'R':
4✔
905
            random_data_flag = 1;
5✔
906
            break;
5✔
907

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

913
        case 'u':
8✔
914
            unreachable_flag = 1;
12✔
915
            break;
12✔
916

917
        case 'a':
18✔
918
            alive_flag = 1;
26✔
919
            break;
26✔
920

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

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

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

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

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

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

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

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

969
            break;
6✔
970
#endif
971

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

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

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

1015
        case 'J':
42✔
1016
            json_flag = 1;
62✔
1017
            break;
62✔
1018

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

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

1044
        case 'o':
6✔
1045
            outage_flag = 1;
7✔
1046
            break;
7✔
1047

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

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

1063
    /* validate various option settings */
1064

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

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

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

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

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

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

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

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

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

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

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

1126
        alive_flag = unreachable_flag = verbose_flag = 0;
157✔
1127
    }
41✔
1128

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

1133
        alive_flag = unreachable_flag = verbose_flag = 0;
14✔
1134
    }
4✔
1135

1136
    if (alive_flag || unreachable_flag || min_reachable)
531✔
1137
        verbose_flag = 0;
37✔
1138

1139
    trials = (count > retry + 1) ? count : retry + 1;
547✔
1140

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

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

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

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

1166
    if (debugging & DBG_PRINT_PER_SYSTEM)
1167
        print_per_system_flag = 1;
1168

1169
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1170
        report_all_rtts_flag = 1;
1171

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

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

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

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

1260
    update_current_time();
547✔
1261
    start_time = current_time_ns;
547✔
1262

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

1266
    argv = &argv[optparse_state.optind];
547✔
1267
    argc -= optparse_state.optind;
547✔
1268

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

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

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

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

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

1313
        if (!ping_file)
96✔
1314
            errno_crash_and_burn("fopen");
3✔
1315

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

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

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

1380
            if (non_empty)
186✔
1381
                add_name(host);
144✔
1382
        }
1383

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

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

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

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

1431
    init_ping_buffer_ipv4(ping_data_size);
403✔
1432
#ifdef IPV6
1433
    init_ping_buffer_ipv6(ping_data_size);
403✔
1434
#endif
1435

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

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

1460
    last_send_time = 0;
373✔
1461

1462
    seqmap_init(seqmap_timeout);
373✔
1463

1464
    /* main loop */
1465
    main_loop();
373✔
1466

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

1474
    finish();
373✔
1475

1476
    return 0;
81✔
1477
}
1478

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1782
    while (event_queue_ping.first || event_queue_timeout.first) {
2,115✔
1783
        dbg_printf("%s", "# main_loop\n");
1784

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

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

1792
            stats_add(h, event->ping_index, 0, -1);
101✔
1793

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

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

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

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

1822
            /* Dequeue the event */
1823
            event = ev_dequeue(&event_queue_ping);
997✔
1824
            h = event->host;
997✔
1825

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

1828
            /* Send the ping */
1829
            send_ping(h, event->ping_index);
997✔
1830

1831
            /* Loop and count mode: schedule next ping */
1832
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
997✔
1833
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
436✔
1834
            }
112✔
1835
        }
259✔
1836

1837
    wait_for_reply:
735✔
1838

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

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

1857
        /* When is the next timeout event? */
1858
        if (event_queue_timeout.first) {
1,657✔
1859
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
1,046✔
1860
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
1,046✔
1861
                wait_time_ns = wait_time_timeout;
436✔
1862
                if (wait_time_ns < 0) {
436✔
UNCOV
1863
                    wait_time_ns = 0;
×
1864
                }
1865
            }
107✔
1866

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

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

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

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

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

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

1901
        update_current_time();
1,626✔
1902

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

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

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

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

1932
  Function: signal_handler
1933

1934
*************************************************************
1935

1936
  Inputs:  int signum
1937

1938
  Description:
1939

1940
  SIGQUIT signal handler - set flag and return
1941
  SIGINT signal handler - set flag and return
1942

1943
************************************************************/
1944

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

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

1958
/************************************************************
1959

1960
  Function: update_current_time
1961

1962
*************************************************************/
1963

1964
void update_current_time()
5,485✔
1965
{
1966
    clock_gettime(CLOCKID, &current_time);
5,485✔
1967
    current_time_ns = timespec_ns(&current_time);
5,485✔
1968
}
5,485✔
1969

1970
/************************************************************
1971

1972
  Function: finish
1973

1974
*************************************************************
1975

1976
  Inputs:  void (none)
1977

1978
  Description:
1979

1980
  Main program clean up and exit point
1981

1982
************************************************************/
1983

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

1989
    update_current_time();
388✔
1990
    end_time = current_time_ns;
388✔
1991

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

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

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

2002
                if (verbose_flag)
47✔
2003
                    printf(" is unreachable");
44✔
2004

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

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

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

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

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

2048
    exit(0);
328✔
2049
}
2050

2051
/************************************************************
2052

2053
  Function: print_recv
2054

2055
*************************************************************
2056

2057
  Inputs: HOST_ENTRY *h, int64_t recv_time, int result,
2058
          int this_count, int64_t this_reply, int avg
2059

2060
  Description:
2061

2062
************************************************************/
2063

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

2068
        if (timestamp_flag)
35✔
2069
            print_timestamp_format(recv_time, timestamp_format_flag);
15✔
2070

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

2078
    /* Normal Output */
2079
    if (timestamp_flag)
240✔
2080
        print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2081

2082
    printf("%-*s : [%d], %d bytes, %s ms",
240✔
2083
        max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
72✔
2084

2085
    printf(" (%s avg, ", sprint_tm(avg));
240✔
2086

2087
    if (h->num_recv <= h->num_sent) {
240✔
2088
        printf("%d%% loss)",
240✔
2089
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
240✔
2090
    }
72✔
2091
    else {
2092
        printf("%d%% return)",
×
2093
            (h->num_recv_total * 100) / h->num_sent);
×
2094
    }
2095
}
83✔
2096

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

2099
  Function: print_timeout
2100

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

2103
  Inputs: HOST_ENTRY *h, int ping_index
2104

2105
  Description:
2106

2107
************************************************************/
2108

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

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

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

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

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

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

2146
/************************************************************
2147

2148
  Function: print_recv_ext
2149

2150
*************************************************************
2151

2152
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2153
           int64_t recv_time, int64_t this_reply
2154

2155
  Description:
2156

2157
************************************************************/
2158

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

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

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

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

2191
/************************************************************
2192

2193
  Function: print_recv_ext_json
2194

2195
*************************************************************
2196

2197
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2198
           int64_t recv_time, int64_t this_reply
2199

2200
  Description:
2201

2202
************************************************************/
2203

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

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

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

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

2234
    printf("}}");
35✔
2235
    printf("\n");
35✔
2236
}
35✔
2237

2238
/************************************************************
2239

2240
  Function: print_per_system_stats
2241

2242
*************************************************************
2243

2244
  Inputs:  void (none)
2245

2246
  Description:
2247

2248

2249
************************************************************/
2250

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

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

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

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

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

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

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

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

2303
/************************************************************
2304

2305
  Function: print_per_system_stats_json
2306

2307
*************************************************************
2308

2309
  Inputs:  void (none)
2310

2311
  Description:
2312

2313

2314
************************************************************/
2315

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

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

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

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

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

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

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

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

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

2377
/************************************************************
2378

2379
  Function: print_netdata
2380

2381
*************************************************************
2382

2383
  Inputs:  void (none)
2384

2385
  Description:
2386

2387

2388
************************************************************/
2389

2390
void print_netdata(void)
3✔
2391
{
2392
    static int sent_charts = 0;
2393

2394
    int i;
2395
    int64_t avg;
2396
    HOST_ENTRY *h;
2397

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

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

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

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

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

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

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

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

2445
        stats_reset_interval(h);
3✔
2446
    }
1✔
2447

2448
    sent_charts = 1;
3✔
2449
}
3✔
2450

2451
/************************************************************
2452

2453
  Function: print_per_system_splits
2454

2455
*************************************************************
2456

2457
  Inputs:  void (none)
2458

2459
  Description:
2460

2461

2462
************************************************************/
2463

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

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

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

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

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

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

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

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

2511
/************************************************************
2512

2513
  Function: print_per_system_splits_json
2514

2515
*************************************************************
2516

2517
  Inputs:  void (none)
2518

2519
  Description:
2520

2521

2522
************************************************************/
2523

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

2529
    update_current_time();
3✔
2530

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

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

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

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

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

2568
/************************************************************
2569

2570
  Function: print_global_stats
2571

2572
*************************************************************
2573

2574
  Inputs:  void (none)
2575

2576
  Description:
2577

2578

2579
************************************************************/
2580

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

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

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

2611
/************************************************************
2612

2613
  Function: print_global_stats_json
2614

2615
*************************************************************
2616

2617
  Inputs:  void (none)
2618

2619
  Description:
2620

2621

2622
************************************************************/
2623

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

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

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

2650
/************************************************************
2651

2652
  Function: send_ping
2653

2654
*************************************************************
2655

2656
  Inputs:  int s, HOST_ENTRY *h
2657

2658
  Description:
2659

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

2666
************************************************************/
2667

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

2675
    update_current_time();
1,032✔
2676
    h->last_send_time = current_time_ns;
1,032✔
2677
    myseq = seqmap_add(h->i, index, current_time_ns);
1,032✔
2678

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

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

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

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

2714
        ret = 0;
34✔
2715
    }
2716
    else {
2717
        /* schedule timeout */
2718
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
998✔
2719

2720
        /* mark this trial as outstanding */
2721
        if (!loop_flag) {
998✔
2722
            h->resp_times[index] = RESP_WAITING;
868✔
2723
        }
238✔
2724
    }
2725

2726
    num_pingsent++;
1,032✔
2727
    last_send_time = h->last_send_time;
1,032✔
2728

2729
    return (ret);
1,032✔
2730
}
268✔
2731

2732
int socket_can_read(struct timeval *timeout)
1,626✔
2733
{
2734
    int nfound;
2735
    fd_set readset;
2736
    int socketmax;
2737

2738
#ifndef IPV6
2739
    socketmax = socket4;
2740
#else
2741
    socketmax = socket4 > socket6 ? socket4 : socket6;
1,626✔
2742
#endif
2743

2744
select_again:
1,211✔
2745
    FD_ZERO(&readset);
21,019✔
2746
    if (socket4 >= 0)
1,643✔
2747
        FD_SET(socket4, &readset);
1,643✔
2748
#ifdef IPV6
2749
    if (socket6 >= 0)
1,643✔
2750
        FD_SET(socket6, &readset);
1,643✔
2751
#endif
2752

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

2764
    if (nfound > 0) {
1,626✔
2765
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
907✔
2766
            return socket4;
814✔
2767
        }
2768
#ifdef IPV6
2769
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
93✔
2770
            return socket6;
93✔
2771
        }
2772
#endif
2773
    }
2774

2775
    return -1;
719✔
2776
}
427✔
2777

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

2808
    /* Wait for a socket to become ready */
2809
#if HAVE_MSG_DONTWAIT
2810
    if (wait_time == 0) {
3,109✔
2811
        /* Optimization: if wait_time is 0, we can skip select() and just try to
2812
         * read from the sockets with MSG_DONTWAIT */
2813
        recv_len = -1;
1,483✔
2814
        if (socket4 >= 0) {
1,483✔
2815
            recv_len = recvmsg(socket4, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT);
1,483✔
2816
        }
247✔
2817
#ifdef IPV6
2818
        if (recv_len <= 0 && socket6 >= 0) {
1,483✔
2819
            /* Reset fields potentially modified by failed recvmsg */
2820
            recv_msghdr.msg_namelen = reply_src_addr_len;
909✔
2821
            recv_msghdr.msg_controllen = sizeof(msg_control);
909✔
2822
            recv_len = recvmsg(socket6, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT);
909✔
2823
        }
247✔
2824
#endif
2825
    }
247✔
2826
    else
2827
#endif
2828
    {
2829
        if (wait_time) {
1,626✔
2830
            to.tv_sec = wait_time / UINT64_C(1000000000);
1,626✔
2831
            to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,626✔
2832
        }
427✔
2833
        else {
NEW
2834
            to.tv_sec = 0;
×
NEW
2835
            to.tv_usec = 0;
×
2836
        }
2837

2838
        s = socket_can_read(&to);
1,626✔
2839
        if (s == -1) {
1,626✔
2840
            return 0; /* timeout */
719✔
2841
        }
2842
        recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
907✔
2843
    }
2844

2845
    if (recv_len <= 0) {
2,390✔
2846
        return 0;
907✔
2847
    }
2848

2849
    /* ancilliary data */
2850
    {
2851
#if HAVE_SO_TIMESTAMPNS
2852
        struct timespec reply_timestamp_ts;
2853
        struct timeval reply_timestamp_tv;
2854
#endif
2855
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,483✔
2856
             cmsg != NULL;
2,727✔
2857
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,244✔
2858
#if HAVE_SO_TIMESTAMPNS
2859
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,244✔
2860
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,236✔
2861
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,236✔
2862
            }
2863
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,244✔
2864
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2865
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2866
            }
2867
#endif
2868
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,244✔
2869
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2870
            }
2871
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,244✔
2872
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2873
            }
2874
#ifdef IPV6
2875
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,244✔
2876
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2877
            }
2878
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,244✔
2879
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2880
            }
2881
#endif
2882
        }
2883
    }
2884

2885
#if defined(DEBUG) || defined(_DEBUG)
2886
    if (randomly_lose_flag) {
2887
        if ((random() & 0x07) <= lose_factor)
2888
            return 0;
2889
    }
2890
#endif
2891

2892
    return recv_len;
1,483✔
2893
}
674✔
2894

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

2909
    if (!success) {
984✔
2910
        if (!loop_flag && index >= 0) {
101✔
2911
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2912
        }
21✔
2913
        num_timeout++;
101✔
2914
        return;
101✔
2915
    }
2916

2917
    /* received count */
2918
    h->num_recv++;
883✔
2919
    h->num_recv_i++;
883✔
2920

2921
    /* maximum */
2922
    if (!h->max_reply || latency > h->max_reply) {
883✔
2923
        h->max_reply = latency;
661✔
2924
    }
184✔
2925
    if (!h->max_reply_i || latency > h->max_reply_i) {
883✔
2926
        h->max_reply_i = latency;
690✔
2927
    }
187✔
2928

2929
    /* minimum */
2930
    if (!h->min_reply || latency < h->min_reply) {
883✔
2931
        h->min_reply = latency;
564✔
2932
    }
161✔
2933
    if (!h->min_reply_i || latency < h->min_reply_i) {
883✔
2934
        h->min_reply_i = latency;
595✔
2935
    }
165✔
2936

2937
    /* total time (for average) */
2938
    h->total_time += latency;
883✔
2939
    h->total_time_i += latency;
883✔
2940

2941
    /* response time per-packet (count mode) */
2942
    if (!loop_flag && index >= 0) {
883✔
2943
        h->resp_times[index] = latency;
767✔
2944
    }
217✔
2945
}
264✔
2946

2947
/* stats_reset_interval: reset interval statistics
2948
 * h: host entry to update
2949
 */
2950
void stats_reset_interval(HOST_ENTRY *h)
31✔
2951
{
2952
    h->num_sent_i = 0;
31✔
2953
    h->num_recv_i = 0;
31✔
2954
    h->max_reply_i = 0;
31✔
2955
    h->min_reply_i = 0;
31✔
2956
    h->total_time_i = 0;
31✔
2957
}
31✔
2958

2959
int decode_icmp_ipv4(
1,388✔
2960
    struct sockaddr *response_addr,
2961
    size_t response_addr_len,
2962
    char *reply_buf,
2963
    size_t reply_buf_len,
2964
    unsigned short *id,
2965
    unsigned short *seq,
2966
    IP_HEADER_RESULT *ip_header_res)
2967
{
2968
    struct icmp *icp;
2969
    int hlen = 0;
1,388✔
2970

2971
    if (!using_sock_dgram4) {
1,388✔
2972
        struct ip *ip = (struct ip *)reply_buf;
1,380✔
2973
        ip_header_res->tos = ip->ip_tos;
1,380✔
2974
        ip_header_res->ttl = ip->ip_ttl;
1,380✔
2975

2976
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2977
        /* The alpha headers are decidedly broken.
2978
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2979
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2980
         */
2981
        hlen = (ip->ip_vhl & 0x0F) << 2;
2982
#else
2983
        hlen = ip->ip_hl << 2;
1,380✔
2984
#endif
2985
    }
232✔
2986

2987
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,388✔
2988
        /* too short */
2989
        if (verbose_flag) {
×
2990
            char buf[INET6_ADDRSTRLEN];
2991
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2992
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2993
        }
2994
        return -1;
×
2995
    }
2996

2997
    icp = (struct icmp *)(reply_buf + hlen);
1,388✔
2998

2999
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,388✔
3000
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
598✔
3001
        /* Handle other ICMP packets */
3002
        struct icmp *sent_icmp;
3003
        SEQMAP_VALUE *seqmap_value;
3004
        char addr_ascii[INET6_ADDRSTRLEN];
3005
        HOST_ENTRY *h;
3006

3007
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
3008
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
1,038✔
3009
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
3010
            return -1;
×
3011
        }
3012

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

3015
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
574✔
3016
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
3017
            sent_icmp->icmp_id != ident4) {
×
3018
            /* not caused by us */
3019
            return -1;
574✔
3020
        }
3021

3022
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
3023
        if (seqmap_value == NULL) {
×
3024
            return -1;
×
3025
        }
3026

3027
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3028

3029
        switch (icp->icmp_type) {
×
3030
        case ICMP_UNREACH:
3031
            h = table[seqmap_value->host_nr];
×
3032
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
3033
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
3034
                    addr_ascii, h->host);
3035
            }
3036
            else {
3037
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3038
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
3039
            }
3040

3041
            print_warning("\n");
×
3042
            num_othericmprcvd++;
×
3043
            break;
×
3044

3045
        case ICMP_SOURCEQUENCH:
3046
        case ICMP_REDIRECT:
3047
        case ICMP_TIMXCEED:
3048
        case ICMP_PARAMPROB:
3049
            h = table[seqmap_value->host_nr];
×
3050
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
3051
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3052
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
3053
            }
3054
            else {
3055
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
3056
                    icp->icmp_type, addr_ascii, h->host);
×
3057
            }
3058
            print_warning("\n");
×
3059
            num_othericmprcvd++;
×
3060
            break;
×
3061
        }
3062

3063
        return -1;
×
3064
    }
3065

3066
    *id = icp->icmp_id;
814✔
3067
    *seq = ntohs(icp->icmp_seq);
814✔
3068
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
814✔
3069

3070
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
3071
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
3072
            if (verbose_flag) {
×
3073
                char buf[INET6_ADDRSTRLEN];
3074
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3075
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3076
            }
3077
            return -1;
×
3078
        }
3079

3080
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
3081
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
3082
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
3083
    }
3084

3085
    return hlen;
814✔
3086
}
232✔
3087

3088
#ifdef IPV6
3089
int decode_icmp_ipv6(
95✔
3090
    struct sockaddr *response_addr,
3091
    size_t response_addr_len,
3092
    char *reply_buf,
3093
    size_t reply_buf_len,
3094
    unsigned short *id,
3095
    unsigned short *seq)
3096
{
3097
    struct icmp6_hdr *icp;
3098

3099
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
95✔
3100
        if (verbose_flag) {
×
3101
            char buf[INET6_ADDRSTRLEN];
3102
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3103
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3104
        }
3105
        return 0; /* too short */
×
3106
    }
3107

3108
    icp = (struct icmp6_hdr *)reply_buf;
95✔
3109

3110
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
95✔
3111
        /* Handle other ICMPv6 packets */
3112
        struct ip6_hdr *sent_ipv6;
3113
        struct icmp6_hdr *sent_icmp;
3114
        SEQMAP_VALUE *seqmap_value;
3115
        char addr_ascii[INET6_ADDRSTRLEN];
3116
        HOST_ENTRY *h;
3117

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

3124
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
6✔
3125
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
6✔
3126
            /* discard ICMPv6 message if we can't tell that it was caused by
3127
             * us, because the IPv6 header is not directly followed by an
3128
             * ICMPv6 header
3129
             */
3130
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
3131
            return 0;
×
3132
        }
3133
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
6✔
3134

3135
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
6✔
3136
            /* not caused by us */
3137
            return 0;
6✔
3138
        }
3139

3140
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
3141
        if (seqmap_value == NULL) {
×
3142
            return 0;
×
3143
        }
3144

3145
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3146
        h = table[seqmap_value->host_nr];
×
3147

3148
        switch (icp->icmp6_type) {
×
3149
        case ICMP6_DST_UNREACH:
3150
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
3151
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3152
                    icp->icmp6_code, addr_ascii, h->host);
×
3153
            } else {
3154
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3155
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
3156
            }
3157
            print_warning("\n");
×
3158
            num_othericmprcvd++;
×
3159
            break;
×
3160

3161
        case ICMP6_PACKET_TOO_BIG:
3162
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
3163
                addr_ascii, h->host);
3164
            num_othericmprcvd++;
×
3165
            break;
×
3166

3167
        case ICMP6_TIME_EXCEEDED:
3168
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
3169
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3170
                    icp->icmp6_code, addr_ascii, h->host);
×
3171
            } else {
3172
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3173
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
3174
            }
3175
            print_warning("\n");
×
3176
            num_othericmprcvd++;
×
3177
            break;
×
3178

3179
        case ICMP6_PARAM_PROB:
3180
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
3181
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3182
                    icp->icmp6_code, addr_ascii, h->host);
×
3183
            } else {
3184
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3185
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
3186
            }
3187
            print_warning("\n");
×
3188
            num_othericmprcvd++;
×
3189
            break;
×
3190

3191
        default:
3192
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
3193
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
3194
            num_othericmprcvd++;
×
3195
            break;
×
3196
        }
3197

3198
        return 0;
×
3199
    }
3200

3201
    *id = icp->icmp6_id;
89✔
3202
    *seq = ntohs(icp->icmp6_seq);
89✔
3203

3204
    return 1;
89✔
3205
}
15✔
3206
#endif
3207

3208
int wait_for_reply(int64_t wait_time)
3,109✔
3209
{
3210
    int result;
3211
    static char buffer[RECV_BUFSIZE];
3212
    struct sockaddr_storage response_addr;
3213
    int n, avg;
3214
    HOST_ENTRY *h;
3215
    int64_t this_reply;
3216
    int this_count;
3217
    int64_t recv_time = 0;
3,109✔
3218
    SEQMAP_VALUE *seqmap_value;
3219
    unsigned short id;
3220
    unsigned short seq;
3221
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
3,109✔
3222

3223
    /* Receive packet */
3224
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,783✔
3225
        &recv_time, /* reply_timestamp */
3226
        (struct sockaddr *)&response_addr, /* reply_src_addr */
3227
        sizeof(response_addr), /* reply_src_addr_len */
3228
        buffer, /* reply_buf */
3229
        sizeof(buffer), /* reply_buf_len */
3230
        &ip_header_res.tos, /* TOS resp. TC byte */
674✔
3231
        &ip_header_res.ttl /* TTL resp. hop limit */
674✔
3232
    );
3233

3234
    if (result <= 0) {
3,109✔
3235
        return 0;
1,626✔
3236
    }
3237

3238
    update_current_time();
1,483✔
3239
    if (recv_time == 0)
1,483✔
3240
        recv_time = current_time_ns;
247✔
3241

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

3284
    seqmap_value = seqmap_fetch(seq, current_time_ns);
903✔
3285
    if (seqmap_value == NULL) {
903✔
3286
        return 1;
12✔
3287
    }
3288

3289
    /* find corresponding host_entry */
3290
    n = seqmap_value->host_nr;
891✔
3291
    h = table[n];
891✔
3292
    this_count = seqmap_value->ping_count;
891✔
3293
    this_reply = recv_time - seqmap_value->ping_ts;
891✔
3294

3295
    /* update stats that include invalid replies */
3296
    h->num_recv_total++;
891✔
3297
    num_pingreceived++;
891✔
3298

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

3301
    /* optionally require reply source equal to target address */
3302
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
891✔
3303
        dbg_printf("%s\n", "discarding reply from wrong source address");
3304
        return 1;
8✔
3305
    }
3306

3307
    /* discard duplicates */
3308
    if (!loop_flag && !(count_flag && quiet_flag) && h->resp_times[this_count] >= 0) {
883✔
3309
        if (!per_recv_flag) {
×
3310
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3311
                h->host, this_count, result, sprint_tm(this_reply));
3312

3313
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3314
                char buf[INET6_ADDRSTRLEN];
3315
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3316
                fprintf(stderr, " [<- %s]", buf);
×
3317
            }
3318
            fprintf(stderr, "\n");
×
3319
        }
3320
        return 1;
×
3321
    }
3322

3323
    /* discard reply if delay is larger than timeout
3324
     * (see also: github #32) */
3325
    if (this_reply > h->timeout) {
883✔
3326
        return 1;
×
3327
    }
3328

3329
    /* update stats */
3330
    stats_add(h, this_count, 1, this_reply);
883✔
3331
    // TODO: move to stats_add?
3332
    if (!max_reply || this_reply > max_reply)
883✔
3333
        max_reply = this_reply;
541✔
3334
    if (!min_reply || this_reply < min_reply)
883✔
3335
        min_reply = this_reply;
457✔
3336
    sum_replies += this_reply;
883✔
3337
    total_replies++;
883✔
3338

3339
    /* initialize timeout to initial timeout (without backoff) */
3340
    h->timeout = timeout;
883✔
3341

3342
    /* remove timeout event */
3343
    struct event *timeout_event = host_get_timeout_event(h, this_count);
883✔
3344
    if (timeout_event) {
883✔
3345
        ev_remove(&event_queue_timeout, timeout_event);
883✔
3346
    }
243✔
3347

3348
    /* print "is alive" */
3349
    if (h->num_recv == 1) {
883✔
3350
        num_alive++;
503✔
3351
        if (fast_reachable && num_alive >= min_reachable)
503✔
3352
            finish_requested = 1;
3✔
3353

3354
        if (verbose_flag || alive_flag) {
503✔
3355
            printf("%s", h->host);
302✔
3356

3357
            if (verbose_flag)
302✔
3358
                printf(" is alive");
282✔
3359
        }
86✔
3360
    }
143✔
3361

3362
    /* print received ping (unless --quiet) */
3363
    if (per_recv_flag) {
883✔
3364
        avg = h->total_time / h->num_recv;
275✔
3365
        print_recv(h,
358✔
3366
            recv_time,
83✔
3367
            result,
83✔
3368
            this_count,
83✔
3369
            this_reply,
83✔
3370
            avg);
83✔
3371
    }
83✔
3372

3373
    if (verbose_flag || alive_flag || per_recv_flag) {
883✔
3374
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
577✔
3375
            char buf[INET6_ADDRSTRLEN];
3376
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3377
            fprintf(stderr, " [<- %s]", buf);
6✔
3378
        }
3379
        if (json_flag) {
577✔
3380
            print_recv_ext_json(&ip_header_res,
35✔
3381
                recv_time,
11✔
3382
                this_reply);
11✔
3383
        }
11✔
3384
        else {
3385
            print_recv_ext(&ip_header_res,
542✔
3386
                recv_time,
158✔
3387
                this_reply);
158✔
3388
        }
3389
    }
169✔
3390
    return 1;
883✔
3391
}
674✔
3392

3393
/************************************************************
3394

3395
  Function: add_name
3396

3397
*************************************************************
3398

3399
  Inputs:  char* name
3400

3401
  Description:
3402

3403
  process input name for addition to target list
3404
  name can turn into multiple targets via multiple interfaces (-m)
3405
  or via NIS groups
3406

3407
************************************************************/
3408

3409
void add_name(char *name)
624✔
3410
{
3411
    struct addrinfo *res0, *res, hints;
3412
    int ret_ga;
3413
    char *printname;
3414
    char namebuf[256];
3415
    char addrbuf[256];
3416

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

3440
        // Handle JSON output for invalid hosts
3441
        if (json_flag) {
29✔
3442
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
3443
            return;
21✔
3444
        }
3445

3446
        return;
26✔
3447
    }
3448

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

3468
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3469
                printname = namebuf;
3✔
3470
            }
1✔
3471
            else {
3472
                printname = name;
×
3473
            }
3474
        }
1✔
3475
        else {
3476
            printname = name;
592✔
3477
        }
3478

3479
        /* addr_flag: name -> addr lookup requested */
3480
        if (addr_flag) {
595✔
3481
            int ret;
3482
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3483
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3484
            if (ret) {
3✔
3485
                if (!quiet_flag) {
×
3486
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3487
                }
3488
                continue;
×
3489
            }
3490

3491
            if (name_flag || rdns_flag) {
3✔
3492
                char nameaddrbuf[512 + 3];
3493
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3494
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3495
            }
3496
            else {
3497
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3498
            }
3499
        }
1✔
3500
        else {
3501
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
592✔
3502
        }
3503

3504
        if (!multif_flag) {
595✔
3505
            break;
595✔
3506
        }
3507
    }
3508

3509
    freeaddrinfo(res0);
595✔
3510
}
166✔
3511

3512
/************************************************************
3513

3514
  Function: add_addr
3515

3516
*************************************************************
3517

3518
  Description:
3519

3520
  add single address to list of hosts to be pinged
3521

3522
************************************************************/
3523

3524
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
595✔
3525
{
3526
    HOST_ENTRY *p;
3527
    int n;
3528
    int64_t *i;
3529

3530
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
595✔
3531
    if (!p)
595✔
3532
        crash_and_burn("can't allocate HOST_ENTRY");
×
3533

3534
    p->name = strdup(name);
595✔
3535
    p->host = strdup(host);
595✔
3536
    memcpy(&p->saddr, ipaddr, ipaddr_len);
595✔
3537
    p->saddr_len = ipaddr_len;
595✔
3538
    p->timeout = timeout;
595✔
3539
    p->min_reply = 0;
595✔
3540

3541
    if (netdata_flag) {
595✔
3542
        char *s = p->name;
3✔
3543
        while (*s) {
30✔
3544
            if (!isalnum(*s))
27✔
3545
                *s = '_';
9✔
3546
            s++;
27✔
3547
        }
3548
    }
1✔
3549

3550
    if (strlen(p->host) > max_hostname_len)
595✔
3551
        max_hostname_len = strlen(p->host);
402✔
3552

3553
    /* array for response time results */
3554
    if (!loop_flag) {
595✔
3555
#if SIZE_MAX <= UINT_MAX
3556
        if (trials > (SIZE_MAX / sizeof(int64_t)))
3557
            crash_and_burn("resp_times array too large for memory");
3558
#endif
3559
        i = (int64_t *)malloc(trials * sizeof(int64_t));
581✔
3560
        if (!i)
581✔
3561
            crash_and_burn("can't allocate resp_times array");
×
3562

3563
        for (n = 0; n < trials; n++)
2,886✔
3564
            i[n] = RESP_UNUSED;
2,305✔
3565

3566
        p->resp_times = i;
581✔
3567
    }
153✔
3568

3569
    /* allocate event storage */
3570
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
595✔
3571
    if (!p->event_storage_ping) {
595✔
3572
        errno_crash_and_burn("can't allocate event_storage_ping");
×
3573
    }
3574
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
595✔
3575
    if (!p->event_storage_timeout) {
595✔
3576
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
3577
    }
3578

3579
    /* schedule first ping */
3580
    host_add_ping_event(p, 0, current_time_ns);
595✔
3581

3582
    num_hosts++;
595✔
3583
}
595✔
3584

3585
/************************************************************
3586

3587
  Function: crash_and_burn
3588

3589
*************************************************************
3590

3591
  Inputs:  char* message
3592

3593
  Description:
3594

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

3597
void crash_and_burn(char *message)
×
3598
{
3599
    fprintf(stderr, "%s: %s\n", prog, message);
×
3600
    exit(4);
×
3601
}
3602

3603
/************************************************************
3604

3605
  Function: errno_crash_and_burn
3606

3607
*************************************************************
3608

3609
  Inputs:  char* message
3610

3611
  Description:
3612

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

3615
void errno_crash_and_burn(char *message)
8✔
3616
{
3617
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3618
    exit(4);
8✔
3619
}
3620

3621
/************************************************************
3622

3623
  Function: print_warning
3624

3625
  Description: fprintf(stderr, ...), unless running with -q
3626

3627
*************************************************************/
3628

3629
void print_warning(char *format, ...)
54✔
3630
{
3631
    va_list args;
3632
    if (!quiet_flag) {
54✔
3633
        va_start(args, format);
54✔
3634
        vfprintf(stderr, format, args);
54✔
3635
        va_end(args);
54✔
3636
    }
8✔
3637
}
54✔
3638

3639
/************************************************************
3640

3641
  Function: sprint_tm
3642

3643
*************************************************************
3644

3645
  render nanosecond int64_t value into milliseconds string with three digits of
3646
  precision.
3647

3648
************************************************************/
3649

3650
const char *sprint_tm(int64_t ns)
1,262✔
3651
{
3652
    static char buf[10];
3653
    double t = (double)ns / 1e6;
1,262✔
3654

3655
    if (t < 0.0) {
1,262✔
3656
        /* negative (unexpected) */
3657
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3658
    }
3659
    else if (t < 1.0) {
1,262✔
3660
        /* <= 0.99 ms */
3661
        snprintf(buf, sizeof(buf), "%.3f", t);
1,260✔
3662
    }
352✔
3663
    else if (t < 10.0) {
2✔
3664
        /* 1.00 - 9.99 ms */
3665
        snprintf(buf, sizeof(buf), "%.2f", t);
2✔
3666
    }
2✔
3667
    else if (t < 100.0) {
×
3668
        /* 10.0 - 99.9 ms */
3669
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3670
    }
3671
    else if (t < 1000000.0) {
×
3672
        /* 100 - 1'000'000 ms */
3673
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3674
    }
3675
    else {
3676
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3677
    }
3678

3679
    return (buf);
1,262✔
3680
}
3681

3682
/************************************************************
3683

3684
  Function: addr_cmp
3685

3686
*************************************************************/
3687
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
595✔
3688
{
3689
    if (a->sa_family != b->sa_family) {
595✔
3690
        return a->sa_family - b->sa_family;
×
3691
    }
3692
    else {
3693
        if (a->sa_family == AF_INET) {
595✔
3694
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
533✔
3695
        }
3696
        else if (a->sa_family == AF_INET6) {
62✔
3697
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
70✔
3698
                &((struct sockaddr_in6 *)b)->sin6_addr,
62✔
3699
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3700
        }
3701
    }
3702

3703
    return 0;
×
3704
}
171✔
3705

3706
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,031✔
3707
{
3708
    struct event *event = &h->event_storage_ping[index % event_storage_count];
1,031✔
3709
    event->host = h;
1,031✔
3710
    event->ping_index = index;
1,031✔
3711
    event->ev_time = ev_time;
1,031✔
3712
    ev_enqueue(&event_queue_ping, event);
1,031✔
3713

3714
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3715
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3716
}
1,031✔
3717

3718
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
998✔
3719
{
3720
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
998✔
3721
    event->host = h;
998✔
3722
    event->ping_index = index;
998✔
3723
    event->ev_time = ev_time;
998✔
3724
    ev_enqueue(&event_queue_timeout, event);
998✔
3725

3726
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3727
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3728
}
998✔
3729

3730
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
883✔
3731
{
3732
    return &h->event_storage_timeout[index % event_storage_count];
883✔
3733
}
3734

3735
/************************************************************
3736

3737
  Function: ev_enqueue
3738

3739
  Enqueue an event
3740

3741
  The queue is sorted by event->ev_time, so that queue->first always points to
3742
  the earliest event.
3743

3744
  We start scanning the queue from the tail, because we assume
3745
  that new events mostly get inserted with a event time higher
3746
  than the others.
3747

3748
*************************************************************/
3749
void ev_enqueue(struct event_queue *queue, struct event *event)
2,029✔
3750
{
3751
    struct event *i;
3752
    struct event *i_prev;
3753

3754
    /* Empty list */
3755
    if (queue->last == NULL) {
2,029✔
3756
        event->ev_next = NULL;
1,637✔
3757
        event->ev_prev = NULL;
1,637✔
3758
        queue->first = event;
1,637✔
3759
        queue->last = event;
1,637✔
3760
        return;
1,637✔
3761
    }
3762

3763
    /* Insert on tail? */
3764
    if (event->ev_time - queue->last->ev_time >= 0) {
392✔
3765
        event->ev_next = NULL;
392✔
3766
        event->ev_prev = queue->last;
392✔
3767
        queue->last->ev_next = event;
392✔
3768
        queue->last = event;
392✔
3769
        return;
392✔
3770
    }
3771

3772
    /* Find insertion point */
3773
    i = queue->last;
×
3774
    while (1) {
3775
        i_prev = i->ev_prev;
×
3776
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3777
            event->ev_prev = i_prev;
×
3778
            event->ev_next = i;
×
3779
            i->ev_prev = event;
×
3780
            if (i_prev != NULL) {
×
3781
                i_prev->ev_next = event;
×
3782
            }
3783
            else {
3784
                queue->first = event;
×
3785
            }
3786
            return;
×
3787
        }
3788
        i = i_prev;
×
3789
    }
3790
}
537✔
3791

3792
/************************************************************
3793

3794
  Function: ev_dequeue
3795

3796
*************************************************************/
3797
struct event *ev_dequeue(struct event_queue *queue)
1,098✔
3798
{
3799
    struct event *dequeued;
3800

3801
    if (queue->first == NULL) {
1,098✔
3802
        return NULL;
×
3803
    }
3804
    dequeued = queue->first;
1,098✔
3805
    ev_remove(queue, dequeued);
1,098✔
3806

3807
    return dequeued;
1,098✔
3808
}
280✔
3809

3810
/************************************************************
3811

3812
  Function: ev_remove
3813

3814
*************************************************************/
3815
void ev_remove(struct event_queue *queue, struct event *event)
1,981✔
3816
{
3817
    if (queue->first == event) {
1,981✔
3818
        queue->first = event->ev_next;
1,972✔
3819
    }
520✔
3820
    if (queue->last == event) {
1,981✔
3821
        queue->last = event->ev_prev;
1,610✔
3822
    }
409✔
3823
    if (event->ev_prev) {
1,981✔
3824
        event->ev_prev->ev_next = event->ev_next;
9✔
3825
    }
3✔
3826
    if (event->ev_next) {
1,981✔
3827
        event->ev_next->ev_prev = event->ev_prev;
371✔
3828
    }
114✔
3829
    event->ev_prev = NULL;
1,981✔
3830
    event->ev_next = NULL;
1,981✔
3831
}
1,981✔
3832

3833
/************************************************************
3834

3835
  Function: print_human_readable_time from current_time_ns
3836

3837
*************************************************************/
3838
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
45✔
3839
{
3840
    char time_buffer[100];
3841
    time_t current_time_s;
3842
    struct tm *local_time;
3843

3844
    current_time_s = current_time_ns / 1000000000;
45✔
3845
    local_time = localtime(&current_time_s);
45✔
3846
    switch(timestamp_format) {
45✔
3847
        case 1:
6✔
3848
            // timestamp-format ctime
3849
            strftime(time_buffer, sizeof(time_buffer), "%c", 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 2:
6✔
3856
            // timestamp-format iso
3857
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", 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
        case 3:
6✔
3864
            // timestamp-format rfc3339
3865
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
9✔
3866
            if (json_flag)
9✔
3867
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3868
            else
3869
                printf("[%s] ", time_buffer);
6✔
3870
            break;
9✔
3871
        default:
12✔
3872
            if (json_flag)
18✔
3873
                printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9);
6✔
3874
            else
3875
                printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3876
    }
6✔
3877
}
45✔
3878

3879
/************************************************************
3880

3881
  Function: ms_since_midnight_utc
3882

3883
*************************************************************
3884

3885
  Input: int64_t: current UTC time in ns
3886

3887
  Output: uint32_t: current time in ms since midnight UTC
3888

3889
  Description:
3890

3891
  Return ICMP Timestamp value corresponding to the given time value.
3892
  The given time value must be in UTC.
3893

3894
*************************************************************/
3895
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
3896
{
3897
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
3898
}
3899

3900
/************************************************************
3901

3902
  Function: usage
3903

3904
*************************************************************
3905

3906
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3907

3908
  Description:
3909

3910
************************************************************/
3911

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