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

schweikert / fping / 15510713739

07 Jun 2025 06:49PM UTC coverage: 86.291% (-1.5%) from 87.756%
15510713739

Pull #391

github

web-flow
Merge bc8cf104b into fb6f8b561
Pull Request #391: fix ICMPv6 error message handling and reporting

7 of 41 new or added lines in 2 files covered. (17.07%)

7 existing lines in 1 file now uncovered.

1410 of 1634 relevant lines covered (86.29%)

324.52 hits per line

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

86.34
/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 <signal.h>
45
#include <stdarg.h>
46
#include <stdint.h>
47
#include <stdio.h>
48
#include <time.h>
49

50
#include "seqmap.h"
51

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

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

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

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

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

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

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

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

84
#include <sys/select.h>
85

86
/*** compatibility ***/
87

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

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

106
/*** externals ***/
107

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

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

118
/*** Constants ***/
119

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

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

136
/*** Ping packet defines ***/
137

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

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

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

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

152
/* maxima and minima */
153
#ifdef FPING_SAFE_LIMITS
154
#define MIN_INTERVAL 1 /* in millisec */
155
#define MIN_PERHOST_INTERVAL 10 /* in millisec */
156
#else
157
#define MIN_INTERVAL 0
158
#define MIN_PERHOST_INTERVAL 0
159
#endif
160

161
/* response time array flags */
162
#define RESP_WAITING -1
163
#define RESP_UNUSED -2
164
#define RESP_ERROR -3
165
#define RESP_TIMEOUT -4
166

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

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

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

220
#define ICMP_UNREACH_MAXTYPE 15
221

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

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

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

262
struct event;
263
typedef struct host_entry {
264
    int i; /* index into array */
265
    char *name; /* name as given by user */
266
    char *host; /* text description of host */
267
    struct sockaddr_storage saddr; /* internet address */
268
    socklen_t saddr_len;
269
    int64_t timeout; /* time to wait for response */
270
    int64_t last_send_time; /* time of last packet sent */
271
    int num_sent; /* number of ping packets sent (for statistics) */
272
    int num_recv; /* number of pings received (duplicates ignored) */
273
    int num_recv_total; /* number of pings received, including duplicates */
274
    int64_t max_reply; /* longest response time */
275
    int64_t min_reply; /* shortest response time */
276
    int64_t total_time; /* sum of response times */
277
    /* _i -> splits (reset on every report interval) */
278
    int num_sent_i; /* number of ping packets sent */
279
    int num_recv_i; /* number of pings received */
280
    int64_t max_reply_i; /* longest response time */
281
    int64_t min_reply_i; /* shortest response time */
282
    int64_t total_time_i; /* sum of response times */
283
    int64_t *resp_times; /* individual response times */
284

285
    /* to avoid allocating two struct events each time that we send a ping, we
286
     * preallocate here two struct events for each ping that we might send for
287
     * this host. */
288
    struct event *event_storage_ping;
289
    struct event *event_storage_timeout;
290
} HOST_ENTRY;
291

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

294
/* basic algorithm to ensure that we have correct data at all times:
295
 *
296
 * 1. when a ping is sent:
297
 *    - two events get added into event_queue:
298
 *      - t+PERIOD: ping event
299
 *      - t+TIMEOUT: timeout event
300
 *
301
 * 2. when a ping is received:
302
 *    - record statistics (increase num_sent and num_received)
303
 *    - remove timeout event (we store the event in seqmap, so that we can retrieve it when the response is received)
304
 *
305
 * 3. when a timeout happens:
306
 *    - record statistics (increase num_sent only)
307
 */
308

309
#define EV_TYPE_PING 1
310
#define EV_TYPE_TIMEOUT 2
311

312
struct event {
313
    struct event *ev_prev;
314
    struct event *ev_next;
315
    int64_t ev_time;
316
    struct host_entry *host;
317
    int ping_index;
318
};
319

320
struct event_queue {
321
    struct event *first;
322
    struct event *last;
323
};
324

325
/*** globals ***/
326

327
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
328

329
/* we keep two separate queues: a ping queue, for when the next ping should be
330
 * sent, and a timeout queue. the reason for having two separate queues is that
331
 * the ping period and the timeout value are different, so if we put them in
332
 * the same event queue, we would need to scan many more entries when inserting
333
 * into the sorted list.
334
 */
335
struct event_queue event_queue_ping;
336
struct event_queue event_queue_timeout;
337

338
char *prog;
339
int ident4 = 0; /* our icmp identity field */
340
int ident6 = 0;
341
const int sock_opt_on = 1; /* to activate a socket option */
342
int socket4 = -1;
343
int socktype4 = -1;
344
int using_sock_dgram4 = 0;
345
#ifndef IPV6
346
int hints_ai_family = AF_INET;
347
#else
348
int socket6 = -1;
349
int socktype6 = -1;
350
int hints_ai_family = AF_UNSPEC;
351
#endif
352

353
volatile sig_atomic_t status_snapshot = 0;
354
volatile sig_atomic_t finish_requested = 0;
355

356
unsigned int debugging = 0;
357

358
/* all time-related values are int64_t nanoseconds */
359
unsigned int retry = DEFAULT_RETRY;
360
int64_t timeout = (int64_t)DEFAULT_TIMEOUT * 1000000;
361
int64_t interval = (int64_t)DEFAULT_INTERVAL * 1000000;
362
int64_t perhost_interval = (int64_t)DEFAULT_PERHOST_INTERVAL * 1000000;
363
float backoff = DEFAULT_BACKOFF_FACTOR;
364
unsigned int ping_data_size = DEFAULT_PING_DATA_SIZE;
365
unsigned int count = 1, min_reachable = 0;
366
unsigned int trials;
367
int64_t report_interval = 0;
368
unsigned int ttl = 0;
369
int src_addr_set = 0;
370
struct in_addr src_addr;
371
#ifdef IPV6
372
int src_addr6_set = 0;
373
struct in6_addr src_addr6;
374
#endif
375

376
/* global stats */
377
int64_t max_reply = 0;
378
int64_t min_reply = 0;
379
int64_t total_replies = 0;
380
int64_t sum_replies = 0;
381
int max_hostname_len = 0;
382
int num_hosts = 0; /* total number of hosts */
383
int num_alive = 0, /* total number alive */
384
    num_unreachable = 0, /* total number unreachable */
385
    num_noaddress = 0; /* total number of addresses not found */
386
int num_timeout = 0, /* number of times select timed out */
387
    num_pingsent = 0, /* total pings sent */
388
    num_pingreceived = 0, /* total pings received */
389
    num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
390

391
struct timespec current_time; /* current time (pseudo) */
392
int64_t current_time_ns;
393
int64_t start_time;
394
int64_t end_time;
395
int64_t last_send_time; /* time last ping was sent */
396
int64_t next_report_time; /* time next -Q report is expected */
397

398
/* switches */
399
int generate_flag = 0; /* flag for IP list generation */
400
int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
401
int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag;
402
int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
403
int multif_flag, timeout_flag, fast_reachable;
404
int outage_flag = 0;
405
int timestamp_flag = 0;
406
int timestamp_format_flag = 0;
407
int random_data_flag = 0;
408
int cumulative_stats_flag = 0;
409
int check_source_flag = 0;
410
int icmp_request_typ = 0;
411
int print_tos_flag = 0;
412
int print_ttl_flag = 0;
413
int size_flag = 0;
414
#if defined(DEBUG) || defined(_DEBUG)
415
int randomly_lose_flag, trace_flag, print_per_system_flag;
416
int lose_factor;
417
#endif /* DEBUG || _DEBUG */
418

419
unsigned int fwmark = 0;
420

421
char *filename = NULL; /* file containing hosts to ping */
422

423
/*** forward declarations ***/
424

425
void add_name(char *name);
426
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len);
427
char *na_cat(char *name, struct in_addr ipaddr);
428
void crash_and_burn(char *message);
429
void errno_crash_and_burn(char *message);
430
char *get_host_by_address(struct in_addr in);
431
int send_ping(HOST_ENTRY *h, int index);
432
void usage(int);
433
int wait_for_reply(int64_t);
434
void print_per_system_stats(void);
435
void print_per_system_splits(void);
436
void stats_reset_interval(HOST_ENTRY *h);
437
void print_netdata(void);
438
void print_global_stats(void);
439
void main_loop();
440
void signal_handler(int);
441
void finish();
442
const char *sprint_tm(int64_t t);
443
void ev_enqueue(struct event_queue *queue, struct event *event);
444
struct event *ev_dequeue(struct event_queue *queue);
445
void ev_remove(struct event_queue *queue, struct event *event);
446
void add_cidr(char *);
447
void add_cidr_ipv4(unsigned long, unsigned long);
448
void add_range(char *, char *);
449
void add_addr_range_ipv4(unsigned long, unsigned long);
450
#ifdef IPV6
451
uint64_t be_octets_to_uint64(uint8_t*);
452
void uint64_to_be_octets(uint64_t, uint8_t*);
453
void add_cidr_ipv6(uint64_t, uint64_t, unsigned long, const char *);
454
void add_addr_range_ipv6(uint64_t, uint64_t, uint64_t, uint64_t, const char *);
455
#endif
456
void print_warning(char *fmt, ...);
457
int addr_cmp(struct sockaddr *a, struct sockaddr *b);
458
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
459
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
460
struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
461
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
462
void update_current_time();
463
void print_timestamp_format(int64_t current_time_ns, int timestamp_format);
464
static uint32_t ms_since_midnight_utc(int64_t time_val);
465

466
/************************************************************
467

468
  Function: p_setsockopt
469

470
*************************************************************
471

472
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
473

474
  Description:
475

476
  Elevates privileges to p_uid when required, calls
477
  setsockopt, and drops privileges back.
478

479
************************************************************/
480

481
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
22✔
482
    const void *optval, socklen_t optlen)
483
{
484
    const uid_t saved_uid = geteuid();
22✔
485
    int res;
486

487
    if (p_uid != saved_uid && seteuid(p_uid)) {
22✔
488
        perror("cannot elevate privileges for setsockopt");
×
489
    }
490

491
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
492

493
    if (p_uid != saved_uid && seteuid(saved_uid)) {
22✔
494
        perror("fatal error: could not drop privileges after setsockopt");
×
495
        /* continuing would be a security hole */
496
        exit(4);
×
497
    }
498

499
    return res;
22✔
500
}
501

502
/************************************************************
503

504
  Function: main
505

506
*************************************************************
507

508
  Inputs:  int argc, char** argv
509

510
  Description:
511

512
  Main program entry point
513

514
************************************************************/
515

516
int main(int argc, char **argv)
641✔
517
{
518
/* Debug: CPU Performance */
519
#if defined(DEBUG) || defined(_DEBUG)
520
    clock_t perf_cpu_start, perf_cpu_end;
521
    double perf_cpu_time_used;
522
    perf_cpu_start = clock();
523
#endif /* DEBUG || _DEBUG */
524

525
    int c;
526
    const uid_t suid = geteuid();
641✔
527
    int tos = 0;
641✔
528
    struct optparse optparse_state;
529
#ifdef USE_SIGACTION
530
    struct sigaction act;
531
#endif
532

533
    /* pre-parse -h/--help, so that we also can output help information
534
     * without trying to open the socket, which might fail */
535
    prog = argv[0];
641✔
536
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
641✔
537
        usage(0);
3✔
538
    }
1✔
539

540
    socket4 = open_ping_socket_ipv4(&socktype4);
639✔
541
#ifdef __linux__
542
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
543
     * structure is missing in the message.
544
     */
545
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
452✔
546
#endif
547

548
#ifdef IPV6
549
    socket6 = open_ping_socket_ipv6(&socktype6);
639✔
550
    /* if called (sym-linked) via 'fping6', imply '-6'
551
     * for backward compatibility */
552
    if (strstr(prog, "fping6")) {
639✔
553
        hints_ai_family = AF_INET6;
×
554
    }
555
#endif
556

557
    memset(&src_addr, 0, sizeof(src_addr));
639✔
558
#ifdef IPV6
559
    memset(&src_addr6, 0, sizeof(src_addr6));
639✔
560
#endif
561

562
    if (!suid && suid != getuid()) {
639✔
563
        /* *temporarily* drop privileges */
564
        if (seteuid(getuid()) == -1)
601✔
565
            perror("cannot setuid");
×
566
    }
161✔
567

568
    optparse_init(&optparse_state, argv);
639✔
569
    ident4 = ident6 = htons(getpid() & 0xFFFF);
639✔
570
    verbose_flag = 1;
639✔
571
    backoff_flag = 1;
639✔
572
    opterr = 1;
639✔
573

574
    /* get command line options */
575

