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

schweikert / fping / 20097480429

10 Dec 2025 11:45AM UTC coverage: 85.236% (-1.3%) from 86.573%
20097480429

Pull #421

github

web-flow
Merge f994b77a6 into 27b86877b
Pull Request #421: New option --traceroute to send a traceroute with fping

11 of 40 new or added lines in 1 file covered. (27.5%)

2 existing lines in 1 file now uncovered.

1605 of 1883 relevant lines covered (85.24%)

322.21 hits per line

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

85.12
/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() {
2,752✔
275
    return (IP_HEADER_RESULT){-1, -1, 0x80000000U, 0x80000000U, 0x80000000U};
2,752✔
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
    int trace_ttl; /* current traceroute ttl */
301

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

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

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

326
#define EV_TYPE_PING 1
327
#define EV_TYPE_TIMEOUT 2
328

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

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

342
/*** globals ***/
343

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

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

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

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

373
unsigned int debugging = 0;
374

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

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

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

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

438
unsigned int fwmark = 0;
439

440
char *filename = NULL; /* file containing hosts to ping */
441

442
/*** forward declarations ***/
443

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

492
/************************************************************
493

494
  Function: p_setsockopt
495

496
*************************************************************
497

498
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
499

500
  Description:
501

502
  Elevates privileges to p_uid when required, calls
503
  setsockopt, and drops privileges back.
504

505
************************************************************/
506

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

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

517
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
518

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

525
    return res;
22✔
526
}
527

528
/************************************************************
529

530
  Function: main
531

532
*************************************************************
533

534
  Inputs:  int argc, char** argv
535

536
  Description:
537

538
  Main program entry point
539

540
************************************************************/
541

542
int main(int argc, char **argv)
728✔
543
{
544
/* Debug: CPU Performance */
545
#if defined(DEBUG) || defined(_DEBUG)
546
    clock_t perf_cpu_start, perf_cpu_end;
547
    double perf_cpu_time_used;
548
    perf_cpu_start = clock();
549
#endif /* DEBUG || _DEBUG */
550

551
    int c;
552
    const uid_t suid = geteuid();
728✔
553
    int tos = 0;
728✔
554
    struct optparse optparse_state;
555
#ifdef USE_SIGACTION
556
    struct sigaction act;
557
#endif
558

559
    /* pre-parse -h/--help, so that we also can output help information
560
     * without trying to open the socket, which might fail */
561
    prog = argv[0];
728✔
562
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
728✔
563
        usage(0);
3✔
564
    }
1✔
565

566
    socket4 = open_ping_socket_ipv4(&socktype4);
726✔
567
#ifdef __linux__
568
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
569
     * structure is missing in the message.
570
     */
571
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
504✔
572
#endif
573

574
#ifdef IPV6
575
    socket6 = open_ping_socket_ipv6(&socktype6);
726✔
576
    /* if called (sym-linked) via 'fping6', imply '-6'
577
     * for backward compatibility */
578
    if (strstr(prog, "fping6")) {
726✔
579
        hints_ai_family = AF_INET6;
×
580
    }
581
#endif
582

583
    memset(&src_addr, 0, sizeof(src_addr));
726✔
584
#ifdef IPV6
585
    memset(&src_addr6, 0, sizeof(src_addr6));
726✔
586
#endif
587

588
    if (!suid && suid != getuid()) {
726✔
589
        /* *temporarily* drop privileges */
590
        if (seteuid(getuid()) == -1)
677✔
591
            perror("cannot setuid");
×
592
    }
185✔
593

594
    optparse_init(&optparse_state, argv);
726✔
595
    ident4 = ident6 = htons(getpid() & 0xFFFF);
726✔
596
    verbose_flag = 1;
726✔
597
    backoff_flag = 1;
726✔
598
    opterr = 1;
726✔
599

600
    /* get command line options */
601

602
    struct optparse_long longopts[] = {
726✔
603
        { "ipv4", '4', OPTPARSE_NONE },
604
        { "ipv6", '6', OPTPARSE_NONE },
605
        { "alive", 'a', OPTPARSE_NONE },
606
        { "addr", 'A', OPTPARSE_NONE },
607
        { "size", 'b', OPTPARSE_REQUIRED },
608
        { "backoff", 'B', OPTPARSE_REQUIRED },
609
        { "count", 'c', OPTPARSE_REQUIRED },
610
        { "vcount", 'C', OPTPARSE_REQUIRED },
611
        { "rdns", 'd', OPTPARSE_NONE },
612
        { "timestamp", 'D', OPTPARSE_NONE },
613
        { "timestamp-format", '0', OPTPARSE_REQUIRED },
614
        { "traceroute", '0', OPTPARSE_NONE },
615
        { "elapsed", 'e', OPTPARSE_NONE },
616
        { "file", 'f', OPTPARSE_REQUIRED },
617
        { "generate", 'g', OPTPARSE_NONE },
618
        { "help", 'h', OPTPARSE_NONE },
619
        { "ttl", 'H', OPTPARSE_REQUIRED },
620
        { "interval", 'i', OPTPARSE_REQUIRED },
621
        { "iface", 'I', OPTPARSE_REQUIRED },
622
        { "json", 'J', OPTPARSE_NONE },
623
        { "icmp-timestamp", '0', OPTPARSE_NONE },
624
#ifdef SO_MARK
625
        { "fwmark", 'k', OPTPARSE_REQUIRED },
626
#endif
627
        { "loop", 'l', OPTPARSE_NONE },
628
        { "all", 'm', OPTPARSE_NONE },
629
        { "dontfrag", 'M', OPTPARSE_NONE },
630
        { "name", 'n', OPTPARSE_NONE },
631
        { "netdata", 'N', OPTPARSE_NONE },
632
        { "outage", 'o', OPTPARSE_NONE },
633
        { "tos", 'O', OPTPARSE_REQUIRED },
634
        { "period", 'p', OPTPARSE_REQUIRED },
635
        { "quiet", 'q', OPTPARSE_NONE },
636
        { "squiet", 'Q', OPTPARSE_REQUIRED },
637
        { "retry", 'r', OPTPARSE_REQUIRED },
638
        { "random", 'R', OPTPARSE_NONE },
639
        { "stats", 's', OPTPARSE_NONE },
640
        { "src", 'S', OPTPARSE_REQUIRED },
641
        { "timeout", 't', OPTPARSE_REQUIRED },
642
        { NULL, 'T', OPTPARSE_REQUIRED },
643
        { "unreach", 'u', OPTPARSE_NONE },
644
        { "version", 'v', OPTPARSE_NONE },
645
        { "reachable", 'x', OPTPARSE_REQUIRED },
646
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
647
        { "check-source", '0', OPTPARSE_NONE },
648
        { "print-tos", '0', OPTPARSE_NONE },
649
        { "print-ttl", '0', OPTPARSE_NONE },
650
        { "seqmap-timeout", '0', OPTPARSE_REQUIRED },
651
#if defined(DEBUG) || defined(_DEBUG)
652
        { NULL, 'z', OPTPARSE_REQUIRED },
653
#endif
654
        { 0, 0, 0 }
655
    };
656

657
    float opt_value_float;
658
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
1,803✔
659
        switch (c) {
1,186✔
660
        case '0':
102✔
661
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
132✔
662
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
24✔
663
                  timestamp_format_flag = 1;
6✔
664
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
20✔
665
                  timestamp_format_flag = 2;
6✔
666
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
14✔
667
                  timestamp_format_flag = 3;
6✔
668
                }else{
2✔
669
                  usage(1);
6✔
670
                }
671
            } else if (strstr(optparse_state.optlongname, "traceroute") != NULL) {
116✔
NEW
672
                traceroute_flag = 1;
×
673
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
108✔
674
                check_source_flag = 1;
9✔
675
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
100✔
676
#ifdef IPV6
677
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
29✔
678
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
679
                    exit(1);
2✔
680
                }
681
                hints_ai_family = AF_INET;
27✔
682
#endif
683
                icmp_request_typ = 13;
27✔
684
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
27✔
685
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
73✔
686
                print_tos_flag = 1;
29✔
687
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
29✔
688
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTOS, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
689
                        perror("setsockopt IP_RECVTOS");
×
690
                    }
691
                }
692
#if defined(IPV6) && defined(IPV6_RECVTCLASS)
693
                if (socket6 >= 0) {
29✔
694
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVTCLASS, &sock_opt_on, sizeof(sock_opt_on))) {
29✔
695
                        perror("setsockopt IPV6_RECVTCLASS");
×
696
                    }
697
                }
7✔
698
#endif
699
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
48✔
700
                print_ttl_flag = 1;
26✔
701
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
26✔
702
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTTL, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
703
                        perror("setsockopt IP_RECVTTL");
×
704
                    }
705
                }
706
#if defined(IPV6) && defined(IPV6_RECVHOPLIMIT)
707
                if (socket6 >= 0) {
26✔
708
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &sock_opt_on, sizeof(sock_opt_on))) {
26✔
709
                        perror("setsockopt IPV6_RECVHOPLIMIT");
×
710
                    }
711
                }
6✔
712
#endif
713
            } else if (strstr(optparse_state.optlongname, "seqmap-timeout") != NULL) {
21✔
714
                if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
15✔
715
                    usage(1);
6✔
716
                if (opt_value_float < 0)
11✔
717
                    usage(1);
3✔
718
                seqmap_timeout = opt_value_float * 1000000;
9✔
719
            } else {
5✔
720
                usage(1);
×
721
            }
722
            break;
120✔
723
        case '4':
32✔
724
#ifdef IPV6
725
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
726
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
727
                exit(1);
3✔
728
            }
729
            hints_ai_family = AF_INET;
40✔
730
#endif
731
            break;
40✔
732
        case '6':
28✔
733
#ifdef IPV6
734
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
31✔
735
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
736
                exit(1);
5✔
737
            }
738
            hints_ai_family = AF_INET6;
26✔
739
#else
740
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
741
            exit(1);
742
#endif
743
            break;
26✔
744
        case 'M':
2✔
745
#ifdef IP_MTU_DISCOVER
746
            if (socket4 >= 0) {
2✔
747
                int val = IP_PMTUDISC_DO;
2✔
748
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
749
                    perror("setsockopt IP_MTU_DISCOVER");
750
                }
751
            }
752
#ifdef IPV6
753
            if (socket6 >= 0) {
2✔
754
                int val = IPV6_PMTUDISC_DO;
2✔
755
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
756
                    perror("setsockopt IPV6_MTU_DISCOVER");
757
                }
758
            }
759
#endif
760
#else
761
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
762
            exit(1);
763
#endif
764
            break;
2✔
765

766
        case 't':
40✔
767
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
45✔
768
                usage(1);
6✔
769
            if (opt_value_float < 0) {
41✔
770
                usage(1);
3✔
771
            }
1✔
772
            timeout = opt_value_float * 1000000;
39✔
773
            timeout_flag = 1;
39✔
774
            break;
39✔
775

776
        case 'r':
36✔
777
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
40✔
778
                usage(1);
6✔
779
            break;
36✔
780

781
        case 'i':
30✔
782
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
36✔
783
                usage(1);
6✔
784
            if (opt_value_float < 0) {
32✔
785
                usage(1);
3✔
786
            }
1✔
787
            interval = opt_value_float * 1000000;
30✔
788
            break;
30✔
789

790
        case 'p':
70✔
791
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
102✔
792
                usage(1);
6✔
793
            if (opt_value_float < 0) {
98✔
794
                usage(1);
3✔
795
            }
1✔
796
            perhost_interval = opt_value_float * 1000000;
96✔
797

798
            break;
96✔
799

800
        case 'c':
90✔
801
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
129✔
802
                usage(1);
6✔
803

804
            count_flag = 1;
125✔
805
            break;
125✔
806

807
        case 'C':
34✔
808
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
48✔
809
                usage(1);
6✔
810

811
            count_flag = 1;
44✔
812
            report_all_rtts_flag = 1;
44✔
813
            break;
44✔
814

815
        case 'b':
14✔
816
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
21✔
817
                usage(1);
6✔
818
            size_flag = 1;
17✔
819
            break;
17✔
820

821
        case 'h':
2✔
822
            usage(0);
3✔
823
            break;
1✔
824

825
        case 'q':
26✔
826
            verbose_flag = 0;
38✔
827
            quiet_flag = 1;
38✔
828
            break;
38✔
829

830
        case 'Q':
24✔
831
            verbose_flag = 0;
36✔
832
            quiet_flag = 1;
36✔
833
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
36✔
834
                usage(1);
6✔
835
            if (opt_value_float < 0) {
32✔
836
                usage(1);
3✔
837
            }
1✔
838
            report_interval = opt_value_float * 1e9;
30✔
839

840
            /* recognize keyword(s) after number, ignore everything else */
