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

schweikert / fping / 16221329819

11 Jul 2025 01:32PM UTC coverage: 86.61% (-1.1%) from 87.748%
16221329819

Pull #391

github

web-flow
Merge 9eb2baf22 into 6b4dd9194
Pull Request #391: fix ICMPv6 error message handling and reporting

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

4 existing lines in 1 file now uncovered.

1423 of 1643 relevant lines covered (86.61%)

328.82 hits per line

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

86.61
/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 seqmap_timeout = (int64_t)DEFAULT_SEQMAP_TIMEOUT * 1000000;
362
int64_t interval = (int64_t)DEFAULT_INTERVAL * 1000000;
363
int64_t perhost_interval = (int64_t)DEFAULT_PERHOST_INTERVAL * 1000000;
364
float backoff = DEFAULT_BACKOFF_FACTOR;
365
unsigned int ping_data_size = DEFAULT_PING_DATA_SIZE;
366
unsigned int count = 1, min_reachable = 0;
367
unsigned int trials;
368
int64_t report_interval = 0;
369
unsigned int ttl = 0;
370
int src_addr_set = 0;
371
struct in_addr src_addr;
372
#ifdef IPV6
373
int src_addr6_set = 0;
374
struct in6_addr src_addr6;
375
#endif
376

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

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

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

420
unsigned int fwmark = 0;
421

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

424
/*** forward declarations ***/
425

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

467
/************************************************************
468

469
  Function: p_setsockopt
470

471
*************************************************************
472

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

475
  Description:
476

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

480
************************************************************/
481

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

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

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

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

500
    return res;
22✔
501
}
502

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

505
  Function: main
506

507
*************************************************************
508

509
  Inputs:  int argc, char** argv
510

511
  Description:
512

513
  Main program entry point
514

515
************************************************************/
516

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

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

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

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

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

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

563
    if (!suid && suid != getuid()) {
651✔
564
        /* *temporarily* drop privileges */
565
        if (seteuid(getuid()) == -1)
616✔
566
            perror("cannot setuid");
×
567
    }
166✔
568

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

575
    /* get command line options */
576

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

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

737
        case 't':
40✔
738
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
45✔
739
                usage(1);
6✔
740
            if (opt_value_float < 0) {
41✔
741
                usage(1);
3✔
742
            }
1✔
743
            timeout = opt_value_float * 1000000;
39✔
744
            timeout_flag = 1;
39✔
745
            break;
39✔
746

747
        case 'r':
36✔
748
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
40✔
749
                usage(1);
6✔
750
            break;
36✔
751

752
        case 'i':
30✔
753
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
36✔
754
                usage(1);
6✔
755
            if (opt_value_float < 0) {
32✔
756
                usage(1);
3✔
757
            }
1✔
758
            interval = opt_value_float * 1000000;
30✔
759
            break;
30✔
760

761
        case 'p':
68✔
762
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
99✔
763
                usage(1);
6✔
764
            if (opt_value_float < 0) {
95✔
765
                usage(1);
3✔
766
            }
1✔
767
            perhost_interval = opt_value_float * 1000000;
93✔
768

769
            break;
93✔
770

771
        case 'c':
60✔
772
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
85✔
773
                usage(1);
6✔
774

775
            count_flag = 1;
81✔
776
            break;
81✔
777

778
        case 'C':
24✔
779
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
33✔
780
                usage(1);
6✔
781

782
            count_flag = 1;
29✔
783
            report_all_rtts_flag = 1;
29✔
784
            break;
29✔
785

786
        case 'b':
14✔
787
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
21✔
788
                usage(1);
6✔
789
            size_flag = 1;
17✔
790
            break;
17✔
791

792
        case 'h':
2✔
793
            usage(0);
3✔
794
            break;
1✔
795

796
        case 'q':
12✔
797
            verbose_flag = 0;
17✔
798
            quiet_flag = 1;
17✔
799
            break;
17✔
800

801
        case 'Q':
22✔
802
            verbose_flag = 0;
33✔
803
            quiet_flag = 1;
33✔
804
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
805
                usage(1);
6✔
806
            if (opt_value_float < 0) {
29✔
807
                usage(1);
3✔
808
            }
1✔
809
            report_interval = opt_value_float * 1e9;
27✔
810

811
            /* recognize keyword(s) after number, ignore everything else */
812
            {
813
                char *comma = strchr(optparse_state.optarg, ',');
27✔
814
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
27✔
815
                    cumulative_stats_flag = 1;
6✔
816
                }
2✔
817
            }
818

819
            break;
27✔
820

821
        case 'e':
6✔
822
            elapsed_flag = 1;
8✔
823
            break;
8✔
824

825
        case 'm':
826
            multif_flag = 1;
×
827
            break;
×
828

829
        case 'N':
2✔
830
            netdata_flag = 1;
3✔
831
            break;
3✔
832

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

841
        case 'd':
6✔
842
            rdns_flag = 1;
9✔
843
            if (name_flag) {
9✔
844
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
845
                exit(1);
3✔
846
            }
847
            break;
6✔
848

849
        case 'A':
2✔
850
            addr_flag = 1;
3✔
851
            break;
3✔
852

853
        case 'B':
10✔
854
            if (!(backoff = atof(optparse_state.optarg)))
12✔
855
                usage(1);
6✔
856

857
            break;
8✔
858

859
        case 's':
6✔
860
            stats_flag = 1;
9✔
861
            break;
9✔
862

863
        case 'D':
14✔
864
            timestamp_flag = 1;
21✔
865
            break;
21✔
866

867
        case 'R':
4✔
868
            random_data_flag = 1;
5✔
869
            break;
5✔
870

871
        case 'l':
2✔
872
            loop_flag = 1;
3✔
873
            backoff_flag = 0;
3✔
874
            break;
3✔
875

876
        case 'u':
8✔
877
            unreachable_flag = 1;
12✔
878
            break;
12✔
879

880
        case 'a':
18✔
881
            alive_flag = 1;
26✔
882
            break;
26✔
883

884
        case 'H':
8✔
885
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
12✔
886
                usage(1);
6✔
887
            break;
8✔
888

889
#if defined(DEBUG) || defined(_DEBUG)
890
        case 'z':
891
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
892
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
893
                    usage(1);
894

895
            break;
896
#endif /* DEBUG || _DEBUG */
897

898
        case 'v':
4✔
899
            printf("%s: Version %s\n", prog, VERSION);
6✔
900
            exit(0);
6✔
901

902
        case 'x':
8✔
903
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
10✔
904
                usage(1);
6✔
905
            break;
6✔
906

907
        case 'X':
6✔
908
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
909
                usage(1);
6✔
910
            fast_reachable = 1;
5✔
911
            break;
5✔
912

913
        case 'f':
18✔
914
            filename = optparse_state.optarg;
27✔
915
            break;
27✔
916
#ifdef SO_MARK
917
        case 'k':
10✔
918
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
10✔
919
                usage(1);
4✔
920

921
            if (socket4 >= 0)
6✔
922
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
923
                    perror("fwmark ipv4");
4✔
924

925
#ifdef IPV6
926
            if (socket6 >= 0)
6✔
927
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
928
                    perror("fwmark ipv6");
4✔
929
#endif
930

931
            break;
6✔
932
#endif
933

934
        case 'g':
98✔
935
            /* use IP list generation */
936
            /* mutually exclusive with using file input or command line targets */
937
            generate_flag = 1;
117✔
938
            break;
117✔
939

940
        case 'S':
10✔
941
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
942
                src_addr_set = 1;
6✔
943
                break;
6✔
944
            }
945
#ifdef IPV6
946
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
947
                src_addr6_set = 1;
4✔
948
                break;
4✔
949
            }
950
#endif
951
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
952
            exit(1);
3✔
953

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

977
        case 'T':
2✔
978
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
979
            break;
3✔
980

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

1001
        case 'o':
6✔
1002
            outage_flag = 1;
9✔
1003
            break;
9✔
1004

1005
        case '?':
4✔
1006
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
1007
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
1008
            exit(1);
6✔
1009
            break;
1010
        }
1011
    }
1012

1013
    /* permanently drop privileges */
1014
    if (suid != getuid() && setuid(getuid())) {
483✔
1015
        perror("fatal: failed to permanently drop privileges");
×
1016
        /* continuing would be a security hole */
1017
        exit(4);
×
1018
    }
1019

1020
    /* validate various option settings */
1021

1022
#ifndef IPV6
1023
    if (socket4 < 0) {
1024
        crash_and_burn("can't create socket (must run as root?)");
1025
    }
1026
#else
1027
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
483✔
1028
        crash_and_burn("can't create socket (must run as root?)");
×
1029
    }
1030
#endif
1031

1032
    if (ttl > 255) {
481✔
1033
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
1034
        exit(1);
3✔
1035
    }
1036

1037
    if (unreachable_flag && alive_flag) {
478✔
1038
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
1039
        exit(1);
3✔
1040
    }
1041

1042
    if (count_flag && loop_flag) {
475✔
1043
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
1044
        exit(1);
3✔
1045
    }
1046

1047
#ifdef FPING_SAFE_LIMITS
1048
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
472✔
1049
        && getuid()) {
120✔
1050
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
1051
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
1052
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
1053
        exit(1);
6✔
1054
    }
1055
#endif
1056

1057
    if (ping_data_size > MAX_PING_DATA) {
466✔
1058
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1059
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1060
        exit(1);
3✔
1061
    }
1062

1063
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
463✔
1064
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1065
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1066
        exit(1);
6✔
1067
    }
1068

1069
    if (icmp_request_typ == 13 && size_flag != 0) {
457✔
1070
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1071
        exit(1);
9✔
1072
    }
1073