576
    struct optparse_long longopts[] = {
639✔
577
        { "ipv4", '4', OPTPARSE_NONE },
578
        { "ipv6", '6', OPTPARSE_NONE },
579
        { "alive", 'a', OPTPARSE_NONE },
580
        { "addr", 'A', OPTPARSE_NONE },
581
        { "size", 'b', OPTPARSE_REQUIRED },
582
        { "backoff", 'B', OPTPARSE_REQUIRED },
583
        { "count", 'c', OPTPARSE_REQUIRED },
584
        { "vcount", 'C', OPTPARSE_REQUIRED },
585
        { "rdns", 'd', OPTPARSE_NONE },
586
        { "timestamp", 'D', OPTPARSE_NONE },
587
        { "timestamp-format", '0', OPTPARSE_REQUIRED },
588
        { "elapsed", 'e', OPTPARSE_NONE },
589
        { "file", 'f', OPTPARSE_REQUIRED },
590
        { "generate", 'g', OPTPARSE_NONE },
591
        { "help", 'h', OPTPARSE_NONE },
592
        { "ttl", 'H', OPTPARSE_REQUIRED },
593
        { "interval", 'i', OPTPARSE_REQUIRED },
594
        { "iface", 'I', OPTPARSE_REQUIRED },
595
        { "icmp-timestamp", '0', OPTPARSE_NONE },
596
#ifdef SO_MARK
597
        { "fwmark", 'k', OPTPARSE_REQUIRED },
598
#endif
599
        { "loop", 'l', OPTPARSE_NONE },
600
        { "all", 'm', OPTPARSE_NONE },
601
        { "dontfrag", 'M', OPTPARSE_NONE },
602
        { "name", 'n', OPTPARSE_NONE },
603
        { "netdata", 'N', OPTPARSE_NONE },
604
        { "outage", 'o', OPTPARSE_NONE },
605
        { "tos", 'O', OPTPARSE_REQUIRED },
606
        { "period", 'p', OPTPARSE_REQUIRED },
607
        { "quiet", 'q', OPTPARSE_NONE },
608
        { "squiet", 'Q', OPTPARSE_REQUIRED },
609
        { "retry", 'r', OPTPARSE_REQUIRED },
610
        { "random", 'R', OPTPARSE_NONE },
611
        { "stats", 's', OPTPARSE_NONE },
612
        { "src", 'S', OPTPARSE_REQUIRED },
613
        { "timeout", 't', OPTPARSE_REQUIRED },
614
        { NULL, 'T', OPTPARSE_REQUIRED },
615
        { "unreach", 'u', OPTPARSE_NONE },
616
        { "version", 'v', OPTPARSE_NONE },
617
        { "reachable", 'x', OPTPARSE_REQUIRED },
618
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
619
        { "check-source", '0', OPTPARSE_NONE },
620
        { "print-tos", '0', OPTPARSE_NONE },
621
        { "print-ttl", '0', OPTPARSE_NONE },
622
#if defined(DEBUG) || defined(_DEBUG)
623
        { NULL, 'z', OPTPARSE_REQUIRED },
624
#endif
625
        { 0, 0, 0 }
626
    };
627

628
    float opt_value_float;
629
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
1,507✔
630
        switch (c) {
972✔
631
        case '0':
76✔
632
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
94✔
633
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
15✔
634
                  timestamp_format_flag = 1;
3✔
635
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
13✔
636
                  timestamp_format_flag = 2;
3✔
637
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
10✔
638
                  timestamp_format_flag = 3;
3✔
639
                }else{
1✔
640
                  usage(1);
6✔
641
                }
642
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
84✔
643
                check_source_flag = 1;
9✔
644
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
71✔
645
#ifdef IPV6
646
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
27✔
647
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
648
                    exit(1);
2✔
649
                }
650
                hints_ai_family = AF_INET;
25✔
651
#endif
652
                icmp_request_typ = 13;
25✔
653
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
25✔
654
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
46✔
655
                print_tos_flag = 1;
23✔
656
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
23✔
657
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTOS, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
658
                        perror("setsockopt IP_RECVTOS");
×
659
                    }
660
                }
661
#if defined(IPV6) && defined(IPV6_RECVTCLASS)
662
                if (socket6 >= 0) {
23✔
663
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVTCLASS, &sock_opt_on, sizeof(sock_opt_on))) {
23✔
664
                        perror("setsockopt IPV6_RECVTCLASS");
×
665
                    }
666
                }
5✔
667
#endif
668
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
25✔
669
                print_ttl_flag = 1;
20✔
670
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
20✔
671
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTTL, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
672
                        perror("setsockopt IP_RECVTTL");
×
673
                    }
674
                }
675
#if defined(IPV6) && defined(IPV6_RECVHOPLIMIT)
676
                if (socket6 >= 0) {
20✔
677
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &sock_opt_on, sizeof(sock_opt_on))) {
20✔
678
                        perror("setsockopt IPV6_RECVHOPLIMIT");
×
679
                    }
680
                }
4✔
681
#endif
682
            } else {
4✔
683
                usage(1);
×
684
            }
685
            break;
88✔
686
        case '4':
32✔
687
#ifdef IPV6
688
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
689
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
690
                exit(1);
3✔
691
            }
692
            hints_ai_family = AF_INET;
40✔
693
#endif
694
            break;
40✔
695
        case '6':
28✔
696
#ifdef IPV6
697
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
31✔
698
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
699
                exit(1);
5✔
700
            }
701
            hints_ai_family = AF_INET6;
26✔
702
#else
703
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
704
            exit(1);
705
#endif
706
            break;
26✔
707
        case 'M':
2✔
708
#ifdef IP_MTU_DISCOVER
709
            if (socket4 >= 0) {
2✔
710
                int val = IP_PMTUDISC_DO;
2✔
711
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
712
                    perror("setsockopt IP_MTU_DISCOVER");
713
                }
714
            }
715
#ifdef IPV6
716
            if (socket6 >= 0) {
2✔
717
                int val = IPV6_PMTUDISC_DO;
2✔
718
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
719
                    perror("setsockopt IPV6_MTU_DISCOVER");
720
                }
721
            }
722
#endif
723
#else
724
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
725
            exit(1);
726
#endif
727
            break;
2✔
728

729
        case 't':
40✔
730
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
45✔
731
                usage(1);
6✔
732
            if (opt_value_float < 0) {
41✔
733
                usage(1);
3✔
734
            }
1✔
735
            timeout = opt_value_float * 1000000;
39✔
736
            timeout_flag = 1;
39✔
737
            break;
39✔
738

739
        case 'r':
36✔
740
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
40✔
741
                usage(1);
6✔
742
            break;
36✔
743

744
        case 'i':
30✔
745
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
36✔
746
                usage(1);
6✔
747
            if (opt_value_float < 0) {
32✔
748
                usage(1);
3✔
749
            }
1✔
750
            interval = opt_value_float * 1000000;
30✔
751
            break;
30✔
752

753
        case 'p':
68✔
754
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
99✔
755
                usage(1);
6✔
756
            if (opt_value_float < 0) {
95✔
757
                usage(1);
3✔
758
            }
1✔
759
            perhost_interval = opt_value_float * 1000000;
93✔
760

761
            break;
93✔
762

763
        case 'c':
60✔
764
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
85✔
765
                usage(1);
6✔
766

767
            count_flag = 1;
81✔
768
            break;
81✔
769

770
        case 'C':
24✔
771
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
33✔
772
                usage(1);
6✔
773

774
            count_flag = 1;
29✔
775
            report_all_rtts_flag = 1;
29✔
776
            break;
29✔
777

778
        case 'b':
14✔
779
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
21✔
780
                usage(1);
6✔
781
            size_flag = 1;
17✔
782
            break;
17✔
783

784
        case 'h':
2✔
785
            usage(0);
3✔
786
            break;
1✔
787

788
        case 'q':
12✔
789
            verbose_flag = 0;
17✔
790
            quiet_flag = 1;
17✔
791
            break;
17✔
792

793
        case 'Q':
22✔
794
            verbose_flag = 0;
33✔
795
            quiet_flag = 1;
33✔
796
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
797
                usage(1);
6✔
798
            if (opt_value_float < 0) {
29✔
799
                usage(1);
3✔
800
            }
1✔
801
            report_interval = opt_value_float * 1e9;
27✔
802

803
            /* recognize keyword(s) after number, ignore everything else */
804
            {
805
                char *comma = strchr(optparse_state.optarg, ',');
27✔
806
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
27✔
807
                    cumulative_stats_flag = 1;
6✔
808
                }
2✔
809
            }
810

811
            break;
27✔
812

813
        case 'e':
6✔
814
            elapsed_flag = 1;
8✔
815
            break;
8✔
816

817
        case 'm':
818
            multif_flag = 1;
×
819
            break;
×
820

821
        case 'N':
2✔
822
            netdata_flag = 1;
3✔
823
            break;
3✔
824

825
        case 'n':
4✔
826
            name_flag = 1;
6✔
827
            if (rdns_flag) {
6✔
828
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
829
                exit(1);
3✔
830
            }
831
            break;
3✔
832

833
        case 'd':
6✔
834
            rdns_flag = 1;
9✔
835
            if (name_flag) {
9✔
836
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
837
                exit(1);
3✔
838
            }
839
            break;
6✔
840

841
        case 'A':
2✔
842
            addr_flag = 1;
3✔
843
            break;
3✔
844

845
        case 'B':
10✔
846
            if (!(backoff = atof(optparse_state.optarg)))
12✔
847
                usage(1);
6✔
848

849
            break;
8✔
850

851
        case 's':
6✔
852
            stats_flag = 1;
9✔
853
            break;
9✔
854

855
        case 'D':
14✔
856
            timestamp_flag = 1;
21✔
857
            break;
21✔
858

859
        case 'R':
4✔
860
            random_data_flag = 1;
5✔
861
            break;
5✔
862

863
        case 'l':
2✔
864
            loop_flag = 1;
3✔
865
            backoff_flag = 0;
3✔
866
            break;
3✔
867

868
        case 'u':
8✔
869
            unreachable_flag = 1;
12✔
870
            break;
12✔
871

872
        case 'a':
18✔
873
            alive_flag = 1;
26✔
874
            break;
26✔
875

876
        case 'H':
8✔
877
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
12✔
878
                usage(1);
6✔
879
            break;
8✔
880

881
#if defined(DEBUG) || defined(_DEBUG)
882
        case 'z':
883
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
884
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
885
                    usage(1);
886

887
            break;
888
#endif /* DEBUG || _DEBUG */
889

890
        case 'v':
4✔
891
            printf("%s: Version %s\n", prog, VERSION);
6✔
892
            exit(0);
6✔
893

894
        case 'x':
8✔
895
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
10✔
896
                usage(1);
6✔
897
            break;
6✔
898

899
        case 'X':
6✔
900
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
901
                usage(1);
6✔
902
            fast_reachable = 1;
5✔
903
            break;
5✔
904

905
        case 'f':
18✔
906
            filename = optparse_state.optarg;
27✔
907
            break;
27✔
908
#ifdef SO_MARK
909
        case 'k':
10✔
910
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
10✔
911
                usage(1);
4✔
912

913
            if (socket4 >= 0)
6✔
914
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
915
                    perror("fwmark ipv4");
4✔
916

917
#ifdef IPV6
918
            if (socket6 >= 0)
6✔
919
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
920
                    perror("fwmark ipv6");
4✔
921
#endif
922

923
            break;
6✔
924
#endif
925

926
        case 'g':
98✔
927
            /* use IP list generation */
928
            /* mutually exclusive with using file input or command line targets */
929
            generate_flag = 1;
117✔
930
            break;
117✔
931

932
        case 'S':
10✔
933
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
934
                src_addr_set = 1;
6✔
935
                break;
6✔
936
            }
937
#ifdef IPV6
938
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
939
                src_addr6_set = 1;
4✔
940
                break;
4✔
941
            }
942
#endif
943
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
944
            exit(1);
3✔
945

946
        case 'I':
6✔
947
#ifdef SO_BINDTODEVICE
948
            if (socket4 >= 0) {
6✔
949
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
950
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
951
                    exit(1);
2✔
952
                }
953
            }
954
#ifdef IPV6
955
            if (socket6 >= 0) {
4✔
956
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
957
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
958
                    exit(1);
959
                }
960
            }
961
#endif
962
#else
963
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
1✔
964
            exit(3);
1✔
965
            ;
966
#endif
967
            break;
4✔
968

969
        case 'T':
2✔
970
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
971
            break;
3✔
972

973
        case 'O':
10✔
974
            if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
15✔
975
                if (socket4 >= 0) {
9✔
976
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
977
                        perror("setting type of service octet IP_TOS");
×
978
                    }
979
                }
3✔
980
#if defined(IPV6) && defined(IPV6_TCLASS)
981
                if (socket6 >= 0) {
9✔
982
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
983
                        perror("setting type of service octet IPV6_TCLASS");
×
984
                    }
985
                }
3✔
986
#endif
987
            }
3✔
988
            else {
989
                usage(1);
6✔
990
            }
991
            break;
11✔
992

993
        case 'o':
6✔
994
            outage_flag = 1;
9✔
995
            break;
9✔
996

997
        case '?':
4✔
998
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
999
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
1000
            exit(1);
6✔
1001
            break;
1002
        }
1003
    }
1004

1005
    /* permanently drop privileges */
