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

schweikert / fping / 16693370439

02 Aug 2025 12:00PM UTC coverage: 86.308% (-0.1%) from 86.427%
16693370439

push

github

auerswal
Update Dockerfile

1557 of 1804 relevant lines covered (86.31%)

331.28 hits per line

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

86.28
/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, json_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_stats_json(void);
437
void print_per_system_splits(void);
438
void print_per_system_splits_json(void);
439
void stats_reset_interval(HOST_ENTRY *h);
440
void print_netdata(void);
441
void print_global_stats(void);
442
void print_global_stats_json(void);
443
void main_loop();
444
void signal_handler(int);
445
void finish();
446
const char *sprint_tm(int64_t t);
447
void ev_enqueue(struct event_queue *queue, struct event *event);
448
struct event *ev_dequeue(struct event_queue *queue);
449
void ev_remove(struct event_queue *queue, struct event *event);
450
void add_cidr(char *);
451
void add_cidr_ipv4(unsigned long, unsigned long);
452
void add_range(char *, char *);
453
void add_addr_range_ipv4(unsigned long, unsigned long);
454
#ifdef IPV6
455
uint64_t be_octets_to_uint64(uint8_t*);
456
void uint64_to_be_octets(uint64_t, uint8_t*);
457
void add_cidr_ipv6(uint64_t, uint64_t, unsigned long, const char *);
458
void add_addr_range_ipv6(uint64_t, uint64_t, uint64_t, uint64_t, const char *);
459
#endif
460
void print_warning(char *fmt, ...);
461
int addr_cmp(struct sockaddr *a, struct sockaddr *b);
462
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
463
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
464
struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
465
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
466
void update_current_time();
467
void print_timestamp_format(int64_t current_time_ns, int timestamp_format);
468
static uint32_t ms_since_midnight_utc(int64_t time_val);
469

470
/************************************************************
471

472
  Function: p_setsockopt
473

474
*************************************************************
475

476
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
477

478
  Description:
479

480
  Elevates privileges to p_uid when required, calls
481
  setsockopt, and drops privileges back.
482

483
************************************************************/
484

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

491
    if (p_uid != saved_uid && seteuid(p_uid)) {
22✔
492
        perror("cannot elevate privileges for setsockopt");
×
493
    }
494

495
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
496

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

503
    return res;
22✔
504
}
505

506
/************************************************************
507

508
  Function: main
509

510
*************************************************************
511

512
  Inputs:  int argc, char** argv
513

514
  Description:
515

516
  Main program entry point
517

518
************************************************************/
519

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

529
    int c;
530
    const uid_t suid = geteuid();
715✔
531
    int tos = 0;
715✔
532
    struct optparse optparse_state;
533
#ifdef USE_SIGACTION
534
    struct sigaction act;
535
#endif
536

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

544
    socket4 = open_ping_socket_ipv4(&socktype4);
713✔
545
#ifdef __linux__
546
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
547
     * structure is missing in the message.
548
     */
549
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
504✔
550
#endif
551

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

561
    memset(&src_addr, 0, sizeof(src_addr));
713✔
562
#ifdef IPV6
563
    memset(&src_addr6, 0, sizeof(src_addr6));
713✔
564
#endif
565

566
    if (!suid && suid != getuid()) {
713✔
567
        /* *temporarily* drop privileges */
568
        if (seteuid(getuid()) == -1)
678✔
569
            perror("cannot setuid");
×
570
    }
186✔
571

572
    optparse_init(&optparse_state, argv);
713✔
573
    ident4 = ident6 = htons(getpid() & 0xFFFF);
713✔
574
    verbose_flag = 1;
713✔
575
    backoff_flag = 1;
713✔
576
    opterr = 1;
713✔
577

578
    /* get command line options */
579

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

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

741
        case 't':
40✔
742
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
42✔
743
                usage(1);
6✔
744
            if (opt_value_float < 0) {
38✔
745
                usage(1);
3✔
746
            }
1✔
747
            timeout = opt_value_float * 1000000;
36✔
748
            timeout_flag = 1;
36✔
749
            break;
36✔
750

751
        case 'r':
36✔
752
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
38✔
753
                usage(1);
6✔
754
            break;
34✔
755

756
        case 'i':
30✔
757
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
758
                usage(1);
6✔
759
            if (opt_value_float < 0) {
29✔
760
                usage(1);
3✔
761
            }
1✔
762
            interval = opt_value_float * 1000000;
27✔
763
            break;
27✔
764

765
        case 'p':
70✔
766
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
99✔
767
                usage(1);
6✔
768
            if (opt_value_float < 0) {
95✔
769
                usage(1);
3✔
770
            }
1✔
771
            perhost_interval = opt_value_float * 1000000;
93✔
772

773
            break;
93✔
774

775
        case 'c':
90✔
776
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
127✔
777
                usage(1);
6✔
778

779
            count_flag = 1;
123✔
780
            break;
123✔
781

782
        case 'C':
34✔
783
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
46✔
784
                usage(1);
6✔
785

786
            count_flag = 1;
42✔
787
            report_all_rtts_flag = 1;
42✔
788
            break;
42✔
789

790
        case 'b':
14✔
791
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
19✔
792
                usage(1);
6✔
793
            size_flag = 1;
15✔
794
            break;
15✔
795

796
        case 'h':
2✔
797
            usage(0);
3✔
798
            break;
1✔
799

800
        case 'q':
26✔
801
            verbose_flag = 0;
38✔
802
            quiet_flag = 1;
38✔
803
            break;
38✔
804

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

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

823
            break;
27✔
824

825
        case 'e':
6✔
826
            elapsed_flag = 1;
8✔
827
            break;
8✔
828

829
        case 'm':
830
            multif_flag = 1;
×
831
            break;
×
832

833
        case 'N':
2✔
834
            netdata_flag = 1;
3✔
835
            break;
3✔
836

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

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

853
        case 'A':
2✔
854
            addr_flag = 1;
3✔
855
            break;
3✔
856

857
        case 'B':
10✔
858
            if (!(backoff = atof(optparse_state.optarg)))
12✔
859
                usage(1);
6✔
860

861
            break;
8✔
862

863
        case 's':
8✔
864
            stats_flag = 1;
12✔
865
            break;
12✔
866

867
        case 'D':
22✔
868
            timestamp_flag = 1;
33✔
869
            break;
33✔
870

871
        case 'R':
4✔
872
            random_data_flag = 1;
5✔
873
            break;
5✔
874

875
        case 'l':
2✔
876
            loop_flag = 1;
3✔
877
            backoff_flag = 0;
3✔
878
            break;
3✔
879

880
        case 'u':
8✔
881
            unreachable_flag = 1;
12✔
882
            break;
12✔
883

884
        case 'a':
18✔
885
            alive_flag = 1;
26✔
886
            break;
26✔
887

888
        case 'H':
8✔
889
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
10✔
890
                usage(1);
6✔
891
            break;
6✔
892

893
#if defined(DEBUG) || defined(_DEBUG)
894
        case 'z':
895
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
896
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
897
                    usage(1);
898

899
            break;
900
#endif /* DEBUG || _DEBUG */
901

902
        case 'v':
4✔
903
            printf("%s: Version %s\n", prog, VERSION);
6✔
904
            exit(0);
6✔
905

906
        case 'x':
8✔
907
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
10✔
908
                usage(1);
6✔
909
            break;
6✔
910

911
        case 'X':
6✔
912
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
913
                usage(1);
6✔
914
            fast_reachable = 1;
5✔
915
            break;
5✔
916

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

925
            if (socket4 >= 0)
6✔
926
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
927
                    perror("fwmark ipv4");
4✔
928

929
#ifdef IPV6
930
            if (socket6 >= 0)
6✔
931
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
932
                    perror("fwmark ipv6");
4✔
933
#endif
934

935
            break;
6✔
936
#endif
937

938
        case 'g':
100✔
939
            /* use IP list generation */
940
            /* mutually exclusive with using file input or command line targets */
941
            generate_flag = 1;
120✔
942
            break;
120✔
943

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

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

981
        case 'J':
42✔
982
            json_flag = 1;
62✔
983
            break;
62✔
984

985
        case 'T':
2✔
986
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
987
            break;
3✔
988

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

1009
        case 'o':
6✔
1010
            outage_flag = 1;
9✔
1011
            break;
9✔
1012

1013
        case '?':
4✔
1014
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
1015
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
1016
            exit(1);
6✔
1017
            break;
1018
        }
1019
    }
1020

1021
    /* permanently drop privileges */
1022
    if (suid != getuid() && setuid(getuid())) {
545✔
1023
        perror("fatal: failed to permanently drop privileges");
×
1024
        /* continuing would be a security hole */
1025
        exit(4);
×
1026
    }
1027

1028
    /* validate various option settings */
1029

1030
#ifndef IPV6
1031
    if (socket4 < 0) {
1032
        crash_and_burn("can't create socket (must run as root?)");
1033
    }
1034
#else
1035
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
545✔
1036
        crash_and_burn("can't create socket (must run as root?)");
×
1037
    }
1038
#endif
1039

1040
    if (ttl > 255) {
543✔
1041
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
1042
        exit(1);
3✔
1043
    }
1044

1045
    if (unreachable_flag && alive_flag) {
540✔
1046
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
1047
        exit(1);
3✔
1048
    }
1049

1050
    if (count_flag && loop_flag) {
537✔
1051
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
1052
        exit(1);
3✔
1053
    }
1054

1055
    if (json_flag && !count_flag) {
534✔
1056
        fprintf(stderr, "%s: option -J, --json requires -c or -C\n", prog);
3✔
1057
        exit(1);
3✔
1058
    }
1059

1060
#ifdef FPING_SAFE_LIMITS
1061
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
531✔
1062
        && getuid()) {
139✔
1063
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
1064
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
1065
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
1066
        exit(1);
6✔
1067
    }
1068
#endif
1069

1070
    if (ping_data_size > MAX_PING_DATA) {
525✔
1071
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1072
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1073
        exit(1);
3✔
1074
    }
1075

1076
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
522✔
1077
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1078
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1079
        exit(1);
6✔
1080
    }
1081

1082
    if (icmp_request_typ == 13 && size_flag != 0) {
516✔
1083
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1084
        exit(1);
9✔
1085
    }
1086

1087
    if (count_flag) {
507✔
1088
        if (verbose_flag)
159✔
1089
            per_recv_flag = 1;
94✔
1090

1091
        alive_flag = unreachable_flag = verbose_flag = 0;
159✔
1092
    }
47✔
1093

1094
    if (loop_flag) {
507✔
1095
        if (!report_interval)
×
1096
            per_recv_flag = 1;
×
1097

1098
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1099
    }
1100

1101
    if (alive_flag || unreachable_flag || min_reachable)
507✔
1102
        verbose_flag = 0;
37✔
1103

1104
    trials = (count > retry + 1) ? count : retry + 1;
523✔
1105

