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

schweikert / fping / 20125656591

11 Dec 2025 07:41AM UTC coverage: 84.135% (-2.4%) from 86.535%
20125656591

Pull #421

github

web-flow
Merge 737a2d60c into 6235b056c
Pull Request #421: New option --traceroute to send a traceroute with fping

13 of 67 new or added lines in 1 file covered. (19.4%)

194 existing lines in 1 file now uncovered.

1591 of 1891 relevant lines covered (84.14%)

320.96 hits per line

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

83.93
/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
/* Traceroute */
172
#define TRACEROUTE_DONE_TTL 100
173

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

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

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

227
#define ICMP_UNREACH_MAXTYPE 15
228

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

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

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

269
typedef struct ip_header_result {
270
    int tos;
271
    int ttl;
272
    uint32_t otime_ms;
273
    uint32_t rtime_ms;
274
    uint32_t ttime_ms;
275
} IP_HEADER_RESULT;
276

277
IP_HEADER_RESULT default_ip_header_result() {
2,752✔
278
    return (IP_HEADER_RESULT){-1, -1, 0x80000000U, 0x80000000U, 0x80000000U};
2,752✔
279
}
280

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

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

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

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

329
#define EV_TYPE_PING 1
330
#define EV_TYPE_TIMEOUT 2
331

332
struct event {
333
    struct event *ev_prev;
334
    struct event *ev_next;
335
    int64_t ev_time;
336
    struct host_entry *host;
337
    int ping_index;
338
};
339

340
struct event_queue {
341
    struct event *first;
342
    struct event *last;
343
};
344

345
/*** globals ***/
346

347
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
348

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

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

373
volatile sig_atomic_t status_snapshot = 0;
374
volatile sig_atomic_t finish_requested = 0;
375

376
unsigned int debugging = 0;
377

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

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

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

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

441
unsigned int fwmark = 0;
442

443
char *filename = NULL; /* file containing hosts to ping */
444

445
/*** forward declarations ***/
446

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

495
/************************************************************
496

497
  Function: p_setsockopt
498

499
*************************************************************
500

501
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
502

503
  Description:
504

505
  Elevates privileges to p_uid when required, calls
506
  setsockopt, and drops privileges back.
507

508
************************************************************/
509

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

516
    if (p_uid != saved_uid && seteuid(p_uid)) {
22✔
517
        perror("cannot elevate privileges for setsockopt");
×
518
    }
519

520
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
521

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

528
    return res;
22✔
529
}
530

531
/************************************************************
532

533
  Function: main
534

535
*************************************************************
536

537
  Inputs:  int argc, char** argv
538

539
  Description:
540

541
  Main program entry point
542

543
************************************************************/
544

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

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

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

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

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

586
    memset(&src_addr, 0, sizeof(src_addr));
726✔
587
#ifdef IPV6
588
    memset(&src_addr6, 0, sizeof(src_addr6));
726✔
589
#endif
590

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

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

603
    /* get command line options */
604

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

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

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

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

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

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

801
            break;
96✔
802

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

807
            count_flag = 1;
125✔
808
            break;
125✔
809

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

814
            count_flag = 1;
44✔
815
            report_all_rtts_flag = 1;
44✔
816
            break;
44✔
817

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

824
        case 'h':
2✔
825
            usage(0);
3✔
826
            break;
1✔
827

828
        case 'q':
26✔
829
            verbose_flag = 0;
38✔
830
            quiet_flag = 1;
38✔
831
            break;
38✔
832

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

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

851
            break;
30✔
852

853
        case 'e':
6✔
854
            elapsed_flag = 1;
8✔
855
            break;
8✔
856

857
        case 'm':
858
            multif_flag = 1;
×
859
            break;
×
860

861
        case 'N':
2✔
862
            netdata_flag = 1;
3✔
863
            break;
3✔
864

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

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

881
        case 'A':
2✔
882
            addr_flag = 1;
3✔
883
            break;
3✔
884

885
        case 'B':
10✔
886
            if (!(backoff = atof(optparse_state.optarg)))
14✔
887
                usage(1);
6✔
888

889
            break;
10✔
890

891
        case 's':
8✔
892
            stats_flag = 1;
12✔
893
            break;
12✔
894

895
        case 'D':
22✔
896
            timestamp_flag = 1;
33✔
897
            break;
33✔
898

899
        case 'R':
4✔
900
            random_data_flag = 1;
5✔
901
            break;
5✔
902

903
        case 'l':
2✔
904
            loop_flag = 1;
3✔
905
            backoff_flag = 0;
3✔
906
            break;
3✔
907

908
        case 'u':
8✔
909
            unreachable_flag = 1;
12✔
910
            break;
12✔
911

912
        case 'a':
18✔
913
            alive_flag = 1;
26✔
914
            break;
26✔
915

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

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

927
            break;
928
#endif /* DEBUG || _DEBUG */
929

930
        case 'v':
4✔
931
            printf("%s: Version %s\n", prog, VERSION);
6✔
932
            exit(0);
6✔
933

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

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

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

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

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

963
            break;
6✔
964
#endif
965

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

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

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

1009
        case 'J':
42✔
1010
            json_flag = 1;
62✔
1011
            break;
62✔
1012

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

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

1037
        case 'o':
6✔
1038
            outage_flag = 1;
9✔
1039
            break;
9✔
1040

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

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

1056
    /* validate various option settings */
1057

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

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

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

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

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

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

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

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

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

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

1115
    if (count_flag) {
507✔
1116
        if (verbose_flag)
159✔
1117
            per_recv_flag = 1;
94✔
1118

1119
        alive_flag = unreachable_flag = verbose_flag = 0;
159✔
1120
    }
47✔
1121

1122
    if (loop_flag) {
507✔
UNCOV
1123
        if (!report_interval)
×
UNCOV
1124
            per_recv_flag = 1;
×
1125

UNCOV
1126
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1127
    }
1128

1129
    if (alive_flag || unreachable_flag || min_reachable)
507✔
1130
        verbose_flag = 0;
37✔
1131

1132
    trials = (count > retry + 1) ? count : retry + 1;
523✔
1133

1134
    if (traceroute_flag)
523✔
NEW
UNCOV
1135
        trials = 255; /* Ensure enough space for up to 255 hops */
×
1136

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

1148
#if defined(DEBUG) || defined(_DEBUG)
1149
    if (debugging & DBG_TRACE)
1150
        trace_flag = 1;
1151

1152
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1153
        randomly_lose_flag = 1;
1154
        lose_factor = 1; /* ie, 1/4 */
1155
    }
1156

1157
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1158
        randomly_lose_flag = 1;
1159
        lose_factor = 5; /* ie, 3/4 */
1160
    }
1161

1162
    if (debugging & DBG_PRINT_PER_SYSTEM)
1163
        print_per_system_flag = 1;
1164

1165
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1166
        report_all_rtts_flag = 1;
1167

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

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

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

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

1256
    update_current_time();
523✔
1257
    start_time = current_time_ns;
523✔
1258

1259
    /* handle host names supplied on command line or in a file */
1260
    /* if the generate_flag is on, then generate the IP list */
1261

1262
    argv = &argv[optparse_state.optind];
523✔
1263
    argc -= optparse_state.optind;
523✔
1264

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

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

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

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

1304
        if (strcmp(filename, "-") == 0)
96✔
1305
            ping_file = fdopen(0, "r");
72✔
1306
        else
1307
            ping_file = fopen(filename, "r");
24✔
1308

1309
        if (!ping_file)
96✔
1310
            errno_crash_and_burn("fopen");
3✔
1311

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

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

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

1376
            if (non_empty)
186✔
1377
                add_name(host);
144✔
1378
        }
1379

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

1398
    if (!num_hosts) {
425✔
1399
        exit(num_noaddress ? 2 : 1);
36✔
1400
    }
1401

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

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

1427
    init_ping_buffer_ipv4(ping_data_size);
385✔
1428
#ifdef IPV6
1429
    init_ping_buffer_ipv6(ping_data_size);
385✔
1430
#endif
1431

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

1448
    if (report_interval) {
361✔
1449
        next_report_time = current_time_ns + report_interval;
27✔
1450
    }
9✔
1451

1452
    last_send_time = 0;
361✔
1453

1454
    seqmap_init(seqmap_timeout);
361✔
1455

1456
    /* main loop */
1457
    main_loop();
361✔
1458

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

1466
    finish();
361✔
1467

1468
    return 0;
85✔
1469
}
1470

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

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

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

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

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

1514
    *addr_end = '\0';
53✔
1515
    mask = atoi(mask_str);
53✔
1516

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

1545
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1546
{
1547
    unsigned long bitmask;
1548
    unsigned long net_last;
1549

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

1556
    /* convert mask integer from 1 to 32 to a bitmask */
1557
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1558

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

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

1569
    /* add all hosts in that network (net_addr and net_last inclusive) */
1570
    add_addr_range_ipv4(net_addr, net_last);
18✔
1571
}
16✔
1572

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

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

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

1588
    /* calculate network range */
1589
    net_lower &= bitmask_lower;
16✔
1590
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1591

1592
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1593
}
12✔
1594
#endif /* IPV6 */
1595

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

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

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

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

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

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

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

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

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

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

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

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