1006
    if (suid != getuid() && setuid(getuid())) {
477✔
1007
        perror("fatal: failed to permanently drop privileges");
×
1008
        /* continuing would be a security hole */
1009
        exit(4);
×
1010
    }
1011

1012
    /* validate various option settings */
1013

1014
#ifndef IPV6
1015
    if (socket4 < 0) {
1016
        crash_and_burn("can't create socket (must run as root?)");
1017
    }
1018
#else
1019
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
477✔
1020
        crash_and_burn("can't create socket (must run as root?)");
×
1021
    }
1022
#endif
1023

1024
    if (ttl > 255) {
475✔
1025
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
1026
        exit(1);
3✔
1027
    }
1028

1029
    if (unreachable_flag && alive_flag) {
472✔
1030
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
1031
        exit(1);
3✔
1032
    }
1033

1034
    if (count_flag && loop_flag) {
469✔
1035
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
1036
        exit(1);
3✔
1037
    }
1038

1039
#ifdef FPING_SAFE_LIMITS
1040
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
466✔
1041
        && getuid()) {
118✔
1042
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
1043
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
1044
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
1045
        exit(1);
6✔
1046
    }
1047
#endif
1048

1049
    if (ping_data_size > MAX_PING_DATA) {
460✔
1050
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1051
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1052
        exit(1);
3✔
1053
    }
1054

1055
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
457✔
1056
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1057
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1058
        exit(1);
6✔
1059
    }
1060

1061
    if (icmp_request_typ == 13 && size_flag != 0) {
451✔
1062
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1063
        exit(1);
9✔
1064
    }
1065

1066
    if (count_flag) {
442✔
1067
        if (verbose_flag)
100✔
1068
            per_recv_flag = 1;
59✔
1069

1070
        alive_flag = unreachable_flag = verbose_flag = 0;
100✔
1071
    }
28✔
1072

1073
    if (loop_flag) {
442✔
1074
        if (!report_interval)
×
1075
            per_recv_flag = 1;
×
1076

1077
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1078
    }
1079

1080
    if (alive_flag || unreachable_flag || min_reachable)
442✔
1081
        verbose_flag = 0;
37✔
1082

1083
    trials = (count > retry + 1) ? count : retry + 1;
458✔
1084

1085
    /* auto-tune default timeout for count/loop modes
1086
     * see also github #32 */
1087
    if (loop_flag || count_flag) {
458✔
1088
        if (!timeout_flag) {
100✔
1089
            timeout = perhost_interval;
95✔
1090
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
95✔
1091
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1092
            }
1093
        }
27✔
1094
    }
28✔
1095

1096
#if defined(DEBUG) || defined(_DEBUG)
1097
    if (debugging & DBG_TRACE)
1098
        trace_flag = 1;
1099

1100
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1101
        randomly_lose_flag = 1;
1102
        lose_factor = 1; /* ie, 1/4 */
1103
    }
1104

1105
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1106
        randomly_lose_flag = 1;
1107
        lose_factor = 5; /* ie, 3/4 */
1108
    }
1109

1110
    if (debugging & DBG_PRINT_PER_SYSTEM)
1111
        print_per_system_flag = 1;
1112

1113
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1114
        report_all_rtts_flag = 1;
1115

1116
    if (trace_flag) {
1117
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1118
            prog, count, retry, interval / 1e6);
1119
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1120
            perhost_interval / 1e6, timeout / 1e6);
1121
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1122
            ping_data_size, trials);
1123

1124
        if (verbose_flag)
1125
            fprintf(stderr, "  verbose_flag set\n");
1126
        if (multif_flag)
1127
            fprintf(stderr, "  multif_flag set\n");
1128
        if (name_flag)
1129
            fprintf(stderr, "  name_flag set\n");
1130
        if (addr_flag)
1131
            fprintf(stderr, "  addr_flag set\n");
1132
        if (stats_flag)
1133
            fprintf(stderr, "  stats_flag set\n");
1134
        if (unreachable_flag)
1135
            fprintf(stderr, "  unreachable_flag set\n");
1136
        if (alive_flag)
1137
            fprintf(stderr, "  alive_flag set\n");
1138
        if (elapsed_flag)
1139
            fprintf(stderr, "  elapsed_flag set\n");
1140
        if (version_flag)
1141
            fprintf(stderr, "  version_flag set\n");
1142
        if (count_flag)
1143
            fprintf(stderr, "  count_flag set\n");
1144
        if (loop_flag)
1145
            fprintf(stderr, "  loop_flag set\n");
1146
        if (backoff_flag)
1147
            fprintf(stderr, "  backoff_flag set\n");
1148
        if (per_recv_flag)
1149
            fprintf(stderr, "  per_recv_flag set\n");
1150
        if (report_all_rtts_flag)
1151
            fprintf(stderr, "  report_all_rtts_flag set\n");
1152
        if (randomly_lose_flag)
1153
            fprintf(stderr, "  randomly_lose_flag set\n");
1154
        if (print_per_system_flag)
1155
            fprintf(stderr, "  print_per_system_flag set\n");
1156
        if (outage_flag)
1157
            fprintf(stderr, "  outage_flag set\n");
1158
        if (netdata_flag)
1159
            fprintf(stderr, "  netdata_flag set\n");
1160
    }
1161
#endif /* DEBUG || _DEBUG */
1162

1163
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1164
    if (ttl > 0) {
458✔
1165
        if (socket4 >= 0) {
3✔
1166
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1167
                perror("setting time to live");
×
1168
            }
1169
        }
1✔
1170
#ifdef IPV6
1171
        if (socket6 >= 0) {
3✔
1172
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1173
                perror("setting time to live");
×
1174
            }
1175
        }
1✔
1176
#endif
1177
    }
1✔
1178

1179
#if HAVE_SO_TIMESTAMPNS
1180
    {
1181
        int opt = 1;
336✔
1182
        if (socket4 >= 0) {
336✔
1183
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
336✔
1184
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1185
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1186
                }
1187
            }
1188
        }
1189
#ifdef IPV6
1190
        if (socket6 >= 0) {
336✔
1191
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
336✔
1192
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1193
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1194
                }
1195
            }
1196
        }
1197
#endif
1198
    }
1199
#endif
1200

1201
    update_current_time();
458✔
1202
    start_time = current_time_ns;
458✔
1203

1204
    /* handle host names supplied on command line or in a file */
1205
    /* if the generate_flag is on, then generate the IP list */
1206

1207
    argv = &argv[optparse_state.optind];
458✔
1208
    argc -= optparse_state.optind;
458✔
1209

1210
    /* calculate how many ping can be in-flight per host */
1211
    if (count_flag) {
458✔
1212
        event_storage_count = count;
115✔
1213
    }
43✔
1214
    else if (loop_flag) {
343✔
1215
        if (perhost_interval > timeout) {
×
1216
            event_storage_count = 1;
×
1217
        }
1218
        else {
1219
            event_storage_count = 1 + timeout / perhost_interval;
×
1220
        }
1221
    }
1222
    else {
1223
        event_storage_count = 1;
343✔
1224
    }
1225

1226
    /* file and generate are mutually exclusive */
1227
    /* file and command line are mutually exclusive */
1228
    /* generate requires command line parameters beyond the switches */
1229
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
458✔
1230
        usage(1);
19✔
1231

1232
    /* if no conditions are specified, then assume input from stdin */
1233
    if (!*argv && !filename && !generate_flag)
440✔
1234
        filename = "-";
69✔
1235

1236
    if (*argv && !generate_flag) {
440✔
1237
        while (*argv) {
524✔
1238
            add_name(*argv);
291✔
1239
            ++argv;
291✔
1240
        }
1241
    }
57✔
1242
    else if (filename) {
207✔
1243
        FILE *ping_file;
1244
        char line[MAX_TARGET_NAME_LEN + 1];
1245
        char host[MAX_TARGET_NAME_LEN + 1];
1246
        char scratch[MAX_TARGET_NAME_LEN + 1];
1247
        int skip, non_empty;
1248

1249
        if (strcmp(filename, "-") == 0)
96✔
1250
            ping_file = fdopen(0, "r");
72✔
1251
        else
1252
            ping_file = fopen(filename, "r");
24✔
1253

1254
        if (!ping_file)
96✔
1255
            errno_crash_and_burn("fopen");
3✔
1256

1257
        /*
1258
         * Read the first word of every non-comment line, skip everything else.
1259
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1260
         * character is a '#' are interpreted as comments and ignored.)
1261
        */
1262
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1263
            skip = non_empty = 0;
309✔
1264

1265
            /* skip over a prefix of the line where sscanf finds nothing */
1266
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1267
                continue;
99✔
1268
            }
1269

1270
            /* the first word of the line can indicate a comment line */
1271
            if (host[0] == '#') {
210✔
1272
                skip = 1; /* skip remainder of line */
42✔
1273
            } else {
14✔
1274
                non_empty = 1; /* we have something to add as a target name */
168✔
1275
                /*
1276
                 * We have found the start of a word.
1277
                 * This part of the line may contain all of the first word.
1278
                 */
1279
                if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
168✔
1280
                    char discard1[MAX_TARGET_NAME_LEN + 1];
1281
                    char discard2[MAX_TARGET_NAME_LEN + 1];
1282
                    if (sscanf(line, "%s%s", discard1, discard2) == 2) {
102✔
1283
                        skip = 1; /* a second word starts in this part */
6✔
1284
                    }
2✔
1285
                    if (isspace(line[sizeof(line) - 2])) {
102✔
1286
                        skip = 1; /* the first word ends in this part */
42✔
1287
                    }
14✔
1288
                }
34✔
1289
            }
1290
            /* read remainder of this input line */
1291
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
336✔
1292
                if (skip) {
150✔
1293
                    continue; /* skip rest of data in this input line */
102✔
1294
                }
1295
                if (isspace(line[0])) {
48✔
1296
                    skip = 1; /* first word ended in previous part */
6✔
1297
                    continue;
6✔
1298
                }
1299
                if ((sscanf(line, "%s", scratch) != 1) || (!*scratch)) {
42✔
1300
                    skip = 1; /* empty or blank part of line, skip the rest */
×
1301
                    continue;
×
1302
                }
1303
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
42✔
1304
                    fprintf(stderr, "%s: target name too long\n", prog);
24✔
1305
                    exit(1);
24✔
1306
                }
1307
                /* append remainder of word started in previous line part */
1308
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
18✔
1309
                /*
1310
                 * Since the "host" buffer is the same size as the "line"
1311
                 * buffer, a target name that fits into the "host" buffer
1312
                 * cannot use more than two consecutive line parts.
1313
                 * A target name that uses two consecutive line parts
1314
                 * and fits into the "host" buffer must end before the
1315
                 * end of the second "line" buffer.  Thus the rest of
1316
                 * the line can be skipped.
1317
                 */
1318
                skip = 1;
18✔
1319
            }
1320

1321
            if (non_empty)
186✔
1322
                add_name(host);
144✔
1323
        }
1324

1325
        fclose(ping_file);
70✔
1326
    }
24✔
1327
    else if (*argv && generate_flag) {
111✔
1328
        if (argc == 1) {
111✔
1329
            /* one target: we expect a cidr range (n.n.n.n/m) */
1330
            add_cidr(argv[0]);
56✔
1331
        }
10✔
1332
        else if (argc == 2) {
55✔
1333
            add_range(argv[0], argv[1]);
52✔
1334
        }
6✔
1335
        else {
1336
            usage(1);
3✔
1337
        }
1338
    }
17✔
1339
    else {
1340
        usage(1);
×
1341
    }
1342

1343
    if (!num_hosts) {
360✔
1344
        exit(num_noaddress ? 2 : 1);
33✔
1345
    }
1346

1347
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
327✔
1348
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1349
    }
2✔
1350
#ifdef IPV6
1351
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
325✔
1352
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1353
    }
1354
#endif
1355

1356
    /* allocate and initialize array to map host nr to host_entry */
1357
    {
1358
        struct event *cursor = event_queue_ping.first;
323✔
1359
        int i = 0;
323✔
1360
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
323✔
1361
        if (!table)
323✔
1362
            crash_and_burn("Can't malloc array of hosts");
×
1363
        /* initialize table of hosts. we know that we have ping events scheduled
1364
         * for each of them */
1365
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
830✔
1366
            table[i] = cursor->host;
507✔
1367
            cursor->host->i = i;
507✔
1368
            i++;
507✔
1369
        }
135✔
1370
    }
1371

1372
    init_ping_buffer_ipv4(ping_data_size);
323✔
1373
#ifdef IPV6
1374
    init_ping_buffer_ipv6(ping_data_size);
323✔
1375
#endif
1376

1377
#ifdef USE_SIGACTION
1378
    memset(&act, 0, sizeof(act));
323✔
1379
    act.sa_handler = signal_handler;
323✔
1380
    sigemptyset(&act.sa_mask);
323✔
1381
    sigaddset(&act.sa_mask, SIGINT);
323✔
1382
    sigaddset(&act.sa_mask, SIGQUIT);