1106
    /* auto-tune default timeout for count/loop modes
1107
     * see also github #32 */
1108
    if (loop_flag || count_flag) {
523✔
1109
        if (!timeout_flag) {
159✔
1110
            timeout = perhost_interval;
154✔
1111
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
154✔
1112
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1113
            }
1114
        }
46✔
1115
    }
47✔
1116

1117
#if defined(DEBUG) || defined(_DEBUG)
1118
    if (debugging & DBG_TRACE)
1119
        trace_flag = 1;
1120

1121
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1122
        randomly_lose_flag = 1;
1123
        lose_factor = 1; /* ie, 1/4 */
1124
    }
1125

1126
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1127
        randomly_lose_flag = 1;
1128
        lose_factor = 5; /* ie, 3/4 */
1129
    }
1130

1131
    if (debugging & DBG_PRINT_PER_SYSTEM)
1132
        print_per_system_flag = 1;
1133

1134
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1135
        report_all_rtts_flag = 1;
1136

1137
    if (trace_flag) {
1138
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1139
            prog, count, retry, interval / 1e6);
1140
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1141
            perhost_interval / 1e6, timeout / 1e6);
1142
        fprintf(stderr, "  seqmap_timeout: %.0f\n", seqmap_timeout / 1e6);
1143
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1144
            ping_data_size, trials);
1145

1146
        if (verbose_flag)
1147
            fprintf(stderr, "  verbose_flag set\n");
1148
        if (multif_flag)
1149
            fprintf(stderr, "  multif_flag set\n");
1150
        if (name_flag)
1151
            fprintf(stderr, "  name_flag set\n");
1152
        if (addr_flag)
1153
            fprintf(stderr, "  addr_flag set\n");
1154
        if (stats_flag)
1155
            fprintf(stderr, "  stats_flag set\n");
1156
        if (unreachable_flag)
1157
            fprintf(stderr, "  unreachable_flag set\n");
1158
        if (alive_flag)
1159
            fprintf(stderr, "  alive_flag set\n");
1160
        if (elapsed_flag)
1161
            fprintf(stderr, "  elapsed_flag set\n");
1162
        if (version_flag)
1163
            fprintf(stderr, "  version_flag set\n");
1164
        if (count_flag)
1165
            fprintf(stderr, "  count_flag set\n");
1166
        if (loop_flag)
1167
            fprintf(stderr, "  loop_flag set\n");
1168
        if (backoff_flag)
1169
            fprintf(stderr, "  backoff_flag set\n");
1170
        if (per_recv_flag)
1171
            fprintf(stderr, "  per_recv_flag set\n");
1172
        if (report_all_rtts_flag)
1173
            fprintf(stderr, "  report_all_rtts_flag set\n");
1174
        if (randomly_lose_flag)
1175
            fprintf(stderr, "  randomly_lose_flag set\n");
1176
        if (print_per_system_flag)
1177
            fprintf(stderr, "  print_per_system_flag set\n");
1178
        if (outage_flag)
1179
            fprintf(stderr, "  outage_flag set\n");
1180
        if (netdata_flag)
1181
            fprintf(stderr, "  netdata_flag set\n");
1182
        if (json_flag)
1183
            fprintf(stderr, "  json_flag set\n");
1184
    }
1185
#endif /* DEBUG || _DEBUG */
1186

1187
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1188
    if (ttl > 0) {
523✔
1189
        if (socket4 >= 0) {
3✔
1190
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1191
                perror("setting time to live");
×
1192
            }
1193
        }
1✔
1194
#ifdef IPV6
1195
        if (socket6 >= 0) {
3✔
1196
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1197
                perror("setting time to live");
×
1198
            }
1199
        }
1✔
1200
#endif
1201
    }
1✔
1202

1203
#if HAVE_SO_TIMESTAMPNS
1204
    {
1205
        int opt = 1;
380✔
1206
        if (socket4 >= 0) {
380✔
1207
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
380✔
1208
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1209
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1210
                }
1211
            }
1212
        }
1213
#ifdef IPV6
1214
        if (socket6 >= 0) {
380✔
1215
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
380✔
1216
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1217
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1218
                }
1219
            }
1220
        }
1221
#endif
1222
    }
1223
#endif
1224

1225
    update_current_time();
523✔
1226
    start_time = current_time_ns;
523✔
1227

1228
    /* handle host names supplied on command line or in a file */
1229
    /* if the generate_flag is on, then generate the IP list */
1230

1231
    argv = &argv[optparse_state.optind];
523✔
1232
    argc -= optparse_state.optind;
523✔
1233

1234
    /* calculate how many ping can be in-flight per host */
1235
    if (count_flag) {
523✔
1236
        event_storage_count = count;
174✔
1237
    }
62✔
1238
    else if (loop_flag) {
349✔
1239
        if (perhost_interval > timeout) {
×
1240
            event_storage_count = 1;
×
1241
        }
1242
        else {
1243
            event_storage_count = 1 + timeout / perhost_interval;
×
1244
        }
1245
    }
1246
    else {
1247
        event_storage_count = 1;
349✔
1248
    }
1249

1250
    /* file and generate are mutually exclusive */
1251
    /* file and command line are mutually exclusive */
1252
    /* generate requires command line parameters beyond the switches */
1253
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
523✔
1254
        usage(1);
19✔
1255

1256
    /* if no conditions are specified, then assume input from stdin */
1257
    if (!*argv && !filename && !generate_flag)
505✔
1258
        filename = "-";
69✔
1259

1260
    if (*argv && !generate_flag) {
505✔
1261
        while (*argv) {
651✔
1262
            add_name(*argv);
356✔
1263
            ++argv;
356✔
1264
        }
1265
    }
77✔
1266
    else if (filename) {
210✔
1267
        FILE *ping_file;
1268
        char line[MAX_TARGET_NAME_LEN + 1];
1269
        char host[MAX_TARGET_NAME_LEN + 1];
1270
        char scratch[MAX_TARGET_NAME_LEN + 1];
1271
        int skip, non_empty;
1272

1273
        if (strcmp(filename, "-") == 0)
96✔
1274
            ping_file = fdopen(0, "r");
72✔
1275
        else
1276
            ping_file = fopen(filename, "r");
24✔
1277

1278
        if (!ping_file)
96✔
1279
            errno_crash_and_burn("fopen");
3✔
1280

1281
        /*
1282
         * Read the first word of every non-comment line, skip everything else.
1283
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1284
         * character is a '#' are interpreted as comments and ignored.)
1285
        */
1286
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1287
            skip = non_empty = 0;
309✔
1288

1289
            /* skip over a prefix of the line where sscanf finds nothing */
1290
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1291
                continue;
99✔
1292
            }
1293

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

1345
            if (non_empty)
186✔
1346
                add_name(host);
144✔
1347
        }
1348

1349
        fclose(ping_file);
70✔
1350
    }
24✔
1351
    else if (*argv && generate_flag) {
114✔
1352
        if (argc == 1) {
114✔
1353
            /* one target: we expect a cidr range (n.n.n.n/m) */
1354
            add_cidr(argv[0]);
59✔
1355
        }
11✔
1356
        else if (argc == 2) {
55✔
1357
            add_range(argv[0], argv[1]);
52✔
1358
        }
6✔
1359
        else {
1360
            usage(1);
3✔
1361
        }
1362
    }
18✔
1363
    else {
1364
        usage(1);
×
1365
    }
1366

1367
    if (!num_hosts) {
425✔
1368
        exit(num_noaddress ? 2 : 1);
36✔
1369
    }
1370

1371
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
389✔
1372
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1373
    }
2✔
1374
#ifdef IPV6
1375
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
387✔
1376
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1377
    }
1378
#endif
1379

1380
    /* allocate and initialize array to map host nr to host_entry */
1381
    {
1382
        struct event *cursor = event_queue_ping.first;
385✔
1383
        int i = 0;
385✔
1384
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
385✔
1385
        if (!table)
385✔
1386
            crash_and_burn("Can't malloc array of hosts");
×
1387
        /* initialize table of hosts. we know that we have ping events scheduled
1388
         * for each of them */
1389
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
960✔
1390
            table[i] = cursor->host;
575✔
1391
            cursor->host->i = i;
575✔
1392
            i++;
575✔
1393
        }
157✔
1394
    }
1395

1396
    init_ping_buffer_ipv4(ping_data_size);
385✔
1397
#ifdef IPV6
1398
    init_ping_buffer_ipv6(ping_data_size);
385✔
1399
#endif
1400

1401
#ifdef USE_SIGACTION
1402
    memset(&act, 0, sizeof(act));
385✔
1403
    act.sa_handler = signal_handler;
385✔
1404
    sigemptyset(&act.sa_mask);
385✔
1405
    sigaddset(&act.sa_mask, SIGINT);
385✔
1406
    sigaddset(&act.sa_mask, SIGQUIT);
385✔
1407
    act.sa_flags = SA_RESTART;
385✔
1408
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
385✔
1409
        crash_and_burn("failure to set signal handler");
24✔
1410
    }
24✔
1411
#else
1412
    signal(SIGINT, signal_handler);
1413
    signal(SIGQUIT, signal_handler);
1414
#endif
1415
    setlinebuf(stdout);
361✔
1416

1417
    if (report_interval) {
361✔
1418
        next_report_time = current_time_ns + report_interval;
27✔
1419
    }
9✔
1420

1421
    last_send_time = 0;
361✔
1422

1423
    seqmap_init(seqmap_timeout);
361✔
1424

1425
    /* main loop */
1426
    main_loop();
361✔
1427

1428
/* Debug: CPU Performance */
1429
#if defined(DEBUG) || defined(_DEBUG)
1430
    perf_cpu_end = clock();
1431
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1432
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1433
#endif /* DEBUG || _DEBUG */
1434

1435
    finish();
361✔
1436

1437
    return 0;
85✔
1438
}
1439

1440
static inline int64_t timespec_ns(struct timespec *a)
5,654✔
1441
{
1442
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,654✔
1443
}
1444

1445
#if HAVE_SO_TIMESTAMPNS
1446
/* convert a struct timeval to nanoseconds */
1447
static inline int64_t timeval_ns(struct timeval *a)
1448
{
1449
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1450
}
1451
#endif /* HAVE_SO_TIMESTAMPNS */
1452