1767
void main_loop()
373✔
1768
{
1769
    int64_t lt;
1770
    int64_t wait_time_ns;
1771
    struct event *event;
1772
    struct host_entry *h;
1773

1774
    while (event_queue_ping.first || event_queue_timeout.first) {
1,947✔
1775
        dbg_printf("%s", "# main_loop\n");
1776

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

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

1784
            stats_add(h, event->ping_index, 0, -1);
113✔
1785

1786
            if (per_recv_flag) {
113✔
1787
                print_timeout(h, event->ping_index);
25✔
1788
            }
7✔
1789

1790
            /* do we need to send a retry? */
1791
            if (!loop_flag && !count_flag) {
113✔
1792
                if (h->num_sent < retry + 1) {
64✔
1793
                    if (backoff_flag) {
39✔
1794
                        h->timeout *= backoff;
39✔
1795
                    }
9✔
1796
                    send_ping(h, event->ping_index);
39✔
1797
                }
9✔
1798
            }
14✔
1799

1800
            /* note: we process first timeout events, because we might need to
1801
             * wait to process ping events, while we for sure never need to
1802
             * wait for timeout events.
1803
             */
1804
            continue;
113✔
1805
        }
1806

1807
        /* ping event ? */
1808
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,737✔
1809
            /* Make sure that we don't ping more than once every "interval" */
1810
            lt = current_time_ns - last_send_time;
1,146✔
1811
            if (lt < interval)
1,146✔
1812
                goto wait_for_reply;
253✔
1813

1814
            /* Dequeue the event */
1815
            event = ev_dequeue(&event_queue_ping);
893✔
1816
            h = event->host;
893✔
1817

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

1820
            /* Traceroute */
1821
            if (traceroute_flag) {
893✔
NEW
UNCOV
1822
                int ttl_set = h->trace_ttl;
×
NEW
UNCOV
1823
                if (ttl_set > 30) ttl_set = 30;
×
NEW
UNCOV
1824
                if (socket4 >= 0) {
×
NEW
UNCOV
1825
                    if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl_set, sizeof(ttl_set)))
×
NEW
UNCOV
1826
                        perror("setsockopt IP_TTL");
×
1827
                }
1828
#ifdef IPV6
NEW
UNCOV
1829
                if (socket6 >= 0) {
×
1830
                    /* Set hop limit for IPv6 */
NEW
UNCOV
1831
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl_set, sizeof(ttl_set))) {
×
NEW
UNCOV
1832
                        perror("setsockopt IPV6_UNICAST_HOPS");
×
1833
                    }
1834
                }
1835
#endif
1836
            }
1837

1838
            /* Send the ping */
1839
            send_ping(h, event->ping_index);
893✔
1840

1841
            /* Loop, count and traceroute mode: schedule next ping */
1842
            if (loop_flag || (count_flag && event->ping_index + 1 < count) || (traceroute_flag && h->trace_ttl < 30)) {
893✔
1843
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
333✔
1844
            }
107✔
1845
        }
259✔
1846

1847
    wait_for_reply:
646✔
1848

1849
        /* When is the next ping next event? */
1850
        wait_time_ns = -1;
1,478✔
1851
        if (event_queue_ping.first) {
1,478✔
1852
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,025✔
1853
            if (wait_time_ns < 0)
1,025✔
1854
                wait_time_ns = 0;
533✔
1855
            /* make sure that we wait enough, so that the inter-ping delay is
1856
             * bigger than 'interval' */
1857
            if (wait_time_ns < interval) {
1,025✔
1858
                lt = current_time_ns - last_send_time;
533✔
1859
                if (lt < interval) {
533✔
1860
                    wait_time_ns = interval - lt;
533✔
1861
                }
177✔
1862
            }
177✔
1863

1864
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1865
        }
324✔
1866

1867
        /* When is the next timeout event? */
1868
        if (event_queue_timeout.first) {
1,478✔
1869
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
955✔
1870
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
955✔
1871
                wait_time_ns = wait_time_timeout;
446✔
1872
                if (wait_time_ns < 0) {
446✔
1873
                    wait_time_ns = 0;
×
1874
                }
1875
            }
116✔
1876

1877
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1878
        }
281✔
1879

1880
        /* When is the next report due? */
1881
        if (report_interval && (loop_flag || count_flag)) {
1,478✔
1882
            int64_t wait_time_next_report = next_report_time - current_time_ns;
215✔
1883
            if (wait_time_next_report < wait_time_ns) {
215✔
1884
                wait_time_ns = wait_time_next_report;
72✔
1885
                if (wait_time_ns < 0) {
72✔
UNCOV
1886
                    wait_time_ns = 0;
×
1887
                }
1888
            }
24✔
1889

1890
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1891
        }
71✔
1892

1893
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1894
        if (wait_time_ns == -1) {
1,478✔
1895
            break;
14✔
1896
        }
1897

1898
        /* end of loop was requested by interrupt signal handler */
1899
        if (finish_requested) {
1,464✔
1900
            break;
3✔
1901
        }
1902

1903
        /* Receive replies */
1904
        /* (this is what sleeps during each loop iteration) */
1905
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1906
        if (wait_for_reply(wait_time_ns)) {
1,461✔
1907
            while (wait_for_reply(0))
1,291✔
1908
                ; /* process other replies in the queue */
1909
        }
243✔
1910

1911
        update_current_time();
1,461✔
1912

1913
        if (status_snapshot) {
1,461✔
1914
            status_snapshot = 0;
×
UNCOV
1915
            if (json_flag)
×
UNCOV
1916
                print_per_system_splits_json();
×
1917
            else
UNCOV
1918
                print_per_system_splits();
×
1919
        }
1920

1921
        /* Print report */
1922
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,461✔
1923
            if (netdata_flag) {
45✔
1924
                print_netdata();
3✔
1925
            }
1✔
1926
            else if (json_flag) {
42✔
1927
                print_per_system_splits_json();
3✔
1928
            }
1✔
1929
            else {
1930
                print_per_system_splits();
39✔
1931
            }
1932

1933
            while (current_time_ns >= next_report_time) {
90✔
1934
                next_report_time += report_interval;
45✔
1935
            }
1936
        }
15✔
1937
    }
1938
}
373✔
1939

1940
/************************************************************
1941

1942
  Function: signal_handler
1943

1944
*************************************************************
1945

1946
  Inputs:  int signum
1947

1948
  Description:
1949

1950
  SIGQUIT signal handler - set flag and return
1951
  SIGINT signal handler - set flag and return
1952

1953
************************************************************/
1954

UNCOV
1955
void signal_handler(int signum)
×
1956
{
UNCOV
1957
    switch (signum) {
×
1958
    case SIGINT:
UNCOV
1959
        finish_requested = 1;
×
UNCOV
1960
        break;
×
1961

1962
    case SIGQUIT:
UNCOV
1963
        status_snapshot = 1;
×
UNCOV
1964
        break;
×
1965
    }
UNCOV
1966
}
×
1967

1968
/************************************************************
1969

1970
  Function: update_current_time
1971

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

1974
void update_current_time()
4,607✔
1975
{
1976
    clock_gettime(CLOCKID, &current_time);
4,607✔
1977
    current_time_ns = timespec_ns(&current_time);
4,607✔
1978
}
4,607✔
1979

1980
/************************************************************
1981

1982
  Function: finish
1983

1984
*************************************************************
1985

1986
  Inputs:  void (none)
1987

1988
  Description:
1989

1990
  Main program clean up and exit point
1991

1992
************************************************************/
1993

1994
void finish()
373✔
1995
{
1996
    int i;
1997
    HOST_ENTRY *h;
1998

1999
    update_current_time();
373✔
2000
    end_time = current_time_ns;
373✔
2001

2002
    /* tot up unreachables */
2003
    for (i = 0; i < num_hosts; i++) {
948✔
2004
        h = table[i];
575✔
2005

2006
        if (!h->num_recv) {
575✔
2007
            num_unreachable++;
89✔
2008

2009
            if (verbose_flag || unreachable_flag) {
89✔
2010
                printf("%s", h->host);
47✔
2011

2012
                if (verbose_flag)
47✔
2013
                    printf(" is unreachable");
44✔
2014

2015
                printf("\n");
47✔
2016
            }
3✔
2017
        }
15✔
2018
    }
157✔
2019

2020
    if (count_flag || loop_flag) {
373✔
2021
        if (json_flag)
156✔
2022
            print_per_system_stats_json();
56✔
2023
        else
2024
            print_per_system_stats();
100✔
2025
    }
46✔
2026
#if defined(DEBUG) || defined(_DEBUG)
2027
    else if (print_per_system_flag) {
2028
        if (json_flag)
2029
            print_per_system_stats_json();
2030
        else
2031
            print_per_system_stats();
2032
    }
2033
#endif /* DEBUG || _DEBUG */
2034

2035
    if (stats_flag) {
373✔
2036
        if (json_flag)
12✔
2037
            print_global_stats_json();
3✔
2038
        else
2039
            print_global_stats();
9✔
2040
    }
4✔
2041

2042
    if (min_reachable) {
373✔
2043
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
2044
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
2045
            exit(0);
6✔
2046
        }
2047
        else {
2048
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
2049
            exit(1);
3✔
2050
        }
2051
    }
2052

2053
    if (num_noaddress)
364✔
2054
        exit(2);
3✔
2055
    else if (num_alive != num_hosts)
361✔
2056
        exit(1);
50✔
2057