1074
    if (count_flag) {
448✔
1075
        if (verbose_flag)
100✔
1076
            per_recv_flag = 1;
59✔
1077

1078
        alive_flag = unreachable_flag = verbose_flag = 0;
100✔
1079
    }
28✔
1080

1081
    if (loop_flag) {
448✔
1082
        if (!report_interval)
×
1083
            per_recv_flag = 1;
×
1084

1085
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1086
    }
1087

1088
    if (alive_flag || unreachable_flag || min_reachable)
448✔
1089
        verbose_flag = 0;
37✔
1090

1091
    trials = (count > retry + 1) ? count : retry + 1;
464✔
1092

1093
    /* auto-tune default timeout for count/loop modes
1094
     * see also github #32 */
1095
    if (loop_flag || count_flag) {
464✔
1096
        if (!timeout_flag) {
100✔
1097
            timeout = perhost_interval;
95✔
1098
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
95✔
1099
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1100
            }
1101
        }
27✔
1102
    }
28✔
1103

1104
#if defined(DEBUG) || defined(_DEBUG)
1105
    if (debugging & DBG_TRACE)
1106
        trace_flag = 1;
1107

1108
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1109
        randomly_lose_flag = 1;
1110
        lose_factor = 1; /* ie, 1/4 */
1111
    }
1112

1113
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1114
        randomly_lose_flag = 1;
1115
        lose_factor = 5; /* ie, 3/4 */
1116
    }
1117

1118
    if (debugging & DBG_PRINT_PER_SYSTEM)
1119
        print_per_system_flag = 1;
1120

1121
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1122
        report_all_rtts_flag = 1;
1123

1124
    if (trace_flag) {
1125
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1126
            prog, count, retry, interval / 1e6);
1127
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1128
            perhost_interval / 1e6, timeout / 1e6);
1129
        fprintf(stderr, "  seqmap_timeout: %.0f\n", seqmap_timeout / 1e6);
1130
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1131
            ping_data_size, trials);
1132

1133
        if (verbose_flag)
1134
            fprintf(stderr, "  verbose_flag set\n");
1135
        if (multif_flag)
1136
            fprintf(stderr, "  multif_flag set\n");
1137
        if (name_flag)
1138
            fprintf(stderr, "  name_flag set\n");
1139
        if (addr_flag)
1140
            fprintf(stderr, "  addr_flag set\n");
1141
        if (stats_flag)
1142
            fprintf(stderr, "  stats_flag set\n");
1143
        if (unreachable_flag)
1144
            fprintf(stderr, "  unreachable_flag set\n");
1145
        if (alive_flag)
1146
            fprintf(stderr, "  alive_flag set\n");
1147
        if (elapsed_flag)
1148
            fprintf(stderr, "  elapsed_flag set\n");
1149
        if (version_flag)
1150
            fprintf(stderr, "  version_flag set\n");
1151
        if (count_flag)
1152
            fprintf(stderr, "  count_flag set\n");
1153
        if (loop_flag)
1154
            fprintf(stderr, "  loop_flag set\n");
1155
        if (backoff_flag)
1156
            fprintf(stderr, "  backoff_flag set\n");
1157
        if (per_recv_flag)
1158
            fprintf(stderr, "  per_recv_flag set\n");
1159
        if (report_all_rtts_flag)
1160
            fprintf(stderr, "  report_all_rtts_flag set\n");
1161
        if (randomly_lose_flag)
1162
            fprintf(stderr, "  randomly_lose_flag set\n");
1163
        if (print_per_system_flag)
1164
            fprintf(stderr, "  print_per_system_flag set\n");
1165
        if (outage_flag)
1166
            fprintf(stderr, "  outage_flag set\n");
1167
        if (netdata_flag)
1168
            fprintf(stderr, "  netdata_flag set\n");
1169
    }
1170
#endif /* DEBUG || _DEBUG */
1171

1172
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1173
    if (ttl > 0) {
464✔
1174
        if (socket4 >= 0) {
3✔
1175
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1176
                perror("setting time to live");
×
1177
            }
1178
        }
1✔
1179
#ifdef IPV6
1180
        if (socket6 >= 0) {
3✔
1181
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1182
                perror("setting time to live");
×
1183
            }
1184
        }
1✔
1185
#endif
1186
    }
1✔
1187

1188
#if HAVE_SO_TIMESTAMPNS
1189
    {
1190
        int opt = 1;
340✔
1191
        if (socket4 >= 0) {
340✔
1192
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
340✔
1193
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1194
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1195
                }
1196
            }
1197
        }
1198
#ifdef IPV6
1199
        if (socket6 >= 0) {
340✔
1200
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
340✔
1201
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1202
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1203
                }
1204
            }
1205
        }
1206
#endif
1207
    }
1208
#endif
1209

1210
    update_current_time();
464✔
1211
    start_time = current_time_ns;
464✔
1212

1213
    /* handle host names supplied on command line or in a file */
1214
    /* if the generate_flag is on, then generate the IP list */
1215

1216
    argv = &argv[optparse_state.optind];
464✔
1217
    argc -= optparse_state.optind;
464✔
1218

1219
    /* calculate how many ping can be in-flight per host */
1220
    if (count_flag) {
464✔
1221
        event_storage_count = count;
115✔
1222
    }
43✔
1223
    else if (loop_flag) {
349✔
1224
        if (perhost_interval > timeout) {
×
1225
            event_storage_count = 1;
×
1226
        }
1227
        else {
1228
            event_storage_count = 1 + timeout / perhost_interval;
×
1229
        }
1230
    }
1231
    else {
1232
        event_storage_count = 1;
349✔
1233
    }
1234

1235
    /* file and generate are mutually exclusive */
1236
    /* file and command line are mutually exclusive */
1237
    /* generate requires command line parameters beyond the switches */
1238
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
464✔
1239
        usage(1);
19✔
1240

1241
    /* if no conditions are specified, then assume input from stdin */
1242
    if (!*argv && !filename && !generate_flag)
446✔
1243
        filename = "-";
69✔
1244

1245
    if (*argv && !generate_flag) {
446✔
1246
        while (*argv) {
536✔
1247
            add_name(*argv);
297✔
1248
            ++argv;
297✔
1249
        }
1250
    }
59✔
1251
    else if (filename) {
207✔
1252
        FILE *ping_file;
1253
        char line[MAX_TARGET_NAME_LEN + 1];
1254
        char host[MAX_TARGET_NAME_LEN + 1];
1255
        char scratch[MAX_TARGET_NAME_LEN + 1];
1256
        int skip, non_empty;
1257

1258
        if (strcmp(filename, "-") == 0)
96✔
1259
            ping_file = fdopen(0, "r");
72✔
1260
        else
1261
            ping_file = fopen(filename, "r");
24✔
1262

1263
        if (!ping_file)
96✔
1264
            errno_crash_and_burn("fopen");
3✔
1265

1266
        /*
1267
         * Read the first word of every non-comment line, skip everything else.
1268
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1269
         * character is a '#' are interpreted as comments and ignored.)
1270
        */
1271
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1272
            skip = non_empty = 0;
309✔
1273

1274
            /* skip over a prefix of the line where sscanf finds nothing */
1275
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1276
                continue;
99✔
1277
            }
1278

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

1330
            if (non_empty)
186✔
1331
                add_name(host);
144✔
1332
        }
1333

1334
        fclose(ping_file);
70✔
1335
    }
24✔
1336
    else if (*argv && generate_flag) {
111✔
1337
        if (argc == 1) {
111✔
1338
            /* one target: we expect a cidr range (n.n.n.n/m) */
1339
            add_cidr(argv[0]);
56✔
1340
        }
10✔
1341
        else if (argc == 2) {
55✔
1342
            add_range(argv[0], argv[1]);
52✔
1343
        }
6✔
1344
        else {
1345
            usage(1);
3✔
1346
        }
1347
    }
17✔
1348
    else {
1349
        usage(1);
×
1350
    }
1351

1352
    if (!num_hosts) {
366✔
1353
        exit(num_noaddress ? 2 : 1);
33✔
1354
    }
1355

1356
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
333✔
1357
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1358
    }
2✔
1359
#ifdef IPV6
1360
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
331✔
1361
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1362
    }
1363
#endif
1364

1365
    /* allocate and initialize array to map host nr to host_entry */
1366
    {
1367
        struct event *cursor = event_queue_ping.first;
329✔
1368
        int i = 0;
329✔
1369
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
329✔
1370
        if (!table)
329✔
1371
            crash_and_burn("Can't malloc array of hosts");
×
1372
        /* initialize table of hosts. we know that we have ping events scheduled
1373
         * for each of them */
1374
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
842✔
1375
            table[i] = cursor->host;
513✔
1376
            cursor->host->i = i;
513✔
1377
            i++;
513✔
1378
        }
137✔
1379
    }
1380

1381
    init_ping_buffer_ipv4(ping_data_size);
329✔
1382
#ifdef IPV6
1383
    init_ping_buffer_ipv6(ping_data_size);
329✔
1384
#endif
1385

1386
#ifdef USE_SIGACTION
1387
    memset(&act, 0, sizeof(act));
329✔
1388
    act.sa_handler = signal_handler;
329✔
1389
    sigemptyset(&act.sa_mask);
329✔
1390
    sigaddset(&act.sa_mask, SIGINT);
329✔
1391
    sigaddset(&act.sa_mask, SIGQUIT);
329✔
1392
    act.sa_flags = SA_RESTART;
329✔
1393
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
329✔
1394
        crash_and_burn("failure to set signal handler");
24✔
1395
    }
24✔
1396
#else
1397
    signal(SIGINT, signal_handler);
1398
    signal(SIGQUIT, signal_handler);