841
            {
842
                char *comma = strchr(optparse_state.optarg, ',');
30✔
843
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
30✔
844
                    cumulative_stats_flag = 1;
6✔
845
                }
2✔
846
            }
847

848
            break;
30✔
849

850
        case 'e':
6✔
851
            elapsed_flag = 1;
8✔
852
            break;
8✔
853

854
        case 'm':
855
            multif_flag = 1;
×
856
            break;
×
857

858
        case 'N':
2✔
859
            netdata_flag = 1;
3✔
860
            break;
3✔
861

862
        case 'n':
4✔
863
            name_flag = 1;
6✔
864
            if (rdns_flag) {
6✔
865
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
866
                exit(1);
3✔
867
            }
868
            break;
3✔
869

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

878
        case 'A':
2✔
879
            addr_flag = 1;
3✔
880
            break;
3✔
881

882
        case 'B':
10✔
883
            if (!(backoff = atof(optparse_state.optarg)))
14✔
884
                usage(1);
6✔
885

886
            break;
10✔
887

888
        case 's':
8✔
889
            stats_flag = 1;
12✔
890
            break;
12✔
891

892
        case 'D':
22✔
893
            timestamp_flag = 1;
33✔
894
            break;
33✔
895

896
        case 'R':
4✔
897
            random_data_flag = 1;
5✔
898
            break;
5✔
899

900
        case 'l':
2✔
901
            loop_flag = 1;
3✔
902
            backoff_flag = 0;
3✔
903
            break;
3✔
904

905
        case 'u':
8✔
906
            unreachable_flag = 1;
12✔
907
            break;
12✔
908

909
        case 'a':
18✔
910
            alive_flag = 1;
26✔
911
            break;
26✔
912

913
        case 'H':
8✔
914
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
12✔
915
                usage(1);
6✔
916
            break;
8✔
917

918
#if defined(DEBUG) || defined(_DEBUG)
919
        case 'z':
920
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
921
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
922
                    usage(1);
923

924
            break;
925
#endif /* DEBUG || _DEBUG */
926

927
        case 'v':
4✔
928
            printf("%s: Version %s\n", prog, VERSION);
6✔
929
            exit(0);
6✔
930

931
        case 'x':
8✔
932
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
12✔
933
                usage(1);
6✔
934
            break;
8✔
935

936
        case 'X':
6✔
937
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
938
                usage(1);
6✔
939
            fast_reachable = 1;
5✔
940
            break;
5✔
941

942
        case 'f':
18✔
943
            filename = optparse_state.optarg;
27✔
944
            break;
27✔
945
#ifdef SO_MARK
946
        case 'k':
10✔
947
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
10✔
948
                usage(1);
4✔
949

950
            if (socket4 >= 0)
6✔
951
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
952
                    perror("fwmark ipv4");
4✔
953

954
#ifdef IPV6
955
            if (socket6 >= 0)
6✔
956
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
957
                    perror("fwmark ipv6");
4✔
958
#endif
959

960
            break;
6✔
961
#endif
962

963
        case 'g':
100✔
964
            /* use IP list generation */
965
            /* mutually exclusive with using file input or command line targets */
966
            generate_flag = 1;
120✔
967
            break;
120✔
968

969
        case 'S':
10✔
970
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
971
                src_addr_set = 1;
6✔
972
                break;
6✔
973
            }
974
#ifdef IPV6
975
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
976
                src_addr6_set = 1;
4✔
977
                break;
4✔
978
            }
979
#endif
980
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
981
            exit(1);
3✔
982

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

1006
        case 'J':
42✔
1007
            json_flag = 1;
62✔
1008
            break;
62✔
1009

1010
        case 'T':
2✔
1011
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
1012
            break;
3✔
1013

1014
        case 'O':
10✔
1015
            if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
15✔
1016
                if (socket4 >= 0) {
9✔
1017
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
1018
                        perror("setting type of service octet IP_TOS");
×
1019
                    }
1020
                }
3✔
1021
#if defined(IPV6) && defined(IPV6_TCLASS)
1022
                if (socket6 >= 0) {
9✔
1023
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
1024
                        perror("setting type of service octet IPV6_TCLASS");
×
1025
                    }
1026
                }
3✔
1027
#endif
1028
            }
3✔
1029
            else {
1030
                usage(1);
6✔
1031
            }
1032
            break;
11✔
1033

1034
        case 'o':
6✔
1035
            outage_flag = 1;
9✔
1036
            break;
9✔
1037

1038
        case '?':
4✔
1039
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
1040
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
1041
            exit(1);
6✔
1042
            break;
1043
        }
1044
    }
1045

1046
    /* permanently drop privileges */
1047
    if (suid != getuid() && setuid(getuid())) {
545✔
1048
        perror("fatal: failed to permanently drop privileges");
×
1049
        /* continuing would be a security hole */
1050
        exit(4);
×
1051
    }
1052

1053
    /* validate various option settings */
1054

1055
#ifndef IPV6
1056
    if (socket4 < 0) {
1057
        crash_and_burn("can't create socket (must run as root?)");
1058
    }
1059
#else
1060
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
545✔
1061
        crash_and_burn("can't create socket (must run as root?)");
×
1062
    }
1063
#endif
1064

1065
    if (ttl > 255) {
543✔
1066
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
1067
        exit(1);
3✔
1068
    }
1069

1070
    if (unreachable_flag && alive_flag) {
540✔
1071
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
1072
        exit(1);
3✔
1073
    }
1074

1075
    if (count_flag && loop_flag) {
537✔
1076
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
1077
        exit(1);
3✔
1078
    }
1079

1080
    if (json_flag && !count_flag && !loop_flag) {
534✔
1081
        fprintf(stderr, "%s: option -J, --json requires -c, -C, or -l\n", prog);
3✔
1082
        exit(1);
3✔
1083
    }
1084

1085
    if (interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
531✔
1086
        fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
3✔
1087
        exit(1);
3✔
1088
    }
1089

1090
    if (perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
528✔
1091
        fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
3✔
1092
        exit(1);
3✔
1093
    }
1094

1095
    if (ping_data_size > MAX_PING_DATA) {
525✔
1096
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1097
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1098
        exit(1);
3✔
1099
    }
1100

1101
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
522✔
1102
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1103
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1104
        exit(1);
6✔
1105
    }
1106

1107
    if (icmp_request_typ == 13 && size_flag != 0) {
516✔
1108
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1109
        exit(1);
9✔
1110
    }
1111

1112
    if (count_flag) {
507✔
1113
        if (verbose_flag)
159✔
1114
            per_recv_flag = 1;
94✔
1115

1116
        alive_flag = unreachable_flag = verbose_flag = 0;
159✔
1117
    }
47✔
1118

1119
    if (loop_flag) {
507✔
1120
        if (!report_interval)
×
1121
            per_recv_flag = 1;
×
1122

1123
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1124
    }
1125

1126
    if (alive_flag || unreachable_flag || min_reachable)
507✔
1127
        verbose_flag = 0;
37✔
1128

1129
    trials = (count > retry + 1) ? count : retry + 1;
523✔
1130

1131
    if (traceroute_flag)
523✔
NEW
1132
        trials = 255; /* Ensure enough space for up to 255 hops */
×
1133

1134
    /* auto-tune default timeout for count/loop modes
1135
     * see also github #32 */
1136
    if (loop_flag || count_flag) {
523✔
1137
        if (!timeout_flag) {
159✔
1138
            timeout = perhost_interval;
154✔
1139
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
154✔
1140
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1141
            }
1142
        }
46✔
1143
    }
47✔
1144

1145
#if defined(DEBUG) || defined(_DEBUG)
1146
    if (debugging & DBG_TRACE)
1147
        trace_flag = 1;
1148

1149
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1150
        randomly_lose_flag = 1;
1151
        lose_factor = 1; /* ie, 1/4 */
1152
    }
1153

1154
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1155
        randomly_lose_flag = 1;
1156
        lose_factor = 5; /* ie, 3/4 */
1157
    }
1158

1159
    if (debugging & DBG_PRINT_PER_SYSTEM)
1160
        print_per_system_flag = 1;
1161

1162
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1163
        report_all_rtts_flag = 1;
1164

1165
    if (trace_flag) {
1166
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1167
            prog, count, retry, interval / 1e6);
1168
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1169
            perhost_interval / 1e6, timeout / 1e6);
1170
        fprintf(stderr, "  seqmap_timeout: %.0f\n", seqmap_timeout / 1e6);
1171
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1172
            ping_data_size, trials);
1173

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

1215
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1216
    if (ttl > 0) {
523✔
1217
        if (socket4 >= 0) {
3✔
1218
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1219
                perror("setting time to live");
×
1220
            }
1221
        }
1✔
1222
#ifdef IPV6
1223
        if (socket6 >= 0) {
3✔
1224
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1225
                perror("setting time to live");
×
1226
            }
1227
        }
1✔
1228
#endif
1229
    }
1✔
1230

1231
#if HAVE_SO_TIMESTAMPNS
1232
    {
1233
        int opt = 1;
380✔
1234
        if (socket4 >= 0) {
380✔
1235
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
380✔
1236
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1237
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1238
                }
1239
            }
1240
        }
1241
#ifdef IPV6
1242
        if (socket6 >= 0) {
380✔
1243
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
380✔
1244
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1245
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1246
                }
1247
            }
1248
        }
1249
#endif
1250
    }
1251
#endif
1252

1253
    update_current_time();
523✔
1254
    start_time = current_time_ns;
523✔
1255

1256
    /* handle host names supplied on command line or in a file */
1257
    /* if the generate_flag is on, then generate the IP list */
1258

1259
    argv = &argv[optparse_state.optind];
523✔
1260
    argc -= optparse_state.optind;
523✔
1261

1262
    /* calculate how many ping can be in-flight per host */
1263
    if (count_flag) {
523✔
1264
        event_storage_count = count;
174✔
1265
    }
62✔
1266
    else if (loop_flag || traceroute_flag) {
349✔
1267
        if (perhost_interval > timeout) {
×
1268
            event_storage_count = 1;
×
1269
        }
1270
        else {
1271
            event_storage_count = 1 + timeout / perhost_interval;
×
1272
        }
1273
    }
1274
    else {
1275
        event_storage_count = 1;
349✔
1276
    }
1277

1278
    /* file and generate are mutually exclusive */
1279
    /* file and command line are mutually exclusive */
1280
    /* generate requires command line parameters beyond the switches */
1281
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
523✔
1282
        usage(1);
19✔
1283

1284
    /* if no conditions are specified, then assume input from stdin */
1285
    if (!*argv && !filename && !generate_flag)
505✔
1286
        filename = "-";
69✔
1287

1288
    if (*argv && !generate_flag) {
505✔
1289
        while (*argv) {
651✔
1290
            add_name(*argv);
356✔
1291
            ++argv;
356✔
1292
        }
1293
    }
77✔
1294
    else if (filename) {
210✔
1295
        FILE *ping_file;
1296
        char line[MAX_TARGET_NAME_LEN + 1];
1297
        char host[MAX_TARGET_NAME_LEN + 1];
1298
        char scratch[MAX_TARGET_NAME_LEN + 1];
1299
        int skip, non_empty;
1300

1301
        if (strcmp(filename, "-") == 0)
96✔
1302
            ping_file = fdopen(0, "r");
72✔
1303
        else
1304
            ping_file = fopen(filename, "r");
24✔
1305

1306
        if (!ping_file)
96✔
1307
            errno_crash_and_burn("fopen");
3✔
1308

1309
        /*
1310
         * Read the first word of every non-comment line, skip everything else.
1311
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1312
         * character is a '#' are interpreted as comments and ignored.)
1313
        */
1314
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1315
            skip = non_empty = 0;
309✔
1316

1317
            /* skip over a prefix of the line where sscanf finds nothing */
1318
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1319
                continue;
99✔
1320
            }
1321

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

1373
            if (non_empty)
186✔
1374
                add_name(host);
144✔
1375
        }
1376

1377
        fclose(ping_file);
70✔
1378
    }
24✔
1379
    else if (*argv && generate_flag) {
114✔
1380
        if (argc == 1) {
114✔
1381
            /* one target: we expect a cidr range (n.n.n.n/m) */
1382
            add_cidr(argv[0]);
59✔
1383
        }
11✔
1384
        else if (argc == 2) {
55✔
1385
            add_range(argv[0], argv[1]);
52✔
1386
        }
6✔
1387
        else {
1388
            usage(1);
3✔
1389
        }
1390
    }
18✔
1391
    else {
1392
        usage(1);
×
1393
    }
1394

1395
    if (!num_hosts) {
425✔
1396
        exit(num_noaddress ? 2 : 1);
36✔
1397
    }
1398