1453
void add_cidr(char *addr)
57✔
1454
{
1455
    char *addr_end;
1456
    char *mask_str;
1457
    unsigned long mask;
1458
    int ret;
1459
    struct addrinfo addr_hints;
1460
    struct addrinfo *addr_res;
1461
    unsigned long net_addr;
1462
#ifdef IPV6
1463
    uint64_t net_upper, net_lower;
1464
    char *scope_str;
1465
#endif /* IPV6 */
1466

1467
    /* Split address from mask */
1468
    addr_end = strrchr(addr, '/');
57✔
1469
    if (addr_end == NULL) {
57✔
1470
        usage(1);
3✔
1471
    }
1✔
1472
    mask_str = addr_end + 1;
55✔
1473

1474
#ifdef IPV6
1475
    /* IPv6 addresses can have a scope */
1476
    scope_str = strchr(addr, '%');
55✔
1477
    if (scope_str && mask_str < scope_str) {
55✔
1478
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1479
        exit(1);
2✔
1480
    }
1481
#endif /*IPV6 */
1482

1483
    *addr_end = '\0';
53✔
1484
    mask = atoi(mask_str);
53✔
1485

1486
    /* parse address */
1487
    memset(&addr_hints, 0, sizeof(struct addrinfo));
53✔
1488
    addr_hints.ai_family = hints_ai_family;
53✔
1489
    addr_hints.ai_flags = AI_NUMERICHOST;
53✔
1490
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
53✔
1491
    if (ret) {
53✔
1492
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1493
        exit(1);
7✔
1494
    }
1495
    if (addr_res->ai_family == AF_INET) {
46✔
1496
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
24✔
1497
        freeaddrinfo(addr_res);
24✔
1498
        add_cidr_ipv4(net_addr, mask);
24✔
1499
#ifdef IPV6
1500
    } else if (addr_res->ai_family == AF_INET6) {
30✔
1501
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1502
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1503
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1504
        freeaddrinfo(addr_res);
22✔
1505
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1506
#endif /* IPV6 */
1507
    } else {
1508
        freeaddrinfo(addr_res);
×
1509
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1510
        exit(1);
×
1511
    }
1512
}
30✔
1513

1514
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1515
{
1516
    unsigned long bitmask;
1517
    unsigned long net_last;
1518

1519
    /* check mask */
1520
    if (mask < 1 || mask > 32) {
24✔
1521
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1522
        exit(1);
6✔
1523
    }
1524

1525
    /* convert mask integer from 1 to 32 to a bitmask */
1526
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1527

1528
    /* calculate network range */
1529
    net_addr &= bitmask;
18✔
1530
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1531

1532
    /* exclude network and broadcast address for regular prefixes */
1533
    if (mask < 31) {
18✔
1534
        net_last--;
12✔
1535
        net_addr++;
12✔
1536
    }
4✔
1537

1538
    /* add all hosts in that network (net_addr and net_last inclusive) */
1539
    add_addr_range_ipv4(net_addr, net_last);
18✔
1540
}
16✔
1541

1542
#ifdef IPV6
1543
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1544
{
1545
    uint64_t bitmask_lower;
1546
    uint64_t last_lower;
1547

1548
    /* check mask -- 2^63 addresses should suffice for now */
1549
    if (mask < 65 || mask > 128) {
23✔
1550
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1551
        exit(1);
7✔
1552
    }
1553

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

1557
    /* calculate network range */
1558
    net_lower &= bitmask_lower;
16✔
1559
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1560

1561
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1562
}
12✔
1563
#endif /* IPV6 */
1564

1565
void add_range(char *start, char *end)
52✔
1566
{
1567
    struct addrinfo addr_hints;
1568
    struct addrinfo *addr_res;
1569
    unsigned long start_long;
1570
    unsigned long end_long;
1571
    int ret;
1572
#ifdef IPV6
1573
    uint64_t start_upper, start_lower;
1574
    uint64_t end_upper, end_lower;
1575
    char *start_scope_str, *end_scope_str;
1576

1577
    /*
1578
     * The compiler does not know that setting the address family hint to
1579
     * ensure that start and end are from the same address family also
1580
     * ensures that either start_long and end_long are initialized and used,
1581
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1582
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1583
     * supported to suppress compiler warnings.
1584
     */
1585
    start_long = -1;
52✔
1586
    end_long = 0;
52✔
1587
    start_upper = start_lower = -1;
52✔
1588
    end_upper = end_lower = 0;
52✔
1589
    start_scope_str = end_scope_str = NULL;
52✔
1590
#endif /* IPV6 */
1591

1592
    /* parse start address */
1593
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1594
    addr_hints.ai_family = hints_ai_family;
52✔
1595
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1596
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1597
    if (ret) {
52✔
1598
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1599
        exit(1);
9✔
1600
    }
1601
    /* start and end must be from the same address family */
1602
    hints_ai_family = addr_res->ai_family;
43✔
1603
    if (addr_res->ai_family == AF_INET) {
43✔
1604
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1605
        freeaddrinfo(addr_res);
17✔
1606
#ifdef IPV6
1607
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1608
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1609
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1610
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1611
        freeaddrinfo(addr_res);
26✔
1612
#endif /* IPV6 */
1613
    } else {
1614
        freeaddrinfo(addr_res);
×
1615
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1616
        exit(1);
×
1617
    }
1618

1619
#ifdef IPV6
1620
    /* IPv6 addresses can have a scope */
1621
    if (hints_ai_family == AF_INET6) {
43✔
1622
        start_scope_str = strchr(start, '%');
26✔
1623
        end_scope_str = strchr(end, '%');
26✔
1624
        if ((!start_scope_str && end_scope_str) ||
26✔
1625
            (start_scope_str && !end_scope_str) ||
24✔
1626
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1627
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1628
                exit(1);
6✔
1629
        }
1630
    }
1631
#endif
1632

1633
    /* parse end address */
1634
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1635
    addr_hints.ai_family = hints_ai_family;
37✔
1636
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1637
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1638
    if (ret) {
37✔
1639
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1640
        exit(1);
7✔
1641
    }
1642
    if (addr_res->ai_family == AF_INET) {
30✔
1643
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1644
        freeaddrinfo(addr_res);
12✔
1645
        add_addr_range_ipv4(start_long, end_long);
12✔
1646
#ifdef IPV6
1647
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1648
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1649
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1650
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1651
        freeaddrinfo(addr_res);
18✔
1652
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1653
#endif /* IPV6 */
1654
    } else {
1655
        freeaddrinfo(addr_res);
×
1656
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1657
        exit(1);
×
1658
    }
1659
}
24✔
1660

1661
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1662
{
1663
    /* check if generator limit is exceeded */
1664
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1665
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1666
        exit(1);
6✔
1667
    }
1668

1669
    /* generate */
1670
    for (; start_long <= end_long; start_long++) {
93✔
1671
        struct in_addr in_addr_tmp;
1672
        char buffer[20];
1673
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1674
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1675
        add_name(buffer);
69✔
1676
    }
23✔
1677
}
24✔
1678

1679
#ifdef IPV6
1680
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1681
{
1682
    int i;
1683
    uint64_t ret = 0;
134✔
1684
    for (i = 0; i < 8; i++) {
1,206✔
1685
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1686
    }
16✔
1687
    return ret;
134✔
1688
}
1689

1690
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1691
{
1692
    int i;
1693
    for (i = 0; i < 8; i++) {
720✔
1694
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1695
    }
1696
}
80✔
1697

1698
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1699
                         uint64_t end_upper, uint64_t end_lower,
1700
                         const char *scope_str)
1701
{
1702
    struct in6_addr in6_addr_tmp;
1703
    char buffer[100];
1704

1705
    /* prevent generating too many addresses */
1706
    if ((start_upper + 1 < end_upper) ||
34✔
1707
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1708
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1709
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1710
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1711
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1712
        exit(1);
8✔
1713
    }
1714

1715
    while ((start_upper < end_upper) ||
66✔
1716
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1717
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1718
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1719
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1720
        if (scope_str) {
40✔
1721
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1722
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1723
                exit(1);
×
1724
            }
1725
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1726
        }
1727
        add_name(buffer);
40✔
1728
        start_lower++;
40✔
1729
        if (start_lower == 0) {
40✔
1730
            start_upper++;
2✔
1731
        }
1732
    }
1733
}
26✔
1734
#endif /* IPv6 */
1735