1399
#endif
1400
    setlinebuf(stdout);
305✔
1401

1402
    if (report_interval) {
305✔
1403
        next_report_time = current_time_ns + report_interval;
24✔
1404
    }
8✔
1405

1406
    last_send_time = 0;
305✔
1407

1408
    seqmap_init(seqmap_timeout);
305✔
1409

1410
    /* main loop */
1411
    main_loop();
305✔
1412

1413
/* Debug: CPU Performance */
1414
#if defined(DEBUG) || defined(_DEBUG)
1415
    perf_cpu_end = clock();
1416
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1417
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1418
#endif /* DEBUG || _DEBUG */
1419

1420
    finish();
305✔
1421

1422
    return 0;
67✔
1423
}
1424

1425
static inline int64_t timespec_ns(struct timespec *a)
5,129✔
1426
{
1427
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,129✔
1428
}
1429

1430
#if HAVE_SO_TIMESTAMPNS
1431
/* convert a struct timeval to nanoseconds */
1432
static inline int64_t timeval_ns(struct timeval *a)
1433
{
1434
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1435
}
1436
#endif /* HAVE_SO_TIMESTAMPNS */
1437

1438
void add_cidr(char *addr)
54✔
1439
{
1440
    char *addr_end;
1441
    char *mask_str;
1442
    unsigned long mask;
1443
    int ret;
1444
    struct addrinfo addr_hints;
1445
    struct addrinfo *addr_res;
1446
    unsigned long net_addr;
1447
#ifdef IPV6
1448
    uint64_t net_upper, net_lower;
1449
    char *scope_str;
1450
#endif /* IPV6 */
1451

1452
    /* Split address from mask */
1453
    addr_end = strrchr(addr, '/');
54✔
1454
    if (addr_end == NULL) {
54✔
1455
        usage(1);
3✔
1456
    }
1✔
1457
    mask_str = addr_end + 1;
52✔
1458

1459
#ifdef IPV6
1460
    /* IPv6 addresses can have a scope */
1461
    scope_str = strchr(addr, '%');
52✔
1462
    if (scope_str && mask_str < scope_str) {
52✔
1463
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1464
        exit(1);
2✔
1465
    }
1466
#endif /*IPV6 */
1467

1468
    *addr_end = '\0';
50✔
1469
    mask = atoi(mask_str);
50✔
1470

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

1499
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
21✔
1500
{
1501
    unsigned long bitmask;
1502
    unsigned long net_last;
1503

1504
    /* check mask */
1505
    if (mask < 1 || mask > 32) {
21✔
1506
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1507
        exit(1);
6✔
1508
    }
1509

1510
    /* convert mask integer from 1 to 32 to a bitmask */
1511
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
15✔
1512

1513
    /* calculate network range */
1514
    net_addr &= bitmask;
15✔
1515
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
15✔
1516

1517
    /* exclude network and broadcast address for regular prefixes */
1518
    if (mask < 31) {
15✔
1519
        net_last--;
9✔
1520
        net_addr++;
9✔
1521
    }
3✔
1522

1523
    /* add all hosts in that network (net_addr and net_last inclusive) */
1524
    add_addr_range_ipv4(net_addr, net_last);
15✔
1525
}
13✔
1526

1527
#ifdef IPV6
1528
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1529
{
1530
    uint64_t bitmask_lower;
1531
    uint64_t last_lower;
1532

1533
    /* check mask -- 2^63 addresses should suffice for now */
1534
    if (mask < 65 || mask > 128) {
23✔
1535
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1536
        exit(1);
7✔
1537
    }
1538

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

1542
    /* calculate network range */
1543
    net_lower &= bitmask_lower;
16✔
1544
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1545

1546
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1547
}
12✔
1548
#endif /* IPV6 */
1549

1550
void add_range(char *start, char *end)
52✔
1551
{
1552
    struct addrinfo addr_hints;
1553
    struct addrinfo *addr_res;
1554
    unsigned long start_long;
1555
    unsigned long end_long;
1556
    int ret;
1557
#ifdef IPV6
1558
    uint64_t start_upper, start_lower;
1559
    uint64_t end_upper, end_lower;
1560
    char *start_scope_str, *end_scope_str;
1561

1562
    /*
1563
     * The compiler does not know that setting the address family hint to
1564
     * ensure that start and end are from the same address family also
1565
     * ensures that either start_long and end_long are initialized and used,
1566
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1567
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1568
     * supported to suppress compiler warnings.
1569
     */
1570
    start_long = -1;
52✔
1571
    end_long = 0;
52✔
1572
    start_upper = start_lower = -1;
52✔
1573
    end_upper = end_lower = 0;
52✔
1574
    start_scope_str = end_scope_str = NULL;
52✔
1575
#endif /* IPV6 */
1576

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

1604
#ifdef IPV6
1605
    /* IPv6 addresses can have a scope */
1606
    if (hints_ai_family == AF_INET6) {
43✔
1607
        start_scope_str = strchr(start, '%');
26✔
1608
        end_scope_str = strchr(end, '%');
26✔
1609
        if ((!start_scope_str && end_scope_str) ||
26✔
1610
            (start_scope_str && !end_scope_str) ||
24✔
1611
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1612
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1613
                exit(1);
6✔
1614
        }
1615
    }
1616
#endif
1617

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

1646
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
27✔
1647
{
1648
    /* check if generator limit is exceeded */
1649
    if (end_long >= start_long + MAX_GENERATE) {
27✔
1650
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1651
        exit(1);
6✔
1652
    }
1653

1654
    /* generate */
1655
    for (; start_long <= end_long; start_long++) {
84✔
1656
        struct in_addr in_addr_tmp;
1657
        char buffer[20];
1658
        in_addr_tmp.s_addr = htonl(start_long);
63✔
1659
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
63✔
1660
        add_name(buffer);
63✔
1661
    }
21✔
1662
}
21✔
1663

1664
#ifdef IPV6
1665
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1666
{
1667
    int i;
1668
    uint64_t ret = 0;
134✔
1669
    for (i = 0; i < 8; i++) {
1,206✔
1670
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1671
    }
16✔
1672
    return ret;
134✔
1673
}
1674

1675
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1676
{
1677
    int i;
1678
    for (i = 0; i < 8; i++) {
720✔
1679
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1680
    }
1681
}
80✔
1682

1683
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1684
                         uint64_t end_upper, uint64_t end_lower,
1685
                         const char *scope_str)
1686
{
1687
    struct in6_addr in6_addr_tmp;
1688
    char buffer[100];
1689

1690
    /* prevent generating too many addresses */
1691
    if ((start_upper + 1 < end_upper) ||
34✔
1692
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1693
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1694
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1695
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1696
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1697
        exit(1);
8✔
1698
    }
1699

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

1721
void main_loop()
317✔
1722
{
1723
    int64_t lt;
1724
    int64_t wait_time_ns;
1725
    struct event *event;
1726
    struct host_entry *h;
1727

1728
    while (event_queue_ping.first || event_queue_timeout.first) {
1,781✔
1729
        dbg_printf("%s", "# main_loop\n");
1730

1731
        /* timeout event ? */
1732
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,481✔
1733
            event = ev_dequeue(&event_queue_timeout);
107✔
1734
            h = event->host;
107✔
1735

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

1738
            stats_add(h, event->ping_index, 0, -1);
107✔
1739

1740
            if (per_recv_flag) {
107✔
1741
                if (timestamp_flag) {
19✔
1742
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1743
                }
1744
                printf("%-*s : [%d], timed out",
19✔
1745
                    max_hostname_len, h->host, event->ping_index);
5✔
1746
                if (h->num_recv > 0) {
19✔
1747
                    printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1748
                }
1749
                else {
1750
                    printf(" (NaN avg, ");
19✔
1751
                }
1752
                if (h->num_recv <= h->num_sent) {
19✔
1753
                    printf("%d%% loss)",
19✔
1754
                        ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1755
                }
5✔
1756
                else {
1757
                    printf("%d%% return)",
×
1758
                        (h->num_recv_total * 100) / h->num_sent);
×
1759
                }
1760
                printf("\n");
19✔
1761
            }
5✔
1762

1763
            /* do we need to send a retry? */
1764
            if (!loop_flag && !count_flag) {
107✔
1765
                if (h->num_sent < retry + 1) {
64✔
1766
                    if (backoff_flag) {
39✔
1767
                        h->timeout *= backoff;
39✔
1768
                    }
9✔
1769
                    send_ping(h, event->ping_index);
39✔
1770
                }
9✔
1771
            }
14✔
1772

1773
            /* note: we process first timeout events, because we might need to
1774
             * wait to process ping events, while we for sure never need to
1775
             * wait for timeout events.
1776
             */
1777
            continue;
107✔
1778
        }
1779

1780
        /* ping event ? */
1781
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,607✔
1782
            /* Make sure that we don't ping more than once every "interval" */
1783
            lt = current_time_ns - last_send_time;
1,065✔
1784
            if (lt < interval)
1,065✔
1785
                goto wait_for_reply;
252✔
1786

1787
            /* Dequeue the event */
1788
            event = ev_dequeue(&event_queue_ping);
813✔
1789
            h = event->host;
813✔
1790

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

1793
            /* Send the ping */
1794
            send_ping(h, event->ping_index);
813✔
1795

1796
            /* Loop and count mode: schedule next ping */
1797
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
813✔
1798
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
315✔
1799
            }
101✔
1800
        }
233✔
1801

1802
    wait_for_reply:
594✔
1803

1804
        /* When is the next ping next event? */
1805
        wait_time_ns = -1;
1,374✔
1806
        if (event_queue_ping.first) {
1,374✔
1807
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
977✔
1808
            if (wait_time_ns < 0)
977✔
1809
                wait_time_ns = 0;
533✔
1810
            /* make sure that we wait enough, so that the inter-ping delay is
1811
             * bigger than 'interval' */
1812
            if (wait_time_ns < interval) {
977✔
1813
                lt = current_time_ns - last_send_time;
539✔
1814
                if (lt < interval) {
539✔
1815
                    wait_time_ns = interval - lt;
539✔
1816
                }
191✔
1817
            }
191✔
1818

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

1822
        /* When is the next timeout event? */
1823
        if (event_queue_timeout.first) {
1,374✔
1824
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
877✔
1825
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
877✔
1826
                wait_time_ns = wait_time_timeout;
392✔
1827
                if (wait_time_ns < 0) {
392✔
1828
                    wait_time_ns = 0;
×
1829
                }
1830
            }
98✔
1831

1832
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1833
        }
255✔
1834

1835
        /* When is the next report due? */
1836
        if (report_interval && (loop_flag || count_flag)) {
1,374✔
1837
            int64_t wait_time_next_report = next_report_time - current_time_ns;
206✔
1838
            if (wait_time_next_report < wait_time_ns) {
206✔
1839
                wait_time_ns = wait_time_next_report;
69✔
1840
                if (wait_time_ns < 0) {
69✔
1841
                    wait_time_ns = 0;
×
1842
                }
1843
            }
23✔
1844

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

1848
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1849
        if (wait_time_ns == -1) {
1,374✔
1850
            break;
14✔
1851
        }
1852

1853
        /* end of loop was requested by interrupt signal handler */
1854
        if (finish_requested) {
1,360✔
1855
            break;
3✔
1856
        }
1857

1858
        /* Receive replies */
1859
        /* (this is what sleeps during each loop iteration) */
1860
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1861
        if (wait_for_reply(wait_time_ns)) {
1,357✔
1862
            while (wait_for_reply(0))
1,167✔
1863
                ; /* process other replies in the queue */
1864
        }
219✔
1865

1866
        update_current_time();
1,357✔
1867

1868
        if (status_snapshot) {
1,357✔
1869
            status_snapshot = 0;
×
1870
            print_per_system_splits();
×
1871
        }
1872

1873
        /* Print report */
1874
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,357✔
1875
            if (netdata_flag)
42✔
1876
                print_netdata();
3✔
1877
            else
1878
                print_per_system_splits();
39✔
1879

1880
            while (current_time_ns >= next_report_time) {
84✔
1881
                next_report_time += report_interval;
42✔
1882
            }
1883
        }
14✔
1884
    }
1885
}
317✔
1886