1399
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
389✔
1400
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1401
    }
2✔
1402
#ifdef IPV6
1403
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
387✔
1404
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1405
    }
1406
#endif
1407

1408
    /* allocate and initialize array to map host nr to host_entry */
1409
    {
1410
        struct event *cursor = event_queue_ping.first;
385✔
1411
        int i = 0;
385✔
1412
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
385✔
1413
        if (!table)
385✔
1414
            crash_and_burn("Can't malloc array of hosts");
×
1415
        /* initialize table of hosts. we know that we have ping events scheduled
1416
         * for each of them */
1417
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
960✔
1418
            table[i] = cursor->host;
575✔
1419
            cursor->host->i = i;
575✔
1420
            i++;
575✔
1421
        }
157✔
1422
    }
1423

1424
    init_ping_buffer_ipv4(ping_data_size);
385✔
1425
#ifdef IPV6
1426
    init_ping_buffer_ipv6(ping_data_size);
385✔
1427
#endif
1428

1429
#ifdef USE_SIGACTION
1430
    memset(&act, 0, sizeof(act));
385✔
1431
    act.sa_handler = signal_handler;
385✔
1432
    sigemptyset(&act.sa_mask);
385✔
1433
    sigaddset(&act.sa_mask, SIGINT);
385✔
1434
    sigaddset(&act.sa_mask, SIGQUIT);
385✔
1435
    act.sa_flags = SA_RESTART;
385✔
1436
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
385✔
1437
        crash_and_burn("failure to set signal handler");
24✔
1438
    }
24✔
1439
#else
1440
    signal(SIGINT, signal_handler);
1441
    signal(SIGQUIT, signal_handler);
1442
#endif
1443
    setlinebuf(stdout);
361✔
1444

1445
    if (report_interval) {
361✔
1446
        next_report_time = current_time_ns + report_interval;
27✔
1447
    }
9✔
1448

1449
    last_send_time = 0;
361✔
1450

1451
    seqmap_init(seqmap_timeout);
361✔
1452

1453
    /* main loop */
1454
    main_loop();
361✔
1455

1456
/* Debug: CPU Performance */
1457
#if defined(DEBUG) || defined(_DEBUG)
1458
    perf_cpu_end = clock();
1459
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1460
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1461
#endif /* DEBUG || _DEBUG */
1462

1463
    finish();
361✔
1464

1465
    return 0;
85✔
1466
}
1467

1468
static inline int64_t timespec_ns(struct timespec *a)
5,655✔
1469
{
1470
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,655✔
1471
}
1472

1473
#if HAVE_SO_TIMESTAMPNS
1474
/* convert a struct timeval to nanoseconds */
1475
static inline int64_t timeval_ns(struct timeval *a)
1476
{
1477
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1478
}
1479
#endif /* HAVE_SO_TIMESTAMPNS */
1480

1481
void add_cidr(char *addr)
57✔
1482
{
1483
    char *addr_end;
1484
    char *mask_str;
1485
    unsigned long mask;
1486
    int ret;
1487
    struct addrinfo addr_hints;
1488
    struct addrinfo *addr_res;
1489
    unsigned long net_addr;
1490
#ifdef IPV6
1491
    uint64_t net_upper, net_lower;
1492
    char *scope_str;
1493
#endif /* IPV6 */
1494

1495
    /* Split address from mask */
1496
    addr_end = strrchr(addr, '/');
57✔
1497
    if (addr_end == NULL) {
57✔
1498
        usage(1);
3✔
1499
    }
1✔
1500
    mask_str = addr_end + 1;
55✔
1501

1502
#ifdef IPV6
1503
    /* IPv6 addresses can have a scope */
1504
    scope_str = strchr(addr, '%');
55✔
1505
    if (scope_str && mask_str < scope_str) {
55✔
1506
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1507
        exit(1);
2✔
1508
    }
1509
#endif /*IPV6 */
1510

1511
    *addr_end = '\0';
53✔
1512
    mask = atoi(mask_str);
53✔
1513

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

1542
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1543
{
1544
    unsigned long bitmask;
1545
    unsigned long net_last;
1546

1547
    /* check mask */
1548
    if (mask < 1 || mask > 32) {
24✔
1549
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1550
        exit(1);
6✔
1551
    }
1552

1553
    /* convert mask integer from 1 to 32 to a bitmask */
1554
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1555

1556
    /* calculate network range */
1557
    net_addr &= bitmask;
18✔
1558
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1559

1560
    /* exclude network and broadcast address for regular prefixes */
1561
    if (mask < 31) {
18✔
1562
        net_last--;
12✔
1563
        net_addr++;
12✔
1564
    }
4✔
1565

1566
    /* add all hosts in that network (net_addr and net_last inclusive) */
1567
    add_addr_range_ipv4(net_addr, net_last);
18✔
1568
}
16✔
1569

1570
#ifdef IPV6
1571
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1572
{
1573
    uint64_t bitmask_lower;
1574
    uint64_t last_lower;
1575

1576
    /* check mask -- 2^63 addresses should suffice for now */
1577
    if (mask < 65 || mask > 128) {
23✔
1578
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1579
        exit(1);
7✔
1580
    }
1581

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

1585
    /* calculate network range */
1586
    net_lower &= bitmask_lower;
16✔
1587
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1588

1589
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1590
}
12✔
1591
#endif /* IPV6 */
1592

1593
void add_range(char *start, char *end)
52✔
1594
{
1595
    struct addrinfo addr_hints;
1596
    struct addrinfo *addr_res;
1597
    unsigned long start_long;
1598
    unsigned long end_long;
1599
    int ret;
1600
#ifdef IPV6
1601
    uint64_t start_upper, start_lower;
1602
    uint64_t end_upper, end_lower;
1603
    char *start_scope_str, *end_scope_str;
1604

1605
    /*
1606
     * The compiler does not know that setting the address family hint to
1607
     * ensure that start and end are from the same address family also
1608
     * ensures that either start_long and end_long are initialized and used,
1609
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1610
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1611
     * supported to suppress compiler warnings.
1612
     */
1613
    start_long = -1;
52✔
1614
    end_long = 0;
52✔
1615
    start_upper = start_lower = -1;
52✔
1616
    end_upper = end_lower = 0;
52✔
1617
    start_scope_str = end_scope_str = NULL;
52✔
1618
#endif /* IPV6 */
1619

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

1647
#ifdef IPV6
1648
    /* IPv6 addresses can have a scope */
1649
    if (hints_ai_family == AF_INET6) {
43✔
1650
        start_scope_str = strchr(start, '%');
26✔
1651
        end_scope_str = strchr(end, '%');
26✔
1652
        if ((!start_scope_str && end_scope_str) ||
26✔
1653
            (start_scope_str && !end_scope_str) ||
24✔
1654
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1655
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1656
                exit(1);
6✔
1657
        }
1658
    }
1659
#endif
1660

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

1689
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1690
{
1691
    /* check if generator limit is exceeded */
1692
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1693
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1694
        exit(1);
6✔
1695
    }
1696

1697
    /* generate */
1698
    for (; start_long <= end_long; start_long++) {
93✔
1699
        struct in_addr in_addr_tmp;
1700
        char buffer[20];
1701
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1702
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1703
        add_name(buffer);
69✔
1704
    }
23✔
1705
}
24✔
1706

1707
#ifdef IPV6
1708
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1709
{
1710
    int i;
1711
    uint64_t ret = 0;
134✔
1712
    for (i = 0; i < 8; i++) {
1,206✔
1713
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1714
    }
16✔
1715
    return ret;
134✔
1716
}
1717

1718
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1719
{
1720
    int i;
1721
    for (i = 0; i < 8; i++) {
720✔
1722
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1723
    }
1724
}
80✔
1725

1726
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1727
                         uint64_t end_upper, uint64_t end_lower,
1728
                         const char *scope_str)
1729
{
1730
    struct in6_addr in6_addr_tmp;
1731
    char buffer[100];
1732

1733
    /* prevent generating too many addresses */
1734
    if ((start_upper + 1 < end_upper) ||
34✔
1735
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1736
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1737
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1738
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1739
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1740
        exit(1);
8✔
1741
    }
1742

1743
    while ((start_upper < end_upper) ||
66✔
1744
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1745
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1746
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1747
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1748
        if (scope_str) {
40✔
1749
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1750
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1751
                exit(1);
×
1752
            }
1753
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1754
        }
1755
        add_name(buffer);
40✔
1756
        start_lower++;
40✔
1757
        if (start_lower == 0) {
40✔
1758
            start_upper++;
2✔
1759
        }
1760
    }
1761
}
26✔
1762
#endif /* IPv6 */
1763

1764
void main_loop()
373✔
1765
{
1766
    int64_t lt;
1767
    int64_t wait_time_ns;
1768
    struct event *event;
1769
    struct host_entry *h;
1770

1771
    while (event_queue_ping.first || event_queue_timeout.first) {
1,947✔
1772
        dbg_printf("%s", "# main_loop\n");
1773

1774
        /* timeout event ? */
1775
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,591✔
1776
            event = ev_dequeue(&event_queue_timeout);
113✔
1777
            h = event->host;
113✔
1778

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

1781
            stats_add(h, event->ping_index, 0, -1);
113✔
1782

1783
            if (per_recv_flag) {
113✔
1784
                if (json_flag)
25✔
1785
                    printf("{\"timeout\": {");
6✔
1786

1787
                if (timestamp_flag)
25✔
1788
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1789

1790
                if (json_flag)
25✔
1791
                {
1792
                  printf("\"host\": \"%s\", ", h->host);
6✔
1793
                  printf("\"seq\": %d", event->ping_index);
6✔
1794
                }
2✔
1795
                else {
1796
                    printf("%-*s : [%d], timed out",
19✔
1797
                        max_hostname_len, h->host, event->ping_index);
5✔
1798
                }
1799
                if (h->num_recv > 0) {
25✔
1800
                    if (json_flag)
×
1801
                        printf(", \"rttAvg\": %s", sprint_tm(h->total_time / h->num_recv));
×
1802
                    else
1803
                        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1804
                }
1805
                else {
1806
                    if (json_flag)
25✔
1807
                        printf(", \"rttAvg\": \"NaN\"");
6✔
1808
                    else
1809
                        printf(" (NaN avg, ");
19✔
1810
                }
1811
                if (h->num_recv <= h->num_sent) {
25✔
1812
                    if (json_flag) {
25✔
1813
                        printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
6✔
1814
                    }
2✔
1815
                    else {
1816
                        printf("%d%% loss)",
19✔
1817
                            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1818
                    }
1819
                }
7✔
1820
                else {
1821
                    if (json_flag) {
×
1822
                        printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
1823
                    }
1824
                    else {
1825
                        printf("%d%% return)",
×
1826
                            (h->num_recv_total * 100) / h->num_sent);
×
1827
                    }
1828
                }
1829

1830
                if (json_flag)
25✔
1831
                    printf("}}");
6✔
1832
                
1833
                printf("\n");
25✔
1834
            }
7✔
1835

1836
            /* do we need to send a retry? */
1837
            if (!loop_flag && !count_flag) {
113✔
1838
                if (h->num_sent < retry + 1) {
64✔
1839
                    if (backoff_flag) {
39✔
1840
                        h->timeout *= backoff;
39✔
1841
                    }
9✔
1842
                    send_ping(h, event->ping_index);
39✔
1843
                }
9✔
1844
            }
14✔
1845

1846
            /* note: we process first timeout events, because we might need to
1847
             * wait to process ping events, while we for sure never need to
1848
             * wait for timeout events.
1849
             */
1850
            continue;
113✔
1851
        }
1852

1853
        /* ping event ? */
1854
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,737✔
1855
            /* Make sure that we don't ping more than once every "interval" */
1856
            lt = current_time_ns - last_send_time;
1,153✔
1857
            if (lt < interval)
1,153✔
1858
                goto wait_for_reply;
260✔
1859

1860
            /* Dequeue the event */
1861
            event = ev_dequeue(&event_queue_ping);
893✔
1862
            h = event->host;
893✔
1863

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

1866
            /* Traceroute */
1867
            if (traceroute_flag) {
893✔
NEW
1868
                int ttl_set = h->trace_ttl;
×
NEW
1869
                if (ttl_set > 30) ttl_set = 30;
×
NEW
1870
                if (socket4 >= 0) {
×
NEW
1871
                    if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl_set, sizeof(ttl_set)))
×
NEW
1872
                        perror("setsockopt IP_TTL");
×
1873
                }
1874
            }
1875

1876
            /* Send the ping */
1877
            send_ping(h, event->ping_index);
893✔
1878

1879
            /* Loop, count and traceroute mode: schedule next ping */