2058
    exit(0);
311✔
2059
}
2060

2061
/************************************************************
2062

2063
  Function: print_recv
2064

2065
*************************************************************
2066

2067
  Inputs: HOST_ENTRY *h, int64_t recv_time, int result,
2068
          int this_count, int64_t this_reply, int avg
2069

2070
  Description:
2071

2072
************************************************************/
2073

2074
void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) {
186✔
2075
    if (json_flag) {
186✔
2076
        printf("{\"resp\": {");
35✔
2077

2078
        if (timestamp_flag)
35✔
2079
            print_timestamp_format(recv_time, timestamp_format_flag);
15✔
2080

2081
        printf("\"host\": \"%s\", ", h->host);
35✔
2082
        printf("\"seq\": %d, ", this_count);
35✔
2083
        printf("\"size\": %d, ", result);
35✔
2084
        printf("\"rtt\": %s", sprint_tm(this_reply));
35✔
2085
        return;
35✔
2086
    }
2087

2088
    /* Normal Output */
2089
    if (timestamp_flag)
151✔
2090
        print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2091

2092
    printf("%-*s : [%d], %d bytes, %s ms",
151✔
2093
        max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2094

2095
    printf(" (%s avg, ", sprint_tm(avg));
151✔
2096

2097
    if (h->num_recv <= h->num_sent) {
151✔
2098
        printf("%d%% loss)",
151✔
2099
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2100
    }
45✔
2101
    else {
UNCOV
2102
        printf("%d%% return)",
×
UNCOV
2103
            (h->num_recv_total * 100) / h->num_sent);
×
2104
    }
2105
}
56✔
2106

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

2109
  Function: print_timeout
2110

2111
*************************************************************
2112

2113
  Inputs: HOST_ENTRY *h, int ping_index
2114

2115
  Description:
2116

2117
************************************************************/
2118

2119
void print_timeout(HOST_ENTRY *h, int ping_index) {
25✔
2120
    if (json_flag) {
25✔
2121
        printf("{\"timeout\": {");
6✔
2122
        if (timestamp_flag)
6✔
UNCOV
2123
            print_timestamp_format(current_time_ns, timestamp_format_flag);
×
2124

2125
        printf("\"host\": \"%s\", ", h->host);
6✔
2126
        printf("\"seq\": %d", ping_index);
6✔
2127
        printf("}}\n");
6✔
2128
        return;
6✔
2129
    }
2130

2131
    /* Normal Output */
2132
    if (timestamp_flag)
19✔
UNCOV
2133
        print_timestamp_format(current_time_ns, timestamp_format_flag);
×
2134

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

2138
    if (h->num_recv > 0) {
19✔
UNCOV
2139
        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
2140
    }
2141
    else {
2142
        printf(" (NaN avg, ");
19✔
2143
    }
2144

2145
    if (h->num_recv <= h->num_sent) {
19✔
2146
        printf("%d%% loss)",
19✔
2147
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
2148
    }
5✔
2149
    else {
UNCOV
2150
        printf("%d%% return)",
×
2151
            (h->num_recv_total * 100) / h->num_sent);
×
2152
    }
2153
    printf("\n");
19✔
2154
}
7✔
2155

2156
/************************************************************
2157

2158
  Function: print_recv_ext
2159

2160
*************************************************************
2161

2162
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2163
           int64_t recv_time, int64_t this_reply
2164

2165
  Description:
2166

2167
************************************************************/
2168

2169
void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { 
450✔
2170
    if (icmp_request_typ == 13) {
450✔
2171
        printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2172
            alive_flag ? "" : ",",
14✔
2173
            ip_header_res->otime_ms, ip_header_res->rtime_ms, ip_header_res->ttime_ms,
2174
            ms_since_midnight_utc(recv_time));
2175
    }
2176

2177
    if(print_tos_flag) {
450✔
2178
        if(ip_header_res->tos != -1) {
43✔
2179
            printf(" (TOS %d)", ip_header_res->tos);
43✔
2180
        }
11✔
2181
        else {
UNCOV
2182
            printf(" (TOS unknown)");
×
2183
        }
2184
    }
11✔
2185

2186
    if (print_ttl_flag) {
450✔
2187
        if(ip_header_res->ttl != -1) {
43✔
2188
            printf(" (TTL %d)", ip_header_res->ttl);
43✔
2189
        }
11✔
2190
        else {
UNCOV
2191
            printf(" (TTL unknown)");
×
2192
        }
2193
    }
11✔
2194

2195
    if (elapsed_flag && !per_recv_flag)
450✔
2196
        printf(" (%s ms)", sprint_tm(this_reply));
8✔
2197
    
2198
    printf("\n");
450✔
2199
}
450✔
2200

2201
/************************************************************
2202

2203
  Function: print_recv_ext_json
2204

2205
*************************************************************
2206

2207
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2208
           int64_t recv_time, int64_t this_reply
2209

2210
  Description:
2211

2212
************************************************************/
2213

2214
void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) {
35✔
2215
    if (icmp_request_typ == 13) {
35✔
2216
        printf(", \"timestamps\": {");
2✔
2217
        printf("\"originate\": %u, ", ip_header_res->otime_ms);
2✔
2218
        printf("\"receive\": %u, ", ip_header_res->rtime_ms);
2✔
2219
        printf("\"transmit\": %u, ", ip_header_res->ttime_ms);
2✔
2220
        printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
2221
    }
2222

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

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

2241
    if (elapsed_flag && !per_recv_flag)
35✔
UNCOV
2242
        printf(" (%s ms)", sprint_tm(this_reply));
×
2243

2244
    printf("}}");
35✔
2245
    printf("\n");
35✔
2246
}
35✔
2247

2248
/************************************************************
2249

2250
  Function: print_per_system_stats
2251

2252
*************************************************************
2253

2254
  Inputs:  void (none)
2255

2256
  Description:
2257

2258

2259
************************************************************/
2260

2261
void print_per_system_stats(void)
100✔
2262
{
2263
    int i, j, avg, outage_ms;
2264
    HOST_ENTRY *h;
2265
    int64_t resp;
2266

2267
    if (verbose_flag || per_recv_flag)
100✔
2268
        fprintf(stderr, "\n");
59✔
2269

2270
    for (i = 0; i < num_hosts; i++) {
234✔
2271
        h = table[i];
134✔
2272
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
2273

2274
        if (report_all_rtts_flag) {
134✔
2275
            for (j = 0; j < h->num_sent; j++) {
248✔
2276
                if ((resp = h->resp_times[j]) >= 0)
204✔
2277
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2278
                else
2279
                    fprintf(stderr, " -");
8✔
2280
            }
64✔
2281

2282
            fprintf(stderr, "\n");
44✔
2283
        }
12✔
2284
        else {
2285
            if (h->num_recv <= h->num_sent) {
90✔
2286
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
2287
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
2288

2289
                if (outage_flag) {
90✔
2290
                    /* Time outage total */
2291
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
2292
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
2293
                }
3✔
2294
            }
26✔
2295
            else {
UNCOV
2296
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2297
                    h->num_sent, h->num_recv,
UNCOV
2298
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2299
            }
2300

2301
            if (h->num_recv) {
90✔
2302
                avg = h->total_time / h->num_recv;
79✔
2303
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
2304
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
2305
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
2306
            }
23✔
2307

2308
            fprintf(stderr, "\n");
90✔
2309
        }
2310
    }
38✔
2311
}
100✔
2312

2313
/************************************************************
2314

2315
  Function: print_per_system_stats_json
2316

2317
*************************************************************
2318

2319
  Inputs:  void (none)
2320

2321
  Description:
2322

2323

2324
************************************************************/
2325

2326
void print_per_system_stats_json(void)
56✔
2327
{
2328
    int i, j, avg, outage_ms;
2329
    HOST_ENTRY *h;
2330
    int64_t resp;
2331

2332
    for (i = 0; i < num_hosts; i++) {
118✔
2333
        h = table[i];
62✔
2334

2335
        if (report_all_rtts_flag)
62✔
2336
            fprintf(stdout, "{\"vSum\": {");
18✔
2337
        else
2338
            fprintf(stdout, "{\"summary\": {");
44✔
2339

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

2342
        if (report_all_rtts_flag) {
62✔
2343
            fprintf(stdout, "\"values\": [");
18✔
2344
            for (j = 0; j < h->num_sent; j++) {
42✔
2345
                if (j > 0)
24✔
2346
                  fprintf(stdout, ", ");
6✔
2347
                
2348
                if ((resp = h->resp_times[j]) >= 0)
24✔
2349
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2350
                else
2351
                    fprintf(stdout, "null");
3✔
2352
            }
8✔
2353

2354
            fprintf(stdout, "]}");
18✔
2355
        }
6✔
2356
        else {
2357
            if (h->num_recv <= h->num_sent) {
44✔
2358
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
44✔
2359
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
44✔
2360
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
44✔
2361

2362
                if (outage_flag) {
44✔
2363
                    /* Time outage total */
UNCOV
2364
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
UNCOV
2365
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2366
                }
2367
            }
14✔
2368
            else {
UNCOV
2369
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
UNCOV
2370
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
UNCOV
2371
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2372
            }
2373

2374
            if (h->num_recv) {
44✔
2375
                avg = h->total_time / h->num_recv;
41✔
2376
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
41✔
2377
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
41✔
2378
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
41✔
2379
            }
13✔
2380

2381
            fprintf(stdout, "}");
44✔
2382
        }
2383
        fprintf(stdout, "}\n");
62✔
2384
    }
20✔
2385
}
56✔
2386

2387
/************************************************************
2388

2389
  Function: print_netdata
2390

2391
*************************************************************
2392

2393
  Inputs:  void (none)
2394

2395
  Description:
2396

2397

2398
************************************************************/
2399

2400
void print_netdata(void)
3✔
2401
{
2402
    static int sent_charts = 0;
2403

2404
    int i;
2405
    int64_t avg;
2406
    HOST_ENTRY *h;
2407

2408
    for (i = 0; i < num_hosts; i++) {
6✔
2409
        h = table[i];
3✔
2410

2411
        if (!sent_charts) {
3✔
2412
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2413
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2414
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2415
        }
1✔
2416

2417
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2418
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2419
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2420
        printf("END\n");
3✔
2421

2422
        if (!sent_charts) {
3✔
2423
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2424
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2425
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2426
        }
1✔
2427

2428
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2429
        /*
2430
        if( h->num_recv_i <= h->num_sent_i )
2431
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2432
        else
2433
            printf("SET lost = 0\n");
2434
*/
2435

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

2439
        if (!sent_charts) {
3✔
2440
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2441
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2442
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2443
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2444
        }
1✔
2445

2446
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2447
        if (h->num_recv_i) {
3✔
2448
            avg = h->total_time_i / h->num_recv_i;
3✔
2449
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2450
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2451
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2452
        }
1✔
2453
        printf("END\n");
3✔
2454

2455
        stats_reset_interval(h);
3✔
2456
    }
1✔
2457

2458
    sent_charts = 1;
3✔
2459
}
3✔
2460

2461
/************************************************************
2462

2463
  Function: print_per_system_splits
2464

2465
*************************************************************
2466

2467
  Inputs:  void (none)
2468

2469
  Description:
2470

2471

2472
************************************************************/
2473

2474
void print_per_system_splits(void)
39✔
2475
{
2476
    int i, avg, outage_ms_i;
2477
    HOST_ENTRY *h;
2478
    struct tm *curr_tm;
2479

2480
    if (verbose_flag || per_recv_flag)
39✔
UNCOV
2481
        fprintf(stderr, "\n");
×
2482

2483
    update_current_time();
39✔
2484
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2485
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2486
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2487

2488
    for (i = 0; i < num_hosts; i++) {
78✔
2489
        h = table[i];
39✔
2490
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2491

2492
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2493
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2494
                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✔
2495

2496
            if (outage_flag) {
39✔
2497
                /* Time outage  */
2498
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2499
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2500
            }
4✔
2501
        }
13✔
2502
        else {
UNCOV
2503
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
UNCOV
2504
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2505
        }
2506

2507
        if (h->num_recv_i) {
39✔
2508
            avg = h->total_time_i / h->num_recv_i;
27✔
2509
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2510
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2511
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2512
        }
9✔
2513

2514
        fprintf(stderr, "\n");
39✔
2515
        if (!cumulative_stats_flag) {
39✔
2516
            stats_reset_interval(h);
27✔
2517
        }
9✔
2518
    }
13✔
2519
}
39✔
2520