1887
/************************************************************
1888

1889
  Function: signal_handler
1890

1891
*************************************************************
1892

1893
  Inputs:  int signum
1894

1895
  Description:
1896

1897
  SIGQUIT signal handler - set flag and return
1898
  SIGINT signal handler - set flag and return
1899

1900
************************************************************/
1901

1902
void signal_handler(int signum)
×
1903
{
1904
    switch (signum) {
×
1905
    case SIGINT:
1906
        finish_requested = 1;
×
1907
        break;
×
1908

1909
    case SIGQUIT:
1910
        status_snapshot = 1;
×
1911
        break;
×
1912
    }
1913
}
×
1914

1915
/************************************************************
1916

1917
  Function: update_current_time
1918

1919
*************************************************************/
1920

1921
void update_current_time()
4,181✔
1922
{
1923
    clock_gettime(CLOCKID, &current_time);
4,181✔
1924
    current_time_ns = timespec_ns(&current_time);
4,181✔
1925
}
4,181✔
1926

1927
/************************************************************
1928

1929
  Function: finish
1930

1931
*************************************************************
1932

1933
  Inputs:  void (none)
1934

1935
  Description:
1936

1937
  Main program clean up and exit point
1938

1939
************************************************************/
1940

1941
void finish()
317✔
1942
{
1943
    int i;
1944
    HOST_ENTRY *h;
1945

1946
    update_current_time();
317✔
1947
    end_time = current_time_ns;
317✔
1948

1949
    /* tot up unreachables */
1950
    for (i = 0; i < num_hosts; i++) {
830✔
1951
        h = table[i];
513✔
1952

1953
        if (!h->num_recv) {
513✔
1954
            num_unreachable++;
83✔
1955

1956
            if (verbose_flag || unreachable_flag) {
83✔
1957
                printf("%s", h->host);
47✔
1958

1959
                if (verbose_flag)
47✔
1960
                    printf(" is unreachable");
44✔
1961

1962
                printf("\n");
47✔
1963
            }
3✔
1964
        }
13✔
1965
    }
137✔
1966

1967
    if (count_flag || loop_flag)
317✔
1968
        print_per_system_stats();
100✔
1969
#if defined(DEBUG) || defined(_DEBUG)
1970
    else if (print_per_system_flag)
1971
        print_per_system_stats();
1972
#endif /* DEBUG || _DEBUG */
1973

1974
    if (stats_flag)
317✔
1975
        print_global_stats();
9✔
1976

1977
    if (min_reachable) {
317✔
1978
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
1979
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1980
            exit(0);
6✔
1981
        }
1982
        else {
1983
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
1984
            exit(1);
3✔
1985
        }
1986
    }
1987

1988
    if (num_noaddress)
308✔
1989
        exit(2);
3✔
1990
    else if (num_alive != num_hosts)
305✔
1991
        exit(1);
44✔
1992

1993
    exit(0);
261✔
1994
}
1995

1996
/************************************************************
1997

1998
  Function: print_per_system_stats
1999

2000
*************************************************************
2001

2002
  Inputs:  void (none)
2003

2004
  Description:
2005

2006

2007
************************************************************/
2008

2009
void print_per_system_stats(void)
100✔
2010
{
2011
    int i, j, avg, outage_ms;
2012
    HOST_ENTRY *h;
2013
    int64_t resp;
2014

2015
    if (verbose_flag || per_recv_flag)
100✔
2016
        fprintf(stderr, "\n");
59✔
2017

2018
    for (i = 0; i < num_hosts; i++) {
234✔
2019
        h = table[i];
134✔
2020
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
2021

2022
        if (report_all_rtts_flag) {
134✔
2023
            for (j = 0; j < h->num_sent; j++) {
248✔
2024
                if ((resp = h->resp_times[j]) >= 0)
204✔
2025
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2026
                else
2027
                    fprintf(stderr, " -");
8✔
2028
            }
64✔
2029

2030
            fprintf(stderr, "\n");
44✔
2031
        }
12✔
2032
        else {
2033
            if (h->num_recv <= h->num_sent) {
90✔
2034
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
2035
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
2036

2037
                if (outage_flag) {
90✔
2038
                    /* Time outage total */
2039
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
2040
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
2041
                }
3✔
2042
            }
26✔
2043
            else {
2044
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2045
                    h->num_sent, h->num_recv,
2046
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2047
            }
2048

2049
            if (h->num_recv) {
90✔
2050
                avg = h->total_time / h->num_recv;
79✔
2051
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
2052
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
2053
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
2054
            }
23✔
2055

2056
            fprintf(stderr, "\n");
90✔
2057
        }
2058
    }
38✔
2059
}
100✔
2060

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

2063
  Function: print_netdata
2064

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

2067
  Inputs:  void (none)
2068

2069
  Description:
2070

2071

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

2074
void print_netdata(void)
3✔
2075
{
2076
    static int sent_charts = 0;
2077

2078
    int i;
2079
    int64_t avg;
2080
    HOST_ENTRY *h;
2081

2082
    for (i = 0; i < num_hosts; i++) {
6✔
2083
        h = table[i];
3✔
2084

2085
        if (!sent_charts) {
3✔
2086
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2087
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2088
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2089
        }
1✔
2090

2091
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2092
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2093
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2094
        printf("END\n");
3✔
2095

2096
        if (!sent_charts) {
3✔
2097
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2098
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2099
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2100
        }
1✔
2101

2102
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2103
        /*
2104
        if( h->num_recv_i <= h->num_sent_i )
2105
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2106
        else
2107
            printf("SET lost = 0\n");
2108
*/
2109

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

2113
        if (!sent_charts) {
3✔
2114
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2115
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2116
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2117
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2118
        }
1✔
2119

2120
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2121
        if (h->num_recv_i) {
3✔
2122
            avg = h->total_time_i / h->num_recv_i;
3✔
2123
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2124
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2125
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2126
        }
1✔
2127
        printf("END\n");
3✔
2128

2129
        stats_reset_interval(h);
3✔
2130
    }
1✔
2131

2132
    sent_charts = 1;
3✔
2133
}
3✔
2134

2135
/************************************************************
2136

2137
  Function: print_per_system_splits
2138

2139
*************************************************************
2140

2141
  Inputs:  void (none)
2142

2143
  Description:
2144

2145

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

2148
void print_per_system_splits(void)
39✔
2149
{
2150
    int i, avg, outage_ms_i;
2151
    HOST_ENTRY *h;
2152
    struct tm *curr_tm;
2153

2154
    if (verbose_flag || per_recv_flag)
39✔
2155
        fprintf(stderr, "\n");
×
2156

2157
    update_current_time();
39✔
2158
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2159
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2160
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2161

2162
    for (i = 0; i < num_hosts; i++) {
78✔
2163
        h = table[i];
39✔
2164
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2165

2166
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2167
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2168
                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✔
2169

2170
            if (outage_flag) {
39✔
2171
                /* Time outage  */
2172
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2173
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2174
            }
4✔
2175
        }
13✔
2176
        else {
2177
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2178
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2179
        }
2180