323✔
1383
    act.sa_flags = SA_RESTART;
323✔
1384
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
323✔
1385
        crash_and_burn("failure to set signal handler");
24✔
1386
    }
24✔
1387
#else
1388
    signal(SIGINT, signal_handler);
1389
    signal(SIGQUIT, signal_handler);
1390
#endif
1391
    setlinebuf(stdout);
299✔
1392

1393
    if (report_interval) {
299✔
1394
        next_report_time = current_time_ns + report_interval;
24✔
1395
    }
8✔
1396

1397
    last_send_time = 0;
299✔
1398

1399
    seqmap_init();
299✔
1400

1401
    /* main loop */
1402
    main_loop();
299✔
1403

1404
/* Debug: CPU Performance */
1405
#if defined(DEBUG) || defined(_DEBUG)
1406
    perf_cpu_end = clock();
1407
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1408
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1409
#endif /* DEBUG || _DEBUG */
1410

1411
    finish();
299✔
1412

1413
    return 0;
65✔
1414
}
1415

1416
static inline int64_t timespec_ns(struct timespec *a)
5,030✔
1417
{
1418
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,030✔
1419
}
1420

1421
#if HAVE_SO_TIMESTAMPNS
1422
/* convert a struct timeval to nanoseconds */
1423
static inline int64_t timeval_ns(struct timeval *a)
1424
{
1425
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1426
}
1427
#endif /* HAVE_SO_TIMESTAMPNS */
1428

1429
void add_cidr(char *addr)
54✔
1430
{
1431
    char *addr_end;
1432
    char *mask_str;
1433
    unsigned long mask;
1434
    int ret;
1435
    struct addrinfo addr_hints;
1436
    struct addrinfo *addr_res;
1437
    unsigned long net_addr;
1438
#ifdef IPV6
1439
    uint64_t net_upper, net_lower;
1440
    char *scope_str;
1441
#endif /* IPV6 */
1442

1443
    /* Split address from mask */
1444
    addr_end = strrchr(addr, '/');
54✔
1445
    if (addr_end == NULL) {
54✔
1446
        usage(1);
3✔
1447
    }
1✔
1448
    mask_str = addr_end + 1;
52✔
1449

1450
#ifdef IPV6
1451
    /* IPv6 addresses can have a scope */
1452
    scope_str = strchr(addr, '%');
52✔
1453
    if (scope_str && mask_str < scope_str) {
52✔
1454
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1455
        exit(1);
2✔
1456
    }
1457
#endif /*IPV6 */
1458

1459
    *addr_end = '\0';
50✔
1460
    mask = atoi(mask_str);
50✔
1461

1462
    /* parse address */
1463
    memset(&addr_hints, 0, sizeof(struct addrinfo));
50✔
1464
    addr_hints.ai_family = hints_ai_family;
50✔
1465
    addr_hints.ai_flags = AI_NUMERICHOST;
50✔
1466
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
50✔
1467
    if (ret) {
50✔
1468
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1469
        exit(1);
7✔
1470
    }
1471
    if (addr_res->ai_family == AF_INET) {
43✔
1472
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
21✔
1473
        freeaddrinfo(addr_res);
21✔
1474
        add_cidr_ipv4(net_addr, mask);
21✔
1475
#ifdef IPV6
1476
    } else if (addr_res->ai_family == AF_INET6) {
29✔
1477
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1478
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1479
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1480
        freeaddrinfo(addr_res);
22✔
1481
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1482
#endif /* IPV6 */
1483
    } else {
1484
        freeaddrinfo(addr_res);
×
1485
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1486
        exit(1);
×
1487
    }
1488
}
27✔
1489

1490
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
21✔
1491
{
1492
    unsigned long bitmask;
1493
    unsigned long net_last;
1494

1495
    /* check mask */
1496
    if (mask < 1 || mask > 32) {
21✔
1497
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1498
        exit(1);
6✔
1499
    }
1500

1501
    /* convert mask integer from 1 to 32 to a bitmask */
1502
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
15✔
1503

1504
    /* calculate network range */
1505
    net_addr &= bitmask;
15✔
1506
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
15✔
1507

1508
    /* exclude network and broadcast address for regular prefixes */
1509
    if (mask < 31) {
15✔
1510
        net_last--;
9✔
1511
        net_addr++;
9✔
1512
    }
3✔
1513

1514
    /* add all hosts in that network (net_addr and net_last inclusive) */
1515
    add_addr_range_ipv4(net_addr, net_last);
15✔
1516
}
13✔
1517

1518
#ifdef IPV6
1519
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1520
{
1521
    uint64_t bitmask_lower;
1522
    uint64_t last_lower;
1523

1524
    /* check mask -- 2^63 addresses should suffice for now */
1525
    if (mask < 65 || mask > 128) {
23✔
1526
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1527
        exit(1);
7✔
1528
    }
1529

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

1533
    /* calculate network range */
1534
    net_lower &= bitmask_lower;
16✔
1535
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1536

1537
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1538
}
12✔
1539
#endif /* IPV6 */
1540

1541
void add_range(char *start, char *end)
52✔
1542
{
1543
    struct addrinfo addr_hints;
1544
    struct addrinfo *addr_res;
1545
    unsigned long start_long;
1546
    unsigned long end_long;
1547
    int ret;
1548
#ifdef IPV6
1549
    uint64_t start_upper, start_lower;
1550
    uint64_t end_upper, end_lower;
1551
    char *start_scope_str, *end_scope_str;
1552

1553
    /*
1554
     * The compiler does not know that setting the address family hint to
1555
     * ensure that start and end are from the same address family also
1556
     * ensures that either start_long and end_long are initialized and used,
1557
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1558
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1559
     * supported to suppress compiler warnings.
1560
     */
1561
    start_long = -1;
52✔
1562
    end_long = 0;
52✔
1563
    start_upper = start_lower = -1;
52✔
1564
    end_upper = end_lower = 0;
52✔
1565
    start_scope_str = end_scope_str = NULL;
52✔
1566
#endif /* IPV6 */
1567

1568
    /* parse start address */
1569
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1570
    addr_hints.ai_family = hints_ai_family;
52✔
1571
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1572
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1573
    if (ret) {
52✔
1574
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1575
        exit(1);
9✔
1576
    }
1577
    /* start and end must be from the same address family */
1578
    hints_ai_family = addr_res->ai_family;
43✔
1579
    if (addr_res->ai_family == AF_INET) {
43✔
1580
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1581
        freeaddrinfo(addr_res);
17✔
1582
#ifdef IPV6
1583
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1584
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1585
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1586
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1587
        freeaddrinfo(addr_res);
26✔
1588
#endif /* IPV6 */
1589
    } else {
1590
        freeaddrinfo(addr_res);
×
1591
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1592
        exit(1);
×
1593
    }
1594

1595
#ifdef IPV6
1596
    /* IPv6 addresses can have a scope */
1597
    if (hints_ai_family == AF_INET6) {
43✔
1598
        start_scope_str = strchr(start, '%');
26✔
1599
        end_scope_str = strchr(end, '%');
26✔
1600
        if ((!start_scope_str && end_scope_str) ||
26✔
1601
            (start_scope_str && !end_scope_str) ||
24✔
1602
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1603
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1604
                exit(1);
6✔
1605
        }
1606
    }
1607
#endif
1608

1609
    /* parse end address */
1610
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1611
    addr_hints.ai_family = hints_ai_family;
37✔
1612
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1613
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1614
    if (ret) {
37✔
1615
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1616
        exit(1);
7✔
1617
    }
1618
    if (addr_res->ai_family == AF_INET) {
30✔
1619
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1620
        freeaddrinfo(addr_res);
12✔
1621
        add_addr_range_ipv4(start_long, end_long);
12✔
1622
#ifdef IPV6
1623
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1624
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1625
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1626
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1627
        freeaddrinfo(addr_res);
18✔
1628
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1629
#endif /* IPV6 */
1630
    } else {
1631
        freeaddrinfo(addr_res);
×
1632
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1633
        exit(1);
×
1634
    }
1635
}
24✔
1636

1637
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
27✔
1638
{
1639
    /* check if generator limit is exceeded */
1640
    if (end_long >= start_long + MAX_GENERATE) {
27✔
1641
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1642
        exit(1);
6✔
1643
    }
1644

1645
    /* generate */
1646
    for (; start_long <= end_long; start_long++) {
84✔
1647
        struct in_addr in_addr_tmp;
1648
        char buffer[20];
1649
        in_addr_tmp.s_addr = htonl(start_long);
63✔
1650
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
63✔
1651
        add_name(buffer);
63✔
1652
    }
21✔
1653
}
21✔
1654

1655
#ifdef IPV6
1656
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1657
{
1658
    int i;
1659
    uint64_t ret = 0;
134✔
1660
    for (i = 0; i < 8; i++) {
1,206✔
1661
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1662
    }
16✔
1663
    return ret;
134✔
1664
}
1665

1666
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1667
{
1668
    int i;
1669
    for (i = 0; i < 8; i++) {
720✔
1670
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1671
    }
1672
}
80✔
1673

1674
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1675
                         uint64_t end_upper, uint64_t end_lower,
1676
                         const char *scope_str)
1677
{
1678
    struct in6_addr in6_addr_tmp;
1679
    char buffer[100];
1680

1681
    /* prevent generating too many addresses */
1682
    if ((start_upper + 1 < end_upper) ||
34✔
1683
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1684
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1685
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1686
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1687
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1688
        exit(1);
8✔
1689
    }
1690

1691
    while ((start_upper < end_upper) ||
66✔
1692
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1693
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1694
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1695
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1696
        if (scope_str) {
40✔
1697
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1698
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1699
                exit(1);
×
1700
            }
1701
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1702
        }
1703
        add_name(buffer);
40✔
1704
        start_lower++;
40✔
1705
        if (start_lower == 0) {
40✔
1706
            start_upper++;
2✔
1707
        }
1708
    }
1709
}
26✔
1710
#endif /* IPv6 */
1711

1712
void main_loop()
311✔
1713
{
1714
    int64_t lt;
1715
    int64_t wait_time_ns;
1716
    struct event *event;
1717
    struct host_entry *h;
1718

1719
    while (event_queue_ping.first || event_queue_timeout.first) {
1,736✔
1720
        dbg_printf("%s", "# main_loop\n");
1721

1722
        /* timeout event ? */
1723
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,442✔
1724
            event = ev_dequeue(&event_queue_timeout);
95✔
1725
            h = event->host;
95✔
1726

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

1729
            stats_add(h, event->ping_index, 0, -1);
95✔
1730

1731
            if (per_recv_flag) {
95✔
1732
                if (timestamp_flag) {
19✔
1733
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1734
                }
1735
                printf("%-*s : [%d], timed out",
19✔
1736
                    max_hostname_len, h->host, event->ping_index);
5✔
1737
                if (h->num_recv > 0) {
19✔
1738
                    printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1739
                }
1740
                else {
1741
                    printf(" (NaN avg, ");
19✔
1742
                }
1743
                if (h->num_recv <= h->num_sent) {
19✔
1744
                    printf("%d%% loss)",
19✔
1745
                        ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1746
                }
5✔
1747
                else {
1748
                    printf("%d%% return)",
×
1749
                        (h->num_recv_total * 100) / h->num_sent);
×
1750
                }
1751
                printf("\n");
19✔
1752
            }
5✔
1753

1754
            /* do we need to send a retry? */
1755
            if (!loop_flag && !count_flag) {
95✔
1756
                if (h->num_sent < retry + 1) {
52✔
1757
                    if (backoff_flag) {
30✔
1758
                        h->timeout *= backoff;
30✔
1759
                    }
6✔
1760
                    send_ping(h, event->ping_index);
30✔
1761
                }
6✔
1762
            }
10✔
1763

1764
            /* note: we process first timeout events, because we might need to
1765
             * wait to process ping events, while we for sure never need to
1766
             * wait for timeout events.
1767
             */
1768
            continue;
95✔
1769
        }
1770

1771
        /* ping event ? */
1772
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,578✔
1773
            /* Make sure that we don't ping more than once every "interval" */
1774
            lt = current_time_ns - last_send_time;
1,059✔
1775
            if (lt < interval)
1,059✔
1776
                goto wait_for_reply;
252✔
1777

1778
            /* Dequeue the event */
1779
            event = ev_dequeue(&event_queue_ping);
807✔
1780
            h = event->host;
807✔
1781

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

1784
            /* Send the ping */
1785
            send_ping(h, event->ping_index);
807✔
1786

1787
            /* Loop and count mode: schedule next ping */
1788
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
807✔
1789
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
315✔
1790
            }
101✔
1791
        }
231✔
1792

1793
    wait_for_reply:
574✔
1794

1795
        /* When is the next ping next event? */
1796
        wait_time_ns = -1;