2521
/************************************************************
2522

2523
  Function: print_per_system_splits_json
2524

2525
*************************************************************
2526

2527
  Inputs:  void (none)
2528

2529
  Description:
2530

2531

2532
************************************************************/
2533

2534
void print_per_system_splits_json(void)
3✔
2535
{
2536
    int i, avg, outage_ms_i;
2537
    HOST_ENTRY *h;
2538

2539
    update_current_time();
3✔
2540

2541
    for (i = 0; i < num_hosts; i++) {
6✔
2542
        h = table[i];
3✔
2543
        fprintf(stdout, "{\"intSum\": {");
3✔
2544
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2545
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2546

2547
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2548
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2549
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2550
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2551

2552
            if (outage_flag) {
3✔
2553
                /* Time outage  */
UNCOV
2554
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
UNCOV
2555
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2556
            }
2557
        }
1✔
2558
        else {
UNCOV
2559
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
UNCOV
2560
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
UNCOV
2561
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2562
        }
2563

2564
        if (h->num_recv_i) {
3✔
2565
            avg = h->total_time_i / h->num_recv_i;
3✔
2566
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2567
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2568
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2569
        }
1✔
2570

2571
        fprintf(stdout, "}}\n");
3✔
2572
        if (!cumulative_stats_flag) {
3✔
2573
            stats_reset_interval(h);
3✔
2574
        }
1✔
2575
    }
1✔
2576
}
3✔
2577

2578
/************************************************************
2579

2580
  Function: print_global_stats
2581

2582
*************************************************************
2583

2584
  Inputs:  void (none)
2585

2586
  Description:
2587

2588

2589
************************************************************/
2590

2591
void print_global_stats(void)
9✔
2592
{
2593
    fprintf(stderr, "\n");
9✔
2594
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2595
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2596
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2597
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2598
    fprintf(stderr, "\n");
9✔
2599
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2600
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2601
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2602
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2603
    fprintf(stderr, "\n");
9✔
2604

2605
    if (total_replies == 0) {
9✔
2606
        min_reply = 0;
3✔
2607
        max_reply = 0;
3✔
2608
        total_replies = 1;
3✔
2609
        sum_replies = 0;
3✔
2610
    }
1✔
2611

2612
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2613
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2614
        sprint_tm(sum_replies / total_replies));
3✔
2615
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2616
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2617
        (end_time - start_time) / 1e9);
9✔
2618
    fprintf(stderr, "\n");
9✔
2619
}
9✔
2620

2621
/************************************************************
2622

2623
  Function: print_global_stats_json
2624

2625
*************************************************************
2626

2627
  Inputs:  void (none)
2628

2629
  Description:
2630

2631

2632
************************************************************/
2633

2634
void print_global_stats_json(void)
3✔
2635
{
2636
    fprintf(stdout, "{\"globalSum\": {");
3✔
2637
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2638
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2639
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2640
    fprintf(stdout, "\"unknown addresses\": %d, ", num_noaddress);
3✔
2641
    fprintf(stdout, "\"timeouts (waiting for response)\": %d, ", num_timeout);
3✔
2642
    fprintf(stdout, "\"ICMP Echos sent\": %d, ", num_pingsent);
3✔
2643
    fprintf(stdout, "\"ICMP Echo Replies received\": %d, ", num_pingreceived);
3✔
2644
    fprintf(stdout, "\"other ICMP received\": %d, ", num_othericmprcvd);
3✔
2645

2646
    if (total_replies == 0) {
3✔
UNCOV
2647
        min_reply = 0;
×
UNCOV
2648
        max_reply = 0;
×
UNCOV
2649
        total_replies = 1;
×
UNCOV
2650
        sum_replies = 0;
×
2651
    }
2652

2653
    fprintf(stdout, "\"ms (min round trip time)\": %s, ", sprint_tm(min_reply));
3✔
2654
    fprintf(stdout, "\"ms (avg round trip time)\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2655
    fprintf(stdout, "\"ms (max round trip time)\": %s, ", sprint_tm(max_reply));
3✔
2656
    fprintf(stdout, "\"sec (elapsed real time)\": %.3f", (end_time - start_time) / 1e9);
3✔
2657
    fprintf(stdout, "}}\n");
3✔
2658
}
3✔
2659

2660
/************************************************************
2661

2662
  Function: send_ping
2663

2664
*************************************************************
2665

2666
  Inputs:  int s, HOST_ENTRY *h
2667

2668
  Description:
2669

2670
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2671
  will be added on by the kernel.  The ID field is our UNIX process ID,
2672
  and the sequence number is an index into an array of outstanding
2673
  ping requests. The sequence number will later be used to quickly
2674
  figure out who the ping reply came from.
2675

2676
************************************************************/
2677

2678
int send_ping(HOST_ENTRY *h, int index)
932✔
2679
{
2680
    int n;
2681
    int myseq;
2682
    int ret = 1;
932✔
2683
    uint8_t proto = ICMP_ECHO;
932✔
2684

2685
    update_current_time();
932✔
2686
    h->last_send_time = current_time_ns;
932✔
2687
    myseq = seqmap_add(h->i, index, current_time_ns);
932✔
2688

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

2691
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
932✔
2692
        if(icmp_request_typ == 13)
809✔
2693
            proto = ICMP_TSTAMP;
18✔
2694
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
809✔
2695
    }
253✔
2696
#ifdef IPV6
2697
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2698
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2699
    }
15✔
2700
#endif
2701
    else {
UNCOV
2702
        return 0;
×
2703
    }
2704

2705
    /* error sending? */
2706
    if (
664✔
2707
        (n < 0)
268✔
2708
#if defined(EHOSTDOWN)
2709
        && errno != EHOSTDOWN
302✔
2710
#endif
2711
    ) {
2712
        if (verbose_flag) {
34✔
2713
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2714
        }
2715
        else {
2716
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2717
        }
2718

2719
        h->num_sent++;
34✔
2720
        h->num_sent_i++;
34✔
2721
        if (!loop_flag)
34✔
2722
            h->resp_times[index] = RESP_ERROR;
34✔
2723

2724
        ret = 0;
34✔
2725
    }