1736
void main_loop()
373✔
1737
{
1738
    int64_t lt;
1739
    int64_t wait_time_ns;
1740
    struct event *event;
1741
    struct host_entry *h;
1742

1743
    while (event_queue_ping.first || event_queue_timeout.first) {
1,946✔
1744
        dbg_printf("%s", "# main_loop\n");
1745

1746
        /* timeout event ? */
1747
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,590✔
1748
            event = ev_dequeue(&event_queue_timeout);
113✔
1749
            h = event->host;
113✔
1750

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

1753
            stats_add(h, event->ping_index, 0, -1);
113✔
1754

1755
            if (per_recv_flag) {
113✔
1756
                if (json_flag)
25✔
1757
                    printf("{\"timeout\": {");
6✔
1758

1759
                if (timestamp_flag)
25✔
1760
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1761

1762
                if (json_flag)
25✔
1763
                {
1764
                  printf("\"host\": \"%s\", ", h->host);
6✔
1765
                  printf("\"seq\": %d", event->ping_index);
6✔
1766
                }
2✔
1767
                else {
1768
                    printf("%-*s : [%d], timed out",
19✔
1769
                        max_hostname_len, h->host, event->ping_index);
5✔
1770
                }
1771
                if (h->num_recv > 0) {
25✔
1772
                    if (json_flag)
×
1773
                        printf(", \"rttAvg\": %s", sprint_tm(h->total_time / h->num_recv));
×
1774
                    else
1775
                        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1776
                }
1777
                else {
1778
                    if (json_flag)
25✔
1779
                        printf(", \"rttAvg\": \"NaN\"");
6✔
1780
                    else
1781
                        printf(" (NaN avg, ");
19✔
1782
                }
1783
                if (h->num_recv <= h->num_sent) {
25✔
1784
                    if (json_flag) {
25✔
1785
                        printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
6✔
1786
                    }
2✔
1787
                    else {
1788
                        printf("%d%% loss)",
19✔
1789
                            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1790
                    }
1791
                }
7✔
1792
                else {
1793
                    if (json_flag) {
×
1794
                        printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
1795
                    }
1796
                    else {
1797
                        printf("%d%% return)",
×
1798
                            (h->num_recv_total * 100) / h->num_sent);
×
1799
                    }
1800
                }
1801

1802
                if (json_flag)
25✔
1803
                    printf("}}");
6✔
1804
                
1805
                printf("\n");
25✔
1806
            }
7✔
1807

1808
            /* do we need to send a retry? */
1809
            if (!loop_flag && !count_flag) {
113✔
1810
                if (h->num_sent < retry + 1) {
64✔
1811
                    if (backoff_flag) {
39✔
1812
                        h->timeout *= backoff;
39✔
1813
                    }
9✔
1814
                    send_ping(h, event->ping_index);
39✔
1815
                }
9✔
1816
            }
14✔
1817

1818
            /* note: we process first timeout events, because we might need to
1819
             * wait to process ping events, while we for sure never need to
1820
             * wait for timeout events.
1821
             */
1822
            continue;
113✔
1823
        }
1824

1825
        /* ping event ? */
1826
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,736✔
1827
            /* Make sure that we don't ping more than once every "interval" */
1828
            lt = current_time_ns - last_send_time;
1,154✔
1829
            if (lt < interval)
1,154✔
1830
                goto wait_for_reply;
261✔
1831

1832
            /* Dequeue the event */
1833
            event = ev_dequeue(&event_queue_ping);
893✔
1834
            h = event->host;
893✔
1835

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

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

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

1847
    wait_for_reply:
646✔
1848

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

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

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

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

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

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

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

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

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

1911
        update_current_time();
1,460✔
1912

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

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

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

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

1942
  Function: signal_handler
1943

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

1946
  Inputs:  int signum
1947

1948
  Description:
1949

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

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

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

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

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

1970
  Function: update_current_time
1971

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

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

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

1982
  Function: finish
1983

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

1986
  Inputs:  void (none)
1987

1988
  Description:
1989

1990
  Main program clean up and exit point
1991

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

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

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

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

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

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

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

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

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

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

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

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

2058
    exit(0);
311✔
2059
}
2060

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

2063
  Function: print_per_system_stats
2064

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

2067
  Inputs:  void (none)
2068

2069
  Description:
2070

2071

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

2074
void print_per_system_stats(void)
100✔
2075
{
2076
    int i, j, avg, outage_ms;
2077
    HOST_ENTRY *h;
2078
    int64_t resp;
2079

2080
    if (verbose_flag || per_recv_flag)
100✔
2081
        fprintf(stderr, "\n");
59✔
2082

2083
    for (i = 0; i < num_hosts; i++) {
234✔
2084
        h = table[i];
134✔
2085
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
2086

2087
        if (report_all_rtts_flag) {
134✔
2088
            for (j = 0; j < h->num_sent; j++) {
248✔
2089
                if ((resp = h->resp_times[j]) >= 0)
204✔
2090
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2091
                else
2092
                    fprintf(stderr, " -");
8✔
2093
            }
64✔
2094

2095
            fprintf(stderr, "\n");
44✔
2096
        }
12✔
2097
        else {
2098
            if (h->num_recv <= h->num_sent) {
90✔
2099
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
2100
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
2101

2102
                if (outage_flag) {
90✔
2103
                    /* Time outage total */
2104
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
2105
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
2106
                }
3✔
2107
            }
26✔
2108
            else {
2109
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2110
                    h->num_sent, h->num_recv,
2111
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2112
            }
2113

2114
            if (h->num_recv) {
90✔
2115
                avg = h->total_time / h->num_recv;
79✔
2116
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
2117
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
2118
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
2119
            }
23✔
2120

2121
            fprintf(stderr, "\n");
90✔
2122
        }
2123
    }
38✔
2124
}
100✔
2125

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

2128
  Function: print_per_system_stats_json
2129

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

2132
  Inputs:  void (none)
2133

2134
  Description:
2135

2136

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

2139
void print_per_system_stats_json(void)
56✔
2140
{
2141
    int i, j, avg, outage_ms;
2142
    HOST_ENTRY *h;
2143
    int64_t resp;
2144

2145
    for (i = 0; i < num_hosts; i++) {
118✔
2146
        h = table[i];
62✔
2147

2148
        if (report_all_rtts_flag)
62✔
2149
            fprintf(stdout, "{\"vSum\": {");
18✔
2150
        else
2151
            fprintf(stdout, "{\"summary\": {");
44✔
2152

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

2155
        if (report_all_rtts_flag) {
62✔
2156
            fprintf(stdout, "\"values\": [");
18✔
2157
            for (j = 0; j < h->num_sent; j++) {
42✔
2158
                if (j > 0)
24✔
2159
                  fprintf(stdout, ", ");
6✔
2160
                
2161
                if ((resp = h->resp_times[j]) >= 0)
24✔
2162
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2163
                else
2164
                    fprintf(stdout, "null");
3✔
2165
            }
8✔
2166

2167
            fprintf(stdout, "]}");
18✔
2168
        }
6✔
2169
        else {
2170
            if (h->num_recv <= h->num_sent) {
44✔
2171
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
44✔
2172
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
44✔
2173
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
44✔
2174

2175
                if (outage_flag) {
44✔
2176
                    /* Time outage total */
2177
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
2178
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2179
                }
2180
            }
14✔
2181
            else {
2182
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
2183
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
2184
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2185
            }
2186

2187
            if (h->num_recv) {
44✔
2188
                avg = h->total_time / h->num_recv;
41✔
2189
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
41✔
2190
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
41✔
2191
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
41✔
2192
            }
13✔
2193

2194
            fprintf(stdout, "}");
44✔
2195
        }
2196
        fprintf(stdout, "}\n");
62✔
2197
    }
20✔
2198
}
56✔
2199

2200
/************************************************************
2201

2202
  Function: print_netdata
2203

2204
*************************************************************
2205

2206
  Inputs:  void (none)
2207

2208
  Description:
2209

2210

2211
************************************************************/
2212

2213
void print_netdata(void)
3✔
2214
{
2215
    static int sent_charts = 0;
2216

2217
    int i;
2218
    int64_t avg;
2219
    HOST_ENTRY *h;
2220

2221
    for (i = 0; i < num_hosts; i++) {
6✔
2222
        h = table[i];
3✔
2223

2224
        if (!sent_charts) {
3✔
2225
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2226
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2227
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2228
        }
1✔
2229

2230
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2231
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2232
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2233
        printf("END\n");
3✔
2234

2235
        if (!sent_charts) {
3✔
2236
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2237
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2238
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2239
        }
1✔
2240

2241
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2242
        /*
2243
        if( h->num_recv_i <= h->num_sent_i )
2244
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2245
        else
2246
            printf("SET lost = 0\n");
2247
*/
2248

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

2252
        if (!sent_charts) {
3✔
2253
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2254
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2255
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2256
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2257
        }
1✔
2258

2259
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2260
        if (h->num_recv_i) {
3✔
2261
            avg = h->total_time_i / h->num_recv_i;
3✔
2262
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2263
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2264
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2265
        }
1✔
2266
        printf("END\n");
3✔
2267

2268
        stats_reset_interval(h);
3✔
2269
    }
1✔
2270

2271
    sent_charts = 1;
3✔
2272
}
3✔
2273

2274
/************************************************************
2275

2276
  Function: print_per_system_splits
2277

2278
*************************************************************
2279

2280
  Inputs:  void (none)
2281

2282
  Description:
2283

2284

2285
************************************************************/
2286

2287
void print_per_system_splits(void)
39✔
2288
{
2289
    int i, avg, outage_ms_i;
2290
    HOST_ENTRY *h;
2291
    struct tm *curr_tm;
2292

2293
    if (verbose_flag || per_recv_flag)
39✔
2294
        fprintf(stderr, "\n");
×
2295

2296
    update_current_time();
39✔
2297
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2298
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2299
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2300

2301
    for (i = 0; i < num_hosts; i++) {
78✔
2302
        h = table[i];
39✔
2303
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2304

2305
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2306
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2307
                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✔
2308

2309
            if (outage_flag) {
39✔
2310
                /* Time outage  */
2311
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2312
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2313
            }
4✔
2314
        }
13✔
2315
        else {
2316
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2317
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2318
        }
2319

2320
        if (h->num_recv_i) {
39✔
2321
            avg = h->total_time_i / h->num_recv_i;
27✔
2322
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2323
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2324
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2325
        }
9✔
2326

2327
        fprintf(stderr, "\n");
39✔
2328
        if (!cumulative_stats_flag) {
39✔
2329
            stats_reset_interval(h);
27✔
2330
        }
9✔
2331
    }
13✔
2332
}
39✔
2333

2334
/************************************************************
2335

2336
  Function: print_per_system_splits_json
2337

2338
*************************************************************
2339

2340
  Inputs:  void (none)
2341

2342
  Description:
2343

2344

2345
************************************************************/
2346

2347
void print_per_system_splits_json(void)
3✔
2348
{
2349
    int i, avg, outage_ms_i;
2350
    HOST_ENTRY *h;
2351

2352
    update_current_time();
3✔
2353

2354
    for (i = 0; i < num_hosts; i++) {
6✔
2355
        h = table[i];
3✔
2356
        fprintf(stdout, "{\"intSum\": {");
3✔
2357
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2358
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2359

2360
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2361
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2362
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2363
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2364

2365
            if (outage_flag) {
3✔
2366
                /* Time outage  */
2367
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
2368
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2369
            }
2370
        }
1✔
2371
        else {
2372
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
2373
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
2374
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2375
        }
2376

2377
        if (h->num_recv_i) {
3✔
2378
            avg = h->total_time_i / h->num_recv_i;
3✔
2379
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2380
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2381
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2382
        }
1✔
2383

2384
        fprintf(stdout, "}}\n");
3✔
2385
        if (!cumulative_stats_flag) {
3✔
2386
            stats_reset_interval(h);
3✔
2387
        }
1✔
2388
    }
1✔
2389
}
3✔
2390

2391
/************************************************************
2392

2393
  Function: print_global_stats
2394

2395
*************************************************************
2396

2397
  Inputs:  void (none)
2398

2399
  Description:
2400

2401

2402
************************************************************/
2403

2404
void print_global_stats(void)
9✔
2405
{
2406
    fprintf(stderr, "\n");
9✔
2407
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2408
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2409
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2410
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2411
    fprintf(stderr, "\n");
9✔
2412
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2413
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2414
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2415
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2416
    fprintf(stderr, "\n");
9✔
2417

2418
    if (total_replies == 0) {
9✔
2419
        min_reply = 0;
3✔
2420
        max_reply = 0;
3✔
2421
        total_replies = 1;
3✔
2422
        sum_replies = 0;
3✔
2423
    }
1✔
2424

2425
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2426
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2427
        sprint_tm(sum_replies / total_replies));
3✔
2428
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2429
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2430
        (end_time - start_time) / 1e9);
9✔
2431
    fprintf(stderr, "\n");
9✔
2432
}
9✔
2433

2434
/************************************************************
2435

2436
  Function: print_global_stats_json
2437

2438
*************************************************************
2439

2440
  Inputs:  void (none)
2441

2442
  Description:
2443

2444

2445
************************************************************/
2446