1880
            if (loop_flag || (count_flag && event->ping_index + 1 < count) || (traceroute_flag && h->trace_ttl < 30)) {
893✔
1881
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
333✔
1882
            }
107✔
1883
        }
259✔
1884

1885
    wait_for_reply:
647✔
1886

1887
        /* When is the next ping next event? */
1888
        wait_time_ns = -1;
1,478✔
1889
        if (event_queue_ping.first) {
1,478✔
1890
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,025✔
1891
            if (wait_time_ns < 0)
1,025✔
1892
                wait_time_ns = 0;
547✔
1893
            /* make sure that we wait enough, so that the inter-ping delay is
1894
             * bigger than 'interval' */
1895
            if (wait_time_ns < interval) {
1,025✔
1896
                lt = current_time_ns - last_send_time;
547✔
1897
                if (lt < interval) {
547✔
1898
                    wait_time_ns = interval - lt;
547✔
1899
                }
191✔
1900
            }
191✔
1901

1902
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1903
        }
323✔
1904

1905
        /* When is the next timeout event? */
1906
        if (event_queue_timeout.first) {
1,478✔
1907
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
957✔
1908
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
957✔
1909
                wait_time_ns = wait_time_timeout;
449✔
1910
                if (wait_time_ns < 0) {
449✔
1911
                    wait_time_ns = 0;
1✔
1912
                }
1913
            }
118✔
1914

1915
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1916
        }
282✔
1917

1918
        /* When is the next report due? */
1919
        if (report_interval && (loop_flag || count_flag)) {
1,478✔
1920
            int64_t wait_time_next_report = next_report_time - current_time_ns;
214✔
1921
            if (wait_time_next_report < wait_time_ns) {
214✔
1922
                wait_time_ns = wait_time_next_report;
72✔
1923
                if (wait_time_ns < 0) {
72✔
1924
                    wait_time_ns = 0;
×
1925
                }
1926
            }
24✔
1927

1928
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1929
        }
70✔
1930

1931
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1932
        if (wait_time_ns == -1) {
1,478✔
1933
            break;
14✔
1934
        }
1935

1936
        /* end of loop was requested by interrupt signal handler */
1937
        if (finish_requested) {
1,464✔
1938
            break;
3✔
1939
        }
1940

1941
        /* Receive replies */
1942
        /* (this is what sleeps during each loop iteration) */
1943
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1944
        if (wait_for_reply(wait_time_ns)) {
1,461✔
1945
            while (wait_for_reply(0))
1,291✔
1946
                ; /* process other replies in the queue */
1947
        }
243✔
1948

1949
        update_current_time();
1,461✔
1950

1951
        if (status_snapshot) {
1,461✔
1952
            status_snapshot = 0;
×
1953
            if (json_flag)
×
1954
                print_per_system_splits_json();
×
1955
            else
1956
                print_per_system_splits();
×
1957
        }
1958

1959
        /* Print report */
1960
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,461✔
1961
            if (netdata_flag) {
45✔
1962
                print_netdata();
3✔
1963
            }
1✔
1964
            else if (json_flag) {
42✔
1965
                print_per_system_splits_json();
3✔
1966
            }
1✔
1967
            else {
1968
                print_per_system_splits();
39✔
1969
            }
1970

1971
            while (current_time_ns >= next_report_time) {
90✔
1972
                next_report_time += report_interval;
45✔
1973
            }
1974
        }
15✔
1975
    }
1976
}
373✔
1977

1978
/************************************************************
1979

1980
  Function: signal_handler
1981

1982
*************************************************************
1983

1984
  Inputs:  int signum
1985

1986
  Description:
1987

1988
  SIGQUIT signal handler - set flag and return
1989
  SIGINT signal handler - set flag and return
1990

1991
************************************************************/
1992

1993
void signal_handler(int signum)
×
1994
{
1995
    switch (signum) {
×
1996
    case SIGINT:
1997
        finish_requested = 1;
×
1998
        break;
×
1999

2000
    case SIGQUIT:
2001
        status_snapshot = 1;
×
2002
        break;
×
2003
    }
2004
}
×
2005

2006
/************************************************************
2007

2008
  Function: update_current_time
2009

2010
*************************************************************/
2011

2012
void update_current_time()
4,607✔
2013
{
2014
    clock_gettime(CLOCKID, &current_time);
4,607✔
2015
    current_time_ns = timespec_ns(&current_time);
4,607✔
2016
}
4,607✔
2017

2018
/************************************************************
2019

2020
  Function: finish
2021

2022
*************************************************************
2023

2024
  Inputs:  void (none)
2025

2026
  Description:
2027

2028
  Main program clean up and exit point
2029

2030
************************************************************/
2031

2032
void finish()
373✔
2033
{
2034
    int i;
2035
    HOST_ENTRY *h;
2036

2037
    update_current_time();
373✔
2038
    end_time = current_time_ns;
373✔
2039

2040
    /* tot up unreachables */
2041
    for (i = 0; i < num_hosts; i++) {
948✔
2042
        h = table[i];
575✔
2043

2044
        if (!h->num_recv) {
575✔
2045
            num_unreachable++;
89✔
2046

2047
            if (verbose_flag || unreachable_flag) {
89✔
2048
                printf("%s", h->host);
47✔
2049

2050
                if (verbose_flag)
47✔
2051
                    printf(" is unreachable");
44✔
2052

2053
                printf("\n");
47✔
2054
            }
3✔
2055
        }
15✔
2056
    }
157✔
2057

2058
    if (count_flag || loop_flag) {
373✔
2059
        if (json_flag)
156✔
2060
            print_per_system_stats_json();
56✔
2061
        else
2062
            print_per_system_stats();
100✔
2063
    }
46✔
2064
#if defined(DEBUG) || defined(_DEBUG)
2065
    else if (print_per_system_flag) {
2066
        if (json_flag)
2067
            print_per_system_stats_json();
2068
        else
2069
            print_per_system_stats();
2070
    }
2071
#endif /* DEBUG || _DEBUG */
2072

2073
    if (stats_flag) {
373✔
2074
        if (json_flag)
12✔
2075
            print_global_stats_json();
3✔
2076
        else
2077
            print_global_stats();
9✔
2078
    }
4✔
2079

2080
    if (min_reachable) {
373✔
2081
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
2082
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
2083
            exit(0);
6✔
2084
        }
2085
        else {
2086
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
2087
            exit(1);
3✔
2088
        }
2089
    }
2090

2091
    if (num_noaddress)
364✔
2092
        exit(2);
3✔
2093
    else if (num_alive != num_hosts)
361✔
2094
        exit(1);
50✔
2095

2096
    exit(0);
311✔
2097
}
2098

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

2101
  Function: print_recv
2102

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

2105
  Inputs: HOST_ENTRY *h, int64_t recv_time, int result,
2106
          int this_count, int64_t this_reply, int avg
2107

2108
  Description:
2109

2110
************************************************************/
2111

2112
void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) {
151✔
2113
    if (timestamp_flag)
151✔
2114
        print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2115

2116
    printf("%-*s : [%d], %d bytes, %s ms",
151✔
2117
        max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2118
    
2119
    printf(" (%s avg, ", sprint_tm(avg));
151✔
2120

2121
    if (h->num_recv <= h->num_sent) {
151✔
2122
        printf("%d%% loss)",
151✔
2123
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2124
    }
45✔
2125
    else {
2126
        printf("%d%% return)",
×
2127
            (h->num_recv_total * 100) / h->num_sent);
×
2128
    }
2129
}
151✔
2130

2131
/************************************************************
2132

2133
  Function: print_recv_json
2134

2135
*************************************************************
2136

2137
  Inputs: HOST_ENTRY *h, int64_t recv_time, int result,
2138
          int this_count, int64_t this_reply, int avg
2139

2140
  Description:
2141

2142
************************************************************/
2143

2144
void print_recv_json(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) {
35✔
2145
    printf("{\"resp\": {");
35✔
2146

2147
    if (timestamp_flag)
35✔
2148
        print_timestamp_format(recv_time, timestamp_format_flag);
15✔
2149
    
2150
    printf("\"host\": \"%s\", ", h->host);
35✔
2151
    printf("\"seq\": %d, ", this_count);
35✔
2152
    printf("\"size\": %d, ", result);
35✔
2153
    printf("\"rtt\": %s, ", sprint_tm(this_reply));
35✔
2154
    printf("\"rttAvg\": %s", sprint_tm(avg));
35✔
2155

2156
    if (h->num_recv <= h->num_sent) {
35✔
2157
        printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
35✔
2158
    }
11✔
2159
    else {
2160
        printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
2161
    }
2162
}
35✔
2163

2164
/************************************************************
2165

2166
  Function: print_recv_ext
2167

2168
*************************************************************
2169

2170
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2171
           int64_t recv_time, int64_t this_reply
2172

2173
  Description:
2174

2175
************************************************************/
2176

2177
void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { 
450✔
2178
    if (icmp_request_typ == 13) {
450✔
2179
        printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2180
            alive_flag ? "" : ",",
14✔
2181
            ip_header_res->otime_ms, ip_header_res->rtime_ms, ip_header_res->ttime_ms,
2182
            ms_since_midnight_utc(recv_time));
2183
    }
2184

2185
    if(print_tos_flag) {
450✔
2186
        if(ip_header_res->tos != -1) {
43✔
2187
            printf(" (TOS %d)", ip_header_res->tos);
43✔
2188
        }
11✔
2189
        else {
2190
            printf(" (TOS unknown)");
×
2191
        }
2192
    }
11✔
2193

2194
    if (print_ttl_flag) {
450✔
2195
        if(ip_header_res->ttl != -1) {
43✔
2196
            printf(" (TTL %d)", ip_header_res->ttl);
43✔
2197
        }
11✔
2198
        else {
2199
            printf(" (TTL unknown)");
×
2200
        }
2201
    }
11✔
2202

2203
    if (elapsed_flag && !per_recv_flag)
450✔
2204
        printf(" (%s ms)", sprint_tm(this_reply));
8✔
2205
    
2206
    printf("\n");
450✔
2207
}
450✔
2208

2209
/************************************************************
2210

2211
  Function: print_recv_ext_json
2212

2213
*************************************************************
2214

2215
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2216
           int64_t recv_time, int64_t this_reply
2217

2218
  Description:
2219

2220
************************************************************/
2221

2222
void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) {
35✔
2223
    if (icmp_request_typ == 13) {
35✔
2224
        printf(", \"timestamps\": {");
2✔
2225
        printf("\"originate\": %u, ", ip_header_res->otime_ms);
2✔
2226
        printf("\"receive\": %u, ", ip_header_res->rtime_ms);
2✔
2227
        printf("\"transmit\": %u, ", ip_header_res->ttime_ms);
2✔
2228
        printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
2229
    }
2230

2231
    if(print_tos_flag) {
35✔
2232
        if(ip_header_res->tos != -1) {
6✔
2233
            printf(", \"tos\": %d", ip_header_res->tos);
6✔
2234
        }
2✔
2235
        else {
2236
            printf(", \"tos\": -1");
×
2237
        }
2238
    }
2✔
2239

2240
    if (print_ttl_flag) {
35✔
2241
        if(ip_header_res->ttl != -1) {
6✔
2242
            printf(", \"ttl\": %d", ip_header_res->ttl);
6✔
2243
        }
2✔
2244
        else {
2245
            printf(", \"ttl\": -1");
×
2246
        }
2247
    }
2✔
2248

2249
    if (elapsed_flag && !per_recv_flag)
35✔
2250
        printf(" (%s ms)", sprint_tm(this_reply));
×
2251

2252
    printf("}}");
35✔
2253
    printf("\n");
35✔
2254
}
35✔
2255

2256
/************************************************************
2257

2258
  Function: print_per_system_stats
2259

2260
*************************************************************
2261

2262
  Inputs:  void (none)
2263

2264
  Description:
2265

2266

2267
************************************************************/
2268

2269
void print_per_system_stats(void)
100✔
2270
{
2271
    int i, j, avg, outage_ms;
2272
    HOST_ENTRY *h;
2273
    int64_t resp;
2274

2275
    if (verbose_flag || per_recv_flag)
100✔
2276
        fprintf(stderr, "\n");
59✔
2277

2278
    for (i = 0; i < num_hosts; i++) {
234✔
2279
        h = table[i];
134✔
2280
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
2281

2282
        if (report_all_rtts_flag) {
134✔
2283
            for (j = 0; j < h->num_sent; j++) {
248✔
2284
                if ((resp = h->resp_times[j]) >= 0)
204✔
2285
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2286
                else
2287
                    fprintf(stderr, " -");
8✔
2288
            }
64✔
2289

2290
            fprintf(stderr, "\n");
44✔
2291
        }
12✔
2292
        else {
2293
            if (h->num_recv <= h->num_sent) {
90✔
2294
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
2295
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
2296

2297
                if (outage_flag) {
90✔
2298
                    /* Time outage total */
2299
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
2300
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
2301
                }
3✔
2302
            }
26✔
2303
            else {
2304
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2305
                    h->num_sent, h->num_recv,
2306
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2307
            }
2308

2309
            if (h->num_recv) {
90✔
2310
                avg = h->total_time / h->num_recv;
79✔
2311
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
2312
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
2313
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
2314
            }
23✔
2315

2316
            fprintf(stderr, "\n");
90✔
2317
        }
2318
    }
38✔
2319
}
100✔
2320