2726
    else {
2727
        /* schedule timeout */
2728
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
898✔
2729

2730
        /* mark this trial as outstanding */
2731
        if (!loop_flag) {
898✔
2732
            h->resp_times[index] = RESP_WAITING;
898✔
2733
        }
268✔
2734
    }
2735

2736
    num_pingsent++;
932✔
2737
    last_send_time = h->last_send_time;
932✔
2738

2739
    return (ret);
932✔
2740
}
268✔
2741

2742
int socket_can_read(struct timeval *timeout)
2,752✔
2743
{
2744
    int nfound;
2745
    fd_set readset;
2746
    int socketmax;
2747

2748
#ifndef IPV6
2749
    socketmax = socket4;
2750
#else
2751
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,752✔
2752
#endif
2753

2754
select_again:
2,070✔
2755
    FD_ZERO(&readset);
35,872✔
2756
    if (socket4 >= 0)
2,752✔
2757
        FD_SET(socket4, &readset);
2,752✔
2758
#ifdef IPV6
2759
    if (socket6 >= 0)
2,752✔
2760
        FD_SET(socket6, &readset);
2,752✔
2761
#endif
2762

2763
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,752✔
2764
    if (nfound < 0) {
2,752✔
UNCOV
2765
        if (errno == EINTR) {
×
2766
            /* interrupted system call: redo the select */
UNCOV
2767
            goto select_again;
×
2768
        }
2769
        else {
UNCOV
2770
            perror("select");
×
2771
        }
2772
    }
2773

2774
    if (nfound > 0) {
2,752✔
2775
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,291✔
2776
            return socket4;
1,200✔
2777
        }
2778
#ifdef IPV6
2779
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
91✔
2780
            return socket6;
91✔
2781
        }
2782
#endif
2783
    }
2784

2785
    return -1;
1,461✔
2786
}
682✔
2787

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

2818
    /* Wait for a socket to become ready */
2819
    if (wait_time) {
2,752✔
2820
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,461✔
2821
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,461✔
2822
    }
439✔
2823
    else {
2824
        to.tv_sec = 0;
1,291✔
2825
        to.tv_usec = 0;
1,291✔
2826
    }
2827
    s = socket_can_read(&to);
2,752✔
2828
    if (s == -1) {
2,752✔
2829
        return 0; /* timeout */
1,461✔
2830
    }
2831

2832
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,291✔
2833
    if (recv_len <= 0) {
1,291✔
UNCOV
2834
        return 0;
×
2835
    }
2836

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

2873
#if defined(DEBUG) || defined(_DEBUG)
2874
    if (randomly_lose_flag) {
2875
        if ((random() & 0x07) <= lose_factor)
2876
            return 0;
2877
    }
2878
#endif
2879

2880
    return recv_len;
1,291✔
2881
}
682✔
2882

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

2897
    if (!success) {
898✔
2898
        if (!loop_flag && index >= 0) {
113✔
2899
            h->resp_times[index] = RESP_TIMEOUT;
113✔
2900
        }
29✔
2901
        num_timeout++;
113✔
2902
        return;
113✔
2903
    }
2904

2905
    /* received count */
2906
    h->num_recv++;
785✔
2907
    h->num_recv_i++;
785✔
2908

2909
    /* maximum */
2910
    if (!h->max_reply || latency > h->max_reply) {
785✔
2911
        h->max_reply = latency;
613✔
2912
    }
183✔
2913
    if (!h->max_reply_i || latency > h->max_reply_i) {
785✔
2914
        h->max_reply_i = latency;
628✔
2915
    }
189✔
2916

2917
    /* minimum */
2918
    if (!h->min_reply || latency < h->min_reply) {
785✔
2919
        h->min_reply = latency;
529✔
2920
    }
160✔
2921
    if (!h->min_reply_i || latency < h->min_reply_i) {
785✔
2922
        h->min_reply_i = latency;
560✔
2923
    }
169✔
2924

2925
    /* total time (for average) */
2926
    h->total_time += latency;
785✔
2927
    h->total_time_i += latency;
785✔
2928

2929
    /* response time per-packet (count mode) */
2930
    if (!loop_flag && index >= 0) {
785✔
2931
        h->resp_times[index] = latency;
785✔
2932
    }
239✔
2933
}
268✔
2934

2935
/* stats_reset_interval: reset interval statistics
2936
 * h: host entry to update
2937
 */
2938
void stats_reset_interval(HOST_ENTRY *h)
33✔
2939
{
2940
    h->num_sent_i = 0;
33✔
2941
    h->num_recv_i = 0;
33✔
2942
    h->max_reply_i = 0;
33✔
2943
    h->min_reply_i = 0;
33✔
2944
    h->total_time_i = 0;
33✔
2945
}
33✔
2946

2947
int decode_icmp_ipv4(
1,200✔
2948
    struct sockaddr *response_addr,
2949
    size_t response_addr_len,
2950
    char *reply_buf,
2951
    size_t reply_buf_len,
2952
    unsigned short *id,
2953
    unsigned short *seq,
2954
    IP_HEADER_RESULT *ip_header_res)
2955
{
2956
    struct icmp *icp;
2957
    int hlen = 0;
1,200✔
2958

2959
    if (!using_sock_dgram4) {
1,200✔
2960
        struct ip *ip = (struct ip *)reply_buf;
1,192✔
2961
        ip_header_res->tos = ip->ip_tos;
1,192✔
2962
        ip_header_res->ttl = ip->ip_ttl;
1,192✔
2963

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

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

2985
    icp = (struct icmp *)(reply_buf + hlen);
1,200✔
2986

2987
    if (traceroute_flag && icp->icmp_type == ICMP_TIMXCEED) {
1,200✔
2988
        struct ip *inner_ip;
2989
        int inner_hlen;
2990
        struct icmp *inner_icmp;
2991

NEW
UNCOV
2992
        if (reply_buf_len >= hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
×
NEW
UNCOV
2993
            inner_ip = (struct ip *) (reply_buf + hlen + ICMP_MINLEN);
×
NEW
UNCOV
2994
            inner_hlen = inner_ip->ip_hl << 2;
×
NEW
UNCOV
2995
            inner_icmp = (struct icmp *) ((char *)inner_ip + inner_hlen);
×
NEW
UNCOV
2996
            if (inner_icmp->icmp_id == ident4) {
×
NEW
UNCOV
2997
                *id = inner_icmp->icmp_id;
×
NEW
UNCOV
2998
                *seq = ntohs(inner_icmp->icmp_seq);
×
NEW
UNCOV
2999
                return hlen;
×
3000
            }
3001
        }
3002
    }
3003

3004
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,200✔
3005
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
506✔
3006
        /* Handle other ICMP packets */
3007
        struct icmp *sent_icmp;
3008
        SEQMAP_VALUE *seqmap_value;
3009
        char addr_ascii[INET6_ADDRSTRLEN];
3010
        HOST_ENTRY *h;
3011

3012
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
3013
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
938✔
3014
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
UNCOV
3015
            return -1;
×
3016
        }
3017

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

3020
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
482✔
3021
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
3022
            sent_icmp->icmp_id != ident4) {
×
3023
            /* not caused by us */
3024
            return -1;
482✔
3025
        }
3026

3027
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
3028
        if (seqmap_value == NULL) {
×
3029
            return -1;
×
3030
        }
3031

3032
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3033

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

UNCOV
3046
            print_warning("\n");
×
UNCOV
3047
            num_othericmprcvd++;
×
UNCOV
3048
            break;
×
3049

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

UNCOV
3068
        return -1;
×
3069
    }
3070

3071
    *id = icp->icmp_id;
718✔
3072
    *seq = ntohs(icp->icmp_seq);
718✔
3073
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
718✔
3074

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

3085
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
3086
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
3087
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
3088
    }
3089

3090
    return hlen;
718✔
3091
}
228✔
3092

3093
#ifdef IPV6
3094
int decode_icmp_ipv6(
91✔
3095
    struct sockaddr *response_addr,
3096
    size_t response_addr_len,
3097
    char *reply_buf,
3098
    size_t reply_buf_len,
3099
    unsigned short *id,
3100
    unsigned short *seq)
3101
{
3102
    struct icmp6_hdr *icp;
3103

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

3113
    icp = (struct icmp6_hdr *)reply_buf;
91✔
3114

3115
    /* Traceroute Logic for IPv6 Time Exceeded */
3116
    if (traceroute_flag && icp->icmp6_type == ICMP6_TIME_EXCEEDED) {
91✔
3117
        struct ip6_hdr *inner_ip6;
3118
        struct icmp6_hdr *inner_icmp6;
3119

NEW
UNCOV
3120
        if (reply_buf_len >= sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
×
NEW
UNCOV
3121
            inner_ip6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
×
3122

3123
            /* Check whether the inner packet is ICMPv6 */
NEW
UNCOV
3124
            if (inner_ip6->ip6_nxt == IPPROTO_ICMPV6) {
×
NEW
UNCOV
3125
                inner_icmp6 = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
×
3126

NEW
UNCOV
3127
                if (inner_icmp6->icmp6_id == ident6) {
×
NEW
UNCOV
3128
                    *id = inner_icmp6->icmp6_id;
×
NEW
UNCOV
3129
                    *seq = ntohs(inner_icmp6->icmp6_seq);
×
NEW
UNCOV
3130
                    return 1;
×
3131
                }
3132
            }
3133
        }
3134
    }
3135

3136
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
91✔
3137
        /* Handle other ICMPv6 packets */
3138
        struct ip6_hdr *sent_ipv6;
3139
        struct icmp6_hdr *sent_icmp;
3140
        SEQMAP_VALUE *seqmap_value;
3141
        char addr_ascii[INET6_ADDRSTRLEN];
3142
        HOST_ENTRY *h;
3143

3144
        /* reply icmp packet (ICMPv6 header) followed by "sent packet" (IPv6 + ICMPv6 header) */
UNCOV
3145
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
×
3146
            /* discard ICMPv6 message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
3147
            return 0;
×
3148
        }
3149

3150
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
×
3151
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
×
3152
            /* discard ICMPv6 message if we can't tell that it was caused by
3153
             * us, because the IPv6 header is not directly followed by an
3154
             * ICMPv6 header
3155
             */
3156
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
3157
            return 0;
×
3158
        }