2447
void print_global_stats_json(void)
3✔
2448
{
2449
    fprintf(stdout, "{\"globalSum\": {");
3✔
2450
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2451
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2452
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2453
    fprintf(stdout, "\"unknown addresses\": %d, ", num_noaddress);
3✔
2454
    fprintf(stdout, "\"timeouts (waiting for response)\": %d, ", num_timeout);
3✔
2455
    fprintf(stdout, "\"ICMP Echos sent\": %d, ", num_pingsent);
3✔
2456
    fprintf(stdout, "\"ICMP Echo Replies received\": %d, ", num_pingreceived);
3✔
2457
    fprintf(stdout, "\"other ICMP received\": %d, ", num_othericmprcvd);
3✔
2458

2459
    if (total_replies == 0) {
3✔
2460
        min_reply = 0;
×
2461
        max_reply = 0;
×
2462
        total_replies = 1;
×
2463
        sum_replies = 0;
×
2464
    }
2465

2466
    fprintf(stdout, "\"ms (min round trip time)\": %s, ", sprint_tm(min_reply));
3✔
2467
    fprintf(stdout, "\"ms (avg round trip time)\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2468
    fprintf(stdout, "\"ms (max round trip time)\": %s, ", sprint_tm(max_reply));
3✔
2469
    fprintf(stdout, "\"sec (elapsed real time)\": %.3f", (end_time - start_time) / 1e9);
3✔
2470
    fprintf(stdout, "}}\n");
3✔
2471
}
3✔
2472

2473
/************************************************************
2474

2475
  Function: send_ping
2476

2477
*************************************************************
2478

2479
  Inputs:  int s, HOST_ENTRY *h
2480

2481
  Description:
2482

2483
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2484
  will be added on by the kernel.  The ID field is our UNIX process ID,
2485
  and the sequence number is an index into an array of outstanding
2486
  ping requests. The sequence number will later be used to quickly
2487
  figure out who the ping reply came from.
2488

2489
************************************************************/
2490

2491
int send_ping(HOST_ENTRY *h, int index)
932✔
2492
{
2493
    int n;
2494
    int myseq;
2495
    int ret = 1;
932✔
2496
    uint8_t proto = ICMP_ECHO;
932✔
2497

2498
    update_current_time();
932✔
2499
    h->last_send_time = current_time_ns;
932✔
2500
    myseq = seqmap_add(h->i, index, current_time_ns);
932✔
2501

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

2504
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
932✔
2505
        if(icmp_request_typ == 13)
809✔
2506
            proto = ICMP_TSTAMP;
18✔
2507
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
809✔
2508
    }
253✔
2509
#ifdef IPV6
2510
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2511
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2512
    }
15✔
2513
#endif
2514
    else {
2515
        return 0;
×
2516
    }
2517

2518
    /* error sending? */
2519
    if (
664✔
2520
        (n < 0)
268✔
2521
#if defined(EHOSTDOWN)
2522
        && errno != EHOSTDOWN
302✔
2523
#endif
2524
    ) {
2525
        if (verbose_flag) {
34✔
2526
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2527
        }
2528
        else {
2529
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2530
        }
2531

2532
        h->num_sent++;
34✔
2533
        h->num_sent_i++;
34✔
2534
        if (!loop_flag)
34✔
2535
            h->resp_times[index] = RESP_ERROR;
34✔
2536

2537
        ret = 0;
34✔
2538
    }
2539
    else {
2540
        /* schedule timeout */
2541
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
898✔
2542

2543
        /* mark this trial as outstanding */
2544
        if (!loop_flag) {
898✔
2545
            h->resp_times[index] = RESP_WAITING;
898✔
2546
        }
268✔
2547
    }
2548

2549
    num_pingsent++;
932✔
2550
    last_send_time = h->last_send_time;
932✔
2551

2552
    return (ret);
932✔
2553
}
268✔
2554

2555
int socket_can_read(struct timeval *timeout)
2,751✔
2556
{
2557
    int nfound;
2558
    fd_set readset;
2559
    int socketmax;
2560

2561
#ifndef IPV6
2562
    socketmax = socket4;
2563
#else
2564
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,751✔
2565
#endif
2566

2567
select_again:
2,070✔
2568
    FD_ZERO(&readset);
35,871✔
2569
    if (socket4 >= 0)
2,751✔
2570
        FD_SET(socket4, &readset);
2,751✔
2571
#ifdef IPV6
2572
    if (socket6 >= 0)
2,751✔
2573
        FD_SET(socket6, &readset);
2,751✔
2574
#endif
2575

2576
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,751✔
2577
    if (nfound < 0) {
2,751✔
2578
        if (errno == EINTR) {
×
2579
            /* interrupted system call: redo the select */
2580
            goto select_again;
×
2581
        }
2582
        else {
2583
            perror("select");
×
2584
        }
2585
    }
2586

2587
    if (nfound > 0) {
2,751✔
2588
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,291✔
2589
            return socket4;
1,200✔
2590
        }
2591
#ifdef IPV6
2592
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
91✔
2593
            return socket6;
91✔
2594
        }
2595
#endif
2596
    }
2597

2598
    return -1;
1,460✔
2599
}
681✔
2600

2601
int receive_packet(int64_t wait_time,
2,751✔
2602
#if HAVE_SO_TIMESTAMPNS
2603
    int64_t *reply_timestamp,
2604
#else
2605
    int64_t *reply_timestamp __attribute__((unused)),
2606
#endif
2607
    struct sockaddr *reply_src_addr,
2608
    size_t reply_src_addr_len,
2609
    char *reply_buf,
2610
    size_t reply_buf_len,
2611
    int *ip_header_tos,
2612
    int *ip_header_ttl)
2613
{
2614
    struct timeval to;
2615
    int s = 0;
2,751✔
2616
    int recv_len;
2617
    static unsigned char msg_control[128];
2618
    struct iovec msg_iov = {
4,113✔
2619
        reply_buf,
681✔
2620
        reply_buf_len
681✔
2621
    };
2622
    struct msghdr recv_msghdr = {0};
2,751✔
2623
    recv_msghdr.msg_name = reply_src_addr;
2,751✔
2624
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,751✔
2625
    recv_msghdr.msg_iov = &msg_iov;
2,751✔
2626
    recv_msghdr.msg_iovlen = 1;
2,751✔
2627
    recv_msghdr.msg_control = &msg_control;
2,751✔
2628
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,751✔
2629
    struct cmsghdr *cmsg;
2630

2631
    /* Wait for a socket to become ready */
2632
    if (wait_time) {
2,751✔
2633
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,460✔
2634
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,460✔
2635
    }
438✔
2636
    else {
2637
        to.tv_sec = 0;
1,291✔
2638
        to.tv_usec = 0;
1,291✔
2639
    }
2640
    s = socket_can_read(&to);
2,751✔
2641
    if (s == -1) {
2,751✔
2642
        return 0; /* timeout */
1,460✔
2643
    }
2644

2645
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,291✔
2646
    if (recv_len <= 0) {
1,291✔
2647
        return 0;
×
2648
    }
2649

2650
    /* ancilliary data */
2651
    {
2652
#if HAVE_SO_TIMESTAMPNS
2653
        struct timespec reply_timestamp_ts;
2654
        struct timeval reply_timestamp_tv;
2655
#endif
2656
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,291✔
2657
             cmsg != NULL;
2,347✔
2658
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,056✔
2659
#if HAVE_SO_TIMESTAMPNS
2660
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,056✔
2661
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,048✔
2662
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,048✔
2663
            }
2664
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,056✔
2665
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2666
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2667
            }
2668
#endif
2669
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,056✔
2670
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2671
            }
2672
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,056✔
2673
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2674
            }
2675
#ifdef IPV6
2676
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,056✔
2677
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2678
            }
2679
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,056✔
2680
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2681
            }
2682
#endif
2683
        }
2684
    }
2685

2686
#if defined(DEBUG) || defined(_DEBUG)
2687
    if (randomly_lose_flag) {
2688
        if ((random() & 0x07) <= lose_factor)
2689
            return 0;
2690
    }
2691
#endif
2692

2693
    return recv_len;
1,291✔
2694
}
681✔
2695

2696
/* stats_add: update host statistics for a single packet that was received (or timed out)
2697
 * h: host entry to update
2698
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2699
 * success: 1 if response received, 0 otherwise
2700
 * latency: response time, in ns
2701
 */
2702
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
898✔
2703
{
2704
    /* sent count - we update only on receive/timeout, so that we don't get
2705
     * weird loss percentage, just because a packet was note recived yet.
2706
     */
2707
    h->num_sent++;
898✔
2708
    h->num_sent_i++;
898✔
2709

2710
    if (!success) {
898✔
2711
        if (!loop_flag && index >= 0) {
113✔
2712
            h->resp_times[index] = RESP_TIMEOUT;
113✔
2713
        }
29✔
2714
        num_timeout++;
113✔
2715
        return;
113✔
2716
    }
2717

2718
    /* received count */
2719
    h->num_recv++;
785✔
2720
    h->num_recv_i++;
785✔
2721

2722
    /* maximum */
2723
    if (!h->max_reply || latency > h->max_reply) {
785✔
2724
        h->max_reply = latency;
634✔
2725
    }
181✔
2726
    if (!h->max_reply_i || latency > h->max_reply_i) {
785✔
2727
        h->max_reply_i = latency;
645✔
2728
    }
184✔
2729

2730
    /* minimum */
2731
    if (!h->min_reply || latency < h->min_reply) {
785✔
2732
        h->min_reply = latency;
524✔
2733
    }
161✔
2734
    if (!h->min_reply_i || latency < h->min_reply_i) {
785✔
2735
        h->min_reply_i = latency;
554✔
2736
    }
171✔
2737

2738
    /* total time (for average) */
2739
    h->total_time += latency;
785✔
2740
    h->total_time_i += latency;
785✔
2741

2742
    /* response time per-packet (count mode) */
2743
    if (!loop_flag && index >= 0) {
785✔
2744
        h->resp_times[index] = latency;
785✔
2745
    }
239✔
2746
}
268✔
2747

2748
/* stats_reset_interval: reset interval statistics
2749
 * h: host entry to update
2750
 */
2751
void stats_reset_interval(HOST_ENTRY *h)
33✔
2752
{
2753
    h->num_sent_i = 0;
33✔
2754
    h->num_recv_i = 0;
33✔
2755
    h->max_reply_i = 0;
33✔
2756
    h->min_reply_i = 0;
33✔
2757
    h->total_time_i = 0;
33✔
2758
}
33✔
2759

2760
int decode_icmp_ipv4(
1,200✔
2761
    struct sockaddr *response_addr,
2762
    size_t response_addr_len,
2763
    char *reply_buf,
2764
    size_t reply_buf_len,
2765
    unsigned short *id,
2766
    unsigned short *seq,
2767
    int *ip_header_tos,
2768
    int *ip_header_ttl,
2769
    uint32_t *ip_header_otime_ms,
2770
    uint32_t *ip_header_rtime_ms,
2771
    uint32_t *ip_header_ttime_ms)
2772
{
2773
    struct icmp *icp;
2774
    int hlen = 0;
1,200✔
2775

2776
    if (!using_sock_dgram4) {
1,200✔
2777
        struct ip *ip = (struct ip *)reply_buf;
1,192✔
2778
        *ip_header_tos = ip->ip_tos;
1,192✔
2779
        *ip_header_ttl = ip->ip_ttl;
1,192✔
2780

2781
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2782
        /* The alpha headers are decidedly broken.
2783
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2784
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2785
         */
2786
        hlen = (ip->ip_vhl & 0x0F) << 2;
2787
#else
2788
        hlen = ip->ip_hl << 2;
1,192✔
2789
#endif
2790
    }
228✔
2791

2792
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,200✔
2793
        /* too short */
2794
        if (verbose_flag) {
×
2795
            char buf[INET6_ADDRSTRLEN];
2796
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2797
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2798
        }
2799
        return -1;
×
2800
    }