2321
/************************************************************
2322

2323
  Function: print_per_system_stats_json
2324

2325
*************************************************************
2326

2327
  Inputs:  void (none)
2328

2329
  Description:
2330

2331

2332
************************************************************/
2333

2334
void print_per_system_stats_json(void)
56✔
2335
{
2336
    int i, j, avg, outage_ms;
2337
    HOST_ENTRY *h;
2338
    int64_t resp;
2339

2340
    for (i = 0; i < num_hosts; i++) {
118✔
2341
        h = table[i];
62✔
2342

2343
        if (report_all_rtts_flag)
62✔
2344
            fprintf(stdout, "{\"vSum\": {");
18✔
2345
        else
2346
            fprintf(stdout, "{\"summary\": {");
44✔
2347

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

2350
        if (report_all_rtts_flag) {
62✔
2351
            fprintf(stdout, "\"values\": [");
18✔
2352
            for (j = 0; j < h->num_sent; j++) {
42✔
2353
                if (j > 0)
24✔
2354
                  fprintf(stdout, ", ");
6✔
2355
                
2356
                if ((resp = h->resp_times[j]) >= 0)
24✔
2357
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2358
                else
2359
                    fprintf(stdout, "null");
3✔
2360
            }
8✔
2361

2362
            fprintf(stdout, "]}");
18✔
2363
        }
6✔
2364
        else {
2365
            if (h->num_recv <= h->num_sent) {
44✔
2366
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
44✔
2367
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
44✔
2368
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
44✔
2369

2370
                if (outage_flag) {
44✔
2371
                    /* Time outage total */
2372
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
2373
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2374
                }
2375
            }
14✔
2376
            else {
2377
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
2378
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
2379
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2380
            }
2381

2382
            if (h->num_recv) {
44✔
2383
                avg = h->total_time / h->num_recv;
41✔
2384
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
41✔
2385
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
41✔
2386
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
41✔
2387
            }
13✔
2388

2389
            fprintf(stdout, "}");
44✔
2390
        }
2391
        fprintf(stdout, "}\n");
62✔
2392
    }
20✔
2393
}
56✔
2394

2395
/************************************************************
2396

2397
  Function: print_netdata
2398

2399
*************************************************************
2400

2401
  Inputs:  void (none)
2402

2403
  Description:
2404

2405

2406
************************************************************/
2407

2408
void print_netdata(void)
3✔
2409
{
2410
    static int sent_charts = 0;
2411

2412
    int i;
2413
    int64_t avg;
2414
    HOST_ENTRY *h;
2415

2416
    for (i = 0; i < num_hosts; i++) {
6✔
2417
        h = table[i];
3✔
2418

2419
        if (!sent_charts) {
3✔
2420
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2421
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2422
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2423
        }
1✔
2424

2425
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2426
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2427
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2428
        printf("END\n");
3✔
2429

2430
        if (!sent_charts) {
3✔
2431
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2432
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2433
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2434
        }
1✔
2435

2436
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2437
        /*
2438
        if( h->num_recv_i <= h->num_sent_i )
2439
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2440
        else
2441
            printf("SET lost = 0\n");
2442
*/
2443

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

2447
        if (!sent_charts) {
3✔
2448
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2449
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2450
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2451
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2452
        }
1✔
2453

2454
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2455
        if (h->num_recv_i) {
3✔
2456
            avg = h->total_time_i / h->num_recv_i;
3✔
2457
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2458
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2459
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2460
        }
1✔
2461
        printf("END\n");
3✔
2462

2463
        stats_reset_interval(h);
3✔
2464
    }
1✔
2465

2466
    sent_charts = 1;
3✔
2467
}
3✔
2468

2469
/************************************************************
2470

2471
  Function: print_per_system_splits
2472

2473
*************************************************************
2474

2475
  Inputs:  void (none)
2476

2477
  Description:
2478

2479

2480
************************************************************/
2481

2482
void print_per_system_splits(void)
39✔
2483
{
2484
    int i, avg, outage_ms_i;
2485
    HOST_ENTRY *h;
2486
    struct tm *curr_tm;
2487

2488
    if (verbose_flag || per_recv_flag)
39✔
2489
        fprintf(stderr, "\n");
×
2490

2491
    update_current_time();
39✔
2492
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2493
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2494
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2495

2496
    for (i = 0; i < num_hosts; i++) {
78✔
2497
        h = table[i];
39✔
2498
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2499

2500
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2501
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2502
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
39✔
2503

2504
            if (outage_flag) {
39✔
2505
                /* Time outage  */
2506
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2507
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2508
            }
4✔
2509
        }
13✔
2510
        else {
2511
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2512
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2513
        }
2514

2515
        if (h->num_recv_i) {
39✔
2516
            avg = h->total_time_i / h->num_recv_i;
27✔
2517
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2518
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2519
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2520
        }
9✔
2521

2522
        fprintf(stderr, "\n");
39✔
2523
        if (!cumulative_stats_flag) {
39✔
2524
            stats_reset_interval(h);
27✔
2525
        }
9✔
2526
    }
13✔
2527
}
39✔
2528

2529
/************************************************************
2530

2531
  Function: print_per_system_splits_json
2532

2533
*************************************************************
2534

2535
  Inputs:  void (none)
2536

2537
  Description:
2538

2539

2540
************************************************************/
2541

2542
void print_per_system_splits_json(void)
3✔
2543
{
2544
    int i, avg, outage_ms_i;
2545
    HOST_ENTRY *h;
2546

2547
    update_current_time();
3✔
2548

2549
    for (i = 0; i < num_hosts; i++) {
6✔
2550
        h = table[i];
3✔
2551
        fprintf(stdout, "{\"intSum\": {");
3✔
2552
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2553
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2554

2555
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2556
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2557
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2558
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2559

2560
            if (outage_flag) {
3✔
2561
                /* Time outage  */
2562
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
2563
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2564
            }
2565
        }
1✔
2566
        else {
2567
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
2568
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
2569
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2570
        }
2571

2572
        if (h->num_recv_i) {
3✔
2573
            avg = h->total_time_i / h->num_recv_i;
3✔
2574
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2575
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2576
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2577
        }
1✔
2578

2579
        fprintf(stdout, "}}\n");
3✔
2580
        if (!cumulative_stats_flag) {
3✔
2581
            stats_reset_interval(h);
3✔
2582
        }
1✔
2583
    }
1✔
2584
}
3✔
2585

2586
/************************************************************
2587

2588
  Function: print_global_stats
2589

2590
*************************************************************
2591

2592
  Inputs:  void (none)
2593

2594
  Description:
2595

2596

2597
************************************************************/
2598

2599
void print_global_stats(void)
9✔
2600
{
2601
    fprintf(stderr, "\n");
9✔
2602
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2603
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2604
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2605
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2606
    fprintf(stderr, "\n");
9✔
2607
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2608
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2609
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2610
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2611
    fprintf(stderr, "\n");
9✔
2612

2613
    if (total_replies == 0) {
9✔
2614
        min_reply = 0;
3✔
2615
        max_reply = 0;
3✔
2616
        total_replies = 1;
3✔
2617
        sum_replies = 0;
3✔
2618
    }
1✔
2619

2620
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2621
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2622
        sprint_tm(sum_replies / total_replies));
3✔
2623
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2624
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2625
        (end_time - start_time) / 1e9);
9✔
2626
    fprintf(stderr, "\n");
9✔
2627
}
9✔
2628

2629
/************************************************************
2630

2631
  Function: print_global_stats_json
2632

2633
*************************************************************
2634

2635
  Inputs:  void (none)
2636

2637
  Description:
2638

2639

2640
************************************************************/
2641

2642
void print_global_stats_json(void)
3✔
2643
{
2644
    fprintf(stdout, "{\"globalSum\": {");
3✔
2645
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2646
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2647
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2648
    fprintf(stdout, "\"unknown addresses\": %d, ", num_noaddress);
3✔
2649
    fprintf(stdout, "\"timeouts (waiting for response)\": %d, ", num_timeout);
3✔
2650
    fprintf(stdout, "\"ICMP Echos sent\": %d, ", num_pingsent);
3✔
2651
    fprintf(stdout, "\"ICMP Echo Replies received\": %d, ", num_pingreceived);
3✔
2652
    fprintf(stdout, "\"other ICMP received\": %d, ", num_othericmprcvd);
3✔
2653

2654
    if (total_replies == 0) {
3✔
2655
        min_reply = 0;
×
2656
        max_reply = 0;
×
2657
        total_replies = 1;
×
2658
        sum_replies = 0;
×
2659
    }
2660

2661
    fprintf(stdout, "\"ms (min round trip time)\": %s, ", sprint_tm(min_reply));
3✔
2662
    fprintf(stdout, "\"ms (avg round trip time)\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2663
    fprintf(stdout, "\"ms (max round trip time)\": %s, ", sprint_tm(max_reply));
3✔
2664
    fprintf(stdout, "\"sec (elapsed real time)\": %.3f", (end_time - start_time) / 1e9);
3✔
2665
    fprintf(stdout, "}}\n");
3✔
2666
}
3✔
2667

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

2670
  Function: send_ping
2671

2672
*************************************************************
2673

2674
  Inputs:  int s, HOST_ENTRY *h
2675

2676
  Description:
2677

2678
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2679
  will be added on by the kernel.  The ID field is our UNIX process ID,
2680
  and the sequence number is an index into an array of outstanding
2681
  ping requests. The sequence number will later be used to quickly
2682
  figure out who the ping reply came from.
2683

2684
************************************************************/
2685

2686
int send_ping(HOST_ENTRY *h, int index)
932✔
2687
{
2688
    int n;
2689
    int myseq;
2690
    int ret = 1;
932✔
2691
    uint8_t proto = ICMP_ECHO;
932✔
2692

2693
    update_current_time();
932✔
2694
    h->last_send_time = current_time_ns;
932✔
2695
    myseq = seqmap_add(h->i, index, current_time_ns);
932✔
2696

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

2699
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
932✔
2700
        if(icmp_request_typ == 13)
809✔
2701
            proto = ICMP_TSTAMP;
18✔
2702
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
809✔
2703
    }
253✔
2704
#ifdef IPV6
2705
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2706
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2707
    }
15✔
2708
#endif
2709
    else {
2710
        return 0;
×
2711
    }
2712

2713
    /* error sending? */
2714
    if (
664✔
2715
        (n < 0)
268✔
2716
#if defined(EHOSTDOWN)
2717
        && errno != EHOSTDOWN
302✔
2718
#endif
2719
    ) {
2720
        if (verbose_flag) {
34✔
2721
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2722
        }
2723
        else {
2724
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2725
        }
2726

2727
        h->num_sent++;
34✔
2728
        h->num_sent_i++;
34✔
2729
        if (!loop_flag)
34✔
2730
            h->resp_times[index] = RESP_ERROR;
34✔
2731

2732
        ret = 0;
34✔
2733
    }
2734
    else {
2735
        /* schedule timeout */
2736
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
898✔
2737

2738
        /* mark this trial as outstanding */
2739
        if (!loop_flag) {
898✔
2740
            h->resp_times[index] = RESP_WAITING;
898✔
2741
        }
268✔
2742
    }
2743

2744
    num_pingsent++;
932✔
2745
    last_send_time = h->last_send_time;
932✔
2746

2747
    return (ret);
932✔
2748
}
268✔
2749

2750
int socket_can_read(struct timeval *timeout)
2,752✔
2751
{
2752
    int nfound;
2753
    fd_set readset;
2754
    int socketmax;
2755

2756
#ifndef IPV6
2757
    socketmax = socket4;
2758
#else
2759
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,752✔
2760
#endif
2761

2762
select_again:
2,071✔
2763
    FD_ZERO(&readset);
35,888✔
2764
    if (socket4 >= 0)
2,752✔
2765
        FD_SET(socket4, &readset);
2,752✔
2766
#ifdef IPV6
2767
    if (socket6 >= 0)
2,752✔
2768
        FD_SET(socket6, &readset);
2,752✔
2769
#endif
2770

2771
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,752✔
2772
    if (nfound < 0) {
2,752✔
2773
        if (errno == EINTR) {
×
2774
            /* interrupted system call: redo the select */
2775
            goto select_again;
×
2776
        }
2777
        else {
2778
            perror("select");
×
2779
        }
2780
    }
2781

2782
    if (nfound > 0) {
2,752✔
2783
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,291✔
2784
            return socket4;
1,200✔
2785
        }
2786
#ifdef IPV6
2787
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
91✔
2788
            return socket6;
91✔
2789
        }