1,347✔
1797
        if (event_queue_ping.first) {
1,347✔
1798
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
976✔
1799
            if (wait_time_ns < 0)
976✔
1800
                wait_time_ns = 0;
533✔
1801
            /* make sure that we wait enough, so that the inter-ping delay is
1802
             * bigger than 'interval' */
1803
            if (wait_time_ns < interval) {
976✔
1804
                lt = current_time_ns - last_send_time;
537✔
1805
                if (lt < interval) {
537✔
1806
                    wait_time_ns = interval - lt;
537✔
1807
                }
189✔
1808
            }
189✔
1809

1810
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1811
        }
307✔
1812

1813
        /* When is the next timeout event? */
1814
        if (event_queue_timeout.first) {
1,347✔
1815
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
850✔
1816
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
850✔
1817
                wait_time_ns = wait_time_timeout;
366✔
1818
                if (wait_time_ns < 0) {
366✔
UNCOV
1819
                    wait_time_ns = 0;
×
1820
                }
1821
            }
92✔
1822

1823
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1824
        }
248✔
1825

1826
        /* When is the next report due? */
1827
        if (report_interval && (loop_flag || count_flag)) {
1,347✔
1828
            int64_t wait_time_next_report = next_report_time - current_time_ns;
206✔
1829
            if (wait_time_next_report < wait_time_ns) {
206✔
1830
                wait_time_ns = wait_time_next_report;
69✔
1831
                if (wait_time_ns < 0) {
69✔
1832
                    wait_time_ns = 0;
×
1833
                }
1834
            }
23✔
1835

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

1839
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1840
        if (wait_time_ns == -1) {
1,347✔
1841
            break;
14✔
1842
        }
1843

1844
        /* end of loop was requested by interrupt signal handler */
1845
        if (finish_requested) {
1,333✔
1846
            break;
3✔
1847
        }
1848

1849
        /* Receive replies */
1850
        /* (this is what sleeps during each loop iteration) */
1851
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1852
        if (wait_for_reply(wait_time_ns)) {
1,330✔
1853
            while (wait_for_reply(0))
1,142✔
1854
                ; /* process other replies in the queue */
1855
        }
214✔
1856

1857
        update_current_time();
1,330✔
1858

1859
        if (status_snapshot) {
1,330✔
1860
            status_snapshot = 0;
×
1861
            print_per_system_splits();
×
1862
        }
1863

1864
        /* Print report */
1865
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,330✔
1866
            if (netdata_flag)
42✔
1867
                print_netdata();
3✔
1868
            else
1869
                print_per_system_splits();
39✔
1870

1871
            while (current_time_ns >= next_report_time) {
84✔
1872
                next_report_time += report_interval;
42✔
1873
            }
1874
        }
14✔
1875
    }
1876
}
311✔
1877

1878
/************************************************************
1879

1880
  Function: signal_handler
1881

1882
*************************************************************
1883

1884
  Inputs:  int signum
1885

1886
  Description:
1887

1888
  SIGQUIT signal handler - set flag and return
1889
  SIGINT signal handler - set flag and return
1890

1891
************************************************************/
1892

1893
void signal_handler(int signum)
×
1894
{
1895
    switch (signum) {
×
1896
    case SIGINT:
1897
        finish_requested = 1;
×
1898
        break;
×
1899

1900
    case SIGQUIT:
1901
        status_snapshot = 1;
×
1902
        break;
×
1903
    }
1904
}
×
1905

1906
/************************************************************
1907

1908
  Function: update_current_time
1909

1910
*************************************************************/
1911

1912
void update_current_time()
4,102✔
1913
{
1914
    clock_gettime(CLOCKID, &current_time);
4,102✔
1915
    current_time_ns = timespec_ns(&current_time);
4,102✔
1916
}
4,102✔
1917

1918
/************************************************************
1919

1920
  Function: finish
1921

1922
*************************************************************
1923

1924
  Inputs:  void (none)
1925

1926
  Description:
1927

1928
  Main program clean up and exit point
1929

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

1932
void finish()
311✔
1933
{
1934
    int i;
1935
    HOST_ENTRY *h;
1936

1937
    update_current_time();
311✔
1938
    end_time = current_time_ns;
311✔
1939

1940
    /* tot up unreachables */
1941
    for (i = 0; i < num_hosts; i++) {
818✔
1942
        h = table[i];
507✔
1943

1944
        if (!h->num_recv) {
507✔
1945
            num_unreachable++;
80✔
1946

1947
            if (verbose_flag || unreachable_flag) {
80✔
1948
                printf("%s", h->host);
44✔
1949

1950
                if (verbose_flag)
44✔
1951
                    printf(" is unreachable");
41✔
1952

1953
                printf("\n");
44✔
1954
            }
2✔
1955
        }
12✔
1956
    }
135✔
1957

1958
    if (count_flag || loop_flag)
311✔
1959
        print_per_system_stats();
100✔
1960
#if defined(DEBUG) || defined(_DEBUG)
1961
    else if (print_per_system_flag)
1962
        print_per_system_stats();
1963
#endif /* DEBUG || _DEBUG */
1964

1965
    if (stats_flag)
311✔
1966
        print_global_stats();
9✔
1967

1968
    if (min_reachable) {
311✔
1969
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
1970
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1971
            exit(0);
6✔
1972
        }
1973
        else {
1974
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
1975
            exit(1);
3✔
1976
        }
1977
    }
1978

1979
    if (num_noaddress)
302✔
1980
        exit(2);
3✔
1981
    else if (num_alive != num_hosts)
299✔
1982
        exit(1);
41✔
1983

1984
    exit(0);
258✔
1985
}
1986

1987
/************************************************************
1988

1989
  Function: print_per_system_stats
1990

1991
*************************************************************
1992

1993
  Inputs:  void (none)
1994

1995
  Description:
1996

1997

1998
************************************************************/
1999

2000
void print_per_system_stats(void)
100✔
2001
{
2002
    int i, j, avg, outage_ms;
2003
    HOST_ENTRY *h;
2004
    int64_t resp;
2005

2006
    if (verbose_flag || per_recv_flag)
100✔
2007
        fprintf(stderr, "\n");
59✔
2008

2009
    for (i = 0; i < num_hosts; i++) {
234✔
2010
        h = table[i];
134✔
2011
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
2012

2013
        if (report_all_rtts_flag) {
134✔
2014
            for (j = 0; j < h->num_sent; j++) {
248✔
2015
                if ((resp = h->resp_times[j]) >= 0)
204✔
2016
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2017
                else
2018
                    fprintf(stderr, " -");
8✔
2019
            }
64✔
2020

2021
            fprintf(stderr, "\n");
44✔
2022
        }
12✔
2023
        else {
2024
            if (h->num_recv <= h->num_sent) {
90✔
2025
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
2026
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
2027

2028
                if (outage_flag) {
90✔
2029
                    /* Time outage total */
2030
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
2031
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
2032
                }
3✔
2033
            }
26✔
2034
            else {
2035
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2036
                    h->num_sent, h->num_recv,
2037
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2038
            }
2039

2040
            if (h->num_recv) {
90✔
2041
                avg = h->total_time / h->num_recv;
79✔
2042
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
2043
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
2044
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
2045
            }
23✔
2046

2047
            fprintf(stderr, "\n");
90✔
2048
        }
2049
    }
38✔
2050
}
100✔
2051

2052
/************************************************************
2053

2054
  Function: print_netdata
2055

2056
*************************************************************
2057

2058
  Inputs:  void (none)
2059

2060
  Description:
2061

2062

2063
************************************************************/
2064

2065
void print_netdata(void)
3✔
2066
{
2067
    static int sent_charts = 0;
2068

2069
    int i;
2070
    int64_t avg;
2071
    HOST_ENTRY *h;
2072

2073
    for (i = 0; i < num_hosts; i++) {
6✔
2074
        h = table[i];
3✔
2075

2076
        if (!sent_charts) {
3✔
2077
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2078
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2079
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2080
        }
1✔
2081

2082
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2083
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2084
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2085
        printf("END\n");
3✔
2086

2087
        if (!sent_charts) {
3✔
2088
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2089
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2090
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2091
        }
1✔
2092

2093
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2094
        /*
2095
        if( h->num_recv_i <= h->num_sent_i )
2096
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2097
        else
2098
            printf("SET lost = 0\n");
2099
*/
2100

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

2104
        if (!sent_charts) {
3✔
2105
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2106
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2107
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2108
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2109
        }
1✔
2110

2111
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2112
        if (h->num_recv_i) {
3✔
2113
            avg = h->total_time_i / h->num_recv_i;
3✔
2114
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2115
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2116
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2117
        }
1✔
2118
        printf("END\n");
3✔
2119

2120
        stats_reset_interval(h);
3✔
2121
    }
1✔
2122

2123
    sent_charts = 1;
3✔
2124
}
3✔
2125

2126
/************************************************************
2127

2128
  Function: print_per_system_splits
2129

2130
*************************************************************
2131

2132
  Inputs:  void (none)
2133

2134
  Description:
2135

2136

2137
************************************************************/
2138

2139
void print_per_system_splits(void)
39✔
2140
{
2141
    int i, avg, outage_ms_i;
2142
    HOST_ENTRY *h;
2143
    struct tm *curr_tm;
2144

2145
    if (verbose_flag || per_recv_flag)
39✔
2146
        fprintf(stderr, "\n");
×
2147

2148
    update_current_time();
39✔
2149
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2150
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2151
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2152

2153
    for (i = 0; i < num_hosts; i++) {
78✔
2154
        h = table[i];
39✔
2155
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2156

2157
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2158
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2159
                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✔
2160

2161
            if (outage_flag) {
39✔
2162
                /* Time outage  */
2163
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2164
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2165
            }
4✔
2166
        }
13✔
2167
        else {
2168
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2169
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2170
        }
2171

2172
        if (h->num_recv_i) {
39✔
2173
            avg = h->total_time_i / h->num_recv_i;
27✔
2174
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2175
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2176
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2177
        }
9✔
2178

2179
        fprintf(stderr, "\n");
39✔
2180
        if (!cumulative_stats_flag) {
39✔
2181
            stats_reset_interval(h);
27✔
2182
        }
9✔
2183
    }
13✔
2184
}
39✔
2185

2186
/************************************************************
2187

2188
  Function: print_global_stats
2189

2190
*************************************************************
2191

2192
  Inputs:  void (none)
2193

2194
  Description:
2195

2196

2197
************************************************************/
2198

2199
void print_global_stats(void)
9✔
2200
{
2201
    fprintf(stderr, "\n");
9✔
2202
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2203
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2204
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2205
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2206
    fprintf(stderr, "\n");
9✔
2207
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2208
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2209
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2210
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2211
    fprintf(stderr, "\n");
9✔
2212

2213
    if (total_replies == 0) {
9✔
2214
        min_reply = 0;
3✔
2215
        max_reply = 0;
3✔
2216
        total_replies = 1;
3✔
2217
        sum_replies = 0;
3✔
2218
    }
1✔
2219

2220
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2221
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2222
        sprint_tm(sum_replies / total_replies));
3✔
2223
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2224
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2225
        (end_time - start_time) / 1e9);
9✔
2226
    fprintf(stderr, "\n");
9✔
2227
}
9✔
2228

2229
/************************************************************
2230

2231
  Function: send_ping
2232

2233
*************************************************************
2234

2235
  Inputs:  int s, HOST_ENTRY *h
2236

2237
  Description:
2238

2239
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2240
  will be added on by the kernel.  The ID field is our UNIX process ID,
2241
  and the sequence number is an index into an array of outstanding
2242
  ping requests. The sequence number will later be used to quickly
2243
  figure out who the ping reply came from.
2244

2245
************************************************************/
2246

2247
int send_ping(HOST_ENTRY *h, int index)
837✔
2248
{
2249
    int n;
2250
    int myseq;
2251
    int ret = 1;
837✔
2252
    uint8_t proto = ICMP_ECHO;
837✔
2253

2254
    update_current_time();
837✔
2255
    h->last_send_time = current_time_ns;
837✔
2256
    myseq = seqmap_add(h->i, index, current_time_ns);
837✔
2257

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

2260
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
837✔
2261
        if(icmp_request_typ == 13)
714✔
2262
            proto = ICMP_TSTAMP;
16✔
2263
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
714✔
2264
    }
222✔
2265
#ifdef IPV6
2266
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2267
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2268
    }
15✔
2269
#endif
2270
    else {
2271
        return 0;
×
2272
    }
2273

2274
    /* error sending? */
2275
    if (
600✔
2276
        (n < 0)
237✔
2277
#if defined(EHOSTDOWN)
2278
        && errno != EHOSTDOWN
271✔
2279
#endif
2280
    ) {
2281
        if (verbose_flag) {
34✔
2282
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2283
        }
2284
        else {
2285
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2286
        }
2287

2288
        h->num_sent++;
34✔
2289
        h->num_sent_i++;
34✔
2290
        if (!loop_flag)
34✔
2291
            h->resp_times[index] = RESP_ERROR;
34✔
2292

2293
        ret = 0;
34✔
2294
    }
2295
    else {
2296
        /* schedule timeout */
2297
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
803✔
2298

2299
        /* mark this trial as outstanding */
2300
        if (!loop_flag) {
803✔
2301
            h->resp_times[index] = RESP_WAITING;
803✔
2302
        }
237✔
2303
    }