2181
        if (h->num_recv_i) {
39✔
2182
            avg = h->total_time_i / h->num_recv_i;
27✔
2183
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2184
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2185
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2186
        }
9✔
2187

2188
        fprintf(stderr, "\n");
39✔
2189
        if (!cumulative_stats_flag) {
39✔
2190
            stats_reset_interval(h);
27✔
2191
        }
9✔
2192
    }
13✔
2193
}
39✔
2194

2195
/************************************************************
2196

2197
  Function: print_global_stats
2198

2199
*************************************************************
2200

2201
  Inputs:  void (none)
2202

2203
  Description:
2204

2205

2206
************************************************************/
2207

2208
void print_global_stats(void)
9✔
2209
{
2210
    fprintf(stderr, "\n");
9✔
2211
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2212
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2213
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2214
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2215
    fprintf(stderr, "\n");
9✔
2216
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2217
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2218
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2219
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2220
    fprintf(stderr, "\n");
9✔
2221

2222
    if (total_replies == 0) {
9✔
2223
        min_reply = 0;
3✔
2224
        max_reply = 0;
3✔
2225
        total_replies = 1;
3✔
2226
        sum_replies = 0;
3✔
2227
    }
1✔
2228

2229
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2230
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2231
        sprint_tm(sum_replies / total_replies));
3✔
2232
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2233
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2234
        (end_time - start_time) / 1e9);
9✔
2235
    fprintf(stderr, "\n");
9✔
2236
}
9✔
2237

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

2240
  Function: send_ping
2241

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

2244
  Inputs:  int s, HOST_ENTRY *h
2245

2246
  Description:
2247

2248
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2249
  will be added on by the kernel.  The ID field is our UNIX process ID,
2250
  and the sequence number is an index into an array of outstanding
2251
  ping requests. The sequence number will later be used to quickly
2252
  figure out who the ping reply came from.
2253

2254
************************************************************/
2255

2256
int send_ping(HOST_ENTRY *h, int index)
852✔
2257
{
2258
    int n;
2259
    int myseq;
2260
    int ret = 1;
852✔
2261
    uint8_t proto = ICMP_ECHO;
852✔
2262

2263
    update_current_time();
852✔
2264
    h->last_send_time = current_time_ns;
852✔
2265
    myseq = seqmap_add(h->i, index, current_time_ns);
852✔
2266

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

2269
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
852✔
2270
        if(icmp_request_typ == 13)
729✔
2271
            proto = ICMP_TSTAMP;
16✔
2272
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
729✔
2273
    }
227✔
2274
#ifdef IPV6
2275
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2276
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2277
    }
15✔
2278
#endif
2279
    else {
2280
        return 0;
×
2281
    }
2282

2283
    /* error sending? */
2284
    if (
610✔
2285
        (n < 0)
242✔
2286
#if defined(EHOSTDOWN)
2287
        && errno != EHOSTDOWN
276✔
2288
#endif
2289
    ) {
2290
        if (verbose_flag) {
34✔
2291
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2292
        }
2293
        else {
2294
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2295
        }
2296

2297
        h->num_sent++;
34✔
2298
        h->num_sent_i++;
34✔
2299
        if (!loop_flag)
34✔
2300
            h->resp_times[index] = RESP_ERROR;
34✔
2301

2302
        ret = 0;
34✔
2303
    }
2304
    else {
2305
        /* schedule timeout */
2306
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
818✔
2307

2308
        /* mark this trial as outstanding */
2309
        if (!loop_flag) {
818✔
2310
            h->resp_times[index] = RESP_WAITING;
818✔
2311
        }
242✔
2312
    }
2313

2314
    num_pingsent++;
852✔
2315
    last_send_time = h->last_send_time;
852✔
2316

2317
    return (ret);
852✔
2318
}
242✔
2319

2320
int socket_can_read(struct timeval *timeout)
2,524✔
2321
{
2322
    int nfound;
2323
    fd_set readset;
2324
    int socketmax;
2325

2326
#ifndef IPV6
2327
    socketmax = socket4;
2328
#else
2329
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,524✔
2330
#endif
2331

2332
select_again:
1,902✔
2333
    FD_ZERO(&readset);
32,956✔
2334
    if (socket4 >= 0)
2,524✔
2335
        FD_SET(socket4, &readset);
2,524✔
2336
#ifdef IPV6
2337
    if (socket6 >= 0)
2,524✔
2338
        FD_SET(socket6, &readset);
2,524✔
2339
#endif
2340

2341
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,524✔
2342
    if (nfound < 0) {
2,524✔
2343
        if (errno == EINTR) {
×
2344
            /* interrupted system call: redo the select */
2345
            goto select_again;
×
2346
        }
2347
        else {
2348
            perror("select");
×
2349
        }
2350
    }
2351

2352
    if (nfound > 0) {
2,524✔
2353
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,167✔
2354
            return socket4;
1,076✔
2355
        }
2356
#ifdef IPV6
2357
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
91✔
2358
            return socket6;
91✔
2359
        }
2360
#endif
2361
    }
2362

2363
    return -1;
1,357✔
2364
}
622✔
2365

2366
int receive_packet(int64_t wait_time,
2,524✔
2367
#if HAVE_SO_TIMESTAMPNS
2368
    int64_t *reply_timestamp,
2369
#else
2370
    int64_t *reply_timestamp __attribute__((unused)),
2371
#endif
2372
    struct sockaddr *reply_src_addr,
2373
    size_t reply_src_addr_len,
2374
    char *reply_buf,
2375
    size_t reply_buf_len,
2376
    int *ip_header_tos,
2377
    int *ip_header_ttl)
2378
{
2379
    struct timeval to;
2380
    int s = 0;
2,524✔
2381
    int recv_len;
2382
    static unsigned char msg_control[128];
2383
    struct iovec msg_iov = {
3,768✔
2384
        reply_buf,
622✔
2385
        reply_buf_len
622✔
2386
    };
2387
    struct msghdr recv_msghdr = {0};
2,524✔
2388
    recv_msghdr.msg_name = reply_src_addr;
2,524✔
2389
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,524✔
2390
    recv_msghdr.msg_iov = &msg_iov;
2,524✔
2391
    recv_msghdr.msg_iovlen = 1;
2,524✔
2392
    recv_msghdr.msg_control = &msg_control;
2,524✔
2393
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,524✔
2394
    struct cmsghdr *cmsg;
2395

2396
    /* Wait for a socket to become ready */
2397
    if (wait_time) {
2,524✔
2398
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,357✔
2399
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,357✔
2400
    }
403✔
2401
    else {
2402
        to.tv_sec = 0;
1,167✔
2403
        to.tv_usec = 0;
1,167✔
2404
    }
2405
    s = socket_can_read(&to);
2,524✔
2406
    if (s == -1) {
2,524✔
2407
        return 0; /* timeout */
1,357✔
2408
    }
2409

2410
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,167✔
2411
    if (recv_len <= 0) {
1,167✔
2412
        return 0;
×
2413
    }
2414

2415
    /* ancilliary data */
2416
    {
2417
#if HAVE_SO_TIMESTAMPNS
2418
        struct timespec reply_timestamp_ts;
2419
        struct timeval reply_timestamp_tv;
2420
#endif
2421
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,167✔
2422
             cmsg != NULL;
2,123✔
2423
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
956✔
2424
#if HAVE_SO_TIMESTAMPNS
2425
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
956✔
2426
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
948✔
2427
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
948✔
2428
            }
2429
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
956✔
2430
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2431
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2432
            }
2433
#endif
2434
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
956✔
2435
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2436
            }
2437
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
956✔
2438
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2439
            }
2440
#ifdef IPV6
2441
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
956✔
2442
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2443
            }
2444
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
956✔
2445
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2446
            }
2447
#endif
2448
        }
2449
    }
2450

2451
#if defined(DEBUG) || defined(_DEBUG)
2452
    if (randomly_lose_flag) {
2453
        if ((random() & 0x07) <= lose_factor)
2454
            return 0;
2455
    }
2456
#endif
2457

2458
    return recv_len;
1,167✔
2459
}
622✔
2460

2461
/* stats_add: update host statistics for a single packet that was received (or timed out)
2462
 * h: host entry to update
2463
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2464
 * success: 1 if response received, 0 otherwise
2465
 * latency: response time, in ns
2466
 */
2467
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
818✔
2468
{
2469
    /* sent count - we update only on receive/timeout, so that we don't get
2470
     * weird loss percentage, just because a packet was note recived yet.
2471
     */
2472
    h->num_sent++;
818✔
2473
    h->num_sent_i++;
818✔
2474

2475
    if (!success) {
818✔
2476
        if (!loop_flag && index >= 0) {
107✔
2477
            h->resp_times[index] = RESP_TIMEOUT;
107✔
2478
        }
27✔
2479
        num_timeout++;
107✔
2480
        return;
107✔
2481
    }
2482

2483
    /* received count */
2484
    h->num_recv++;
711✔
2485
    h->num_recv_i++;
711✔
2486

2487
    /* maximum */
2488
    if (!h->max_reply || latency > h->max_reply) {
711✔
2489
        h->max_reply = latency;
560✔
2490
    }
168✔
2491
    if (!h->max_reply_i || latency > h->max_reply_i) {
711✔
2492
        h->max_reply_i = latency;
574✔
2493
    }
173✔
2494

2495
    /* minimum */
2496
    if (!h->min_reply || latency < h->min_reply) {
711✔
2497
        h->min_reply = latency;
471✔
2498
    }
134✔
2499
    if (!h->min_reply_i || latency < h->min_reply_i) {
711✔
2500
        h->min_reply_i = latency;
500✔
2501
    }
145✔
2502

2503
    /* total time (for average) */
2504
    h->total_time += latency;
711✔
2505
    h->total_time_i += latency;
711✔
2506

2507
    /* response time per-packet (count mode) */
2508
    if (!loop_flag && index >= 0) {
711✔
2509
        h->resp_times[index] = latency;
711✔
2510
    }
215✔
2511
}
242✔
2512