2790
#endif
2791
    }
2792

2793
    return -1;
1,461✔
2794
}
681✔
2795

2796
int receive_packet(int64_t wait_time,
2,752✔
2797
#if HAVE_SO_TIMESTAMPNS
2798
    int64_t *reply_timestamp,
2799
#else
2800
    int64_t *reply_timestamp __attribute__((unused)),
2801
#endif
2802
    struct sockaddr *reply_src_addr,
2803
    size_t reply_src_addr_len,
2804
    char *reply_buf,
2805
    size_t reply_buf_len,
2806
    int *ip_header_tos,
2807
    int *ip_header_ttl)
2808
{
2809
    struct timeval to;
2810
    int s = 0;
2,752✔
2811
    int recv_len;
2812
    static unsigned char msg_control[128];
2813
    struct iovec msg_iov = {
4,114✔
2814
        reply_buf,
681✔
2815
        reply_buf_len
681✔
2816
    };
2817
    struct msghdr recv_msghdr = {0};
2,752✔
2818
    recv_msghdr.msg_name = reply_src_addr;
2,752✔
2819
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,752✔
2820
    recv_msghdr.msg_iov = &msg_iov;
2,752✔
2821
    recv_msghdr.msg_iovlen = 1;
2,752✔
2822
    recv_msghdr.msg_control = &msg_control;
2,752✔
2823
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,752✔
2824
    struct cmsghdr *cmsg;
2825

2826
    /* Wait for a socket to become ready */
2827
    if (wait_time) {
2,752✔
2828
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,460✔
2829
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,460✔
2830
    }
438✔
2831
    else {
2832
        to.tv_sec = 0;
1,292✔
2833
        to.tv_usec = 0;
1,292✔
2834
    }
2835
    s = socket_can_read(&to);
2,752✔
2836
    if (s == -1) {
2,752✔
2837
        return 0; /* timeout */
1,461✔
2838
    }
2839

2840
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,291✔
2841
    if (recv_len <= 0) {
1,291✔
2842
        return 0;
×
2843
    }
2844

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

2881
#if defined(DEBUG) || defined(_DEBUG)
2882
    if (randomly_lose_flag) {
2883
        if ((random() & 0x07) <= lose_factor)
2884
            return 0;
2885
    }
2886
#endif
2887

2888
    return recv_len;
1,291✔
2889
}
681✔
2890

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

2905
    if (!success) {
898✔
2906
        if (!loop_flag && index >= 0) {
113✔
2907
            h->resp_times[index] = RESP_TIMEOUT;
113✔
2908
        }
29✔
2909
        num_timeout++;
113✔
2910
        return;
113✔
2911
    }
2912

2913
    /* received count */
2914
    h->num_recv++;
785✔
2915
    h->num_recv_i++;
785✔
2916

2917
    /* maximum */
2918
    if (!h->max_reply || latency > h->max_reply) {
785✔
2919
        h->max_reply = latency;
622✔
2920
    }
173✔
2921
    if (!h->max_reply_i || latency > h->max_reply_i) {
785✔
2922
        h->max_reply_i = latency;
640✔
2923
    }
180✔
2924

2925
    /* minimum */
2926
    if (!h->min_reply || latency < h->min_reply) {
785✔
2927
        h->min_reply = latency;
527✔
2928
    }
166✔
2929
    if (!h->min_reply_i || latency < h->min_reply_i) {
785✔
2930
        h->min_reply_i = latency;
555✔
2931
    }
174✔
2932

2933
    /* total time (for average) */
2934
    h->total_time += latency;
785✔
2935
    h->total_time_i += latency;
785✔
2936

2937
    /* response time per-packet (count mode) */
2938
    if (!loop_flag && index >= 0) {
785✔
2939
        h->resp_times[index] = latency;
785✔
2940
    }
239✔
2941
}
268✔
2942

2943
/* stats_reset_interval: reset interval statistics
2944
 * h: host entry to update
2945
 */
2946
void stats_reset_interval(HOST_ENTRY *h)
33✔
2947
{
2948
    h->num_sent_i = 0;
33✔
2949
    h->num_recv_i = 0;
33✔
2950
    h->max_reply_i = 0;
33✔
2951
    h->min_reply_i = 0;
33✔
2952
    h->total_time_i = 0;
33✔
2953
}
33✔
2954

2955
int decode_icmp_ipv4(
1,200✔
2956
    struct sockaddr *response_addr,
2957
    size_t response_addr_len,
2958
    char *reply_buf,
2959
    size_t reply_buf_len,
2960
    unsigned short *id,
2961
    unsigned short *seq,
2962
    IP_HEADER_RESULT *ip_header_res)
2963
{
2964
    struct icmp *icp;
2965
    int hlen = 0;
1,200✔
2966

2967
    if (!using_sock_dgram4) {
1,200✔
2968
        struct ip *ip = (struct ip *)reply_buf;
1,192✔
2969
        ip_header_res->tos = ip->ip_tos;
1,192✔
2970
        ip_header_res->ttl = ip->ip_ttl;
1,192✔
2971

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

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

2993
    icp = (struct icmp *)(reply_buf + hlen);
1,200✔
2994

2995
    if (traceroute_flag && icp->icmp_type == ICMP_TIMXCEED) {
1,200✔
2996
        struct ip *inner_ip;
2997
        int inner_hlen;
2998
        struct icmp *inner_icmp;
2999

NEW
3000
        if (reply_buf_len >= hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
×
NEW
3001
            inner_ip = (struct ip *) (reply_buf + hlen + ICMP_MINLEN);
×
NEW
3002
            inner_hlen = inner_ip->ip_hl << 2;
×
NEW
3003
            inner_icmp = (struct icmp *) ((char *)inner_ip + inner_hlen);
×
NEW
3004
            if (inner_icmp->icmp_id == ident4) {
×
NEW
3005
                *id = inner_icmp->icmp_id;
×
NEW
3006
                *seq = ntohs(inner_icmp->icmp_seq);
×
NEW
3007
                return hlen;
×
3008
            }
3009
        }
3010
    }
3011

3012
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,200✔
3013
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
506✔
3014
        /* Handle other ICMP packets */
3015
        struct icmp *sent_icmp;
3016
        SEQMAP_VALUE *seqmap_value;
3017
        char addr_ascii[INET6_ADDRSTRLEN];
3018
        HOST_ENTRY *h;
3019

3020
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
3021
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
938✔
3022
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
3023
            return -1;
×
3024
        }
3025

3026
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
482✔
3027

3028
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
482✔
3029
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
3030
            sent_icmp->icmp_id != ident4) {
×
3031
            /* not caused by us */
3032
            return -1;
482✔
3033
        }
3034

3035
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
3036
        if (seqmap_value == NULL) {
×
3037
            return -1;
×
3038
        }
3039

3040
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3041

3042
        switch (icp->icmp_type) {
×
3043
        case ICMP_UNREACH:
3044
            h = table[seqmap_value->host_nr];
×
3045
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
3046
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
3047
                    addr_ascii, h->host);
3048
            }
3049
            else {
3050
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3051
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
3052
            }
3053

3054
            print_warning("\n");
×
3055
            num_othericmprcvd++;
×
3056
            break;
×
3057

3058
        case ICMP_SOURCEQUENCH:
3059
        case ICMP_REDIRECT:
3060
        case ICMP_TIMXCEED:
3061
        case ICMP_PARAMPROB:
3062
            h = table[seqmap_value->host_nr];
×
3063
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
3064
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3065
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
3066
            }
3067
            else {
3068
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
3069
                    icp->icmp_type, addr_ascii, h->host);
×
3070
            }
3071
            print_warning("\n");
×
3072
            num_othericmprcvd++;
×
3073
            break;
×
3074
        }
3075

3076
        return -1;
×
3077
    }
3078

3079
    *id = icp->icmp_id;
718✔
3080
    *seq = ntohs(icp->icmp_seq);
718✔
3081
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
718✔
3082

3083
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
3084
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
3085
            if (verbose_flag) {
×
3086
                char buf[INET6_ADDRSTRLEN];
3087
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3088
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3089
            }
3090
            return -1;
×
3091
        }
3092

3093
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
3094
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
3095
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
3096
    }
3097

3098
    return hlen;
718✔
3099
}
228✔
3100

3101
#ifdef IPV6
3102
int decode_icmp_ipv6(
91✔
3103
    struct sockaddr *response_addr,
3104
    size_t response_addr_len,
3105
    char *reply_buf,
3106
    size_t reply_buf_len,
3107
    unsigned short *id,
3108
    unsigned short *seq)
3109
{
3110
    struct icmp6_hdr *icp;
3111

3112
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
91✔
3113
        if (verbose_flag) {
×
3114
            char buf[INET6_ADDRSTRLEN];
3115
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3116
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3117
        }
3118
        return 0; /* too short */
×
3119
    }
3120

3121
    icp = (struct icmp6_hdr *)reply_buf;
91✔
3122

3123
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
91✔
3124
        /* Handle other ICMPv6 packets */
3125
        struct ip6_hdr *sent_ipv6;
3126
        struct icmp6_hdr *sent_icmp;
3127
        SEQMAP_VALUE *seqmap_value;
3128
        char addr_ascii[INET6_ADDRSTRLEN];
3129
        HOST_ENTRY *h;
3130

3131
        /* reply icmp packet (ICMPv6 header) followed by "sent packet" (IPv6 + ICMPv6 header) */
3132
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
×
3133
            /* discard ICMPv6 message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
3134
            return 0;
×
3135
        }
3136

3137
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
×
3138
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
×
3139
            /* discard ICMPv6 message if we can't tell that it was caused by
3140
             * us, because the IPv6 header is not directly followed by an
3141
             * ICMPv6 header
3142
             */
3143
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
3144
            return 0;
×
3145
        }
3146
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
×
3147

3148
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
×
3149
            /* not caused by us */
3150
            return 0;
×
3151
        }
3152

3153
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
3154
        if (seqmap_value == NULL) {
×
3155
            return 0;
×
3156
        }
3157

3158
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3159
        h = table[seqmap_value->host_nr];
×
3160

3161
        switch (icp->icmp6_type) {
×
3162
        case ICMP6_DST_UNREACH:
3163
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
3164
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3165
                    icp->icmp6_code, addr_ascii, h->host);
×
3166
            } else {
3167
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3168
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
3169
            }
3170
            print_warning("\n");
×
3171
            num_othericmprcvd++;
×
3172
            break;
×
3173

3174
        case ICMP6_PACKET_TOO_BIG:
3175
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
3176
                addr_ascii, h->host);
3177
            num_othericmprcvd++;
×
3178
            break;
×
3179

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

3192
        case ICMP6_PARAM_PROB:
3193
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
3194
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3195
                    icp->icmp6_code, addr_ascii, h->host);
×
3196
            } else {
3197
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3198
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
3199
            }
3200
            print_warning("\n");
×
3201
            num_othericmprcvd++;
×
3202
            break;
×
3203

3204
        default:
3205
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
3206
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
3207
            num_othericmprcvd++;
×
3208
            break;
×
3209
        }
3210

3211
        return 0;
×
3212
    }
3213

3214
    *id = icp->icmp6_id;
91✔
3215
    *seq = ntohs(icp->icmp6_seq);
91✔
3216

3217
    return 1;
91✔
3218
}
15✔
3219
#endif
3220

3221
int wait_for_reply(int64_t wait_time)
2,752✔
3222
{
3223
    int result;
3224
    static char buffer[RECV_BUFSIZE];
3225
    struct sockaddr_storage response_addr;
3226
    int n, avg;
3227
    int ip_hlen = 0;
2,752✔
3228
    HOST_ENTRY *h;
3229
    int64_t this_reply;
3230
    int this_count;
3231
    int64_t recv_time = 0;
2,752✔
3232
    SEQMAP_VALUE *seqmap_value;
3233
    unsigned short id;
3234
    unsigned short seq;
3235
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
2,752✔
3236

3237
    /* Receive packet */
3238
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,433✔
3239
        &recv_time, /* reply_timestamp */
3240
        (struct sockaddr *)&response_addr, /* reply_src_addr */
3241
        sizeof(response_addr), /* reply_src_addr_len */
3242
        buffer, /* reply_buf */
3243
        sizeof(buffer), /* reply_buf_len */
3244
        &ip_header_res.tos, /* TOS resp. TC byte */
681✔
3245
        &ip_header_res.ttl /* TTL resp. hop limit */
681✔
3246
    );