2801

2802
    icp = (struct icmp *)(reply_buf + hlen);
1,200✔
2803

2804
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,200✔
2805
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
506✔
2806
        /* Handle other ICMP packets */
2807
        struct icmp *sent_icmp;
2808
        SEQMAP_VALUE *seqmap_value;
2809
        char addr_ascii[INET6_ADDRSTRLEN];
2810
        HOST_ENTRY *h;
2811

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

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

2820
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
482✔
2821
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
2822
            sent_icmp->icmp_id != ident4) {
×
2823
            /* not caused by us */
2824
            return -1;
482✔
2825
        }
2826

2827
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2828
        if (seqmap_value == NULL) {
×
2829
            return -1;
×
2830
        }
2831

2832
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2833

2834
        switch (icp->icmp_type) {
×
2835
        case ICMP_UNREACH:
2836
            h = table[seqmap_value->host_nr];
×
2837
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2838
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2839
                    addr_ascii, h->host);
2840
            }
2841
            else {
2842
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2843
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2844
            }
2845

2846
            print_warning("\n");
×
2847
            num_othericmprcvd++;
×
2848
            break;
×
2849

2850
        case ICMP_SOURCEQUENCH:
2851
        case ICMP_REDIRECT:
2852
        case ICMP_TIMXCEED:
2853
        case ICMP_PARAMPROB:
2854
            h = table[seqmap_value->host_nr];
×
2855
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2856
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2857
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2858
            }
2859
            else {
2860
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2861
                    icp->icmp_type, addr_ascii, h->host);
×
2862
            }
2863
            print_warning("\n");
×
2864
            num_othericmprcvd++;
×
2865
            break;
×
2866
        }
2867

2868
        return -1;
×
2869
    }
2870

2871
    *id = icp->icmp_id;
718✔
2872
    *seq = ntohs(icp->icmp_seq);
718✔
2873
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
718✔
2874

2875
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2876
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
2877
            if (verbose_flag) {
×
2878
                char buf[INET6_ADDRSTRLEN];
2879
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2880
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2881
            }
2882
            return -1;
×
2883
        }
2884

2885
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
2886
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
2887
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
2888
    }
2889

2890
    return hlen;
718✔
2891
}
228✔
2892

2893
#ifdef IPV6
2894
int decode_icmp_ipv6(
91✔
2895
    struct sockaddr *response_addr,
2896
    size_t response_addr_len,
2897
    char *reply_buf,
2898
    size_t reply_buf_len,
2899
    unsigned short *id,
2900
    unsigned short *seq)
2901
{
2902
    struct icmp6_hdr *icp;
2903

2904
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
91✔
2905
        if (verbose_flag) {
×
2906
            char buf[INET6_ADDRSTRLEN];
2907
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2908
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2909
        }
2910
        return 0; /* too short */
×
2911
    }
2912

2913
    icp = (struct icmp6_hdr *)reply_buf;
91✔
2914

2915
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
91✔
2916
        /* Handle other ICMPv6 packets */
2917
        struct ip6_hdr *sent_ipv6;
2918
        struct icmp6_hdr *sent_icmp;
2919
        SEQMAP_VALUE *seqmap_value;
2920
        char addr_ascii[INET6_ADDRSTRLEN];
2921
        HOST_ENTRY *h;
2922

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

2929
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
×
2930
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
×
2931
            /* discard ICMPv6 message if we can't tell that it was caused by
2932
             * us, because the IPv6 header is not directly followed by an
2933
             * ICMPv6 header
2934
             */
2935
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
2936
            return 0;
×
2937
        }
2938
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
×
2939

2940
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
×
2941
            /* not caused by us */
2942
            return 0;
×
2943
        }
2944

2945
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2946
        if (seqmap_value == NULL) {
×
2947
            return 0;
×
2948
        }
2949

2950
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2951
        h = table[seqmap_value->host_nr];
×
2952

2953
        switch (icp->icmp6_type) {
×
2954
        case ICMP6_DST_UNREACH:
2955
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
2956
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2957
                    icp->icmp6_code, addr_ascii, h->host);
×
2958
            } else {
2959
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2960
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2961
            }
2962
            print_warning("\n");
×
2963
            num_othericmprcvd++;
×
2964
            break;
×
2965

2966
        case ICMP6_PACKET_TOO_BIG:
2967
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
2968
                addr_ascii, h->host);
2969
            num_othericmprcvd++;
×
2970
            break;
×
2971

2972
        case ICMP6_TIME_EXCEEDED:
2973
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
2974
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2975
                    icp->icmp6_code, addr_ascii, h->host);
×
2976
            } else {
2977
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2978
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
2979
            }
2980
            print_warning("\n");
×
2981
            num_othericmprcvd++;
×
2982
            break;
×
2983

2984
        case ICMP6_PARAM_PROB:
2985
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
2986
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2987
                    icp->icmp6_code, addr_ascii, h->host);
×
2988
            } else {
2989
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2990
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
2991
            }
2992
            print_warning("\n");
×
2993
            num_othericmprcvd++;
×
2994
            break;
×
2995

2996
        default:
2997
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
2998
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
2999
            num_othericmprcvd++;
×
3000
            break;
×
3001
        }
3002

3003
        return 0;
×
3004
    }
3005

3006
    *id = icp->icmp6_id;
91✔
3007
    *seq = ntohs(icp->icmp6_seq);
91✔
3008

3009
    return 1;
91✔
3010
}
15✔
3011
#endif
3012

3013
int wait_for_reply(int64_t wait_time)
2,751✔
3014
{
3015
    int result;
3016
    static char buffer[RECV_BUFSIZE];
3017
    struct sockaddr_storage response_addr;
3018
    int n, avg;
3019
    HOST_ENTRY *h;
3020
    int64_t this_reply;
3021
    int this_count;
3022
    int64_t recv_time = 0;
2,751✔
3023
    SEQMAP_VALUE *seqmap_value;
3024
    unsigned short id;
3025
    unsigned short seq;
3026
    int ip_header_tos = -1;
2,751✔
3027
    int ip_header_ttl = -1;
2,751✔
3028
    // ICMP Timestamp
3029
    uint32_t ip_header_otime_ms = 0x80000000U;
2,751✔
3030
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,751✔
3031
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,751✔
3032

3033
    /* Receive packet */
3034
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,751✔
3035
        &recv_time, /* reply_timestamp */
3036
        (struct sockaddr *)&response_addr, /* reply_src_addr */
3037
        sizeof(response_addr), /* reply_src_addr_len */
3038
        buffer, /* reply_buf */
3039
        sizeof(buffer), /* reply_buf_len */
3040
        &ip_header_tos, /* TOS resp. TC byte */
3041
        &ip_header_ttl /* TTL resp. hop limit */
3042
    );
3043

3044
    if (result <= 0) {
2,751✔
3045
        return 0;
1,460✔
3046
    }
3047

3048
    update_current_time();
1,291✔
3049
    if (recv_time == 0)
1,291✔
3050
        recv_time = current_time_ns;
243✔
3051

3052
    /* Process ICMP packet and retrieve id/seq */
3053
    if (response_addr.ss_family == AF_INET) {
1,291✔
3054
        int ip_hlen = decode_icmp_ipv4(
1,200✔
3055
            (struct sockaddr *)&response_addr,
3056
            sizeof(response_addr),
3057
            buffer,
3058
            sizeof(buffer),
3059
            &id,
3060
            &seq,
3061
            &ip_header_tos,
3062
            &ip_header_ttl,
3063
            &ip_header_otime_ms,
3064
            &ip_header_rtime_ms,
3065
            &ip_header_ttime_ms);
3066
        if (ip_hlen < 0) {
1,200✔
3067
            return 1;
482✔
3068
        }
3069
        if (id != ident4) {
718✔
3070
            return 1; /* packet received, but not the one we are looking for! */
×
3071
        }
3072
        if (!using_sock_dgram4) {
718✔
3073
            /* do not include IP header in returned size, to be consistent with ping(8) and also
3074
             * with fping with IPv6 hosts */
3075
            result -= ip_hlen;
710✔
3076
        }
228✔
3077
    }
228✔
3078
#ifdef IPV6
3079
    else if (response_addr.ss_family == AF_INET6) {
91✔
3080
        if (!decode_icmp_ipv6(
91✔
3081
                (struct sockaddr *)&response_addr,
3082
                sizeof(response_addr),
3083
                buffer,
3084
                sizeof(buffer),
3085
                &id,
3086
                &seq)) {
3087
            return 1;
×
3088
        }
3089
        if (id != ident6) {
91✔
3090
            return 1; /* packet received, but not the one we are looking for! */
×
3091
        }
3092
    }
15✔
3093
#endif
3094
    else {
3095
        return 1;
×
3096
    }
3097

3098
    seqmap_value = seqmap_fetch(seq, current_time_ns);
809✔
3099
    if (seqmap_value == NULL) {
809✔
3100
        return 1;
12✔
3101
    }
3102

3103
    /* find corresponding host_entry */
3104
    n = seqmap_value->host_nr;
797✔
3105
    h = table[n];
797✔
3106
    this_count = seqmap_value->ping_count;
797✔
3107
    this_reply = recv_time - seqmap_value->ping_ts;
797✔
3108

3109
    /* update stats that include invalid replies */
3110
    h->num_recv_total++;
797✔
3111
    num_pingreceived++;
797✔
3112

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

3115
    /* optionally require reply source equal to target address */
3116
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
797✔
3117
        dbg_printf("%s\n", "discarding reply from wrong source address");
3118
        return 1;
12✔
3119
    }
3120

3121
    /* discard duplicates */
3122
    if (!loop_flag && !(count_flag && quiet_flag) && h->resp_times[this_count] >= 0) {
785✔
3123
        if (!per_recv_flag) {
×
3124
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3125
                h->host, this_count, result, sprint_tm(this_reply));
3126

3127
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3128
                char buf[INET6_ADDRSTRLEN];
3129
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3130
                fprintf(stderr, " [<- %s]", buf);
×
3131
            }
3132
            fprintf(stderr, "\n");
×
3133
        }
3134
        return 1;
×
3135
    }
3136

3137
    /* discard reply if delay is larger than timeout
3138
     * (see also: github #32) */
3139
    if (this_reply > h->timeout) {
785✔
3140
        return 1;
×
3141
    }
3142

3143
    /* update stats */
3144
    stats_add(h, this_count, 1, this_reply);
785✔
3145
    // TODO: move to stats_add?
3146
    if (!max_reply || this_reply > max_reply)
785✔
3147
        max_reply = this_reply;
510✔
3148
    if (!min_reply || this_reply < min_reply)
785✔
3149
        min_reply = this_reply;
433✔
3150
    sum_replies += this_reply;