2513
/* stats_reset_interval: reset interval statistics
2514
 * h: host entry to update
2515
 */
2516
void stats_reset_interval(HOST_ENTRY *h)
30✔
2517
{
2518
    h->num_sent_i = 0;
30✔
2519
    h->num_recv_i = 0;
30✔
2520
    h->max_reply_i = 0;
30✔
2521
    h->min_reply_i = 0;
30✔
2522
    h->total_time_i = 0;
30✔
2523
}
30✔
2524

2525
int decode_icmp_ipv4(
1,076✔
2526
    struct sockaddr *response_addr,
2527
    size_t response_addr_len,
2528
    char *reply_buf,
2529
    size_t reply_buf_len,
2530
    unsigned short *id,
2531
    unsigned short *seq,
2532
    int *ip_header_tos,
2533
    int *ip_header_ttl,
2534
    uint32_t *ip_header_otime_ms,
2535
    uint32_t *ip_header_rtime_ms,
2536
    uint32_t *ip_header_ttime_ms)
2537
{
2538
    struct icmp *icp;
2539
    int hlen = 0;
1,076✔
2540

2541
    if (!using_sock_dgram4) {
1,076✔
2542
        struct ip *ip = (struct ip *)reply_buf;
1,068✔
2543
        *ip_header_tos = ip->ip_tos;
1,068✔
2544
        *ip_header_ttl = ip->ip_ttl;
1,068✔
2545

2546
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2547
        /* The alpha headers are decidedly broken.
2548
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2549
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2550
         */
2551
        hlen = (ip->ip_vhl & 0x0F) << 2;
2552
#else
2553
        hlen = ip->ip_hl << 2;
1,068✔
2554
#endif
2555
    }
204✔
2556

2557
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,076✔
2558
        /* too short */
2559
        if (verbose_flag) {
×
2560
            char buf[INET6_ADDRSTRLEN];
2561
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2562
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2563
        }
2564
        return -1;
×
2565
    }
2566

2567
    icp = (struct icmp *)(reply_buf + hlen);
1,076✔
2568

2569
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,076✔
2570
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
454✔
2571
        /* Handle other ICMP packets */
2572
        struct icmp *sent_icmp;
2573
        SEQMAP_VALUE *seqmap_value;
2574
        char addr_ascii[INET6_ADDRSTRLEN];
2575
        HOST_ENTRY *h;
2576

2577
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2578
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
840✔
2579
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2580
            return -1;
×
2581
        }
2582

2583
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
432✔
2584

2585
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
432✔
2586
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
14✔
2587
            sent_icmp->icmp_id != ident4) {
×
2588
            /* not caused by us */
2589
            return -1;
432✔
2590
        }
2591

2592
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2593
        if (seqmap_value == NULL) {
×
2594
            return -1;
×
2595
        }
2596

2597
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2598

2599
        switch (icp->icmp_type) {
×
2600
        case ICMP_UNREACH:
2601
            h = table[seqmap_value->host_nr];
×
2602
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2603
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2604
                    addr_ascii, h->host);
2605
            }
2606
            else {
2607
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2608
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2609
            }
2610

2611
            print_warning("\n");
×
2612
            num_othericmprcvd++;
×
2613
            break;
×
2614

2615
        case ICMP_SOURCEQUENCH:
2616
        case ICMP_REDIRECT:
2617
        case ICMP_TIMXCEED:
2618
        case ICMP_PARAMPROB:
2619
            h = table[seqmap_value->host_nr];
×
2620
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2621
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2622
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2623
            }
2624
            else {
2625
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2626
                    icp->icmp_type, addr_ascii, h->host);
×
2627
            }
2628
            print_warning("\n");
×
2629
            num_othericmprcvd++;
×
2630
            break;
×
2631
        }
2632

2633
        return -1;
×
2634
    }
2635

2636
    *id = icp->icmp_id;
644✔
2637
    *seq = ntohs(icp->icmp_seq);
644✔
2638
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
644✔
2639

2640
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2641
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
14✔
2642
            if (verbose_flag) {
×
2643
                char buf[INET6_ADDRSTRLEN];
2644
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2645
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2646
            }
2647
            return -1;
×
2648
        }
2649

2650
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
14✔
2651
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
14✔
2652
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
14✔
2653
    }
2654

2655
    return hlen;
644✔
2656
}
204✔
2657

2658
#ifdef IPV6
2659
int decode_icmp_ipv6(
91✔
2660
    struct sockaddr *response_addr,
2661
    size_t response_addr_len,
2662
    char *reply_buf,
2663
    size_t reply_buf_len,
2664
    unsigned short *id,
2665
    unsigned short *seq)
2666
{
2667
    struct icmp6_hdr *icp;
2668

2669
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
91✔
2670
        if (verbose_flag) {
×
2671
            char buf[INET6_ADDRSTRLEN];
2672
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
NEW
2673
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2674
        }
2675
        return 0; /* too short */
×
2676
    }
2677

2678
    icp = (struct icmp6_hdr *)reply_buf;
91✔
2679

2680
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
91✔
2681
        /* Handle other ICMPv6 packets */
2682
        struct ip6_hdr *sent_ipv6;
2683
        struct icmp6_hdr *sent_icmp;
2684
        SEQMAP_VALUE *seqmap_value;
2685
        char addr_ascii[INET6_ADDRSTRLEN];
2686
        HOST_ENTRY *h;
2687

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

NEW
2694
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
×
NEW
2695
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
×
2696
            /* discard ICMPv6 message if we can't tell that it was caused by
2697
             * us, because the IPv6 header is not directly followed by an
2698
             * ICMPv6 header
2699
             */
2700
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
NEW
2701
            return 0;
×
2702
        }
NEW
2703
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
×
2704

NEW
2705
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
×
2706
            /* not caused by us */
UNCOV
2707
            return 0;
×
2708
        }
2709

2710
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2711
        if (seqmap_value == NULL) {
×
2712
            return 0;
×
2713
        }
2714

2715
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
NEW
2716
        h = table[seqmap_value->host_nr];
×
2717

2718
        switch (icp->icmp6_type) {
×
2719
        case ICMP6_DST_UNREACH:
NEW
2720
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
NEW
2721
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2722
                    icp->icmp6_code, addr_ascii, h->host);
×
2723
            } else {
NEW
2724
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2725
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2726
            }
UNCOV
2727
            print_warning("\n");
×
2728
            num_othericmprcvd++;
×
2729
            break;
×
2730

2731
        case ICMP6_PACKET_TOO_BIG:
NEW
2732
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
2733
                addr_ascii, h->host);
NEW
2734
            num_othericmprcvd++;
×
NEW
2735
            break;
×
2736

2737
        case ICMP6_TIME_EXCEEDED:
NEW
2738
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
NEW
2739
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2740
                    icp->icmp6_code, addr_ascii, h->host);
×
2741
            } else {
NEW
2742
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2743
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
2744
            }
NEW
2745
            print_warning("\n");
×
NEW
2746
            num_othericmprcvd++;
×
NEW
2747
            break;
×
2748

2749
        case ICMP6_PARAM_PROB:
NEW
2750
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
NEW
2751
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2752
                    icp->icmp6_code, addr_ascii, h->host);
×
2753
            } else {
NEW
2754
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
NEW
2755
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
2756
            }
2757
            print_warning("\n");
×
2758
            num_othericmprcvd++;
×
2759
            break;
×
2760

2761
        default:
NEW
2762
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
NEW
2763
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
NEW
2764
            num_othericmprcvd++;
×
NEW
2765
            break;
×
2766
        }
2767

2768
        return 0;
×
2769
    }
2770

2771
    *id = icp->icmp6_id;
91✔
2772
    *seq = ntohs(icp->icmp6_seq);
91✔
2773

2774
    return 1;
91✔
2775
}
15✔
2776
#endif
2777

2778
int wait_for_reply(int64_t wait_time)
2,524✔
2779
{
2780
    int result;
2781
    static char buffer[RECV_BUFSIZE];
2782
    struct sockaddr_storage response_addr;
2783
    int n, avg;
2784
    HOST_ENTRY *h;
2785
    int64_t this_reply;
2786
    int this_count;
2787
    int64_t recv_time = 0;
2,524✔
2788
    SEQMAP_VALUE *seqmap_value;
2789
    unsigned short id;
2790
    unsigned short seq;
2791
    int ip_header_tos = -1;
2,524✔
2792
    int ip_header_ttl = -1;
2,524✔
2793
    // ICMP Timestamp
2794
    uint32_t ip_header_otime_ms = 0x80000000U;
2,524✔
2795
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,524✔
2796
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,524✔
2797

2798
    /* Receive packet */
2799
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,524✔
2800
        &recv_time, /* reply_timestamp */
2801
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2802
        sizeof(response_addr), /* reply_src_addr_len */
2803
        buffer, /* reply_buf */
2804
        sizeof(buffer), /* reply_buf_len */
2805
        &ip_header_tos, /* TOS resp. TC byte */
2806
        &ip_header_ttl /* TTL resp. hop limit */
2807
    );
2808

2809
    if (result <= 0) {
2,524✔
2810
        return 0;
1,357✔
2811
    }
2812

2813
    update_current_time();
1,167✔
2814
    if (recv_time == 0)
1,167✔
2815
        recv_time = current_time_ns;
219✔
2816

2817
    /* Process ICMP packet and retrieve id/seq */