3159
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
×
3160

3161
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
×
3162
            /* not caused by us */
3163
            return 0;
×
3164
        }
3165

3166
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
3167
        if (seqmap_value == NULL) {
×
UNCOV
3168
            return 0;
×
3169
        }
3170

UNCOV
3171
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
UNCOV
3172
        h = table[seqmap_value->host_nr];
×
3173

UNCOV
3174
        switch (icp->icmp6_type) {
×
3175
        case ICMP6_DST_UNREACH:
UNCOV
3176
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
UNCOV
3177
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3178
                    icp->icmp6_code, addr_ascii, h->host);
×
3179
            } else {
UNCOV
3180
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
UNCOV
3181
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
3182
            }
UNCOV
3183
            print_warning("\n");
×
UNCOV
3184
            num_othericmprcvd++;
×
3185
            break;
×
3186

3187
        case ICMP6_PACKET_TOO_BIG:
UNCOV
3188
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
3189
                addr_ascii, h->host);
UNCOV
3190
            num_othericmprcvd++;
×
3191
            break;
×
3192

3193
        case ICMP6_TIME_EXCEEDED:
3194
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
3195
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3196
                    icp->icmp6_code, addr_ascii, h->host);
×
3197
            } else {
UNCOV
3198
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3199
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
3200
            }
UNCOV
3201
            print_warning("\n");
×
3202
            num_othericmprcvd++;
×
UNCOV
3203
            break;
×
3204

3205
        case ICMP6_PARAM_PROB:
3206
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
UNCOV
3207
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3208
                    icp->icmp6_code, addr_ascii, h->host);
×
3209
            } else {
UNCOV
3210
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3211
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
3212
            }
3213
            print_warning("\n");
×
UNCOV
3214
            num_othericmprcvd++;
×
UNCOV
3215
            break;
×
3216

3217
        default:
3218
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
3219
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
UNCOV
3220
            num_othericmprcvd++;
×
UNCOV
3221
            break;
×
3222
        }
3223

3224
        return 0;
×
3225
    }
3226

3227
    *id = icp->icmp6_id;
91✔
3228
    *seq = ntohs(icp->icmp6_seq);
91✔
3229

3230
    return 1;
91✔
3231
}
15✔
3232
#endif
3233

3234
int wait_for_reply(int64_t wait_time)
2,752✔
3235
{
3236
    int result;
3237
    static char buffer[RECV_BUFSIZE];
3238
    struct sockaddr_storage response_addr;
3239
    int n, avg;
3240
    int ip_hlen = 0;
2,752✔
3241
    HOST_ENTRY *h;
3242
    int64_t this_reply;
3243
    int this_count;
3244
    int64_t recv_time = 0;
2,752✔
3245
    SEQMAP_VALUE *seqmap_value;
3246
    unsigned short id;
3247
    unsigned short seq;
3248
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
2,752✔
3249

3250
    /* Receive packet */
3251
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,434✔
3252
        &recv_time, /* reply_timestamp */
3253
        (struct sockaddr *)&response_addr, /* reply_src_addr */
3254
        sizeof(response_addr), /* reply_src_addr_len */
3255
        buffer, /* reply_buf */
3256
        sizeof(buffer), /* reply_buf_len */
3257
        &ip_header_res.tos, /* TOS resp. TC byte */
682✔
3258
        &ip_header_res.ttl /* TTL resp. hop limit */
682✔
3259
    );
3260

3261
    if (result <= 0) {
2,752✔
3262
        return 0;
1,461✔
3263
    }
3264

3265
    update_current_time();
1,291✔
3266
    if (recv_time == 0)
1,291✔
3267
        recv_time = current_time_ns;
243✔
3268

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

3311
    seqmap_value = seqmap_fetch(seq, current_time_ns);
809✔
3312
    if (seqmap_value == NULL) {
809✔
3313
        return 1;
12✔
3314
    }
3315

3316
    /* find corresponding host_entry */
3317
    n = seqmap_value->host_nr;
797✔
3318
    h = table[n];
797✔
3319
    this_count = seqmap_value->ping_count;
797✔
3320
    this_reply = recv_time - seqmap_value->ping_ts;
797✔
3321

3322
    if (traceroute_flag && response_addr.ss_family == AF_INET) {
797✔
NEW
UNCOV
3323
        struct icmp *icp = (struct icmp *)(buffer + ip_hlen);
×
3324
        char ip_str[INET_ADDRSTRLEN];
NEW
UNCOV
3325
        getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), ip_str, sizeof(ip_str), NULL, 0, NI_NUMERICHOST);
×
3326

NEW
UNCOV
3327
        if (icp->icmp_type == ICMP_TIMXCEED) {
×
NEW
3328
            printf("%s: hop %d reached %s (%s ms)\n", h->host, h->trace_ttl, ip_str, sprint_tm(this_reply));
×
NEW
UNCOV
3329
            h->trace_ttl++;
×
NEW
UNCOV
3330
            if (h->trace_ttl > 30) h->trace_ttl = 30;
×
3331

NEW
UNCOV
3332
            stats_add(h, this_count, 1, this_reply);
×
NEW
UNCOV
3333
            struct event *timeout_event = host_get_timeout_event(h, this_count);
×
NEW
UNCOV
3334
            if (timeout_event) {
×
NEW
UNCOV
3335
                ev_remove(&event_queue_timeout, timeout_event);
×
3336
            }
NEW
UNCOV
3337
            return 1;
×
NEW
UNCOV
3338
        } else if (icp->icmp_type == ICMP_ECHOREPLY) {
×
NEW
UNCOV
3339
            printf("%s: hop %d reached DESTINATION %s (%s ms)\n", h->host, h->trace_ttl, ip_str, sprint_tm(this_reply));
×
NEW
UNCOV
3340
            h->trace_ttl = TRACEROUTE_DONE_TTL; /* Goal achieved: artificially increase TTL to stop loop in main_loop */
×
3341
        }
3342
    }
3343
#ifdef IPV6
3344
    else if (traceroute_flag && response_addr.ss_family == AF_INET6) {
797✔
NEW
UNCOV
3345
        struct icmp6_hdr *icp = (struct icmp6_hdr *)buffer;
×
3346

3347
        /* With IPv6, buffer is directly the ICMP header payload, since receive_packet uses recvmsg */
3348
        char ip_str[INET6_ADDRSTRLEN];
NEW
UNCOV
3349
        getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), ip_str, sizeof(ip_str), NULL, 0, NI_NUMERICHOST);
×
3350

NEW
UNCOV
3351
        if (icp->icmp6_type == ICMP6_TIME_EXCEEDED) {
×
NEW
UNCOV
3352
            printf("%s: hop %d reached %s (%s ms)\n", h->host, h->trace_ttl, ip_str, sprint_tm(this_reply));
×
NEW
UNCOV
3353
            h->trace_ttl++;
×
NEW
3354
            if (h->trace_ttl > 30) h->trace_ttl = 30;
×
3355

NEW
3356
            stats_add(h, this_count, 1, this_reply);
×
NEW
3357
            struct event *timeout_event = host_get_timeout_event(h, this_count);
×
NEW
3358
            if (timeout_event) {
×
NEW
3359
                ev_remove(&event_queue_timeout, timeout_event);
×
3360
            }
NEW
3361
            return 1;
×
NEW
3362
        } else if (icp->icmp6_type == ICMP6_ECHO_REPLY) {
×
NEW
3363
            printf("%s: hop %d reached DESTINATION %s (%s ms)\n", h->host, h->trace_ttl, ip_str, sprint_tm(this_reply));
×
NEW
3364
            h->trace_ttl = TRACEROUTE_DONE_TTL;
×
3365
        }
3366
    }
3367
#endif
3368

3369
    /* update stats that include invalid replies */
3370
    h->num_recv_total++;
797✔
3371
    num_pingreceived++;
797✔
3372

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

3375
    /* optionally require reply source equal to target address */
3376
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
797✔
3377
        dbg_printf("%s\n", "discarding reply from wrong source address");
3378
        return 1;
12✔
3379
    }
3380

3381
    /* discard duplicates */
3382
    if (!loop_flag && !(count_flag && quiet_flag) && h->resp_times[this_count] >= 0) {
785✔
3383
        if (!per_recv_flag) {
×
3384
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3385
                h->host, this_count, result, sprint_tm(this_reply));
3386

3387
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3388
                char buf[INET6_ADDRSTRLEN];
3389
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3390
                fprintf(stderr, " [<- %s]", buf);
×
3391
            }