785✔
3151
    total_replies++;
785✔
3152

3153
    /* initialize timeout to initial timeout (without backoff) */
3154
    h->timeout = timeout;
785✔
3155

3156
    /* remove timeout event */
3157
    struct event *timeout_event = host_get_timeout_event(h, this_count);
785✔
3158
    if (timeout_event) {
785✔
3159
        ev_remove(&event_queue_timeout, timeout_event);
785✔
3160
    }
239✔
3161

3162
    /* print "is alive" */
3163
    if (h->num_recv == 1) {
785✔
3164
        num_alive++;
486✔
3165
        if (fast_reachable && num_alive >= min_reachable)
486✔
3166
                finish_requested = 1;
3✔
3167

3168
        if (verbose_flag || alive_flag) {
486✔
3169
            printf("%s", h->host);
299✔
3170

3171
            if (verbose_flag)
299✔
3172
                printf(" is alive");
279✔
3173
        }
85✔
3174
    }
142✔
3175

3176
    /* print received ping (unless --quiet) */
3177
    if (per_recv_flag) {
785✔
3178
        if (json_flag)
186✔
3179
            printf("{\"resp\": {");
35✔
3180

3181
        if (timestamp_flag)
186✔
3182
            print_timestamp_format(recv_time, timestamp_format_flag);
45✔
3183

3184
        avg = h->total_time / h->num_recv;
186✔
3185
        if (json_flag) {
186✔
3186
            printf("\"host\": \"%s\", ", h->host);
35✔
3187
            printf("\"seq\": %d, ", this_count);
35✔
3188
            printf("\"size\": %d, ", result);
35✔
3189
            printf("\"rtt\": %s, ", sprint_tm(this_reply));
35✔
3190
            printf("\"rttAvg\": %s", sprint_tm(avg));
35✔
3191
        }
11✔
3192
        else {
3193
            printf("%-*s : [%d], %d bytes, %s ms", max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
151✔
3194
            printf(" (%s avg, ", sprint_tm(avg));
151✔
3195
        }
3196

3197
        if (h->num_recv <= h->num_sent) {
186✔
3198
            if (json_flag)
186✔
3199
                printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
35✔
3200
            else
3201
                printf("%d%% loss)", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
3202
        }
56✔
3203
        else {
3204
            if (json_flag)
×
3205
                printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
3206
            else
3207
                printf("%d%% return)", (h->num_recv_total * 100) / h->num_sent);
×
3208
        }
3209
    }
56✔
3210

3211
    if (verbose_flag || alive_flag || per_recv_flag) {
785✔
3212

3213
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
485✔
3214
            char buf[INET6_ADDRSTRLEN];
3215
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3216
            fprintf(stderr, " [<- %s]", buf);
6✔
3217
        }
3218

3219
        if (icmp_request_typ == 13) {
485✔
3220
            if (json_flag) {
16✔
3221
              printf(", \"timestamps\": {");
2✔
3222
              printf("\"originate\": %u, ", ip_header_otime_ms);
2✔
3223
              printf("\"receive\": %u, ", ip_header_rtime_ms);
2✔
3224
              printf("\"transmit\": %u, ", ip_header_ttime_ms);
2✔
3225
              printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
3226
            }
3227
            else {
3228
                printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
3229
                      alive_flag ? "" : ",",
14✔
3230
                      ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
3231
                      ms_since_midnight_utc(recv_time));
3232
            }
3233
        }
3234

3235
        if(print_tos_flag) {
485✔
3236
            if(ip_header_tos != -1) {
49✔
3237
                if (json_flag)
49✔
3238
                    printf(", \"tos\": %d", ip_header_tos);
6✔
3239
                else
3240
                    printf(" (TOS %d)", ip_header_tos);
43✔
3241
            }
13✔
3242
            else {
3243
                if (json_flag)
×
3244
                    printf(", \"tos\": -1");
×
3245
                else
3246
                    printf(" (TOS unknown)");
×
3247
            }
3248
        }
13✔
3249

3250
        if (print_ttl_flag) {
485✔
3251
          if(ip_header_ttl != -1) {
49✔
3252
              if (json_flag)
49✔
3253
                  printf(", \"ttl\": %d", ip_header_ttl);
6✔
3254
              else
3255
                  printf(" (TTL %d)", ip_header_ttl);
43✔
3256
          }
13✔
3257
          else {
3258
              if (json_flag)
×
3259
                  printf(", \"ttl\": -1");
×
3260
              else
3261
                  printf(" (TTL unknown)");
×
3262
          }
3263
        }
13✔
3264

3265
        if (elapsed_flag && !per_recv_flag)
485✔
3266
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
3267

3268
        if (json_flag)
485✔
3269
            printf("}}");
35✔
3270

3271
        printf("\n");
485✔
3272
    }
141✔
3273

3274
    return 1;
785✔
3275
}
681✔
3276

3277
/************************************************************
3278

3279
  Function: add_name
3280

3281
*************************************************************
3282

3283
  Inputs:  char* name
3284

3285
  Description:
3286

3287
  process input name for addition to target list
3288
  name can turn into multiple targets via multiple interfaces (-m)
3289
  or via NIS groups
3290

3291
************************************************************/
3292

3293
void add_name(char *name)
609✔
3294
{
3295
    struct addrinfo *res0, *res, hints;
3296
    int ret_ga;
3297
    char *printname;
3298
    char namebuf[256];
3299
    char addrbuf[256];
3300

3301
    /* getaddrinfo */
3302
    memset(&hints, 0, sizeof(struct addrinfo));
609✔
3303
    hints.ai_flags = AI_UNUSABLE;
609✔
3304
    hints.ai_socktype = SOCK_RAW;
609✔
3305
    hints.ai_family = hints_ai_family;
609✔
3306
    if (hints_ai_family == AF_INET) {
609✔
3307
        hints.ai_protocol = IPPROTO_ICMP;
81✔
3308
    }
21✔
3309
#ifdef IPV6
3310
    else if (hints_ai_family == AF_INET6) {
528✔
3311
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
3312
    }
1✔
3313
#endif
3314
    else {
3315
        hints.ai_socktype = SOCK_STREAM;
497✔
3316
        hints.ai_protocol = 0;
497✔
3317
    }
3318
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
609✔
3319
    if (ret_ga) {
609✔
3320
        if (!quiet_flag)
29✔
3321
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3322
        num_noaddress++;
29✔
3323

3324
        // Handle JSON output for invalid hosts
3325
        if (json_flag) {
29✔
3326
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}", name, gai_strerror(ret_ga));
3✔
3327
            return;
21✔
3328
        }
3329

3330
        return;
26✔
3331
    }
3332

3333
    /* NOTE: we could/should loop with res on all addresses like this:
3334
     * for (res = res0; res; res = res->ai_next) {
3335
     * We don't do it yet, however, because is is an incompatible change
3336
     * (need to implement a separate option for this)
3337
     */
3338
    for (res = res0; res; res = res->ai_next) {
580✔
3339
        /* name_flag: addr -> name lookup requested) */
3340
        if (name_flag || rdns_flag) {
582✔
3341
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3342
            if (name_flag) {
3✔
3343
                /* Was it a numerical address? Only then do a rdns-query */
3344
                struct addrinfo *nres;
3345
                hints.ai_flags = AI_NUMERICHOST;
×
3346
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3347
                    do_rdns = 1;
×
3348
                    freeaddrinfo(nres);
×
3349
                }
3350
            }
3351

3352
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3353
                printname = namebuf;
3✔
3354
            }
1✔
3355
            else {
3356
                printname = name;
×
3357
            }
3358
        }
1✔
3359
        else {
3360
            printname = name;
577✔
3361
        }
3362

3363
        /* addr_flag: name -> addr lookup requested */
3364
        if (addr_flag) {
580✔
3365
            int ret;
3366
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3367
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3368
            if (ret) {
3✔
3369
                if (!quiet_flag) {
×
3370
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3371
                }
3372
                continue;
×
3373
            }
3374

3375
            if (name_flag || rdns_flag) {
3✔
3376
                char nameaddrbuf[512 + 3];
3377
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3378
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3379
            }
3380
            else {
3381
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3382
            }
3383
        }
1✔
3384
        else {
3385
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
577✔
3386
        }
3387

3388
        if (!multif_flag) {
580✔
3389
            break;
580✔
3390
        }
3391
    }
3392

3393
    freeaddrinfo(res0);
580✔
3394
}
167✔
3395

3396
/************************************************************
3397

3398
  Function: add_addr
3399

3400
*************************************************************
3401

3402
  Description:
3403

3404
  add single address to list of hosts to be pinged
3405

3406
************************************************************/
3407

3408
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
580✔
3409
{
3410
    HOST_ENTRY *p;
3411
    int n;
3412
    int64_t *i;
3413

3414
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
580✔
3415
    if (!p)
580✔
3416
        crash_and_burn("can't allocate HOST_ENTRY");
×
3417

3418
    p->name = strdup(name);
580✔
3419
    p->host = strdup(host);
580✔
3420
    memcpy(&p->saddr, ipaddr, ipaddr_len);
580✔
3421
    p->saddr_len = ipaddr_len;
580✔
3422
    p->timeout = timeout;
580✔
3423
    p->min_reply = 0;
580✔
3424

3425
    if (netdata_flag) {
580✔
3426
        char *s = p->name;
3✔
3427
        while (*s) {
30✔
3428
            if (!isalnum(*s))
27✔
3429
                *s = '_';
9✔
3430
            s++;
27✔
3431
        }
3432
    }
1✔
3433

3434
    if (strlen(p->host) > max_hostname_len)
580✔
3435
        max_hostname_len = strlen(p->host);
387✔
3436

3437
    /* array for response time results */
3438
    if (!loop_flag) {
580✔
3439
        i = (int64_t *)malloc(trials * sizeof(int64_t));
580✔
3440
        if (!i)
580✔
3441
            crash_and_burn("can't allocate resp_times array");
×
3442

3443
        for (n = 1; n < trials; n++)
2,311✔
3444
            i[n] = RESP_UNUSED;
1,731✔
3445

3446
        p->resp_times = i;
580✔
3447
    }
158✔
3448

3449
    /* allocate event storage */
3450
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
580✔
3451
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
580✔
3452

3453
    /* schedule first ping */
3454
    host_add_ping_event(p, 0, current_time_ns);
580✔
3455

3456
    num_hosts++;
580✔
3457
}
580✔
3458

3459
/************************************************************
3460

3461
  Function: crash_and_burn
3462

3463
*************************************************************
3464

3465
  Inputs:  char* message
3466

3467
  Description:
3468

3469
************************************************************/
3470

3471
void crash_and_burn(char *message)
×
3472
{
3473
    fprintf(stderr, "%s: %s\n", prog, message);
×
3474
    exit(4);
×
3475
}
3476

3477
/************************************************************
3478

3479
  Function: errno_crash_and_burn
3480

3481
*************************************************************
3482

3483
  Inputs:  char* message
3484

3485
  Description:
3486

3487
************************************************************/
3488