2304

2305
    num_pingsent++;
837✔
2306
    last_send_time = h->last_send_time;
837✔
2307

2308
    return (ret);
837✔
2309
}
237✔
2310

2311
int socket_can_read(struct timeval *timeout)
2,472✔
2312
{
2313
    int nfound;
2314
    fd_set readset;
2315
    int socketmax;
2316

2317
#ifndef IPV6
2318
    socketmax = socket4;
2319
#else
2320
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,472✔
2321
#endif
2322

2323
select_again:
1,862✔
2324
    FD_ZERO(&readset);
32,264✔
2325
    if (socket4 >= 0)
2,472✔
2326
        FD_SET(socket4, &readset);
2,472✔
2327
#ifdef IPV6
2328
    if (socket6 >= 0)
2,472✔
2329
        FD_SET(socket6, &readset);
2,472✔
2330
#endif
2331

2332
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,472✔
2333
    if (nfound < 0) {
2,472✔
2334
        if (errno == EINTR) {
×
2335
            /* interrupted system call: redo the select */
2336
            goto select_again;
×
2337
        }
2338
        else {
2339
            perror("select");
×
2340
        }
2341
    }
2342

2343
    if (nfound > 0) {
2,472✔
2344
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,142✔
2345
            return socket4;
1,051✔
2346
        }
2347
#ifdef IPV6
2348
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
91✔
2349
            return socket6;
91✔
2350
        }
2351
#endif
2352
    }
2353

2354
    return -1;
1,330✔
2355
}
610✔
2356

2357
int receive_packet(int64_t wait_time,
2,472✔
2358
#if HAVE_SO_TIMESTAMPNS
2359
    int64_t *reply_timestamp,
2360
#else
2361
    int64_t *reply_timestamp __attribute__((unused)),
2362
#endif
2363
    struct sockaddr *reply_src_addr,
2364
    size_t reply_src_addr_len,
2365
    char *reply_buf,
2366
    size_t reply_buf_len,
2367
    int *ip_header_tos,
2368
    int *ip_header_ttl)
2369
{
2370
    struct timeval to;
2371
    int s = 0;
2,472✔
2372
    int recv_len;
2373
    static unsigned char msg_control[128];
2374
    struct iovec msg_iov = {
3,692✔
2375
        reply_buf,
610✔
2376
        reply_buf_len
610✔
2377
    };
2378
    struct msghdr recv_msghdr = {0};
2,472✔
2379
    recv_msghdr.msg_name = reply_src_addr;
2,472✔
2380
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,472✔
2381
    recv_msghdr.msg_iov = &msg_iov;
2,472✔
2382
    recv_msghdr.msg_iovlen = 1;
2,472✔
2383
    recv_msghdr.msg_control = &msg_control;
2,472✔
2384
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,472✔
2385
    struct cmsghdr *cmsg;
2386

2387
    /* Wait for a socket to become ready */
2388
    if (wait_time) {
2,472✔
2389
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,330✔
2390
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,330✔
2391
    }
396✔
2392
    else {
2393
        to.tv_sec = 0;
1,142✔
2394
        to.tv_usec = 0;
1,142✔
2395
    }
2396
    s = socket_can_read(&to);
2,472✔
2397
    if (s == -1) {
2,472✔
2398
        return 0; /* timeout */
1,330✔
2399
    }
2400

2401
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,142✔
2402
    if (recv_len <= 0) {
1,142✔
2403
        return 0;
×
2404
    }
2405

2406
    /* ancilliary data */
2407
    {
2408
#if HAVE_SO_TIMESTAMPNS
2409
        struct timespec reply_timestamp_ts;
2410
        struct timeval reply_timestamp_tv;
2411
#endif
2412
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,142✔
2413
             cmsg != NULL;
2,078✔
2414
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
936✔
2415
#if HAVE_SO_TIMESTAMPNS
2416
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
936✔
2417
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
928✔
2418
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
928✔
2419
            }
2420
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
936✔
2421
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2422
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2423
            }
2424
#endif
2425
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
936✔
2426
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2427
            }
2428
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
936✔
2429
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2430
            }
2431
#ifdef IPV6
2432
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
936✔
2433
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2434
            }
2435
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
936✔
2436
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2437
            }
2438
#endif
2439
        }
2440
    }
2441

2442
#if defined(DEBUG) || defined(_DEBUG)
2443
    if (randomly_lose_flag) {
2444
        if ((random() & 0x07) <= lose_factor)
2445
            return 0;
2446
    }
2447
#endif
2448

2449
    return recv_len;
1,142✔
2450
}
610✔
2451

2452
/* stats_add: update host statistics for a single packet that was received (or timed out)
2453
 * h: host entry to update
2454
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2455
 * success: 1 if response received, 0 otherwise
2456
 * latency: response time, in ns
2457
 */
2458
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
803✔
2459
{
2460
    /* sent count - we update only on receive/timeout, so that we don't get
2461
     * weird loss percentage, just because a packet was note recived yet.
2462
     */
2463
    h->num_sent++;
803✔
2464
    h->num_sent_i++;
803✔
2465

2466
    if (!success) {
803✔
2467
        if (!loop_flag && index >= 0) {
95✔
2468
            h->resp_times[index] = RESP_TIMEOUT;
95✔
2469
        }
23✔
2470
        num_timeout++;
95✔
2471
        return;
95✔
2472
    }
2473

2474
    /* received count */
2475
    h->num_recv++;
708✔
2476
    h->num_recv_i++;
708✔
2477

2478
    /* maximum */
2479
    if (!h->max_reply || latency > h->max_reply) {
708✔
2480
        h->max_reply = latency;
559✔
2481
    }
167✔
2482
    if (!h->max_reply_i || latency > h->max_reply_i) {
708✔
2483
        h->max_reply_i = latency;
574✔
2484
    }
171✔
2485

2486
    /* minimum */
2487
    if (!h->min_reply || latency < h->min_reply) {
708✔
2488
        h->min_reply = latency;
471✔
2489
    }
138✔
2490
    if (!h->min_reply_i || latency < h->min_reply_i) {
708✔
2491
        h->min_reply_i = latency;
495✔
2492
    }
145✔
2493

2494
    /* total time (for average) */
2495
    h->total_time += latency;
708✔
2496
    h->total_time_i += latency;
708✔
2497

2498
    /* response time per-packet (count mode) */
2499
    if (!loop_flag && index >= 0) {
708✔
2500
        h->resp_times[index] = latency;
708✔
2501
    }
214✔
2502
}
237✔
2503

2504
/* stats_reset_interval: reset interval statistics
2505
 * h: host entry to update
2506
 */
2507
void stats_reset_interval(HOST_ENTRY *h)
30✔
2508
{
2509
    h->num_sent_i = 0;
30✔
2510
    h->num_recv_i = 0;
30✔
2511
    h->max_reply_i = 0;
30✔
2512
    h->min_reply_i = 0;
30✔
2513
    h->total_time_i = 0;
30✔
2514
}
30✔
2515

2516
int decode_icmp_ipv4(
1,051✔
2517
    struct sockaddr *response_addr,
2518
    size_t response_addr_len,
2519
    char *reply_buf,
2520
    size_t reply_buf_len,
2521
    unsigned short *id,
2522
    unsigned short *seq,
2523
    int *ip_header_tos,
2524
    int *ip_header_ttl,
2525
    uint32_t *ip_header_otime_ms,
2526
    uint32_t *ip_header_rtime_ms,
2527
    uint32_t *ip_header_ttime_ms)
2528
{
2529
    struct icmp *icp;
2530
    int hlen = 0;
1,051✔
2531

2532
    if (!using_sock_dgram4) {
1,051✔
2533
        struct ip *ip = (struct ip *)reply_buf;
1,043✔
2534
        *ip_header_tos = ip->ip_tos;
1,043✔
2535
        *ip_header_ttl = ip->ip_ttl;
1,043✔
2536

2537
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2538
        /* The alpha headers are decidedly broken.
2539
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2540
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2541
         */
2542
        hlen = (ip->ip_vhl & 0x0F) << 2;
2543
#else
2544
        hlen = ip->ip_hl << 2;
1,043✔
2545
#endif
2546
    }
199✔
2547

2548
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,051✔
2549
        /* too short */
2550
        if (verbose_flag) {
×
2551
            char buf[INET6_ADDRSTRLEN];
2552
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2553
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2554
        }
2555
        return -1;
×
2556
    }
2557

2558
    icp = (struct icmp *)(reply_buf + hlen);
1,051✔
2559

2560
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,051✔
2561
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
444✔
2562
        /* Handle other ICMP packets */
2563
        struct icmp *sent_icmp;
2564
        SEQMAP_VALUE *seqmap_value;
2565
        char addr_ascii[INET6_ADDRSTRLEN];
2566
        HOST_ENTRY *h;
2567

2568
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2569
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
820✔
2570
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2571
            return -1;
×
2572
        }
2573

2574
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
422✔
2575

2576
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
422✔
2577
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
14✔
2578
            sent_icmp->icmp_id != ident4) {
×
2579
            /* not caused by us */
2580
            return -1;
422✔
2581
        }
2582

2583
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2584
        if (seqmap_value == NULL) {
×
2585
            return -1;
×
2586
        }
2587

2588
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2589

2590
        switch (icp->icmp_type) {
×
2591
        case ICMP_UNREACH:
2592
            h = table[seqmap_value->host_nr];
×
2593
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2594
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2595
                    addr_ascii, h->host);
2596
            }
2597
            else {
2598
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2599
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2600
            }
2601

2602
            print_warning("\n");
×
2603
            num_othericmprcvd++;
×
2604
            break;
×
2605

2606
        case ICMP_SOURCEQUENCH:
2607
        case ICMP_REDIRECT:
2608
        case ICMP_TIMXCEED:
2609
        case ICMP_PARAMPROB:
2610
            h = table[seqmap_value->host_nr];
×
2611
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2612
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2613
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2614
            }
2615
            else {
2616
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2617
                    icp->icmp_type, addr_ascii, h->host);
×
2618
            }
2619
            print_warning("\n");
×
2620
            num_othericmprcvd++;
×
2621
            break;
×
2622
        }
2623

2624
        return -1;
×
2625
    }
2626

2627
    *id = icp->icmp_id;
629✔
2628
    *seq = ntohs(icp->icmp_seq);
629✔
2629
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
629✔
2630

2631
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2632
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
14✔
2633
            if (verbose_flag) {
×
2634
                char buf[INET6_ADDRSTRLEN];
2635
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2636
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2637
            }
2638
            return -1;
×
2639
        }
2640

2641
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
14✔
2642
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
14✔
2643
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
14✔
2644
    }
2645

2646
    return hlen;
629✔
2647
}
199✔
2648

2649
#ifdef IPV6
2650
int decode_icmp_ipv6(
91✔
2651
    struct sockaddr *response_addr,
2652
    size_t response_addr_len,
2653
    char *reply_buf,
2654
    size_t reply_buf_len,
2655
    unsigned short *id,
2656
    unsigned short *seq)
2657
{
2658
    struct icmp6_hdr *icp;
2659

2660
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
91✔
2661
        if (verbose_flag) {
×
2662
            char buf[INET6_ADDRSTRLEN];
2663
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
NEW
2664
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2665
        }
2666
        return 0; /* too short */
×
2667
    }
2668

2669
    icp = (struct icmp6_hdr *)reply_buf;
91✔
2670

2671
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
91✔
2672
        /* Handle other ICMPv6 packets */
2673
        struct ip6_hdr *sent_ipv6;
2674
        struct icmp6_hdr *sent_icmp;
2675
        SEQMAP_VALUE *seqmap_value;
2676
        char addr_ascii[INET6_ADDRSTRLEN];
2677
        HOST_ENTRY *h;
2678

2679
        /* reply icmp packet (ICMPv6 header) followed by "sent packet" (IPv6 + ICMPv6 header) */
NEW
2680
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
×
2681
            /* discard ICMPv6 message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
UNCOV
2682
            return 0;
×
2683
        }
2684

NEW
2685
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
×
NEW
2686
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
×
2687
            /* discard ICMPv6 message if we can't tell that it was caused by
2688
             * us, because the IPv6 header is not directly followed by an
2689
             * ICMPv6 header
2690
             */
2691
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
NEW
2692
            return 0;
×
2693
        }
NEW
2694
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
×
2695

NEW
2696
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
×
2697
            /* not caused by us */
UNCOV
2698
            return 0;
×
2699
        }
2700

2701
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2702
        if (seqmap_value == NULL) {
×
2703
            return 0;
×
2704
        }
2705

2706
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
NEW
2707
        h = table[seqmap_value->host_nr];
×
2708

2709
        switch (icp->icmp6_type) {
×
2710
        case ICMP6_DST_UNREACH:
NEW
2711
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
NEW
2712
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2713
                    icp->icmp6_code, addr_ascii, h->host);
×
2714
            } else {
NEW
2715
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2716
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2717
            }
UNCOV
2718
            print_warning("\n");