3392
            fprintf(stderr, "\n");
×
3393
        }
3394
        return 1;
×
3395
    }
3396

3397
    /* discard reply if delay is larger than timeout
3398
     * (see also: github #32) */
3399
    if (this_reply > h->timeout) {
785✔
3400
        return 1;
×
3401
    }
3402

3403
    /* update stats */
3404
    stats_add(h, this_count, 1, this_reply);
785✔
3405
    // TODO: move to stats_add?
3406
    if (!max_reply || this_reply > max_reply)
785✔
3407
        max_reply = this_reply;
490✔
3408
    if (!min_reply || this_reply < min_reply)
785✔
3409
        min_reply = this_reply;
427✔
3410
    sum_replies += this_reply;
785✔
3411
    total_replies++;
785✔
3412

3413
    /* initialize timeout to initial timeout (without backoff) */
3414
    h->timeout = timeout;
785✔
3415

3416
    /* remove timeout event */
3417
    struct event *timeout_event = host_get_timeout_event(h, this_count);
785✔
3418
    if (timeout_event) {
785✔
3419
        ev_remove(&event_queue_timeout, timeout_event);
785✔
3420
    }
239✔
3421

3422
    /* print "is alive" */
3423
    if (h->num_recv == 1) {
785✔
3424
        num_alive++;
486✔
3425
        if (fast_reachable && num_alive >= min_reachable)
486✔
3426
            finish_requested = 1;
3✔
3427

3428
        if (verbose_flag || alive_flag) {
486✔
3429
            printf("%s", h->host);
299✔
3430

3431
            if (verbose_flag)
299✔
3432
                printf(" is alive");
279✔
3433
        }
85✔
3434
    }
142✔
3435

3436
    /* print received ping (unless --quiet) */
3437
    if (per_recv_flag) {
785✔
3438
        avg = h->total_time / h->num_recv;
186✔
3439
        print_recv(h,
242✔
3440
            recv_time,
56✔
3441
            result,
56✔
3442
            this_count,
56✔
3443
            this_reply,
56✔
3444
            avg);
56✔
3445
    }
56✔
3446

3447
    if (verbose_flag || alive_flag || per_recv_flag) {
785✔
3448
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
485✔
3449
            char buf[INET6_ADDRSTRLEN];
3450
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3451
            fprintf(stderr, " [<- %s]", buf);
6✔
3452
        }
3453
        if (json_flag) {
485✔
3454
            print_recv_ext_json(&ip_header_res,
35✔
3455
                recv_time,
11✔
3456
                this_reply);
11✔
3457
        }
11✔
3458
        else {
3459
            print_recv_ext(&ip_header_res,
450✔
3460
                recv_time,
130✔
3461
                this_reply);
130✔
3462
        }
3463
    }
141✔
3464
    return 1;
785✔
3465
}
682✔
3466

3467
/************************************************************
3468

3469
  Function: add_name
3470

3471
*************************************************************
3472

3473
  Inputs:  char* name
3474

3475
  Description:
3476

3477
  process input name for addition to target list
3478
  name can turn into multiple targets via multiple interfaces (-m)
3479
  or via NIS groups
3480

3481
************************************************************/
3482

3483
void add_name(char *name)
609✔
3484
{
3485
    struct addrinfo *res0, *res, hints;
3486
    int ret_ga;
3487
    char *printname;
3488
    char namebuf[256];
3489
    char addrbuf[256];
3490

3491
    /* getaddrinfo */
3492
    memset(&hints, 0, sizeof(struct addrinfo));
609✔
3493
    hints.ai_flags = AI_UNUSABLE;
609✔
3494
    hints.ai_socktype = SOCK_RAW;
609✔
3495
    hints.ai_family = hints_ai_family;
609✔
3496
    if (hints_ai_family == AF_INET) {
609✔
3497
        hints.ai_protocol = IPPROTO_ICMP;
81✔
3498
    }
21✔
3499
#ifdef IPV6
3500
    else if (hints_ai_family == AF_INET6) {
528✔
3501
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
3502
    }
1✔
3503
#endif
3504
    else {
3505
        hints.ai_socktype = SOCK_STREAM;
497✔
3506
        hints.ai_protocol = 0;
497✔
3507
    }
3508
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
609✔
3509
    if (ret_ga) {
609✔
3510
        if (!quiet_flag)
29✔
3511
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3512
        num_noaddress++;
29✔
3513

3514
        // Handle JSON output for invalid hosts
3515
        if (json_flag) {
29✔
3516
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
3517
            return;
21✔
3518
        }
3519

3520
        return;
26✔
3521
    }
3522

3523
    /* NOTE: we could/should loop with res on all addresses like this:
3524
     * for (res = res0; res; res = res->ai_next) {
3525
     * We don't do it yet, however, because is is an incompatible change
3526
     * (need to implement a separate option for this)
3527
     */
3528
    for (res = res0; res; res = res->ai_next) {
580✔
3529
        /* name_flag: addr -> name lookup requested) */
3530
        if (name_flag || rdns_flag) {
582✔
3531
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3532
            if (name_flag) {
3✔
3533
                /* Was it a numerical address? Only then do a rdns-query */
3534
                struct addrinfo *nres;
UNCOV
3535
                hints.ai_flags = AI_NUMERICHOST;
×
UNCOV
3536
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
UNCOV
3537
                    do_rdns = 1;
×
UNCOV
3538
                    freeaddrinfo(nres);
×
3539
                }
3540
            }
3541

3542
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3543
                printname = namebuf;
3✔
3544
            }
1✔
3545
            else {
UNCOV
3546
                printname = name;
×
3547
            }
3548
        }
1✔
3549
        else {
3550
            printname = name;
577✔
3551
        }
3552

3553
        /* addr_flag: name -> addr lookup requested */
3554
        if (addr_flag) {
580✔
3555
            int ret;
3556
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3557
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3558
            if (ret) {
3✔
UNCOV
3559
                if (!quiet_flag) {
×
UNCOV
3560
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3561
                }
UNCOV
3562
                continue;
×
3563
            }
3564

3565
            if (name_flag || rdns_flag) {
3✔
3566
                char nameaddrbuf[512 + 3];
UNCOV
3567
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
UNCOV
3568
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3569
            }
3570
            else {
3571
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3572
            }
3573
        }
1✔
3574
        else {
3575
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
577✔
3576
        }
3577

3578
        if (!multif_flag) {
580✔
3579
            break;
580✔
3580
        }
3581
    }
3582

3583
    freeaddrinfo(res0);
580✔
3584
}
167✔
3585

3586
/************************************************************
3587

3588
  Function: add_addr
3589

3590
*************************************************************
3591

3592
  Description:
3593

3594
  add single address to list of hosts to be pinged
3595

3596
************************************************************/
3597

3598
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
580✔
3599
{
3600
    HOST_ENTRY *p;
3601
    int n;
3602
    int64_t *i;
3603

3604
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
580✔
3605
    if (!p)
580✔
UNCOV
3606
        crash_and_burn("can't allocate HOST_ENTRY");
×
3607

3608
    p->name = strdup(name);
580✔
3609
    p->host = strdup(host);
580✔
3610
    memcpy(&p->saddr, ipaddr, ipaddr_len);
580✔
3611
    p->saddr_len = ipaddr_len;
580✔
3612
    p->timeout = timeout;
580✔
3613
    p->min_reply = 0;
580✔
3614
    p->trace_ttl = 1;
580✔
3615

3616
    if (netdata_flag) {
580✔
3617
        char *s = p->name;
3✔
3618
        while (*s) {
30✔
3619
            if (!isalnum(*s))
27✔
3620
                *s = '_';
9✔
3621
            s++;
27✔
3622
        }
3623
    }
1✔
3624

3625
    if (strlen(p->host) > max_hostname_len)
580✔
3626
        max_hostname_len = strlen(p->host);
387✔
3627

3628
    /* array for response time results */
3629
    if (!loop_flag) {
580✔
3630
#if SIZE_MAX <= UINT_MAX
3631
        if (trials > (SIZE_MAX / sizeof(int64_t)))
3632
            crash_and_burn("resp_times array too large for memory");
3633
#endif
3634
        i = (int64_t *)malloc(trials * sizeof(int64_t));
580✔
3635
        if (!i)
580✔
UNCOV
3636
            crash_and_burn("can't allocate resp_times array");
×
3637

3638
        for (n = 0; n < trials; n++)
2,891✔
3639
            i[n] = RESP_UNUSED;
2,311✔
3640

3641
        p->resp_times = i;
580✔
3642
    }
158✔
3643

3644
    /* allocate event storage */
3645
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
580✔
3646
    if (!p->event_storage_ping) {
580✔
UNCOV
3647
        errno_crash_and_burn("can't allocate event_storage_ping");
×
3648
    }
3649
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
580✔
3650
    if (!p->event_storage_timeout) {
580✔
UNCOV
3651
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
3652
    }
3653

3654
    /* schedule first ping */
3655
    host_add_ping_event(p, 0, current_time_ns);
580✔
3656

3657
    num_hosts++;
580✔
3658
}
580✔
3659

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

3662
  Function: crash_and_burn
3663

3664
*************************************************************
3665

3666
  Inputs:  char* message
3667