3247

3248
    if (result <= 0) {
2,752✔
3249
        return 0;
1,461✔
3250
    }
3251

3252
    update_current_time();
1,291✔
3253
    if (recv_time == 0)
1,291✔
3254
        recv_time = current_time_ns;
243✔
3255

3256
    /* Process ICMP packet and retrieve id/seq */
3257
    if (response_addr.ss_family == AF_INET) {
1,291✔
3258
        ip_hlen = decode_icmp_ipv4(
1,200✔
3259
            (struct sockaddr *)&response_addr,
3260
            sizeof(response_addr),
3261
            buffer,
3262
            sizeof(buffer),
3263
            &id,
3264
            &seq,
3265
            &ip_header_res);
3266
        if (ip_hlen < 0) {
1,200✔
3267
            return 1;
482✔
3268
        }
3269
        if (id != ident4) {
718✔
3270
            return 1; /* packet received, but not the one we are looking for! */
×
3271
        }
3272
        if (!using_sock_dgram4) {
718✔
3273
            /* do not include IP header in returned size, to be consistent with ping(8) and also
3274
             * with fping with IPv6 hosts */
3275
            result -= ip_hlen;
710✔
3276
        }
228✔
3277
    }
228✔
3278
#ifdef IPV6
3279
    else if (response_addr.ss_family == AF_INET6) {
91✔
3280
        if (!decode_icmp_ipv6(
91✔
3281
                (struct sockaddr *)&response_addr,
3282
                sizeof(response_addr),
3283
                buffer,
3284
                sizeof(buffer),
3285
                &id,
3286
                &seq)) {
3287
            return 1;
×
3288
        }
3289
        if (id != ident6) {
91✔
3290
            return 1; /* packet received, but not the one we are looking for! */
×
3291
        }
3292
    }
15✔
3293
#endif
3294
    else {
3295
        return 1;
×
3296
    }
3297

3298
    seqmap_value = seqmap_fetch(seq, current_time_ns);
809✔
3299
    if (seqmap_value == NULL) {
809✔
3300
        return 1;
12✔
3301
    }
3302

3303
    /* find corresponding host_entry */
3304
    n = seqmap_value->host_nr;
797✔
3305
    h = table[n];
797✔
3306
    this_count = seqmap_value->ping_count;
797✔
3307
    this_reply = recv_time - seqmap_value->ping_ts;
797✔
3308

3309
    if (traceroute_flag && response_addr.ss_family == AF_INET) {
797✔
NEW
3310
        struct icmp *icp = (struct icmp *)(buffer + ip_hlen);
×
3311
        char ip_str[INET_ADDRSTRLEN];
NEW
3312
        getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), ip_str, sizeof(ip_str), NULL, 0, NI_NUMERICHOST);
×
3313

NEW
3314
        if (icp->icmp_type == ICMP_TIMXCEED) {
×
NEW
3315
            printf("%s: hop %d reached %s (%.2f ms)\n", h->host, h->trace_ttl, ip_str, (double)this_reply/1000000.0);
×
NEW
3316
            h->trace_ttl++;
×
NEW
3317
            if (h->trace_ttl > 30) h->trace_ttl = 30;
×
3318

NEW
3319
            stats_add(h, this_count, 1, this_reply);
×
NEW
3320
            struct event *timeout_event = host_get_timeout_event(h, this_count);
×
NEW
3321
            if (timeout_event) {
×
NEW
3322
                ev_remove(&event_queue_timeout, timeout_event);
×
3323
            }
NEW
3324
            return 1;
×
NEW
3325
        } else if (icp->icmp_type == ICMP_ECHOREPLY) {
×
NEW
3326
            printf("%s: hop %d reached DESTINATION %s (%.2f ms)\n", h->host, h->trace_ttl, ip_str, (double)this_reply/1000000.0);
×
NEW
3327
            h->trace_ttl = 100; /* Goal achieved: artificially increase TTL to stop loop in main_loop */
×
3328
        }
3329
    }
3330

3331
    /* update stats that include invalid replies */
3332
    h->num_recv_total++;
797✔
3333
    num_pingreceived++;
797✔
3334

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

3337
    /* optionally require reply source equal to target address */
3338
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
797✔
3339
        dbg_printf("%s\n", "discarding reply from wrong source address");
3340
        return 1;
12✔
3341
    }
3342

3343
    /* discard duplicates */
3344
    if (!loop_flag && !(count_flag && quiet_flag) && h->resp_times[this_count] >= 0) {
785✔
3345
        if (!per_recv_flag) {
×
3346
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3347
                h->host, this_count, result, sprint_tm(this_reply));
3348

3349
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3350
                char buf[INET6_ADDRSTRLEN];
3351
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3352
                fprintf(stderr, " [<- %s]", buf);
×
3353
            }
3354
            fprintf(stderr, "\n");
×
3355
        }
3356
        return 1;
×
3357
    }
3358

3359
    /* discard reply if delay is larger than timeout
3360
     * (see also: github #32) */
3361
    if (this_reply > h->timeout) {
785✔
3362
        return 1;
×
3363
    }
3364

3365
    /* update stats */
3366
    stats_add(h, this_count, 1, this_reply);
785✔
3367
    // TODO: move to stats_add?
3368
    if (!max_reply || this_reply > max_reply)
785✔
3369
        max_reply = this_reply;
498✔
3370
    if (!min_reply || this_reply < min_reply)
785✔
3371
        min_reply = this_reply;
440✔
3372
    sum_replies += this_reply;
785✔
3373
    total_replies++;
785✔
3374

3375
    /* initialize timeout to initial timeout (without backoff) */
3376
    h->timeout = timeout;
785✔
3377

3378
    /* remove timeout event */
3379
    struct event *timeout_event = host_get_timeout_event(h, this_count);
785✔
3380
    if (timeout_event) {
785✔
3381
        ev_remove(&event_queue_timeout, timeout_event);
785✔
3382
    }
239✔
3383

3384
    /* print "is alive" */
3385
    if (h->num_recv == 1) {
785✔
3386
        num_alive++;
486✔
3387
        if (fast_reachable && num_alive >= min_reachable)
486✔
3388
            finish_requested = 1;
3✔
3389

3390
        if (verbose_flag || alive_flag) {
486✔
3391
            printf("%s", h->host);
299✔
3392

3393
            if (verbose_flag)
299✔
3394
                printf(" is alive");
279✔
3395
        }
85✔
3396
    }
142✔
3397

3398
    /* print received ping (unless --quiet) */
3399
    if (per_recv_flag) {
785✔
3400
        avg = h->total_time / h->num_recv;
186✔
3401
        if (json_flag) {
186✔
3402
            print_recv_json(h,
46✔
3403
                recv_time,
11✔
3404
                result,
11✔
3405
                this_count,
11✔
3406
                this_reply,
11✔
3407
                avg);
11✔
3408
        }
11✔
3409
        else {
3410
            print_recv(h,
196✔
3411
                recv_time,
45✔
3412
                result,
45✔
3413
                this_count,
45✔
3414
                this_reply,
45✔
3415
                avg);
45✔
3416
        }
3417
    }
56✔
3418

3419
    if (verbose_flag || alive_flag || per_recv_flag) {
785✔
3420
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
485✔
3421
            char buf[INET6_ADDRSTRLEN];
3422
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3423
            fprintf(stderr, " [<- %s]", buf);
6✔
3424
        }
3425
        if (json_flag) {
485✔
3426
            print_recv_ext_json(&ip_header_res,
35✔
3427
                recv_time,
11✔
3428
                this_reply);
11✔
3429
        }
11✔
3430
        else {
3431
            print_recv_ext(&ip_header_res,
450✔
3432
                recv_time,
130✔
3433
                this_reply);
130✔
3434
        }
3435
    }
141✔
3436
    return 1;
785✔
3437
}
681✔
3438

3439
/************************************************************
3440

3441
  Function: add_name
3442

3443
*************************************************************
3444

3445
  Inputs:  char* name
3446

3447
  Description:
3448

3449
  process input name for addition to target list
3450
  name can turn into multiple targets via multiple interfaces (-m)
3451
  or via NIS groups
3452

3453
************************************************************/
3454

3455
void add_name(char *name)
609✔
3456
{
3457
    struct addrinfo *res0, *res, hints;
3458
    int ret_ga;
3459
    char *printname;
3460
    char namebuf[256];
3461
    char addrbuf[256];
3462

3463
    /* getaddrinfo */
3464
    memset(&hints, 0, sizeof(struct addrinfo));
609✔
3465
    hints.ai_flags = AI_UNUSABLE;
609✔
3466
    hints.ai_socktype = SOCK_RAW;
609✔
3467
    hints.ai_family = hints_ai_family;
609✔
3468
    if (hints_ai_family == AF_INET) {
609✔
3469
        hints.ai_protocol = IPPROTO_ICMP;
81✔
3470
    }
21✔
3471
#ifdef IPV6
3472
    else if (hints_ai_family == AF_INET6) {
528✔
3473
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
3474
    }
1✔
3475
#endif
3476
    else {
3477
        hints.ai_socktype = SOCK_STREAM;
497✔
3478
        hints.ai_protocol = 0;
497✔
3479
    }
3480
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
609✔
3481
    if (ret_ga) {
609✔
3482
        if (!quiet_flag)
29✔
3483
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3484
        num_noaddress++;
29✔
3485

3486
        // Handle JSON output for invalid hosts
3487
        if (json_flag) {
29✔
3488
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
3489
            return;
21✔
3490
        }
3491

3492
        return;
26✔
3493
    }
3494

3495
    /* NOTE: we could/should loop with res on all addresses like this:
3496
     * for (res = res0; res; res = res->ai_next) {
3497
     * We don't do it yet, however, because is is an incompatible change
3498
     * (need to implement a separate option for this)
3499
     */
3500
    for (res = res0; res; res = res->ai_next) {
580✔
3501
        /* name_flag: addr -> name lookup requested) */
3502
        if (name_flag || rdns_flag) {
582✔
3503
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3504
            if (name_flag) {
3✔
3505
                /* Was it a numerical address? Only then do a rdns-query */
3506
                struct addrinfo *nres;
3507
                hints.ai_flags = AI_NUMERICHOST;
×
3508
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3509
                    do_rdns = 1;
×
3510
                    freeaddrinfo(nres);
×
3511
                }
3512
            }
3513

3514
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3515
                printname = namebuf;
3✔
3516
            }
1✔
3517
            else {
3518
                printname = name;
×
3519
            }
3520
        }
1✔
3521
        else {
3522
            printname = name;
577✔
3523
        }
3524

3525
        /* addr_flag: name -> addr lookup requested */
3526
        if (addr_flag) {
580✔
3527
            int ret;
3528
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3529
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3530
            if (ret) {
3✔
3531
                if (!quiet_flag) {
×
3532
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3533
                }
3534
                continue;
×
3535
            }
3536

3537
            if (name_flag || rdns_flag) {
3✔
3538
                char nameaddrbuf[512 + 3];
3539
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3540
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3541
            }
3542
            else {
3543
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3544
            }
3545
        }
1✔
3546
        else {
3547
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
577✔
3548
        }
3549

3550
        if (!multif_flag) {
580✔
3551
            break;
580✔
3552
        }
3553
    }
3554

3555
    freeaddrinfo(res0);
580✔
3556
}
167✔
3557

3558
/************************************************************
3559

3560
  Function: add_addr
3561

3562
*************************************************************
3563

3564
  Description:
3565

3566
  add single address to list of hosts to be pinged
3567

3568
************************************************************/
3569

3570
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
580✔
3571
{
3572
    HOST_ENTRY *p;
3573
    int n;
3574
    int64_t *i;
3575

3576
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
580✔
3577
    if (!p)
580✔
3578
        crash_and_burn("can't allocate HOST_ENTRY");
×
3579

3580
    p->name = strdup(name);
580✔
3581
    p->host = strdup(host);
580✔
3582
    memcpy(&p->saddr, ipaddr, ipaddr_len);
580✔
3583
    p->saddr_len = ipaddr_len;
580✔
3584
    p->timeout = timeout;
580✔
3585
    p->min_reply = 0;
580✔
3586
    p->trace_ttl = 1;
580✔
3587

3588
    if (netdata_flag) {
580✔
3589
        char *s = p->name;
3✔
3590
        while (*s) {
30✔
3591
            if (!isalnum(*s))
27✔
3592
                *s = '_';
9✔
3593
            s++;
27✔
3594
        }
3595
    }
1✔
3596

3597
    if (strlen(p->host) > max_hostname_len)
580✔
3598
        max_hostname_len = strlen(p->host);
387✔
3599

3600
    /* array for response time results */
3601
    if (!loop_flag) {
580✔
3602
#if SIZE_MAX <= UINT_MAX
3603
        if (trials > (SIZE_MAX / sizeof(int64_t)))
3604
            crash_and_burn("resp_times array too large for memory");
3605
#endif
3606
        i = (int64_t *)malloc(trials * sizeof(int64_t));
580✔
3607
        if (!i)
580✔
3608
            crash_and_burn("can't allocate resp_times array");
×
3609

3610
        for (n = 0; n < trials; n++)
2,891✔
3611
            i[n] = RESP_UNUSED;
2,311✔
3612

3613
        p->resp_times = i;
580✔
3614
    }
158✔
3615

3616
    /* allocate event storage */
3617
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
580✔
3618
    if (!p->event_storage_ping) {
580✔
3619
        errno_crash_and_burn("can't allocate event_storage_ping");
×
3620
    }
3621
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
580✔
3622
    if (!p->event_storage_timeout) {
580✔
3623
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
3624
    }
3625

3626
    /* schedule first ping */
3627
    host_add_ping_event(p, 0, current_time_ns);
580✔
3628

3629
    num_hosts++;
580✔
3630
}
580✔
3631