3489
void errno_crash_and_burn(char *message)
8✔
3490
{
3491
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3492
    exit(4);
8✔
3493
}
3494

3495
/************************************************************
3496

3497
  Function: print_warning
3498

3499
  Description: fprintf(stderr, ...), unless running with -q
3500

3501
*************************************************************/
3502

3503
void print_warning(char *format, ...)
54✔
3504
{
3505
    va_list args;
3506
    if (!quiet_flag) {
54✔
3507
        va_start(args, format);
54✔
3508
        vfprintf(stderr, format, args);
54✔
3509
        va_end(args);
54✔
3510
    }
8✔
3511
}
54✔
3512

3513
/************************************************************
3514

3515
  Function: sprint_tm
3516

3517
*************************************************************
3518

3519
  render nanosecond int64_t value into milliseconds string with three digits of
3520
  precision.
3521

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

3524
const char *sprint_tm(int64_t ns)
1,083✔
3525
{
3526
    static char buf[10];
3527
    double t = (double)ns / 1e6;
1,083✔
3528

3529
    if (t < 0.0) {
1,083✔
3530
        /* negative (unexpected) */
3531
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3532
    }
3533
    else if (t < 1.0) {
1,083✔
3534
        /* <= 0.99 ms */
3535
        snprintf(buf, sizeof(buf), "%.3f", t);
1,083✔
3536
    }
335✔
3537
    else if (t < 10.0) {
×
3538
        /* 1.00 - 9.99 ms */
3539
        snprintf(buf, sizeof(buf), "%.2f", t);
×
3540
    }
3541
    else if (t < 100.0) {
×
3542
        /* 10.0 - 99.9 ms */
3543
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3544
    }
3545
    else if (t < 1000000.0) {
×
3546
        /* 100 - 1'000'000 ms */
3547
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3548
    }
3549
    else {
3550
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3551
    }
3552

3553
    return (buf);
1,083✔
3554
}
3555

3556
/************************************************************
3557

3558
  Function: addr_cmp
3559

3560
*************************************************************/
3561
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
507✔
3562
{
3563
    if (a->sa_family != b->sa_family) {
507✔
3564
        return a->sa_family - b->sa_family;
×
3565
    }
3566
    else {
3567
        if (a->sa_family == AF_INET) {
507✔
3568
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
443✔
3569
        }
3570
        else if (a->sa_family == AF_INET6) {
64✔
3571
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3572
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3573
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3574
        }
3575
    }
3576

3577
    return 0;
×
3578
}
143✔
3579

3580
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
913✔
3581
{
3582
    struct event *event = &h->event_storage_ping[index % event_storage_count];
913✔
3583
    event->host = h;
913✔
3584
    event->ping_index = index;
913✔
3585
    event->ev_time = ev_time;
913✔
3586
    ev_enqueue(&event_queue_ping, event);
913✔
3587

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

3592
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
898✔
3593
{
3594
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
898✔
3595
    event->host = h;
898✔
3596
    event->ping_index = index;
898✔
3597
    event->ev_time = ev_time;
898✔
3598
    ev_enqueue(&event_queue_timeout, event);
898✔
3599

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

3604
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
785✔
3605
{
3606
    return &h->event_storage_timeout[index % event_storage_count];
785✔
3607
}
3608

3609
/************************************************************
3610

3611
  Function: ev_enqueue
3612

3613
  Enqueue an event
3614

3615
  The queue is sorted by event->ev_time, so that queue->first always points to
3616
  the earliest event.
3617

3618
  We start scanning the queue from the tail, because we assume
3619
  that new events mostly get inserted with a event time higher
3620
  than the others.
3621

3622
*************************************************************/
3623
void ev_enqueue(struct event_queue *queue, struct event *event)
1,811✔
3624
{
3625
    struct event *i;
3626
    struct event *i_prev;
3627

3628
    /* Empty list */
3629
    if (queue->last == NULL) {
1,811✔
3630
        event->ev_next = NULL;
1,419✔
3631
        event->ev_prev = NULL;
1,419✔
3632
        queue->first = event;
1,419✔
3633
        queue->last = event;
1,419✔
3634
        return;
1,419✔
3635
    }
3636

3637
    /* Insert on tail? */
3638
    if (event->ev_time - queue->last->ev_time >= 0) {
392✔
3639
        event->ev_next = NULL;
392✔
3640
        event->ev_prev = queue->last;
392✔
3641
        queue->last->ev_next = event;
392✔
3642
        queue->last = event;
392✔
3643
        return;
392✔
3644
    }
3645

3646
    /* Find insertion point */
3647
    i = queue->last;
×
3648
    while (1) {
3649
        i_prev = i->ev_prev;
×
3650
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3651
            event->ev_prev = i_prev;
×
3652
            event->ev_next = i;
×
3653
            i->ev_prev = event;
×
3654
            if (i_prev != NULL) {
×
3655
                i_prev->ev_next = event;
×
3656
            }
3657
            else {
3658
                queue->first = event;
×
3659
            }
3660
            return;
×
3661
        }
3662
        i = i_prev;
×
3663
    }
3664
}
533✔
3665

3666
/************************************************************
3667

3668
  Function: ev_dequeue
3669

3670
*************************************************************/
3671
struct event *ev_dequeue(struct event_queue *queue)
1,006✔
3672
{
3673
    struct event *dequeued;
3674

3675
    if (queue->first == NULL) {
1,006✔
3676
        return NULL;
×
3677
    }
3678
    dequeued = queue->first;
1,006✔
3679
    ev_remove(queue, dequeued);
1,006✔
3680

3681
    return dequeued;
1,006✔
3682
}
288✔
3683

3684
/************************************************************
3685

3686
  Function: ev_remove
3687

3688
*************************************************************/
3689
void ev_remove(struct event_queue *queue, struct event *event)
1,791✔
3690
{
3691
    if (queue->first == event) {
1,791✔
3692
        queue->first = event->ev_next;
1,782✔
3693
    }
524✔
3694
    if (queue->last == event) {
1,791✔
3695
        queue->last = event->ev_prev;
1,420✔
3696
    }
412✔
3697
    if (event->ev_prev) {
1,791✔
3698
        event->ev_prev->ev_next = event->ev_next;
9✔
3699
    }
3✔
3700
    if (event->ev_next) {
1,791✔
3701
        event->ev_next->ev_prev = event->ev_prev;
371✔
3702
    }
115✔
3703
    event->ev_prev = NULL;
1,791✔
3704
    event->ev_next = NULL;
1,791✔
3705
}
1,791✔
3706

3707
/************************************************************
3708

3709
  Function: print_human_readable_time from current_time_ns
3710

3711
*************************************************************/
3712
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
45✔
3713
{
3714
    char time_buffer[100];
3715
    time_t current_time_s;
3716
    struct tm *local_time;
3717

3718
    current_time_s = current_time_ns / 1000000000;
45✔
3719
    local_time = localtime(&current_time_s);
45✔
3720
    switch(timestamp_format) {
45✔
3721
        case 1:
6✔
3722
            // timestamp-format ctime
3723
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
9✔
3724
            if (json_flag)
9✔
3725
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3726
            else
3727
                printf("[%s] ", time_buffer);
6✔
3728
            break;
9✔
3729
        case 2:
6✔
3730
            // timestamp-format iso
3731
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
9✔
3732
            if (json_flag)
9✔
3733
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3734
            else
3735
                printf("[%s] ", time_buffer);
6✔
3736
            break;
9✔
3737
        case 3:
6✔
3738
            // timestamp-format rfc3339
3739
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
9✔
3740
            if (json_flag)
9✔
3741
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3742
            else
3743
                printf("[%s] ", time_buffer);
6✔
3744
            break;
9✔
3745
        default:
12✔
3746
            if (json_flag)
18✔
3747
                printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9);
6✔
3748
            else
3749
                printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3750
    }
6✔
3751
}
45✔
3752

3753
/************************************************************
3754

3755
  Function: ms_since_midnight_utc
3756

3757
*************************************************************
3758

3759
  Input: int64_t: current UTC time in ns
3760

3761
  Output: uint32_t: current time in ms since midnight UTC
3762

3763
  Description:
3764

3765
  Return ICMP Timestamp value corresponding to the given time value.
3766
  The given time value must be in UTC.
3767

3768
*************************************************************/
3769
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
3770
{
3771
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
3772
}
3773

3774
/************************************************************
3775

3776
  Function: usage
3777

3778
*************************************************************
3779

3780
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3781

3782
  Description:
3783

3784
************************************************************/
3785

3786
void usage(int is_error)
124✔
3787
{
3788
    FILE *out = is_error ? stderr : stdout;
124✔
3789
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
124✔
3790
    fprintf(out, "\n");
124✔
3791
    fprintf(out, "Probing options:\n");
124✔
3792
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
124✔
3793
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
124✔
3794
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
124✔
3795
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
124✔
3796
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
124✔
3797
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
124✔
3798
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
124✔
3799
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
124✔
3800
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
124✔
3801
    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✔
3802
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
124✔
3803
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
124✔
3804
#ifdef SO_BINDTODEVICE
3805
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
84✔
3806
#endif
3807
#ifdef SO_MARK
3808
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
84✔
3809
#endif
3810
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
124✔
3811
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
124✔
3812
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
124✔
3813
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
124✔
3814
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
124✔
3815
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
124✔
3816
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
124✔
3817
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
124✔
3818
    fprintf(out, "   -S, --src=IP       set source address\n");
124✔
3819
    fprintf(out, "       --seqmap-timeout=MSEC sequence number mapping timeout (default: %.0f ms)\n", seqmap_timeout / 1e6);
124✔
3820
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
124✔
3821
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
124✔
3822
    fprintf(out, "       --check-source discard replies not from target address\n");
124✔
3823
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
124✔
3824
    fprintf(out, "\n");
124✔
3825
    fprintf(out, "Output options:\n");
124✔
3826
    fprintf(out, "   -a, --alive        show targets that are alive\n");
124✔
3827
    fprintf(out, "   -A, --addr         show targets by address\n");
124✔
3828
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
124✔
3829
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
124✔
3830
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
124✔
3831
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
124✔
3832
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
124✔
3833
    fprintf(out, "   -J, --json         output in JSON format (-c or -C required)\n");
124✔
3834
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
124✔
3835
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
124✔
3836
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
124✔
3837
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
124✔
3838
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
124✔
3839
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
124✔
3840
    fprintf(out, "   -s, --stats        print final stats\n");
124✔
3841
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
124✔
3842
    fprintf(out, "   -v, --version      show version\n");
124✔
3843
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
124✔
3844
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
124✔
3845
    fprintf(out, "       --print-tos    show received TOS value\n");
124✔
3846
    fprintf(out, "       --print-ttl    show IP TTL value\n");
124✔
3847
    exit(is_error);
124✔
3848
}
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