2818
    if (response_addr.ss_family == AF_INET) {
1,167✔
2819
        int ip_hlen = decode_icmp_ipv4(
1,076✔
2820
            (struct sockaddr *)&response_addr,
2821
            sizeof(response_addr),
2822
            buffer,
2823
            sizeof(buffer),
2824
            &id,
2825
            &seq,
2826
            &ip_header_tos,
2827
            &ip_header_ttl,
2828
            &ip_header_otime_ms,
2829
            &ip_header_rtime_ms,
2830
            &ip_header_ttime_ms);
2831
        if (ip_hlen < 0) {
1,076✔
2832
            return 1;
432✔
2833
        }
2834
        if (id != ident4) {
644✔
2835
            return 1; /* packet received, but not the one we are looking for! */
×
2836
        }
2837
        if (!using_sock_dgram4) {
644✔
2838
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2839
             * with fping with IPv6 hosts */
2840
            result -= ip_hlen;
636✔
2841
        }
204✔
2842
    }
204✔
2843
#ifdef IPV6
2844
    else if (response_addr.ss_family == AF_INET6) {
91✔
2845
        if (!decode_icmp_ipv6(
91✔
2846
                (struct sockaddr *)&response_addr,
2847
                sizeof(response_addr),
2848
                buffer,
2849
                sizeof(buffer),
2850
                &id,
2851
                &seq)) {
UNCOV
2852
            return 1;
×
2853
        }
2854
        if (id != ident6) {
91✔
2855
            return 1; /* packet received, but not the one we are looking for! */
×
2856
        }
2857
    }
15✔
2858
#endif
2859
    else {
2860
        return 1;
×
2861
    }
2862

2863
    seqmap_value = seqmap_fetch(seq, current_time_ns);
735✔
2864
    if (seqmap_value == NULL) {
735✔
2865
        return 1;
12✔
2866
    }
2867

2868
    /* find corresponding host_entry */
2869
    n = seqmap_value->host_nr;
723✔
2870
    h = table[n];
723✔
2871
    this_count = seqmap_value->ping_count;
723✔
2872
    this_reply = recv_time - seqmap_value->ping_ts;
723✔
2873

2874
    /* update stats that include invalid replies */
2875
    h->num_recv_total++;
723✔
2876
    num_pingreceived++;
723✔
2877

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

2880
    /* optionally require reply source equal to target address */
2881
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
723✔
2882
        dbg_printf("%s\n", "discarding reply from wrong source address");
2883
        return 1;
12✔
2884
    }
2885

2886
    /* discard duplicates */
2887
    if (!loop_flag && h->resp_times[this_count] >= 0) {
711✔
2888
        if (!per_recv_flag) {
×
2889
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2890
                h->host, this_count, result, sprint_tm(this_reply));
2891

2892
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2893
                char buf[INET6_ADDRSTRLEN];
2894
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2895
                fprintf(stderr, " [<- %s]", buf);
×
2896
            }
2897
            fprintf(stderr, "\n");
×
2898
        }
2899
        return 1;
×
2900
    }
2901

2902
    /* discard reply if delay is larger than timeout
2903
     * (see also: github #32) */
2904
    if (this_reply > h->timeout) {
711✔
2905
        return 1;
×
2906
    }
2907

2908
    /* update stats */
2909
    stats_add(h, this_count, 1, this_reply);
711✔
2910
    // TODO: move to stats_add?
2911
    if (!max_reply || this_reply > max_reply)
711✔
2912
        max_reply = this_reply;
439✔
2913
    if (!min_reply || this_reply < min_reply)
711✔
2914
        min_reply = this_reply;
374✔
2915
    sum_replies += this_reply;
711✔
2916
    total_replies++;
711✔
2917

2918
    /* initialize timeout to initial timeout (without backoff) */
2919
    h->timeout = timeout;
711✔
2920

2921
    /* remove timeout event */
2922
    struct event *timeout_event = host_get_timeout_event(h, this_count);
711✔
2923
    if (timeout_event) {
711✔
2924
        ev_remove(&event_queue_timeout, timeout_event);
711✔
2925
    }
215✔
2926

2927
    /* print "is alive" */
2928
    if (h->num_recv == 1) {
711✔
2929
        num_alive++;
430✔
2930
        if (fast_reachable && num_alive >= min_reachable)
430✔
2931
                finish_requested = 1;
3✔
2932

2933
        if (verbose_flag || alive_flag) {
430✔
2934
            printf("%s", h->host);
299✔
2935

2936
            if (verbose_flag)
299✔
2937
                printf(" is alive");
279✔
2938
        }
85✔
2939
    }
124✔
2940

2941
    /* print received ping (unless --quiet) */
2942
    if (per_recv_flag) {
711✔
2943
        if (timestamp_flag) {
151✔
2944
            print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2945
        }
10✔
2946
        avg = h->total_time / h->num_recv;
151✔
2947
        printf("%-*s : [%d], %d bytes, %s ms",
151✔
2948
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2949
        printf(" (%s avg, ", sprint_tm(avg));
151✔
2950

2951
        if (h->num_recv <= h->num_sent) {
151✔
2952
            printf("%d%% loss)",
151✔
2953
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2954
        }
45✔
2955
        else {
2956
            printf("%d%% return)",
×
2957
                (h->num_recv_total * 100) / h->num_sent);
×
2958
        }
2959
    }
45✔
2960

2961
    if (verbose_flag || alive_flag || per_recv_flag) {
711✔
2962

2963
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
450✔
2964
            char buf[INET6_ADDRSTRLEN];
2965
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2966
            fprintf(stderr, " [<- %s]", buf);
6✔
2967
        }
2968

2969
        if (icmp_request_typ == 13) {
450✔
2970
            printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2971
                   alive_flag ? "" : ",",
14✔
2972
                   ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
2973
                   ms_since_midnight_utc(recv_time));
2974
        }
2975

2976
        if(print_tos_flag) {
450✔
2977
            if(ip_header_tos != -1) {
43✔
2978
                printf(" (TOS %d)", ip_header_tos);
43✔
2979
            }
11✔
2980
            else {
2981
                printf(" (TOS unknown)");
×
2982
            }
2983
        }
11✔
2984

2985
        if (print_ttl_flag) {
450✔
2986
          if(ip_header_ttl != -1) {
43✔
2987
              printf(" (TTL %d)", ip_header_ttl);
43✔
2988
          }
11✔
2989
          else {
2990
              printf(" (TTL unknown)");
×
2991
          }
2992
        }
11✔
2993

2994
        if (elapsed_flag && !per_recv_flag)
450✔
2995
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2996

2997
        printf("\n");
450✔
2998
    }
130✔
2999

3000
    return 1;
711✔
3001
}
622✔
3002

3003
/************************************************************
3004

3005
  Function: add_name
3006

3007
*************************************************************
3008

3009
  Inputs:  char* name
3010

3011
  Description:
3012

3013
  process input name for addition to target list
3014
  name can turn into multiple targets via multiple interfaces (-m)
3015
  or via NIS groups
3016

3017
************************************************************/
3018

3019
void add_name(char *name)
544✔
3020
{
3021
    struct addrinfo *res0, *res, hints;
3022
    int ret_ga;
3023
    char *printname;
3024
    char namebuf[256];
3025
    char addrbuf[256];
3026

3027
    /* getaddrinfo */
3028
    memset(&hints, 0, sizeof(struct addrinfo));
544✔
3029
    hints.ai_flags = AI_UNUSABLE;
544✔
3030
    hints.ai_socktype = SOCK_RAW;
544✔
3031
    hints.ai_family = hints_ai_family;
544✔
3032
    if (hints_ai_family == AF_INET) {
544✔
3033
        hints.ai_protocol = IPPROTO_ICMP;
79✔
3034
    }
21✔
3035
#ifdef IPV6
3036
    else if (hints_ai_family == AF_INET6) {
465✔
3037
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
3038
    }
1✔
3039
#endif
3040
    else {
3041
        hints.ai_socktype = SOCK_STREAM;
434✔
3042
        hints.ai_protocol = 0;
434✔
3043
    }
3044
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
544✔
3045
    if (ret_ga) {
544✔
3046
        if (!quiet_flag)
26✔
3047
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3048
        num_noaddress++;
26✔
3049
        return;
26✔
3050
    }
3051

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

3071
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3072
                printname = namebuf;
3✔
3073
            }
1✔
3074
            else {
3075
                printname = name;
×
3076
            }
3077
        }
1✔
3078
        else {
3079
            printname = name;
515✔
3080
        }
3081

3082
        /* addr_flag: name -> addr lookup requested */
3083
        if (addr_flag) {
518✔
3084
            int ret;
3085
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3086
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3087
            if (ret) {
3✔
3088
                if (!quiet_flag) {
×
3089
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3090
                }
3091
                continue;
×
3092
            }
3093

3094
            if (name_flag || rdns_flag) {
3✔
3095
                char nameaddrbuf[512 + 3];
3096
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3097
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3098
            }
3099
            else {
3100
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3101
            }
3102
        }
1✔
3103
        else {
3104
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
515✔
3105
        }
3106

3107
        if (!multif_flag) {
518✔
3108
            break;
518✔
3109
        }
3110
    }
3111

3112
    freeaddrinfo(res0);
518✔
3113
}
146✔
3114

3115
/************************************************************
3116

3117
  Function: add_addr
3118

3119
*************************************************************
3120

3121
  Description:
3122

3123
  add single address to list of hosts to be pinged
3124

3125
************************************************************/
3126