3632
/************************************************************
3633

3634
  Function: crash_and_burn
3635

3636
*************************************************************
3637

3638
  Inputs:  char* message
3639

3640
  Description:
3641

3642
************************************************************/
3643

3644
void crash_and_burn(char *message)
×
3645
{
3646
    fprintf(stderr, "%s: %s\n", prog, message);
×
3647
    exit(4);
×
3648
}
3649

3650
/************************************************************
3651

3652
  Function: errno_crash_and_burn
3653

3654
*************************************************************
3655

3656
  Inputs:  char* message
3657

3658
  Description:
3659

3660
************************************************************/
3661

3662
void errno_crash_and_burn(char *message)
8✔
3663
{
3664
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3665
    exit(4);
8✔
3666
}
3667

3668
/************************************************************
3669

3670
  Function: print_warning
3671

3672
  Description: fprintf(stderr, ...), unless running with -q
3673

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

3676
void print_warning(char *format, ...)
54✔
3677
{
3678
    va_list args;
3679
    if (!quiet_flag) {
54✔
3680
        va_start(args, format);
54✔
3681
        vfprintf(stderr, format, args);
54✔
3682
        va_end(args);
54✔
3683
    }
8✔
3684
}
54✔
3685

3686
/************************************************************
3687

3688
  Function: sprint_tm
3689

3690
*************************************************************
3691

3692
  render nanosecond int64_t value into milliseconds string with three digits of
3693
  precision.
3694

3695
************************************************************/
3696

3697
const char *sprint_tm(int64_t ns)
1,083✔
3698
{
3699
    static char buf[10];
3700
    double t = (double)ns / 1e6;
1,083✔
3701

3702
    if (t < 0.0) {
1,083✔
3703
        /* negative (unexpected) */
3704
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3705
    }
3706
    else if (t < 1.0) {
1,083✔
3707
        /* <= 0.99 ms */
3708
        snprintf(buf, sizeof(buf), "%.3f", t);
1,083✔
3709
    }
335✔
UNCOV
3710
    else if (t < 10.0) {
×
3711
        /* 1.00 - 9.99 ms */
UNCOV
3712
        snprintf(buf, sizeof(buf), "%.2f", t);
×
3713
    }
3714
    else if (t < 100.0) {
×
3715
        /* 10.0 - 99.9 ms */
3716
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3717
    }
3718
    else if (t < 1000000.0) {
×
3719
        /* 100 - 1'000'000 ms */
3720
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3721
    }
3722
    else {
3723
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3724
    }
3725

3726
    return (buf);
1,083✔
3727
}
3728

3729
/************************************************************
3730

3731
  Function: addr_cmp
3732

3733
*************************************************************/
3734
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
507✔
3735
{
3736
    if (a->sa_family != b->sa_family) {
507✔
3737
        return a->sa_family - b->sa_family;
×
3738
    }
3739
    else {
3740
        if (a->sa_family == AF_INET) {
507✔
3741
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
443✔
3742
        }
3743
        else if (a->sa_family == AF_INET6) {
64✔
3744
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3745
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3746
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3747
        }
3748
    }
3749

3750
    return 0;
×
3751
}
143✔
3752

3753
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
913✔
3754
{
3755
    struct event *event = &h->event_storage_ping[index % event_storage_count];
913✔
3756
    event->host = h;
913✔
3757
    event->ping_index = index;
913✔
3758
    event->ev_time = ev_time;
913✔
3759
    ev_enqueue(&event_queue_ping, event);
913✔
3760

3761
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3762
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3763
}
913✔
3764

3765
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
898✔
3766
{
3767
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
898✔
3768
    event->host = h;
898✔
3769
    event->ping_index = index;
898✔
3770
    event->ev_time = ev_time;
898✔
3771
    ev_enqueue(&event_queue_timeout, event);
898✔
3772

3773
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3774
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3775
}
898✔
3776

3777
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
785✔
3778
{
3779
    return &h->event_storage_timeout[index % event_storage_count];
785✔
3780
}
3781

3782
/************************************************************
3783

3784
  Function: ev_enqueue
3785

3786
  Enqueue an event
3787

3788
  The queue is sorted by event->ev_time, so that queue->first always points to
3789
  the earliest event.
3790

3791
  We start scanning the queue from the tail, because we assume
3792
  that new events mostly get inserted with a event time higher
3793
  than the others.
3794

3795
*************************************************************/
3796
void ev_enqueue(struct event_queue *queue, struct event *event)
1,811✔
3797
{
3798
    struct event *i;
3799
    struct event *i_prev;
3800

3801
    /* Empty list */
3802
    if (queue->last == NULL) {
1,811✔
3803
        event->ev_next = NULL;
1,417✔
3804
        event->ev_prev = NULL;
1,417✔
3805
        queue->first = event;
1,417✔
3806
        queue->last = event;
1,417✔
3807
        return;
1,417✔
3808
    }
3809

3810
    /* Insert on tail? */
3811
    if (event->ev_time - queue->last->ev_time >= 0) {
394✔
3812
        event->ev_next = NULL;
394✔
3813
        event->ev_prev = queue->last;
394✔
3814
        queue->last->ev_next = event;
394✔
3815
        queue->last = event;
394✔
3816
        return;
394✔
3817
    }
3818

3819
    /* Find insertion point */
3820
    i = queue->last;
×
3821
    while (1) {
3822
        i_prev = i->ev_prev;
×
3823
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3824
            event->ev_prev = i_prev;
×
3825
            event->ev_next = i;
×
3826
            i->ev_prev = event;
×
3827
            if (i_prev != NULL) {
×
3828
                i_prev->ev_next = event;
×
3829
            }
3830
            else {
3831
                queue->first = event;
×
3832
            }
3833
            return;
×
3834
        }
3835
        i = i_prev;
×
3836
    }
3837
}
533✔
3838

3839
/************************************************************
3840

3841
  Function: ev_dequeue
3842

3843
*************************************************************/
3844
struct event *ev_dequeue(struct event_queue *queue)
1,006✔
3845
{
3846
    struct event *dequeued;
3847

3848
    if (queue->first == NULL) {
1,006✔
3849
        return NULL;
×
3850
    }
3851
    dequeued = queue->first;
1,006✔
3852
    ev_remove(queue, dequeued);
1,006✔
3853

3854
    return dequeued;
1,006✔
3855
}
288✔
3856

3857
/************************************************************
3858

3859
  Function: ev_remove
3860

3861
*************************************************************/
3862
void ev_remove(struct event_queue *queue, struct event *event)
1,791✔
3863
{
3864
    if (queue->first == event) {
1,791✔
3865
        queue->first = event->ev_next;
1,782✔
3866
    }
524✔
3867
    if (queue->last == event) {
1,791✔
3868
        queue->last = event->ev_prev;
1,418✔
3869
    }
411✔
3870
    if (event->ev_prev) {
1,791✔
3871
        event->ev_prev->ev_next = event->ev_next;
9✔
3872
    }
3✔
3873
    if (event->ev_next) {
1,791✔
3874
        event->ev_next->ev_prev = event->ev_prev;
373✔
3875
    }
116✔
3876
    event->ev_prev = NULL;
1,791✔
3877
    event->ev_next = NULL;
1,791✔
3878
}
1,791✔
3879

3880
/************************************************************
3881

3882
  Function: print_human_readable_time from current_time_ns
3883

3884
*************************************************************/
3885
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
45✔
3886
{
3887
    char time_buffer[100];
3888
    time_t current_time_s;
3889
    struct tm *local_time;
3890

3891
    current_time_s = current_time_ns / 1000000000;
45✔
3892
    local_time = localtime(&current_time_s);
45✔
3893
    switch(timestamp_format) {
45✔
3894
        case 1:
6✔
3895
            // timestamp-format ctime
3896
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
9✔
3897
            if (json_flag)
9✔
3898
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3899
            else
3900
                printf("[%s] ", time_buffer);
6✔
3901
            break;
9✔
3902
        case 2:
6✔
3903
            // timestamp-format iso
3904
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
9✔
3905
            if (json_flag)
9✔
3906
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3907
            else
3908
                printf("[%s] ", time_buffer);
6✔
3909
            break;
9✔
3910
        case 3:
6✔
3911
            // timestamp-format rfc3339
3912
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
9✔
3913
            if (json_flag)
9✔
3914
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3915
            else
3916
                printf("[%s] ", time_buffer);
6✔
3917
            break;
9✔
3918
        default:
12✔
3919
            if (json_flag)
18✔
3920
                printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9);
6✔
3921
            else
3922
                printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3923
    }
6✔
3924
}
45✔
3925

3926
/************************************************************
3927

3928
  Function: ms_since_midnight_utc
3929

3930
*************************************************************
3931

3932
  Input: int64_t: current UTC time in ns
3933

3934
  Output: uint32_t: current time in ms since midnight UTC
3935

3936
  Description:
3937

3938
  Return ICMP Timestamp value corresponding to the given time value.
3939
  The given time value must be in UTC.
3940

3941
*************************************************************/
3942
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
3943
{
3944
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
3945
}
3946

3947
/************************************************************
3948

3949
  Function: usage
3950

3951
*************************************************************
3952

3953
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3954

3955
  Description:
3956

3957
************************************************************/
3958

3959
void usage(int is_error)
124✔
3960
{
3961
    FILE *out = is_error ? stderr : stdout;
124✔
3962
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
124✔
3963
    fprintf(out, "\n");
124✔
3964
    fprintf(out, "Probing options:\n");
124✔
3965
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
124✔
3966
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
124✔
3967
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
124✔
3968
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
124✔
3969
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
124✔
3970
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
124✔
3971
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
124✔
3972
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
124✔
3973
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
124✔
3974
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
124✔
3975
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
124✔
3976
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
124✔
3977
#ifdef SO_BINDTODEVICE
3978
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
124✔
3979
#endif
3980
#ifdef SO_MARK
3981
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
84✔
3982
#endif
3983
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
124✔
3984
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
124✔
3985
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
124✔
3986
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
124✔
3987
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
124✔
3988
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
124✔
3989
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
124✔
3990
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
124✔
3991
    fprintf(out, "   -S, --src=IP       set source address\n");
124✔
3992
    fprintf(out, "       --seqmap-timeout=MSEC sequence number mapping timeout (default: %.0f ms)\n", seqmap_timeout / 1e6);
124✔
3993
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
124✔
3994
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
124✔
3995
    fprintf(out, "       --check-source discard replies not from target address\n");
124✔
3996
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
124✔
3997
    fprintf(out, "       --traceroute Traceroute Test\n");
124✔
3998
    fprintf(out, "\n");
124✔
3999
    fprintf(out, "Output options:\n");
124✔
4000
    fprintf(out, "   -a, --alive        show targets that are alive\n");
124✔
4001
    fprintf(out, "   -A, --addr         show targets by address\n");
124✔
4002
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
124✔
4003
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
124✔
4004
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
124✔
4005
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
124✔
4006
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
124✔
4007
    fprintf(out, "   -J, --json         output in JSON format (-c, -C, or -l required)\n");
124✔
4008
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
124✔
4009
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
124✔
4010
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
124✔
4011
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
124✔
4012
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
124✔
4013
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
124✔
4014
    fprintf(out, "   -s, --stats        print final stats\n");
124✔
4015
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
124✔
4016
    fprintf(out, "   -v, --version      show version\n");
124✔
4017
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
124✔
4018
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
124✔
4019
    fprintf(out, "       --print-tos    show received TOS value\n");
124✔
4020
    fprintf(out, "       --print-ttl    show IP TTL value\n");
124✔
4021
    exit(is_error);
124✔
4022
}
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