×
2719
            num_othericmprcvd++;
×
2720
            break;
×
2721

2722
        case ICMP6_PACKET_TOO_BIG:
NEW
2723
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
2724
                addr_ascii, h->host);
NEW
2725
            num_othericmprcvd++;
×
NEW
2726
            break;
×
2727

2728
        case ICMP6_TIME_EXCEEDED:
NEW
2729
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
NEW
2730
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2731
                    icp->icmp6_code, addr_ascii, h->host);
×
2732
            } else {
NEW
2733
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2734
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
2735
            }
NEW
2736
            print_warning("\n");
×
NEW
2737
            num_othericmprcvd++;
×
NEW
2738
            break;
×
2739

2740
        case ICMP6_PARAM_PROB:
NEW
2741
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
NEW
2742
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2743
                    icp->icmp6_code, addr_ascii, h->host);
×
2744
            } else {
NEW
2745
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2746
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
2747
            }
2748
            print_warning("\n");
×
2749
            num_othericmprcvd++;
×
2750
            break;
×
2751

2752
        default:
NEW
2753
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
NEW
2754
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
NEW
2755
            num_othericmprcvd++;
×
NEW
2756
            break;
×
2757
        }
2758

2759
        return 0;
×
2760
    }
2761

2762
    *id = icp->icmp6_id;
91✔
2763
    *seq = ntohs(icp->icmp6_seq);
91✔
2764

2765
    return 1;
91✔
2766
}
15✔
2767
#endif
2768

2769
int wait_for_reply(int64_t wait_time)
2,472✔
2770
{
2771
    int result;
2772
    static char buffer[RECV_BUFSIZE];
2773
    struct sockaddr_storage response_addr;
2774
    int n, avg;
2775
    HOST_ENTRY *h;
2776
    int64_t this_reply;
2777
    int this_count;
2778
    int64_t recv_time = 0;
2,472✔
2779
    SEQMAP_VALUE *seqmap_value;
2780
    unsigned short id;
2781
    unsigned short seq;
2782
    int ip_header_tos = -1;
2,472✔
2783
    int ip_header_ttl = -1;
2,472✔
2784
    // ICMP Timestamp
2785
    uint32_t ip_header_otime_ms = 0x80000000U;
2,472✔
2786
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,472✔
2787
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,472✔
2788

2789
    /* Receive packet */
2790
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,472✔
2791
        &recv_time, /* reply_timestamp */
2792
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2793
        sizeof(response_addr), /* reply_src_addr_len */
2794
        buffer, /* reply_buf */
2795
        sizeof(buffer), /* reply_buf_len */
2796
        &ip_header_tos, /* TOS resp. TC byte */
2797
        &ip_header_ttl /* TTL resp. hop limit */
2798
    );
2799

2800
    if (result <= 0) {
2,472✔
2801
        return 0;
1,330✔
2802
    }
2803

2804
    update_current_time();
1,142✔
2805
    if (recv_time == 0)
1,142✔
2806
        recv_time = current_time_ns;
214✔
2807

2808
    /* Process ICMP packet and retrieve id/seq */
2809
    if (response_addr.ss_family == AF_INET) {
1,142✔
2810
        int ip_hlen = decode_icmp_ipv4(
1,051✔
2811
            (struct sockaddr *)&response_addr,
2812
            sizeof(response_addr),
2813
            buffer,
2814
            sizeof(buffer),
2815
            &id,
2816
            &seq,
2817
            &ip_header_tos,
2818
            &ip_header_ttl,
2819
            &ip_header_otime_ms,
2820
            &ip_header_rtime_ms,
2821
            &ip_header_ttime_ms);
2822
        if (ip_hlen < 0) {
1,051✔
2823
            return 1;
422✔
2824
        }
2825
        if (id != ident4) {
629✔
2826
            return 1; /* packet received, but not the one we are looking for! */
×
2827
        }
2828
        if (!using_sock_dgram4) {
629✔
2829
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2830
             * with fping with IPv6 hosts */
2831
            result -= ip_hlen;
621✔
2832
        }
199✔
2833
    }
199✔
2834
#ifdef IPV6
2835
    else if (response_addr.ss_family == AF_INET6) {
91✔
2836
        if (!decode_icmp_ipv6(
91✔
2837
                (struct sockaddr *)&response_addr,
2838
                sizeof(response_addr),
2839
                buffer,
2840
                sizeof(buffer),
2841
                &id,
2842
                &seq)) {
UNCOV
2843
            return 1;
×
2844
        }
2845
        if (id != ident6) {
91✔
2846
            return 1; /* packet received, but not the one we are looking for! */
×
2847
        }
2848
    }
15✔
2849
#endif
2850
    else {
2851
        return 1;
×
2852
    }
2853

2854
    seqmap_value = seqmap_fetch(seq, current_time_ns);
720✔
2855
    if (seqmap_value == NULL) {
720✔
2856
        return 1;
×
2857
    }
2858

2859
    /* find corresponding host_entry */
2860
    n = seqmap_value->host_nr;
720✔
2861
    h = table[n];
720✔
2862
    this_count = seqmap_value->ping_count;
720✔
2863
    this_reply = recv_time - seqmap_value->ping_ts;
720✔
2864

2865
    /* update stats that include invalid replies */
2866
    h->num_recv_total++;
720✔
2867
    num_pingreceived++;
720✔
2868

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

2871
    /* optionally require reply source equal to target address */
2872
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
720✔
2873
        dbg_printf("%s\n", "discarding reply from wrong source address");
2874
        return 1;
12✔
2875
    }
2876

2877
    /* discard duplicates */
2878
    if (!loop_flag && h->resp_times[this_count] >= 0) {
708✔
2879
        if (!per_recv_flag) {
×
2880
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2881
                h->host, this_count, result, sprint_tm(this_reply));
2882

2883
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2884
                char buf[INET6_ADDRSTRLEN];
2885
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2886
                fprintf(stderr, " [<- %s]", buf);
×
2887
            }
2888
            fprintf(stderr, "\n");
×
2889
        }
2890
        return 1;
×
2891
    }
2892

2893
    /* discard reply if delay is larger than timeout
2894
     * (see also: github #32) */
2895
    if (this_reply > h->timeout) {
708✔
2896
        return 1;
×
2897
    }
2898

2899
    /* update stats */
2900
    stats_add(h, this_count, 1, this_reply);
708✔
2901
    // TODO: move to stats_add?
2902
    if (!max_reply || this_reply > max_reply)
708✔
2903
        max_reply = this_reply;
428✔
2904
    if (!min_reply || this_reply < min_reply)
708✔
2905
        min_reply = this_reply;
388✔
2906
    sum_replies += this_reply;
708✔
2907
    total_replies++;
708✔
2908

2909
    /* initialize timeout to initial timeout (without backoff) */
2910
    h->timeout = timeout;
708✔
2911

2912
    /* remove timeout event */
2913
    struct event *timeout_event = host_get_timeout_event(h, this_count);
708✔
2914
    if (timeout_event) {
708✔
2915
        ev_remove(&event_queue_timeout, timeout_event);
708✔
2916
    }
214✔
2917

2918
    /* print "is alive" */
2919
    if (h->num_recv == 1) {
708✔
2920
        num_alive++;
427✔
2921
        if (fast_reachable && num_alive >= min_reachable)
427✔
2922
                finish_requested = 1;
3✔
2923

2924
        if (verbose_flag || alive_flag) {
427✔
2925
            printf("%s", h->host);
296✔
2926

2927
            if (verbose_flag)
296✔
2928
                printf(" is alive");
276✔
2929
        }
84✔
2930
    }
123✔
2931

2932
    /* print received ping (unless --quiet) */
2933
    if (per_recv_flag) {
708✔
2934
        if (timestamp_flag) {
151✔
2935
            print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2936
        }
10✔
2937
        avg = h->total_time / h->num_recv;
151✔
2938
        printf("%-*s : [%d], %d bytes, %s ms",
151✔
2939
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2940
        printf(" (%s avg, ", sprint_tm(avg));
151✔
2941

2942
        if (h->num_recv <= h->num_sent) {
151✔
2943
            printf("%d%% loss)",
151✔
2944
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2945
        }
45✔
2946
        else {
2947
            printf("%d%% return)",
×
2948
                (h->num_recv_total * 100) / h->num_sent);
×
2949
        }
2950
    }
45✔
2951

2952
    if (verbose_flag || alive_flag || per_recv_flag) {
708✔
2953

2954
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
447✔
2955
            char buf[INET6_ADDRSTRLEN];
2956
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2957
            fprintf(stderr, " [<- %s]", buf);
6✔
2958
        }
2959

2960
        if (icmp_request_typ == 13) {
447✔
2961
            printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2962
                   alive_flag ? "" : ",",
14✔
2963
                   ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
2964
                   ms_since_midnight_utc(recv_time));
2965
        }
2966

2967
        if(print_tos_flag) {
447✔
2968
            if(ip_header_tos != -1) {
43✔
2969
                printf(" (TOS %d)", ip_header_tos);
43✔
2970
            }
11✔
2971
            else {
2972
                printf(" (TOS unknown)");
×
2973
            }
2974
        }
11✔
2975

2976
        if (print_ttl_flag) {
447✔
2977
          if(ip_header_ttl != -1) {
43✔
2978
              printf(" (TTL %d)", ip_header_ttl);
43✔
2979
          }
11✔
2980
          else {
2981
              printf(" (TTL unknown)");
×
2982
          }
2983
        }
11✔
2984

2985
        if (elapsed_flag && !per_recv_flag)
447✔
2986
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2987

2988
        printf("\n");
447✔
2989
    }
129✔
2990

2991
    return 1;
708✔
2992
}
610✔
2993

2994
/************************************************************
2995

2996
  Function: add_name
2997

2998
*************************************************************
2999

3000
  Inputs:  char* name
3001

3002
  Description:
3003

3004
  process input name for addition to target list
3005
  name can turn into multiple targets via multiple interfaces (-m)
3006
  or via NIS groups
3007

3008
************************************************************/
3009

3010
void add_name(char *name)
538✔
3011
{
3012
    struct addrinfo *res0, *res, hints;
3013
    int ret_ga;
3014
    char *printname;
3015
    char namebuf[256];
3016
    char addrbuf[256];
3017

3018
    /* getaddrinfo */
3019
    memset(&hints, 0, sizeof(struct addrinfo));
538✔
3020
    hints.ai_flags = AI_UNUSABLE;
538✔
3021
    hints.ai_socktype = SOCK_RAW;
538✔
3022
    hints.ai_family = hints_ai_family;
538✔
3023
    if (hints_ai_family == AF_INET) {
538✔
3024
        hints.ai_protocol = IPPROTO_ICMP;
79✔
3025
    }
21✔
3026
#ifdef IPV6
3027
    else if (hints_ai_family == AF_INET6) {
459✔
3028
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
3029
    }
1✔
3030
#endif
3031
    else {
3032
        hints.ai_socktype = SOCK_STREAM;
428✔
3033
        hints.ai_protocol = 0;
428✔
3034
    }
3035
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
538✔
3036
    if (ret_ga) {
538✔
3037
        if (!quiet_flag)
26✔
3038
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3039
        num_noaddress++;
26✔
3040
        return;
26✔
3041
    }
3042

3043
    /* NOTE: we could/should loop with res on all addresses like this:
3044
     * for (res = res0; res; res = res->ai_next) {
3045
     * We don't do it yet, however, because is is an incompatible change
3046
     * (need to implement a separate option for this)
3047
     */
3048
    for (res = res0; res; res = res->ai_next) {
512✔
3049
        /* name_flag: addr -> name lookup requested) */
3050
        if (name_flag || rdns_flag) {
514✔
3051
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3052
            if (name_flag) {
3✔
3053
                /* Was it a numerical address? Only then do a rdns-query */
3054
                struct addrinfo *nres;
3055
                hints.ai_flags = AI_NUMERICHOST;
×
3056
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3057
                    do_rdns = 1;
×
3058
                    freeaddrinfo(nres);
×
3059
                }
3060
            }
3061

3062
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3063
                printname = namebuf;
3✔
3064
            }
1✔
3065
            else {
3066
                printname = name;
×
3067
            }
3068
        }
1✔
3069
        else {
3070
            printname = name;
509✔
3071
        }
3072

3073
        /* addr_flag: name -> addr lookup requested */
3074
        if (addr_flag) {
512✔
3075
            int ret;
3076
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3077
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3078
            if (ret) {
3✔
3079
                if (!quiet_flag) {
×
3080
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3081
                }
3082
                continue;
×
3083
            }
3084

3085
            if (name_flag || rdns_flag) {
3✔
3086
                char nameaddrbuf[512 + 3];
3087
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3088
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3089
            }
3090
            else {
3091
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3092
            }
3093
        }
1✔
3094
        else {
3095
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
509✔
3096
        }
3097

3098
        if (!multif_flag) {
512✔
3099
            break;
512✔
3100
        }
3101
    }
3102

3103
    freeaddrinfo(res0);
512✔
3104
}
144✔
3105