3127
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
518✔
3128
{
3129
    HOST_ENTRY *p;
3130
    int n;
3131
    int64_t *i;
3132

3133
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
518✔
3134
    if (!p)
518✔
3135
        crash_and_burn("can't allocate HOST_ENTRY");
×
3136

3137
    p->name = strdup(name);
518✔
3138
    p->host = strdup(host);
518✔
3139
    memcpy(&p->saddr, ipaddr, ipaddr_len);
518✔
3140
    p->saddr_len = ipaddr_len;
518✔
3141
    p->timeout = timeout;
518✔
3142
    p->min_reply = 0;
518✔
3143

3144
    if (netdata_flag) {
518✔
3145
        char *s = p->name;
3✔
3146
        while (*s) {
30✔
3147
            if (!isalnum(*s))
27✔
3148
                *s = '_';
9✔
3149
            s++;
27✔
3150
        }
3151
    }
1✔
3152

3153
    if (strlen(p->host) > max_hostname_len)
518✔
3154
        max_hostname_len = strlen(p->host);
331✔
3155

3156
    /* array for response time results */
3157
    if (!loop_flag) {
518✔
3158
        i = (int64_t *)malloc(trials * sizeof(int64_t));
518✔
3159
        if (!i)
518✔
3160
            crash_and_burn("can't allocate resp_times array");
×
3161

3162
        for (n = 1; n < trials; n++)
2,063✔
3163
            i[n] = RESP_UNUSED;
1,545✔
3164

3165
        p->resp_times = i;
518✔
3166
    }
138✔
3167

3168
    /* allocate event storage */
3169
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
518✔
3170
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
518✔
3171

3172
    /* schedule first ping */
3173
    host_add_ping_event(p, 0, current_time_ns);
518✔
3174

3175
    num_hosts++;
518✔
3176
}
518✔
3177

3178
/************************************************************
3179

3180
  Function: crash_and_burn
3181

3182
*************************************************************
3183

3184
  Inputs:  char* message
3185

3186
  Description:
3187

3188
************************************************************/
3189

3190
void crash_and_burn(char *message)
×
3191
{
3192
    fprintf(stderr, "%s: %s\n", prog, message);
×
3193
    exit(4);
×
3194
}
3195

3196
/************************************************************
3197

3198
  Function: errno_crash_and_burn
3199

3200
*************************************************************
3201

3202
  Inputs:  char* message
3203

3204
  Description:
3205

3206
************************************************************/
3207

3208
void errno_crash_and_burn(char *message)
8✔
3209
{
3210
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3211
    exit(4);
8✔
3212
}
3213

3214
/************************************************************
3215

3216
  Function: print_warning
3217

3218
  Description: fprintf(stderr, ...), unless running with -q
3219

3220
*************************************************************/
3221

3222
void print_warning(char *format, ...)
54✔
3223
{
3224
    va_list args;
3225
    if (!quiet_flag) {
54✔
3226
        va_start(args, format);
54✔
3227
        vfprintf(stderr, format, args);
54✔
3228
        va_end(args);
54✔
3229
    }
8✔
3230
}
54✔
3231

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

3234
  Function: sprint_tm
3235

3236
*************************************************************
3237

3238
  render nanosecond int64_t value into milliseconds string with three digits of
3239
  precision.
3240

3241
************************************************************/
3242

3243
const char *sprint_tm(int64_t ns)
851✔
3244
{
3245
    static char buf[10];
3246
    double t = (double)ns / 1e6;
851✔
3247

3248
    if (t < 0.0) {
851✔
3249
        /* negative (unexpected) */
3250
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3251
    }
3252
    else if (t < 1.0) {
851✔
3253
        /* <= 0.99 ms */
3254
        snprintf(buf, sizeof(buf), "%.3f", t);
850✔
3255
    }
260✔
3256
    else if (t < 10.0) {
1✔
3257
        /* 1.00 - 9.99 ms */
3258
        snprintf(buf, sizeof(buf), "%.2f", t);
1✔
3259
    }
1✔
3260
    else if (t < 100.0) {
×
3261
        /* 10.0 - 99.9 ms */
3262
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3263
    }
3264
    else if (t < 1000000.0) {
×
3265
        /* 100 - 1'000'000 ms */
3266
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3267
    }
3268
    else {
3269
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3270
    }
3271

3272
    return (buf);
851✔
3273
}
3274

3275
/************************************************************
3276

3277
  Function: addr_cmp
3278

3279
*************************************************************/
3280
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
472✔
3281
{
3282
    if (a->sa_family != b->sa_family) {
472✔
3283
        return a->sa_family - b->sa_family;
×
3284
    }
3285
    else {
3286
        if (a->sa_family == AF_INET) {
472✔
3287
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
408✔
3288
        }
3289
        else if (a->sa_family == AF_INET6) {
64✔
3290
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3291
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3292
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3293
        }
3294
    }
3295

3296
    return 0;
×
3297
}
132✔
3298

3299
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
833✔
3300
{
3301
    struct event *event = &h->event_storage_ping[index % event_storage_count];
833✔
3302
    event->host = h;
833✔
3303
    event->ping_index = index;
833✔
3304
    event->ev_time = ev_time;
833✔
3305
    ev_enqueue(&event_queue_ping, event);
833✔
3306

3307
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3308
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3309
}
833✔
3310

3311
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
818✔
3312
{
3313
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
818✔
3314
    event->host = h;
818✔
3315
    event->ping_index = index;
818✔
3316
    event->ev_time = ev_time;
818✔
3317
    ev_enqueue(&event_queue_timeout, event);
818✔
3318

3319
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3320
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3321
}
818✔
3322

3323
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
711✔
3324
{
3325
    return &h->event_storage_timeout[index % event_storage_count];
711✔
3326
}
3327

3328
/************************************************************
3329

3330
  Function: ev_enqueue
3331

3332
  Enqueue an event
3333

3334
  The queue is sorted by event->ev_time, so that queue->first always points to
3335
  the earliest event.
3336

3337
  We start scanning the queue from the tail, because we assume
3338
  that new events mostly get inserted with a event time higher
3339
  than the others.
3340

3341
*************************************************************/
3342
void ev_enqueue(struct event_queue *queue, struct event *event)
1,651✔
3343
{
3344
    struct event *i;
3345
    struct event *i_prev;
3346

3347
    /* Empty list */
3348
    if (queue->last == NULL) {
1,651✔
3349
        event->ev_next = NULL;
1,263✔
3350
        event->ev_prev = NULL;
1,263✔
3351
        queue->first = event;
1,263✔
3352
        queue->last = event;
1,263✔
3353
        return;
1,263✔
3354
    }
3355

3356
    /* Insert on tail? */
3357
    if (event->ev_time - queue->last->ev_time >= 0) {
388✔
3358
        event->ev_next = NULL;
388✔
3359
        event->ev_prev = queue->last;
388✔
3360
        queue->last->ev_next = event;
388✔
3361
        queue->last = event;
388✔
3362
        return;
388✔
3363
    }
3364

3365
    /* Find insertion point */
3366
    i = queue->last;
×
3367
    while (1) {
3368
        i_prev = i->ev_prev;
×
3369
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3370
            event->ev_prev = i_prev;
×
3371
            event->ev_next = i;
×
3372
            i->ev_prev = event;
×
3373
            if (i_prev != NULL) {
×
3374
                i_prev->ev_next = event;
×
3375
            }
3376
            else {
3377
                queue->first = event;
×
3378
            }
3379
            return;
×
3380
        }
3381
        i = i_prev;
×
3382
    }
3383
}
481✔
3384

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

3387
  Function: ev_dequeue
3388

3389
*************************************************************/
3390
struct event *ev_dequeue(struct event_queue *queue)
920✔
3391
{
3392
    struct event *dequeued;
3393

3394
    if (queue->first == NULL) {
920✔
3395
        return NULL;
×
3396
    }
3397
    dequeued = queue->first;
920✔
3398
    ev_remove(queue, dequeued);
920✔
3399

3400
    return dequeued;
920✔
3401
}
260✔
3402

3403
/************************************************************
3404

3405
  Function: ev_remove
3406

3407
*************************************************************/
3408
void ev_remove(struct event_queue *queue, struct event *event)
1,631✔
3409
{
3410
    if (queue->first == event) {
1,631✔
3411
        queue->first = event->ev_next;
1,622✔
3412
    }
472✔
3413
    if (queue->last == event) {
1,631✔
3414
        queue->last = event->ev_prev;
1,264✔
3415
    }
362✔
3416
    if (event->ev_prev) {
1,631✔
3417
        event->ev_prev->ev_next = event->ev_next;
9✔
3418
    }
3✔
3419
    if (event->ev_next) {
1,631✔
3420
        event->ev_next->ev_prev = event->ev_prev;
367✔
3421
    }
113✔
3422
    event->ev_prev = NULL;
1,631✔
3423
    event->ev_next = NULL;
1,631✔
3424
}
1,631✔
3425

3426
/************************************************************
3427

3428
  Function: print_human_readable_time from current_time_ns
3429

3430
*************************************************************/
3431
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
30✔
3432
{
3433
    char time_buffer[100];
3434
    time_t current_time_s;
3435
    struct tm *local_time;
3436

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

3460
/************************************************************
3461

3462
  Function: ms_since_midnight_utc
3463

3464
*************************************************************
3465

3466
  Input: int64_t: current UTC time in ns
3467

3468
  Output: uint32_t: current time in ms since midnight UTC
3469

3470
  Description:
3471

3472
  Return ICMP Timestamp value corresponding to the given time value.
3473
  The given time value must be in UTC.
3474

3475
*************************************************************/
3476
static uint32_t ms_since_midnight_utc(int64_t time_val)
14✔
3477
{
3478
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
14✔
3479
}
3480

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

3483
  Function: usage
3484

3485
*************************************************************
3486

3487
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3488

3489
  Description:
3490

3491
************************************************************/
3492

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