3668
  Description:
3669

3670
************************************************************/
3671

UNCOV
3672
void crash_and_burn(char *message)
×
3673
{
UNCOV
3674
    fprintf(stderr, "%s: %s\n", prog, message);
×
3675
    exit(4);
×
3676
}
3677

3678
/************************************************************
3679

3680
  Function: errno_crash_and_burn
3681

3682
*************************************************************
3683

3684
  Inputs:  char* message
3685

3686
  Description:
3687

3688
************************************************************/
3689

3690
void errno_crash_and_burn(char *message)
8✔
3691
{
3692
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3693
    exit(4);
8✔
3694
}
3695

3696
/************************************************************
3697

3698
  Function: print_warning
3699

3700
  Description: fprintf(stderr, ...), unless running with -q
3701

3702
*************************************************************/
3703

3704
void print_warning(char *format, ...)
54✔
3705
{
3706
    va_list args;
3707
    if (!quiet_flag) {
54✔
3708
        va_start(args, format);
54✔
3709
        vfprintf(stderr, format, args);
54✔
3710
        va_end(args);
54✔
3711
    }
8✔
3712
}
54✔
3713

3714
/************************************************************
3715

3716
  Function: sprint_tm
3717

3718
*************************************************************
3719

3720
  render nanosecond int64_t value into milliseconds string with three digits of
3721
  precision.
3722

3723
************************************************************/
3724

3725
const char *sprint_tm(int64_t ns)
1,048✔
3726
{
3727
    static char buf[10];
3728
    double t = (double)ns / 1e6;
1,048✔
3729

3730
    if (t < 0.0) {
1,048✔
3731
        /* negative (unexpected) */
UNCOV
3732
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3733
    }
3734
    else if (t < 1.0) {
1,048✔
3735
        /* <= 0.99 ms */
3736
        snprintf(buf, sizeof(buf), "%.3f", t);
1,048✔
3737
    }
324✔
UNCOV
3738
    else if (t < 10.0) {
×
3739
        /* 1.00 - 9.99 ms */
UNCOV
3740
        snprintf(buf, sizeof(buf), "%.2f", t);
×
3741
    }
UNCOV
3742
    else if (t < 100.0) {
×
3743
        /* 10.0 - 99.9 ms */
UNCOV
3744
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3745
    }
UNCOV
3746
    else if (t < 1000000.0) {
×
3747
        /* 100 - 1'000'000 ms */
UNCOV
3748
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3749
    }
3750
    else {
UNCOV
3751
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3752
    }
3753

3754
    return (buf);
1,048✔
3755
}
3756

3757
/************************************************************
3758

3759
  Function: addr_cmp
3760

3761
*************************************************************/
3762
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
507✔
3763
{
3764
    if (a->sa_family != b->sa_family) {
507✔
UNCOV
3765
        return a->sa_family - b->sa_family;
×
3766
    }
3767
    else {
3768
        if (a->sa_family == AF_INET) {
507✔
3769
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
443✔
3770
        }
3771
        else if (a->sa_family == AF_INET6) {
64✔
3772
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3773
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3774
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3775
        }
3776
    }
3777

UNCOV
3778
    return 0;
×
3779
}
143✔
3780

3781
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
913✔
3782
{
3783
    struct event *event = &h->event_storage_ping[index % event_storage_count];
913✔
3784
    event->host = h;
913✔
3785
    event->ping_index = index;
913✔
3786
    event->ev_time = ev_time;
913✔
3787
    ev_enqueue(&event_queue_ping, event);
913✔
3788

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

3793
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
898✔
3794
{
3795
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
898✔
3796
    event->host = h;
898✔
3797
    event->ping_index = index;
898✔
3798
    event->ev_time = ev_time;
898✔
3799
    ev_enqueue(&event_queue_timeout, event);
898✔
3800

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

3805
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
785✔
3806
{
3807
    return &h->event_storage_timeout[index % event_storage_count];
785✔
3808
}
3809

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

3812
  Function: ev_enqueue
3813

3814
  Enqueue an event
3815

3816
  The queue is sorted by event->ev_time, so that queue->first always points to
3817
  the earliest event.
3818

3819
  We start scanning the queue from the tail, because we assume
3820
  that new events mostly get inserted with a event time higher
3821
  than the others.
3822

3823
*************************************************************/
3824
void ev_enqueue(struct event_queue *queue, struct event *event)
1,811✔
3825
{
3826
    struct event *i;
3827
    struct event *i_prev;
3828

3829
    /* Empty list */
3830
    if (queue->last == NULL) {
1,811✔
3831
        event->ev_next = NULL;
1,420✔
3832
        event->ev_prev = NULL;
1,420✔
3833
        queue->first = event;
1,420✔
3834
        queue->last = event;
1,420✔
3835
        return;
1,420✔
3836
    }
3837

3838
    /* Insert on tail? */
3839
    if (event->ev_time - queue->last->ev_time >= 0) {
391✔
3840
        event->ev_next = NULL;
391✔
3841
        event->ev_prev = queue->last;
391✔
3842
        queue->last->ev_next = event;
391✔
3843
        queue->last = event;
391✔
3844
        return;
391✔
3845
    }
3846

3847
    /* Find insertion point */
UNCOV
3848
    i = queue->last;
×
3849
    while (1) {
UNCOV
3850
        i_prev = i->ev_prev;
×
UNCOV
3851
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
UNCOV
3852
            event->ev_prev = i_prev;
×
UNCOV
3853
            event->ev_next = i;
×
UNCOV
3854
            i->ev_prev = event;
×
UNCOV
3855
            if (i_prev != NULL) {
×
UNCOV
3856
                i_prev->ev_next = event;
×
3857
            }
3858
            else {
UNCOV
3859
                queue->first = event;
×
3860
            }
UNCOV
3861
            return;
×
3862
        }
UNCOV
3863
        i = i_prev;
×
3864
    }
3865
}
533✔
3866

3867
/************************************************************
3868

3869
  Function: ev_dequeue
3870

3871
*************************************************************/
3872
struct event *ev_dequeue(struct event_queue *queue)
1,006✔
3873
{
3874
    struct event *dequeued;
3875

3876
    if (queue->first == NULL) {
1,006✔
UNCOV
3877
        return NULL;
×
3878
    }
3879
    dequeued = queue->first;
1,006✔
3880
    ev_remove(queue, dequeued);
1,006✔
3881

3882
    return dequeued;
1,006✔
3883
}
288✔
3884

3885
/************************************************************
3886

3887
  Function: ev_remove
3888

3889
*************************************************************/
3890
void ev_remove(struct event_queue *queue, struct event *event)
1,791✔
3891
{
3892
    if (queue->first == event) {
1,791✔
3893
        queue->first = event->ev_next;
1,782✔
3894
    }
524✔
3895
    if (queue->last == event) {
1,791✔
3896
        queue->last = event->ev_prev;
1,421✔
3897
    }
413✔
3898
    if (event->ev_prev) {
1,791✔
3899
        event->ev_prev->ev_next = event->ev_next;
9✔
3900
    }
3✔
3901
    if (event->ev_next) {
1,791✔
3902
        event->ev_next->ev_prev = event->ev_prev;
370✔
3903
    }
114✔
3904
    event->ev_prev = NULL;
1,791✔
3905
    event->ev_next = NULL;
1,791✔
3906
}
1,791✔
3907

3908
/************************************************************
3909

3910
  Function: print_human_readable_time from current_time_ns
3911

3912
*************************************************************/
3913
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
45✔
3914
{
3915
    char time_buffer[100];
3916
    time_t current_time_s;
3917
    struct tm *local_time;
3918

3919
    current_time_s = current_time_ns / 1000000000;
45✔
3920
    local_time = localtime(&current_time_s);
45✔
3921
    switch(timestamp_format) {
45✔
3922
        case 1:
6✔
3923
            // timestamp-format ctime
3924
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
9✔
3925
            if (json_flag)
9✔
3926
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3927
            else
3928
                printf("[%s] ", time_buffer);
6✔
3929
            break;
9✔
3930
        case 2:
6✔
3931
            // timestamp-format iso
3932
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
9✔
3933
            if (json_flag)
9✔
3934
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3935
            else
3936
                printf("[%s] ", time_buffer);
6✔
3937
            break;
9✔
3938
        case 3:
6✔
3939
            // timestamp-format rfc3339
3940
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
9✔
3941
            if (json_flag)
9✔
3942
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3943
            else
3944
                printf("[%s] ", time_buffer);
6✔
3945
            break;
9✔
3946
        default:
12✔
3947
            if (json_flag)
18✔
3948
                printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9);
6✔
3949
            else
3950
                printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3951
    }
6✔
3952
}
45✔
3953

3954
/************************************************************
3955

3956
  Function: ms_since_midnight_utc
3957

3958
*************************************************************
3959

3960
  Input: int64_t: current UTC time in ns
3961

3962
  Output: uint32_t: current time in ms since midnight UTC
3963

3964
  Description:
3965

3966
  Return ICMP Timestamp value corresponding to the given time value.
3967
  The given time value must be in UTC.
3968

3969
*************************************************************/
3970
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
3971
{
3972
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
3973
}
3974

3975
/************************************************************
3976

3977
  Function: usage
3978

3979
*************************************************************
3980

3981
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3982

3983
  Description:
3984

3985
************************************************************/
3986

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