3106
/************************************************************
3107

3108
  Function: add_addr
3109

3110
*************************************************************
3111

3112
  Description:
3113

3114
  add single address to list of hosts to be pinged
3115

3116
************************************************************/
3117

3118
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
512✔
3119
{
3120
    HOST_ENTRY *p;
3121
    int n;
3122
    int64_t *i;
3123

3124
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
512✔
3125
    if (!p)
512✔
3126
        crash_and_burn("can't allocate HOST_ENTRY");
×
3127

3128
    p->name = strdup(name);
512✔
3129
    p->host = strdup(host);
512✔
3130
    memcpy(&p->saddr, ipaddr, ipaddr_len);
512✔
3131
    p->saddr_len = ipaddr_len;
512✔
3132
    p->timeout = timeout;
512✔
3133
    p->min_reply = 0;
512✔
3134

3135
    if (netdata_flag) {
512✔
3136
        char *s = p->name;
3✔
3137
        while (*s) {
30✔
3138
            if (!isalnum(*s))
27✔
3139
                *s = '_';
9✔
3140
            s++;
27✔
3141
        }
3142
    }
1✔
3143

3144
    if (strlen(p->host) > max_hostname_len)
512✔
3145
        max_hostname_len = strlen(p->host);
325✔
3146

3147
    /* array for response time results */
3148
    if (!loop_flag) {
512✔
3149
        i = (int64_t *)malloc(trials * sizeof(int64_t));
512✔
3150
        if (!i)
512✔
3151
            crash_and_burn("can't allocate resp_times array");
×
3152

3153
        for (n = 1; n < trials; n++)
2,039✔
3154
            i[n] = RESP_UNUSED;
1,527✔
3155

3156
        p->resp_times = i;
512✔
3157
    }
136✔
3158

3159
    /* allocate event storage */
3160
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
512✔
3161
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
512✔
3162

3163
    /* schedule first ping */
3164
    host_add_ping_event(p, 0, current_time_ns);
512✔
3165

3166
    num_hosts++;
512✔
3167
}
512✔
3168

3169
/************************************************************
3170

3171
  Function: crash_and_burn
3172

3173
*************************************************************
3174

3175
  Inputs:  char* message
3176

3177
  Description:
3178

3179
************************************************************/
3180

3181
void crash_and_burn(char *message)
×
3182
{
3183
    fprintf(stderr, "%s: %s\n", prog, message);
×
3184
    exit(4);
×
3185
}
3186

3187
/************************************************************
3188

3189
  Function: errno_crash_and_burn
3190

3191
*************************************************************
3192

3193
  Inputs:  char* message
3194

3195
  Description:
3196

3197
************************************************************/
3198

3199
void errno_crash_and_burn(char *message)
8✔
3200
{
3201
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3202
    exit(4);
8✔
3203
}
3204

3205
/************************************************************
3206

3207
  Function: print_warning
3208

3209
  Description: fprintf(stderr, ...), unless running with -q
3210

3211
*************************************************************/
3212

3213
void print_warning(char *format, ...)
54✔
3214
{
3215
    va_list args;
3216
    if (!quiet_flag) {
54✔
3217
        va_start(args, format);
54✔
3218
        vfprintf(stderr, format, args);
54✔
3219
        va_end(args);
54✔
3220
    }
8✔
3221
}
54✔
3222

3223
/************************************************************
3224

3225
  Function: sprint_tm
3226

3227
*************************************************************
3228

3229
  render nanosecond int64_t value into milliseconds string with three digits of
3230
  precision.
3231

3232
************************************************************/
3233

3234
const char *sprint_tm(int64_t ns)
851✔
3235
{
3236
    static char buf[10];
3237
    double t = (double)ns / 1e6;
851✔
3238

3239
    if (t < 0.0) {
851✔
3240
        /* negative (unexpected) */
3241
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3242
    }
3243
    else if (t < 1.0) {
851✔
3244
        /* <= 0.99 ms */
3245
        snprintf(buf, sizeof(buf), "%.3f", t);
851✔
3246
    }
261✔
UNCOV
3247
    else if (t < 10.0) {
×
3248
        /* 1.00 - 9.99 ms */
UNCOV
3249
        snprintf(buf, sizeof(buf), "%.2f", t);
×
3250
    }
3251
    else if (t < 100.0) {
×
3252
        /* 10.0 - 99.9 ms */
3253
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3254
    }
3255
    else if (t < 1000000.0) {
×
3256
        /* 100 - 1'000'000 ms */
3257
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3258
    }
3259
    else {
3260
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3261
    }
3262

3263
    return (buf);
851✔
3264
}
3265

3266
/************************************************************
3267

3268
  Function: addr_cmp
3269

3270
*************************************************************/
3271
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
469✔
3272
{
3273
    if (a->sa_family != b->sa_family) {
469✔
3274
        return a->sa_family - b->sa_family;
×
3275
    }
3276
    else {
3277
        if (a->sa_family == AF_INET) {
469✔
3278
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
405✔
3279
        }
3280
        else if (a->sa_family == AF_INET6) {
64✔
3281
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3282
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3283
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3284
        }
3285
    }
3286

3287
    return 0;
×
3288
}
131✔
3289

3290
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
827✔
3291
{
3292
    struct event *event = &h->event_storage_ping[index % event_storage_count];
827✔
3293
    event->host = h;
827✔
3294
    event->ping_index = index;
827✔
3295
    event->ev_time = ev_time;
827✔
3296
    ev_enqueue(&event_queue_ping, event);
827✔
3297

3298
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3299
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3300
}
827✔
3301

3302
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
803✔
3303
{
3304
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
803✔
3305
    event->host = h;
803✔
3306
    event->ping_index = index;
803✔
3307
    event->ev_time = ev_time;
803✔
3308
    ev_enqueue(&event_queue_timeout, event);
803✔
3309

3310
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3311
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3312
}
803✔
3313

3314
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
708✔
3315
{
3316
    return &h->event_storage_timeout[index % event_storage_count];
708✔
3317
}
3318

3319
/************************************************************
3320

3321
  Function: ev_enqueue
3322

3323
  Enqueue an event
3324

3325
  The queue is sorted by event->ev_time, so that queue->first always points to
3326
  the earliest event.
3327

3328
  We start scanning the queue from the tail, because we assume
3329
  that new events mostly get inserted with a event time higher
3330
  than the others.
3331

3332
*************************************************************/
3333
void ev_enqueue(struct event_queue *queue, struct event *event)
1,630✔
3334
{
3335
    struct event *i;
3336
    struct event *i_prev;
3337

3338
    /* Empty list */
3339
    if (queue->last == NULL) {
1,630✔
3340
        event->ev_next = NULL;
1,241✔
3341
        event->ev_prev = NULL;
1,241✔
3342
        queue->first = event;
1,241✔
3343
        queue->last = event;
1,241✔
3344
        return;
1,241✔
3345
    }
3346

3347
    /* Insert on tail? */
3348
    if (event->ev_time - queue->last->ev_time >= 0) {
389✔
3349
        event->ev_next = NULL;
389✔
3350
        event->ev_prev = queue->last;
389✔
3351
        queue->last->ev_next = event;
389✔
3352
        queue->last = event;
389✔
3353
        return;
389✔
3354
    }
3355

3356
    /* Find insertion point */
3357
    i = queue->last;
×
3358
    while (1) {
3359
        i_prev = i->ev_prev;
×
3360
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3361
            event->ev_prev = i_prev;
×
3362
            event->ev_next = i;
×
3363
            i->ev_prev = event;
×
3364
            if (i_prev != NULL) {
×
3365
                i_prev->ev_next = event;
×
3366
            }
3367
            else {
3368
                queue->first = event;
×
3369
            }
3370
            return;
×
3371
        }
3372
        i = i_prev;
×
3373
    }
3374
}
474✔
3375

3376
/************************************************************
3377

3378
  Function: ev_dequeue
3379

3380
*************************************************************/
3381
struct event *ev_dequeue(struct event_queue *queue)
902✔
3382
{
3383
    struct event *dequeued;
3384

3385
    if (queue->first == NULL) {
902✔
3386
        return NULL;
×
3387
    }
3388
    dequeued = queue->first;
902✔
3389
    ev_remove(queue, dequeued);
902✔
3390

3391
    return dequeued;
902✔
3392
}
254✔
3393

3394
/************************************************************
3395

3396
  Function: ev_remove
3397

3398
*************************************************************/
3399
void ev_remove(struct event_queue *queue, struct event *event)
1,610✔
3400
{
3401
    if (queue->first == event) {
1,610✔
3402
        queue->first = event->ev_next;
1,601✔
3403
    }
465✔
3404
    if (queue->last == event) {
1,610✔
3405
        queue->last = event->ev_prev;
1,242✔
3406
    }
352✔
3407
    if (event->ev_prev) {
1,610✔
3408
        event->ev_prev->ev_next = event->ev_next;
9✔
3409
    }
3✔
3410
    if (event->ev_next) {
1,610✔
3411
        event->ev_next->ev_prev = event->ev_prev;
368✔
3412
    }
116✔
3413
    event->ev_prev = NULL;
1,610✔
3414
    event->ev_next = NULL;
1,610✔
3415
}
1,610✔
3416

3417
/************************************************************
3418

3419
  Function: print_human_readable_time from current_time_ns
3420

3421
*************************************************************/
3422
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
30✔
3423
{
3424
    char time_buffer[100];
3425
    time_t current_time_s;
3426
    struct tm *local_time;
3427

3428
    current_time_s = current_time_ns / 1000000000;
30✔
3429
    local_time = localtime(&current_time_s);
30✔
3430
    switch(timestamp_format) {
30✔
3431
        case 1:
4✔
3432
            // timestamp-format ctime
3433
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
6✔
3434
            printf("[%s] ", time_buffer);
6✔
3435
            break;
6✔
3436
        case 2:
4✔
3437
            // timestamp-format iso
3438
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
6✔
3439
            printf("[%s] ", time_buffer);
6✔
3440
            break;
6✔
3441
        case 3:
4✔
3442
            // timestamp-format rfc3339
3443
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
6✔
3444
            printf("[%s] ", time_buffer);
6✔
3445
            break;
6✔
3446
        default:
8✔
3447
            printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3448
    }
4✔
3449
}
30✔
3450

3451
/************************************************************
3452

3453
  Function: ms_since_midnight_utc
3454

3455
*************************************************************
3456

3457
  Input: int64_t: current UTC time in ns
3458

3459
  Output: uint32_t: current time in ms since midnight UTC
3460

3461
  Description:
3462

3463
  Return ICMP Timestamp value corresponding to the given time value.
3464
  The given time value must be in UTC.
3465

3466
*************************************************************/
3467
static uint32_t ms_since_midnight_utc(int64_t time_val)
14✔
3468
{
3469
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
14✔
3470
}
3471

3472
/************************************************************
3473

3474
  Function: usage
3475

3476
*************************************************************
3477

3478
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3479

3480
  Description:
3481

3482
************************************************************/
3483

3484
void usage(int is_error)
115✔
3485
{
3486
    FILE *out = is_error ? stderr : stdout;
115✔
3487
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
115✔
3488
    fprintf(out, "\n");
115✔
3489
    fprintf(out, "Probing options:\n");
115✔
3490
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
115✔
3491
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
115✔
3492
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
115✔
3493
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
115✔
3494
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
115✔
3495
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
115✔
3496
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
115✔
3497
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
115✔
3498
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
115✔
3499
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
115✔
3500
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
115✔
3501
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
115✔
3502
#ifdef SO_BINDTODEVICE
3503
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
78✔
3504
#endif
3505
#ifdef SO_MARK
3506
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
78✔
3507
#endif
3508
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
115✔
3509
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
115✔
3510
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
115✔
3511
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
115✔
3512
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
115✔
3513
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
115✔
3514
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
115✔
3515
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
115✔
3516
    fprintf(out, "   -S, --src=IP       set source address\n");
115✔
3517
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
115✔
3518
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
115✔
3519
    fprintf(out, "       --check-source discard replies not from target address\n");
115✔
3520
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
115✔
3521
    fprintf(out, "\n");
115✔
3522
    fprintf(out, "Output options:\n");
115✔
3523
    fprintf(out, "   -a, --alive        show targets that are alive\n");
115✔
3524
    fprintf(out, "   -A, --addr         show targets by address\n");
115✔
3525
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
115✔
3526
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
115✔
3527
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
115✔
3528
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
115✔
3529
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
115✔
3530
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
115✔
3531
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
115✔
3532
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
115✔
3533
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
115✔
3534
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
115✔
3535
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
115✔
3536
    fprintf(out, "   -s, --stats        print final stats\n");
115✔
3537
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
115✔
3538
    fprintf(out, "   -v, --version      show version\n");
115✔
3539
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
115✔
3540
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
115✔
3541
    fprintf(out, "       --print-tos    show received TOS value\n");
115✔
3542
    fprintf(out, "       --print-ttl    show IP TTL value\n");
115✔
3543
    exit(is_error);
115✔
3544
}
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