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

schweikert / fping / 15511032324

07 Jun 2025 07:32PM UTC coverage: 87.465% (-0.3%) from 87.756%
15511032324

Pull #386

github

web-flow
Merge a839371c4 into fb6f8b561
Pull Request #386: New option -J / --json for the JSON output

163 of 196 new or added lines in 1 file covered. (83.16%)

1 existing line in 1 file now uncovered.

1549 of 1771 relevant lines covered (87.46%)

333.67 hits per line

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

87.61
/src/fping.c
1
/*
2
 * fping: fast-ping, file-ping, favorite-ping, funky-ping
3
 *
4
 *   Ping a list of target hosts in a round robin fashion.
5
 *   A better ping overall.
6
 *
7
 * fping website:  http://www.fping.org
8
 *
9
 * Current maintainer of fping: David Schweikert
10
 * Please send suggestions and patches to: david@schweikert.ch
11
 *
12
 *
13
 * Original author:  Roland Schemers  <schemers@stanford.edu>
14
 * IPv6 Support:     Jeroen Massar    <jeroen@unfix.org / jeroen@ipng.nl>
15
 * Improved main loop: David Schweikert <david@schweikert.ch>
16
 * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
17
 * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
18
 *
19
 *
20
 * Redistribution and use in source and binary forms are permitted
21
 * provided that the above copyright notice and this paragraph are
22
 * duplicated in all such forms and that any documentation,
23
 * advertising materials, and other materials related to such
24
 * distribution and use acknowledge that the software was developed
25
 * by Stanford University.  The name of the University may not be used
26
 * to endorse or promote products derived from this software without
27
 * specific prior written permission.
28
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31
 */
32

33
#ifdef __cplusplus
34
extern "C" {
35
#endif /* __cplusplus */
36

37
#include "config.h"
38
#include "fping.h"
39
#include "options.h"
40
#include "optparse.h"
41

42
#include <errno.h>
43
#include <inttypes.h>
44
#include <signal.h>
45
#include <stdarg.h>
46
#include <stdint.h>
47
#include <stdio.h>
48
#include <time.h>
49

50
#include "seqmap.h"
51

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

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

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

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

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

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

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

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

83
#include <sys/select.h>
84

85
/*** compatibility ***/
86

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

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

105
/*** externals ***/
106

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

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

117
/*** Constants ***/
118

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

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

135
/*** Ping packet defines ***/
136

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

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

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

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

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

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

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

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

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

219
#define ICMP_UNREACH_MAXTYPE 15
220

221
struct event;
222
typedef struct host_entry {
223
    int i; /* index into array */
224
    char *name; /* name as given by user */
225
    char *host; /* text description of host */
226
    struct sockaddr_storage saddr; /* internet address */
227
    socklen_t saddr_len;
228
    int64_t timeout; /* time to wait for response */
229
    int64_t last_send_time; /* time of last packet sent */
230
    int num_sent; /* number of ping packets sent (for statistics) */
231
    int num_recv; /* number of pings received (duplicates ignored) */
232
    int num_recv_total; /* number of pings received, including duplicates */
233
    int64_t max_reply; /* longest response time */
234
    int64_t min_reply; /* shortest response time */
235
    int64_t total_time; /* sum of response times */
236
    /* _i -> splits (reset on every report interval) */
237
    int num_sent_i; /* number of ping packets sent */
238
    int num_recv_i; /* number of pings received */
239
    int64_t max_reply_i; /* longest response time */
240
    int64_t min_reply_i; /* shortest response time */
241
    int64_t total_time_i; /* sum of response times */
242
    int64_t *resp_times; /* individual response times */
243

244
    /* to avoid allocating two struct events each time that we send a ping, we
245
     * preallocate here two struct events for each ping that we might send for
246
     * this host. */
247
    struct event *event_storage_ping;
248
    struct event *event_storage_timeout;
249
} HOST_ENTRY;
250

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

253
/* basic algorithm to ensure that we have correct data at all times:
254
 *
255
 * 1. when a ping is sent:
256
 *    - two events get added into event_queue:
257
 *      - t+PERIOD: ping event
258
 *      - t+TIMEOUT: timeout event
259
 *
260
 * 2. when a ping is received:
261
 *    - record statistics (increase num_sent and num_received)
262
 *    - remove timeout event (we store the event in seqmap, so that we can retrieve it when the response is received)
263
 *
264
 * 3. when a timeout happens:
265
 *    - record statistics (increase num_sent only)
266
 */
267

268
#define EV_TYPE_PING 1
269
#define EV_TYPE_TIMEOUT 2
270

271
struct event {
272
    struct event *ev_prev;
273
    struct event *ev_next;
274
    int64_t ev_time;
275
    struct host_entry *host;
276
    int ping_index;
277
};
278

279
struct event_queue {
280
    struct event *first;
281
    struct event *last;
282
};
283

284
/*** globals ***/
285

286
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
287

288
/* we keep two separate queues: a ping queue, for when the next ping should be
289
 * sent, and a timeout queue. the reason for having two separate queues is that
290
 * the ping period and the timeout value are different, so if we put them in
291
 * the same event queue, we would need to scan many more entries when inserting
292
 * into the sorted list.
293
 */
294
struct event_queue event_queue_ping;
295
struct event_queue event_queue_timeout;
296

297
char *prog;
298
int ident4 = 0; /* our icmp identity field */
299
int ident6 = 0;
300
const int sock_opt_on = 1; /* to activate a socket option */
301
int socket4 = -1;
302
int socktype4 = -1;
303
int using_sock_dgram4 = 0;
304
#ifndef IPV6
305
int hints_ai_family = AF_INET;
306
#else
307
int socket6 = -1;
308
int socktype6 = -1;
309
int hints_ai_family = AF_UNSPEC;
310
#endif
311

312
volatile sig_atomic_t status_snapshot = 0;
313
volatile sig_atomic_t finish_requested = 0;
314

315
unsigned int debugging = 0;
316

317
/* all time-related values are int64_t nanoseconds */
318
unsigned int retry = DEFAULT_RETRY;
319
int64_t timeout = (int64_t)DEFAULT_TIMEOUT * 1000000;
320
int64_t interval = (int64_t)DEFAULT_INTERVAL * 1000000;
321
int64_t perhost_interval = (int64_t)DEFAULT_PERHOST_INTERVAL * 1000000;
322
float backoff = DEFAULT_BACKOFF_FACTOR;
323
unsigned int ping_data_size = DEFAULT_PING_DATA_SIZE;
324
unsigned int count = 1, min_reachable = 0;
325
unsigned int trials;
326
int64_t report_interval = 0;
327
unsigned int ttl = 0;
328
int src_addr_set = 0;
329
struct in_addr src_addr;
330
#ifdef IPV6
331
int src_addr6_set = 0;
332
struct in6_addr src_addr6;
333
#endif
334

335
/* global stats */
336
int64_t max_reply = 0;
337
int64_t min_reply = 0;
338
int64_t total_replies = 0;
339
int64_t sum_replies = 0;
340
int max_hostname_len = 0;
341
int num_hosts = 0; /* total number of hosts */
342
int num_alive = 0, /* total number alive */
343
    num_unreachable = 0, /* total number unreachable */
344
    num_noaddress = 0; /* total number of addresses not found */
345
int num_timeout = 0, /* number of times select timed out */
346
    num_pingsent = 0, /* total pings sent */
347
    num_pingreceived = 0, /* total pings received */
348
    num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
349

350
struct timespec current_time; /* current time (pseudo) */
351
int64_t current_time_ns;
352
int64_t start_time;
353
int64_t end_time;
354
int64_t last_send_time; /* time last ping was sent */
355
int64_t next_report_time; /* time next -Q report is expected */
356

357
/* switches */
358
int generate_flag = 0; /* flag for IP list generation */
359
int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
360
int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag, json_flag;
361
int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
362
int multif_flag, timeout_flag, fast_reachable;
363
int outage_flag = 0;
364
int timestamp_flag = 0;
365
int timestamp_format_flag = 0;
366
int random_data_flag = 0;
367
int cumulative_stats_flag = 0;
368
int check_source_flag = 0;
369
int icmp_request_typ = 0;
370
int print_tos_flag = 0;
371
int print_ttl_flag = 0;
372
int size_flag = 0;
373
#if defined(DEBUG) || defined(_DEBUG)
374
int randomly_lose_flag, trace_flag, print_per_system_flag;
375
int lose_factor;
376
#endif /* DEBUG || _DEBUG */
377

378
unsigned int fwmark = 0;
379

380
char *filename = NULL; /* file containing hosts to ping */
381

382
/*** forward declarations ***/
383

384
void add_name(char *name);
385
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len);
386
char *na_cat(char *name, struct in_addr ipaddr);
387
void crash_and_burn(char *message);
388
void errno_crash_and_burn(char *message);
389
char *get_host_by_address(struct in_addr in);
390
int send_ping(HOST_ENTRY *h, int index);
391
void usage(int);
392
int wait_for_reply(int64_t);
393
void print_per_system_stats(void);
394
void print_per_system_stats_json(void);
395
void print_per_system_splits(void);
396
void print_per_system_splits_json(void);
397
void stats_reset_interval(HOST_ENTRY *h);
398
void print_netdata(void);
399
void print_global_stats(void);
400
void print_global_stats_json(void);
401
void main_loop();
402
void signal_handler(int);
403
void finish();
404
const char *sprint_tm(int64_t t);
405
void ev_enqueue(struct event_queue *queue, struct event *event);
406
struct event *ev_dequeue(struct event_queue *queue);
407
void ev_remove(struct event_queue *queue, struct event *event);
408
void add_cidr(char *);
409
void add_cidr_ipv4(unsigned long, unsigned long);
410
void add_range(char *, char *);
411
void add_addr_range_ipv4(unsigned long, unsigned long);
412
#ifdef IPV6
413
uint64_t be_octets_to_uint64(uint8_t*);
414
void uint64_to_be_octets(uint64_t, uint8_t*);
415
void add_cidr_ipv6(uint64_t, uint64_t, unsigned long, const char *);
416
void add_addr_range_ipv6(uint64_t, uint64_t, uint64_t, uint64_t, const char *);
417
#endif
418
void print_warning(char *fmt, ...);
419
int addr_cmp(struct sockaddr *a, struct sockaddr *b);
420
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
421
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
422
struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
423
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
424
void update_current_time();
425
void print_timestamp_format(int64_t current_time_ns, int timestamp_format);
426
static uint32_t ms_since_midnight_utc(int64_t time_val);
427

428
/************************************************************
429

430
  Function: p_setsockopt
431

432
*************************************************************
433

434
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
435

436
  Description:
437

438
  Elevates privileges to p_uid when required, calls
439
  setsockopt, and drops privileges back.
440

441
************************************************************/
442

443
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
22✔
444
    const void *optval, socklen_t optlen)
445
{
446
    const uid_t saved_uid = geteuid();
22✔
447
    int res;
448

449
    if (p_uid != saved_uid && seteuid(p_uid)) {
22✔
450
        perror("cannot elevate privileges for setsockopt");
×
451
    }
452

453
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
454

455
    if (p_uid != saved_uid && seteuid(saved_uid)) {
22✔
456
        perror("fatal error: could not drop privileges after setsockopt");
×
457
        /* continuing would be a security hole */
458
        exit(4);
×
459
    }
460

461
    return res;
22✔
462
}
463

464
/************************************************************
465

466
  Function: main
467

468
*************************************************************
469

470
  Inputs:  int argc, char** argv
471

472
  Description:
473

474
  Main program entry point
475

476
************************************************************/
477

478
int main(int argc, char **argv)
697✔
479
{
480
/* Debug: CPU Performance */
481
#if defined(DEBUG) || defined(_DEBUG)
482
    clock_t perf_cpu_start, perf_cpu_end;
483
    double perf_cpu_time_used;
484
    perf_cpu_start = clock();
485
#endif /* DEBUG || _DEBUG */
486

487
    int c;
488
    const uid_t suid = geteuid();
697✔
489
    int tos = 0;
697✔
490
    struct optparse optparse_state;
491
#ifdef USE_SIGACTION
492
    struct sigaction act;
493
#endif
494

495
    /* pre-parse -h/--help, so that we also can output help information
496
     * without trying to open the socket, which might fail */
497
    prog = argv[0];
697✔
498
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
697✔
499
        usage(0);
3✔
500
    }
1✔
501

502
    socket4 = open_ping_socket_ipv4(&socktype4);
695✔
503
#ifdef __linux__
504
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
505
     * structure is missing in the message.
506
     */
507
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
494✔
508
#endif
509

510
#ifdef IPV6
511
    socket6 = open_ping_socket_ipv6(&socktype6);
695✔
512
    /* if called (sym-linked) via 'fping6', imply '-6'
513
     * for backward compatibility */
514
    if (strstr(prog, "fping6")) {
695✔
515
        hints_ai_family = AF_INET6;
×
516
    }
517
#endif
518

519
    memset(&src_addr, 0, sizeof(src_addr));
695✔
520
#ifdef IPV6
521
    memset(&src_addr6, 0, sizeof(src_addr6));
695✔
522
#endif
523

524
    if (!suid && suid != getuid()) {
695✔
525
        /* *temporarily* drop privileges */
526
        if (seteuid(getuid()) == -1)
663✔
527
            perror("cannot setuid");
×
528
    }
181✔
529

530
    optparse_init(&optparse_state, argv);
695✔
531
    ident4 = ident6 = htons(getpid() & 0xFFFF);
695✔
532
    verbose_flag = 1;
695✔
533
    backoff_flag = 1;
695✔
534
    opterr = 1;
695✔
535

536
    /* get command line options */
537

538
    struct optparse_long longopts[] = {
695✔
539
        { "ipv4", '4', OPTPARSE_NONE },
540
        { "ipv6", '6', OPTPARSE_NONE },
541
        { "alive", 'a', OPTPARSE_NONE },
542
        { "addr", 'A', OPTPARSE_NONE },
543
        { "size", 'b', OPTPARSE_REQUIRED },
544
        { "backoff", 'B', OPTPARSE_REQUIRED },
545
        { "count", 'c', OPTPARSE_REQUIRED },
546
        { "vcount", 'C', OPTPARSE_REQUIRED },
547
        { "rdns", 'd', OPTPARSE_NONE },
548
        { "timestamp", 'D', OPTPARSE_NONE },
549
        { "timestamp-format", '0', OPTPARSE_REQUIRED },
550
        { "elapsed", 'e', OPTPARSE_NONE },
551
        { "file", 'f', OPTPARSE_REQUIRED },
552
        { "generate", 'g', OPTPARSE_NONE },
553
        { "help", 'h', OPTPARSE_NONE },
554
        { "ttl", 'H', OPTPARSE_REQUIRED },
555
        { "interval", 'i', OPTPARSE_REQUIRED },
556
        { "iface", 'I', OPTPARSE_REQUIRED },
557
        { "json", 'J', OPTPARSE_NONE },
558
        { "icmp-timestamp", '0', OPTPARSE_NONE },
559
#ifdef SO_MARK
560
        { "fwmark", 'k', OPTPARSE_REQUIRED },
561
#endif
562
        { "loop", 'l', OPTPARSE_NONE },
563
        { "all", 'm', OPTPARSE_NONE },
564
        { "dontfrag", 'M', OPTPARSE_NONE },
565
        { "name", 'n', OPTPARSE_NONE },
566
        { "netdata", 'N', OPTPARSE_NONE },
567
        { "outage", 'o', OPTPARSE_NONE },
568
        { "tos", 'O', OPTPARSE_REQUIRED },
569
        { "period", 'p', OPTPARSE_REQUIRED },
570
        { "quiet", 'q', OPTPARSE_NONE },
571
        { "squiet", 'Q', OPTPARSE_REQUIRED },
572
        { "retry", 'r', OPTPARSE_REQUIRED },
573
        { "random", 'R', OPTPARSE_NONE },
574
        { "stats", 's', OPTPARSE_NONE },
575
        { "src", 'S', OPTPARSE_REQUIRED },
576
        { "timeout", 't', OPTPARSE_REQUIRED },
577
        { NULL, 'T', OPTPARSE_REQUIRED },
578
        { "unreach", 'u', OPTPARSE_NONE },
579
        { "version", 'v', OPTPARSE_NONE },
580
        { "reachable", 'x', OPTPARSE_REQUIRED },
581
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
582
        { "check-source", '0', OPTPARSE_NONE },
583
        { "print-tos", '0', OPTPARSE_NONE },
584
        { "print-ttl", '0', OPTPARSE_NONE },
585
#if defined(DEBUG) || defined(_DEBUG)
586
        { NULL, 'z', OPTPARSE_REQUIRED },
587
#endif
588
        { 0, 0, 0 }
589
    };
590

591
    float opt_value_float;
592
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
1,704✔
593
        switch (c) {
1,115✔
594
        case '0':
92✔
595
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
117✔
596
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
24✔
597
                  timestamp_format_flag = 1;
6✔
598
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
20✔
599
                  timestamp_format_flag = 2;
6✔
600
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
14✔
601
                  timestamp_format_flag = 3;
6✔
602
                }else{
2✔
603
                  usage(1);
6✔
604
                }
605
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
101✔
606
                check_source_flag = 1;
9✔
607
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
85✔
608
#ifdef IPV6
609
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
29✔
610
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
611
                    exit(1);
2✔
612
                }
613
                hints_ai_family = AF_INET;
27✔
614
#endif
615
                icmp_request_typ = 13;
27✔
616
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
27✔
617
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
58✔
618
                print_tos_flag = 1;
29✔
619
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
29✔
620
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTOS, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
621
                        perror("setsockopt IP_RECVTOS");
×
622
                    }
623
                }
624
#if defined(IPV6) && defined(IPV6_RECVTCLASS)
625
                if (socket6 >= 0) {
29✔
626
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVTCLASS, &sock_opt_on, sizeof(sock_opt_on))) {
29✔
627
                        perror("setsockopt IPV6_RECVTCLASS");
×
628
                    }
629
                }
7✔
630
#endif
631
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
33✔
632
                print_ttl_flag = 1;
26✔
633
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
26✔
634
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTTL, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
635
                        perror("setsockopt IP_RECVTTL");
×
636
                    }
637
                }
638
#if defined(IPV6) && defined(IPV6_RECVHOPLIMIT)
639
                if (socket6 >= 0) {
26✔
640
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &sock_opt_on, sizeof(sock_opt_on))) {
26✔
641
                        perror("setsockopt IPV6_RECVHOPLIMIT");
×
642
                    }
643
                }
6✔
644
#endif
645
            } else {
6✔
646
                usage(1);
×
647
            }
648
            break;
111✔
649
        case '4':
32✔
650
#ifdef IPV6
651
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
652
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
653
                exit(1);
3✔
654
            }
655
            hints_ai_family = AF_INET;
40✔
656
#endif
657
            break;
40✔
658
        case '6':
28✔
659
#ifdef IPV6
660
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
31✔
661
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
662
                exit(1);
5✔
663
            }
664
            hints_ai_family = AF_INET6;
26✔
665
#else
666
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
667
            exit(1);
668
#endif
669
            break;
26✔
670
        case 'M':
2✔
671
#ifdef IP_MTU_DISCOVER
672
            if (socket4 >= 0) {
2✔
673
                int val = IP_PMTUDISC_DO;
2✔
674
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
675
                    perror("setsockopt IP_MTU_DISCOVER");
676
                }
677
            }
678
#ifdef IPV6
679
            if (socket6 >= 0) {
2✔
680
                int val = IPV6_PMTUDISC_DO;
2✔
681
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
682
                    perror("setsockopt IPV6_MTU_DISCOVER");
683
                }
684
            }
685
#endif
686
#else
687
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
688
            exit(1);
689
#endif
690
            break;
2✔
691

692
        case 't':
40✔
693
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
42✔
694
                usage(1);
6✔
695
            if (opt_value_float < 0) {
38✔
696
                usage(1);
3✔
697
            }
1✔
698
            timeout = opt_value_float * 1000000;
36✔
699
            timeout_flag = 1;
36✔
700
            break;
36✔
701

702
        case 'r':
36✔
703
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
38✔
704
                usage(1);
6✔
705
            break;
34✔
706

707
        case 'i':
30✔
708
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
709
                usage(1);
6✔
710
            if (opt_value_float < 0) {
29✔
711
                usage(1);
3✔
712
            }
1✔
713
            interval = opt_value_float * 1000000;
27✔
714
            break;
27✔
715

716
        case 'p':
70✔
717
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
99✔
718
                usage(1);
6✔
719
            if (opt_value_float < 0) {
95✔
720
                usage(1);
3✔
721
            }
1✔
722
            perhost_interval = opt_value_float * 1000000;
93✔
723

724
            break;
93✔
725

726
        case 'c':
90✔
727
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
127✔
728
                usage(1);
6✔
729

730
            count_flag = 1;
123✔
731
            break;
123✔
732

733
        case 'C':
34✔
734
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
46✔
735
                usage(1);
6✔
736

737
            count_flag = 1;
42✔
738
            report_all_rtts_flag = 1;
42✔
739
            break;
42✔
740

741
        case 'b':
14✔
742
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
19✔
743
                usage(1);
6✔
744
            size_flag = 1;
15✔
745
            break;
15✔
746

747
        case 'h':
2✔
748
            usage(0);
3✔
749
            break;
1✔
750

751
        case 'q':
26✔
752
            verbose_flag = 0;
38✔
753
            quiet_flag = 1;
38✔
754
            break;
38✔
755

756
        case 'Q':
24✔
757
            verbose_flag = 0;
33✔
758
            quiet_flag = 1;
33✔
759
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
760
                usage(1);
6✔
761
            if (opt_value_float < 0) {
29✔
762
                usage(1);
3✔
763
            }
1✔
764
            report_interval = opt_value_float * 1e9;
27✔
765

766
            /* recognize keyword(s) after number, ignore everything else */
767
            {
768
                char *comma = strchr(optparse_state.optarg, ',');
27✔
769
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
27✔
770
                    cumulative_stats_flag = 1;
6✔
771
                }
2✔
772
            }
773

774
            break;
27✔
775

776
        case 'e':
6✔
777
            elapsed_flag = 1;
8✔
778
            break;
8✔
779

780
        case 'm':
781
            multif_flag = 1;
×
782
            break;
×
783

784
        case 'N':
2✔
785
            netdata_flag = 1;
3✔
786
            break;
3✔
787

788
        case 'n':
4✔
789
            name_flag = 1;
6✔
790
            if (rdns_flag) {
6✔
791
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
792
                exit(1);
3✔
793
            }
794
            break;
3✔
795

796
        case 'd':
6✔
797
            rdns_flag = 1;
9✔
798
            if (name_flag) {
9✔
799
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
800
                exit(1);
3✔
801
            }
802
            break;
6✔
803

804
        case 'A':
2✔
805
            addr_flag = 1;
3✔
806
            break;
3✔
807

808
        case 'B':
10✔
809
            if (!(backoff = atof(optparse_state.optarg)))
12✔
810
                usage(1);
6✔
811

812
            break;
8✔
813

814
        case 's':
8✔
815
            stats_flag = 1;
12✔
816
            break;
12✔
817

818
        case 'D':
22✔
819
            timestamp_flag = 1;
33✔
820
            break;
33✔
821

822
        case 'R':
4✔
823
            random_data_flag = 1;
5✔
824
            break;
5✔
825

826
        case 'l':
2✔
827
            loop_flag = 1;
3✔
828
            backoff_flag = 0;
3✔
829
            break;
3✔
830

831
        case 'u':
8✔
832
            unreachable_flag = 1;
12✔
833
            break;
12✔
834

835
        case 'a':
18✔
836
            alive_flag = 1;
26✔
837
            break;
26✔
838

839
        case 'H':
8✔
840
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
10✔
841
                usage(1);
6✔
842
            break;
6✔
843

844
#if defined(DEBUG) || defined(_DEBUG)
845
        case 'z':
846
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
847
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
848
                    usage(1);
849

850
            break;
851
#endif /* DEBUG || _DEBUG */
852

853
        case 'v':
4✔
854
            printf("%s: Version %s\n", prog, VERSION);
6✔
855
            exit(0);
6✔
856

857
        case 'x':
8✔
858
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
10✔
859
                usage(1);
6✔
860
            break;
6✔
861

862
        case 'X':
6✔
863
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
864
                usage(1);
6✔
865
            fast_reachable = 1;
5✔
866
            break;
5✔
867

868
        case 'f':
18✔
869
            filename = optparse_state.optarg;
27✔
870
            break;
27✔
871
#ifdef SO_MARK
872
        case 'k':
10✔
873
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
10✔
874
                usage(1);
4✔
875

876
            if (socket4 >= 0)
6✔
877
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
878
                    perror("fwmark ipv4");
4✔
879

880
#ifdef IPV6
881
            if (socket6 >= 0)
6✔
882
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
883
                    perror("fwmark ipv6");
4✔
884
#endif
885

886
            break;
6✔
887
#endif
888

889
        case 'g':
100✔
890
            /* use IP list generation */
891
            /* mutually exclusive with using file input or command line targets */
892
            generate_flag = 1;
120✔
893
            break;
120✔
894

895
        case 'S':
10✔
896
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
897
                src_addr_set = 1;
6✔
898
                break;
6✔
899
            }
900
#ifdef IPV6
901
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
902
                src_addr6_set = 1;
4✔
903
                break;
4✔
904
            }
905
#endif
906
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
907
            exit(1);
3✔
908

909
        case 'I':
6✔
910
#ifdef SO_BINDTODEVICE
911
            if (socket4 >= 0) {
6✔
912
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
913
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
914
                    exit(1);
2✔
915
                }
916
            }
917
#ifdef IPV6
918
            if (socket6 >= 0) {
4✔
919
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
920
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
921
                    exit(1);
922
                }
923
            }
924
#endif
925
#else
926
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
1✔
927
            exit(3);
1✔
928
            ;
929
#endif
930
            break;
4✔
931

932
        case 'J':
42✔
933
            json_flag = 1;
62✔
934
            break;
62✔
935

936
        case 'T':
2✔
937
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
938
            break;
3✔
939

940
        case 'O':
10✔
941
            if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
15✔
942
                if (socket4 >= 0) {
9✔
943
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
944
                        perror("setting type of service octet IP_TOS");
×
945
                    }
946
                }
3✔
947
#if defined(IPV6) && defined(IPV6_TCLASS)
948
                if (socket6 >= 0) {
9✔
949
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
950
                        perror("setting type of service octet IPV6_TCLASS");
×
951
                    }
952
                }
3✔
953
#endif
954
            }
3✔
955
            else {
956
                usage(1);
6✔
957
            }
958
            break;
11✔
959

960
        case 'o':
6✔
961
            outage_flag = 1;
9✔
962
            break;
9✔
963

964
        case '?':
4✔
965
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
966
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
967
            exit(1);
6✔
968
            break;
969
        }
970
    }
971

972
    /* permanently drop privileges */
973
    if (suid != getuid() && setuid(getuid())) {
539✔
974
        perror("fatal: failed to permanently drop privileges");
×
975
        /* continuing would be a security hole */
976
        exit(4);
×
977
    }
978

979
    /* validate various option settings */
980

981
#ifndef IPV6
982
    if (socket4 < 0) {
983
        crash_and_burn("can't create socket (must run as root?)");
984
    }
985
#else
986
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
539✔
987
        crash_and_burn("can't create socket (must run as root?)");
×
988
    }
989
#endif
990

991
    if (ttl > 255) {
537✔
992
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
993
        exit(1);
3✔
994
    }
995

996
    if (unreachable_flag && alive_flag) {
534✔
997
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
998
        exit(1);
3✔
999
    }
1000

1001
    if (count_flag && loop_flag) {
531✔
1002
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
1003
        exit(1);
3✔
1004
    }
1005

1006
    if (json_flag && !count_flag) {
528✔
1007
        fprintf(stderr, "%s: option -J, --json requires -c or -C\n", prog);
3✔
1008
        exit(1);
3✔
1009
    }
1010

1011
#ifdef FPING_SAFE_LIMITS
1012
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
525✔
1013
        && getuid()) {
137✔
1014
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
1015
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
1016
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
1017
        exit(1);
6✔
1018
    }
1019
#endif
1020

1021
    if (ping_data_size > MAX_PING_DATA) {
519✔
1022
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1023
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1024
        exit(1);
3✔
1025
    }
1026

1027
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
516✔
1028
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1029
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1030
        exit(1);
6✔
1031
    }
1032

1033
    if (icmp_request_typ == 13 && size_flag != 0) {
510✔
1034
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1035
        exit(1);
9✔
1036
    }
1037

1038
    if (count_flag) {
501✔
1039
        if (verbose_flag)
159✔
1040
            per_recv_flag = 1;
94✔
1041

1042
        alive_flag = unreachable_flag = verbose_flag = 0;
159✔
1043
    }
47✔
1044

1045
    if (loop_flag) {
501✔
1046
        if (!report_interval)
×
1047
            per_recv_flag = 1;
×
1048

1049
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1050
    }
1051

1052
    if (alive_flag || unreachable_flag || min_reachable)
501✔
1053
        verbose_flag = 0;
37✔
1054

1055
    trials = (count > retry + 1) ? count : retry + 1;
517✔
1056

1057
    /* auto-tune default timeout for count/loop modes
1058
     * see also github #32 */
1059
    if (loop_flag || count_flag) {
517✔
1060
        if (!timeout_flag) {
159✔
1061
            timeout = perhost_interval;
154✔
1062
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
154✔
1063
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1064
            }
1065
        }
46✔
1066
    }
47✔
1067

1068
#if defined(DEBUG) || defined(_DEBUG)
1069
    if (debugging & DBG_TRACE)
1070
        trace_flag = 1;
1071

1072
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1073
        randomly_lose_flag = 1;
1074
        lose_factor = 1; /* ie, 1/4 */
1075
    }
1076

1077
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1078
        randomly_lose_flag = 1;
1079
        lose_factor = 5; /* ie, 3/4 */
1080
    }
1081

1082
    if (debugging & DBG_PRINT_PER_SYSTEM)
1083
        print_per_system_flag = 1;
1084

1085
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1086
        report_all_rtts_flag = 1;
1087

1088
    if (trace_flag) {
1089
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1090
            prog, count, retry, interval / 1e6);
1091
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1092
            perhost_interval / 1e6, timeout / 1e6);
1093
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1094
            ping_data_size, trials);
1095

1096
        if (verbose_flag)
1097
            fprintf(stderr, "  verbose_flag set\n");
1098
        if (multif_flag)
1099
            fprintf(stderr, "  multif_flag set\n");
1100
        if (name_flag)
1101
            fprintf(stderr, "  name_flag set\n");
1102
        if (addr_flag)
1103
            fprintf(stderr, "  addr_flag set\n");
1104
        if (stats_flag)
1105
            fprintf(stderr, "  stats_flag set\n");
1106
        if (unreachable_flag)
1107
            fprintf(stderr, "  unreachable_flag set\n");
1108
        if (alive_flag)
1109
            fprintf(stderr, "  alive_flag set\n");
1110
        if (elapsed_flag)
1111
            fprintf(stderr, "  elapsed_flag set\n");
1112
        if (version_flag)
1113
            fprintf(stderr, "  version_flag set\n");
1114
        if (count_flag)
1115
            fprintf(stderr, "  count_flag set\n");
1116
        if (loop_flag)
1117
            fprintf(stderr, "  loop_flag set\n");
1118
        if (backoff_flag)
1119
            fprintf(stderr, "  backoff_flag set\n");
1120
        if (per_recv_flag)
1121
            fprintf(stderr, "  per_recv_flag set\n");
1122
        if (report_all_rtts_flag)
1123
            fprintf(stderr, "  report_all_rtts_flag set\n");
1124
        if (randomly_lose_flag)
1125
            fprintf(stderr, "  randomly_lose_flag set\n");
1126
        if (print_per_system_flag)
1127
            fprintf(stderr, "  print_per_system_flag set\n");
1128
        if (outage_flag)
1129
            fprintf(stderr, "  outage_flag set\n");
1130
        if (netdata_flag)
1131
            fprintf(stderr, "  netdata_flag set\n");
1132
        if (json_flag)
1133
            fprintf(stderr, "  json_flag set\n");
1134
    }
1135
#endif /* DEBUG || _DEBUG */
1136

1137
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1138
    if (ttl > 0) {
517✔
1139
        if (socket4 >= 0) {
3✔
1140
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1141
                perror("setting time to live");
×
1142
            }
1143
        }
1✔
1144
#ifdef IPV6
1145
        if (socket6 >= 0) {
3✔
1146
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1147
                perror("setting time to live");
×
1148
            }
1149
        }
1✔
1150
#endif
1151
    }
1✔
1152

1153
#if HAVE_SO_TIMESTAMPNS
1154
    {
1155
        int opt = 1;
376✔
1156
        if (socket4 >= 0) {
376✔
1157
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
376✔
1158
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1159
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1160
                }
1161
            }
1162
        }
1163
#ifdef IPV6
1164
        if (socket6 >= 0) {
376✔
1165
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
376✔
1166
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1167
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1168
                }
1169
            }
1170
        }
1171
#endif
1172
    }
1173
#endif
1174

1175
    update_current_time();
517✔
1176
    start_time = current_time_ns;
517✔
1177

1178
    /* handle host names supplied on command line or in a file */
1179
    /* if the generate_flag is on, then generate the IP list */
1180

1181
    argv = &argv[optparse_state.optind];
517✔
1182
    argc -= optparse_state.optind;
517✔
1183

1184
    /* calculate how many ping can be in-flight per host */
1185
    if (count_flag) {
517✔
1186
        event_storage_count = count;
174✔
1187
    }
62✔
1188
    else if (loop_flag) {
343✔
1189
        if (perhost_interval > timeout) {
×
1190
            event_storage_count = 1;
×
1191
        }
1192
        else {
1193
            event_storage_count = 1 + timeout / perhost_interval;
×
1194
        }
1195
    }
1196
    else {
1197
        event_storage_count = 1;
343✔
1198
    }
1199

1200
    /* file and generate are mutually exclusive */
1201
    /* file and command line are mutually exclusive */
1202
    /* generate requires command line parameters beyond the switches */
1203
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
517✔
1204
        usage(1);
19✔
1205

1206
    /* if no conditions are specified, then assume input from stdin */
1207
    if (!*argv && !filename && !generate_flag)
499✔
1208
        filename = "-";
69✔
1209

1210
    if (*argv && !generate_flag) {
499✔
1211
        while (*argv) {
639✔
1212
            add_name(*argv);
350✔
1213
            ++argv;
350✔
1214
        }
1215
    }
75✔
1216
    else if (filename) {
210✔
1217
        FILE *ping_file;
1218
        char line[MAX_TARGET_NAME_LEN + 1];
1219
        char host[MAX_TARGET_NAME_LEN + 1];
1220
        char scratch[MAX_TARGET_NAME_LEN + 1];
1221
        int skip, non_empty;
1222

1223
        if (strcmp(filename, "-") == 0)
96✔
1224
            ping_file = fdopen(0, "r");
72✔
1225
        else
1226
            ping_file = fopen(filename, "r");
24✔
1227

1228
        if (!ping_file)
96✔
1229
            errno_crash_and_burn("fopen");
3✔
1230

1231
        /*
1232
         * Read the first word of every non-comment line, skip everything else.
1233
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1234
         * character is a '#' are interpreted as comments and ignored.)
1235
        */
1236
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1237
            skip = non_empty = 0;
309✔
1238

1239
            /* skip over a prefix of the line where sscanf finds nothing */
1240
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1241
                continue;
99✔
1242
            }
1243

1244
            /* the first word of the line can indicate a comment line */
1245
            if (host[0] == '#') {
210✔
1246
                skip = 1; /* skip remainder of line */
42✔
1247
            } else {
14✔
1248
                non_empty = 1; /* we have something to add as a target name */
168✔
1249
                /*
1250
                 * We have found the start of a word.
1251
                 * This part of the line may contain all of the first word.
1252
                 */
1253
                if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
168✔
1254
                    char discard1[MAX_TARGET_NAME_LEN + 1];
1255
                    char discard2[MAX_TARGET_NAME_LEN + 1];
1256
                    if (sscanf(line, "%s%s", discard1, discard2) == 2) {
102✔
1257
                        skip = 1; /* a second word starts in this part */
6✔
1258
                    }
2✔
1259
                    if (isspace(line[sizeof(line) - 2])) {
102✔
1260
                        skip = 1; /* the first word ends in this part */
42✔
1261
                    }
14✔
1262
                }
34✔
1263
            }
1264
            /* read remainder of this input line */
1265
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
336✔
1266
                if (skip) {
150✔
1267
                    continue; /* skip rest of data in this input line */
102✔
1268
                }
1269
                if (isspace(line[0])) {
48✔
1270
                    skip = 1; /* first word ended in previous part */
6✔
1271
                    continue;
6✔
1272
                }
1273
                if ((sscanf(line, "%s", scratch) != 1) || (!*scratch)) {
42✔
1274
                    skip = 1; /* empty or blank part of line, skip the rest */
×
1275
                    continue;
×
1276
                }
1277
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
42✔
1278
                    fprintf(stderr, "%s: target name too long\n", prog);
24✔
1279
                    exit(1);
24✔
1280
                }
1281
                /* append remainder of word started in previous line part */
1282
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
18✔
1283
                /*
1284
                 * Since the "host" buffer is the same size as the "line"
1285
                 * buffer, a target name that fits into the "host" buffer
1286
                 * cannot use more than two consecutive line parts.
1287
                 * A target name that uses two consecutive line parts
1288
                 * and fits into the "host" buffer must end before the
1289
                 * end of the second "line" buffer.  Thus the rest of
1290
                 * the line can be skipped.
1291
                 */
1292
                skip = 1;
18✔
1293
            }
1294

1295
            if (non_empty)
186✔
1296
                add_name(host);
144✔
1297
        }
1298

1299
        fclose(ping_file);
70✔
1300
    }
24✔
1301
    else if (*argv && generate_flag) {
114✔
1302
        if (argc == 1) {
114✔
1303
            /* one target: we expect a cidr range (n.n.n.n/m) */
1304
            add_cidr(argv[0]);
59✔
1305
        }
11✔
1306
        else if (argc == 2) {
55✔
1307
            add_range(argv[0], argv[1]);
52✔
1308
        }
6✔
1309
        else {
1310
            usage(1);
3✔
1311
        }
1312
    }
18✔
1313
    else {
1314
        usage(1);
×
1315
    }
1316

1317
    if (!num_hosts) {
419✔
1318
        exit(num_noaddress ? 2 : 1);
36✔
1319
    }
1320

1321
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
383✔
1322
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1323
    }
2✔
1324
#ifdef IPV6
1325
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
381✔
1326
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1327
    }
1328
#endif
1329

1330
    /* allocate and initialize array to map host nr to host_entry */
1331
    {
1332
        struct event *cursor = event_queue_ping.first;
379✔
1333
        int i = 0;
379✔
1334
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
379✔
1335
        if (!table)
379✔
1336
            crash_and_burn("Can't malloc array of hosts");
×
1337
        /* initialize table of hosts. we know that we have ping events scheduled
1338
         * for each of them */
1339
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
948✔
1340
            table[i] = cursor->host;
569✔
1341
            cursor->host->i = i;
569✔
1342
            i++;
569✔
1343
        }
155✔
1344
    }
1345

1346
    init_ping_buffer_ipv4(ping_data_size);
379✔
1347
#ifdef IPV6
1348
    init_ping_buffer_ipv6(ping_data_size);
379✔
1349
#endif
1350

1351
#ifdef USE_SIGACTION
1352
    memset(&act, 0, sizeof(act));
379✔
1353
    act.sa_handler = signal_handler;
379✔
1354
    sigemptyset(&act.sa_mask);
379✔
1355
    sigaddset(&act.sa_mask, SIGINT);
379✔
1356
    sigaddset(&act.sa_mask, SIGQUIT);
379✔
1357
    act.sa_flags = SA_RESTART;
379✔
1358
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
379✔
1359
        crash_and_burn("failure to set signal handler");
24✔
1360
    }
24✔
1361
#else
1362
    signal(SIGINT, signal_handler);
1363
    signal(SIGQUIT, signal_handler);
1364
#endif
1365
    setlinebuf(stdout);
355✔
1366

1367
    if (report_interval) {
355✔
1368
        next_report_time = current_time_ns + report_interval;
27✔
1369
    }
9✔
1370

1371
    last_send_time = 0;
355✔
1372

1373
    seqmap_init();
355✔
1374

1375
    /* main loop */
1376
    main_loop();
355✔
1377

1378
/* Debug: CPU Performance */
1379
#if defined(DEBUG) || defined(_DEBUG)
1380
    perf_cpu_end = clock();
1381
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1382
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1383
#endif /* DEBUG || _DEBUG */
1384

1385
    finish();
355✔
1386

1387
    return 0;
83✔
1388
}
1389

1390
static inline int64_t timespec_ns(struct timespec *a)
5,720✔
1391
{
1392
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,720✔
1393
}
1394

1395
#if HAVE_SO_TIMESTAMPNS
1396
/* convert a struct timeval to nanoseconds */
1397
static inline int64_t timeval_ns(struct timeval *a)
1398
{
1399
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1400
}
1401
#endif /* HAVE_SO_TIMESTAMPNS */
1402

1403
void add_cidr(char *addr)
57✔
1404
{
1405
    char *addr_end;
1406
    char *mask_str;
1407
    unsigned long mask;
1408
    int ret;
1409
    struct addrinfo addr_hints;
1410
    struct addrinfo *addr_res;
1411
    unsigned long net_addr;
1412
#ifdef IPV6
1413
    uint64_t net_upper, net_lower;
1414
    char *scope_str;
1415
#endif /* IPV6 */
1416

1417
    /* Split address from mask */
1418
    addr_end = strrchr(addr, '/');
57✔
1419
    if (addr_end == NULL) {
57✔
1420
        usage(1);
3✔
1421
    }
1✔
1422
    mask_str = addr_end + 1;
55✔
1423

1424
#ifdef IPV6
1425
    /* IPv6 addresses can have a scope */
1426
    scope_str = strchr(addr, '%');
55✔
1427
    if (scope_str && mask_str < scope_str) {
55✔
1428
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1429
        exit(1);
2✔
1430
    }
1431
#endif /*IPV6 */
1432

1433
    *addr_end = '\0';
53✔
1434
    mask = atoi(mask_str);
53✔
1435

1436
    /* parse address */
1437
    memset(&addr_hints, 0, sizeof(struct addrinfo));
53✔
1438
    addr_hints.ai_family = hints_ai_family;
53✔
1439
    addr_hints.ai_flags = AI_NUMERICHOST;
53✔
1440
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
53✔
1441
    if (ret) {
53✔
1442
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1443
        exit(1);
7✔
1444
    }
1445
    if (addr_res->ai_family == AF_INET) {
46✔
1446
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
24✔
1447
        freeaddrinfo(addr_res);
24✔
1448
        add_cidr_ipv4(net_addr, mask);
24✔
1449
#ifdef IPV6
1450
    } else if (addr_res->ai_family == AF_INET6) {
30✔
1451
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1452
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1453
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1454
        freeaddrinfo(addr_res);
22✔
1455
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1456
#endif /* IPV6 */
1457
    } else {
1458
        freeaddrinfo(addr_res);
×
1459
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1460
        exit(1);
×
1461
    }
1462
}
30✔
1463

1464
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1465
{
1466
    unsigned long bitmask;
1467
    unsigned long net_last;
1468

1469
    /* check mask */
1470
    if (mask < 1 || mask > 32) {
24✔
1471
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1472
        exit(1);
6✔
1473
    }
1474

1475
    /* convert mask integer from 1 to 32 to a bitmask */
1476
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1477

1478
    /* calculate network range */
1479
    net_addr &= bitmask;
18✔
1480
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1481

1482
    /* exclude network and broadcast address for regular prefixes */
1483
    if (mask < 31) {
18✔
1484
        net_last--;
12✔
1485
        net_addr++;
12✔
1486
    }
4✔
1487

1488
    /* add all hosts in that network (net_addr and net_last inclusive) */
1489
    add_addr_range_ipv4(net_addr, net_last);
18✔
1490
}
16✔
1491

1492
#ifdef IPV6
1493
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1494
{
1495
    uint64_t bitmask_lower;
1496
    uint64_t last_lower;
1497

1498
    /* check mask -- 2^63 addresses should suffice for now */
1499
    if (mask < 65 || mask > 128) {
23✔
1500
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1501
        exit(1);
7✔
1502
    }
1503

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

1507
    /* calculate network range */
1508
    net_lower &= bitmask_lower;
16✔
1509
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1510

1511
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1512
}
12✔
1513
#endif /* IPV6 */
1514

1515
void add_range(char *start, char *end)
52✔
1516
{
1517
    struct addrinfo addr_hints;
1518
    struct addrinfo *addr_res;
1519
    unsigned long start_long;
1520
    unsigned long end_long;
1521
    int ret;
1522
#ifdef IPV6
1523
    uint64_t start_upper, start_lower;
1524
    uint64_t end_upper, end_lower;
1525
    char *start_scope_str, *end_scope_str;
1526

1527
    /*
1528
     * The compiler does not know that setting the address family hint to
1529
     * ensure that start and end are from the same address family also
1530
     * ensures that either start_long and end_long are initialized and used,
1531
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1532
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1533
     * supported to suppress compiler warnings.
1534
     */
1535
    start_long = -1;
52✔
1536
    end_long = 0;
52✔
1537
    start_upper = start_lower = -1;
52✔
1538
    end_upper = end_lower = 0;
52✔
1539
    start_scope_str = end_scope_str = NULL;
52✔
1540
#endif /* IPV6 */
1541

1542
    /* parse start address */
1543
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1544
    addr_hints.ai_family = hints_ai_family;
52✔
1545
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1546
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1547
    if (ret) {
52✔
1548
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1549
        exit(1);
9✔
1550
    }
1551
    /* start and end must be from the same address family */
1552
    hints_ai_family = addr_res->ai_family;
43✔
1553
    if (addr_res->ai_family == AF_INET) {
43✔
1554
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1555
        freeaddrinfo(addr_res);
17✔
1556
#ifdef IPV6
1557
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1558
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1559
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1560
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1561
        freeaddrinfo(addr_res);
26✔
1562
#endif /* IPV6 */
1563
    } else {
1564
        freeaddrinfo(addr_res);
×
1565
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1566
        exit(1);
×
1567
    }
1568

1569
#ifdef IPV6
1570
    /* IPv6 addresses can have a scope */
1571
    if (hints_ai_family == AF_INET6) {
43✔
1572
        start_scope_str = strchr(start, '%');
26✔
1573
        end_scope_str = strchr(end, '%');
26✔
1574
        if ((!start_scope_str && end_scope_str) ||
26✔
1575
            (start_scope_str && !end_scope_str) ||
24✔
1576
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1577
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1578
                exit(1);
6✔
1579
        }
1580
    }
1581
#endif
1582

1583
    /* parse end address */
1584
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1585
    addr_hints.ai_family = hints_ai_family;
37✔
1586
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1587
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1588
    if (ret) {
37✔
1589
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1590
        exit(1);
7✔
1591
    }
1592
    if (addr_res->ai_family == AF_INET) {
30✔
1593
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1594
        freeaddrinfo(addr_res);
12✔
1595
        add_addr_range_ipv4(start_long, end_long);
12✔
1596
#ifdef IPV6
1597
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1598
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1599
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1600
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1601
        freeaddrinfo(addr_res);
18✔
1602
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1603
#endif /* IPV6 */
1604
    } else {
1605
        freeaddrinfo(addr_res);
×
1606
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1607
        exit(1);
×
1608
    }
1609
}
24✔
1610

1611
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1612
{
1613
    /* check if generator limit is exceeded */
1614
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1615
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1616
        exit(1);
6✔
1617
    }
1618

1619
    /* generate */
1620
    for (; start_long <= end_long; start_long++) {
93✔
1621
        struct in_addr in_addr_tmp;
1622
        char buffer[20];
1623
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1624
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1625
        add_name(buffer);
69✔
1626
    }
23✔
1627
}
24✔
1628

1629
#ifdef IPV6
1630
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1631
{
1632
    int i;
1633
    uint64_t ret = 0;
134✔
1634
    for (i = 0; i < 8; i++) {
1,206✔
1635
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1636
    }
16✔
1637
    return ret;
134✔
1638
}
1639

1640
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1641
{
1642
    int i;
1643
    for (i = 0; i < 8; i++) {
720✔
1644
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1645
    }
1646
}
80✔
1647

1648
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1649
                         uint64_t end_upper, uint64_t end_lower,
1650
                         const char *scope_str)
1651
{
1652
    struct in6_addr in6_addr_tmp;
1653
    char buffer[100];
1654

1655
    /* prevent generating too many addresses */
1656
    if ((start_upper + 1 < end_upper) ||
34✔
1657
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1658
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1659
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1660
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1661
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1662
        exit(1);
8✔
1663
    }
1664

1665
    while ((start_upper < end_upper) ||
66✔
1666
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1667
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1668
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1669
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1670
        if (scope_str) {
40✔
1671
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1672
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1673
                exit(1);
×
1674
            }
1675
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1676
        }
1677
        add_name(buffer);
40✔
1678
        start_lower++;
40✔
1679
        if (start_lower == 0) {
40✔
1680
            start_upper++;
2✔
1681
        }
1682
    }
1683
}
26✔
1684
#endif /* IPv6 */
1685

1686
void main_loop()
367✔
1687
{
1688
    int64_t lt;
1689
    int64_t wait_time_ns;
1690
    struct event *event;
1691
    struct host_entry *h;
1692

1693
    while (event_queue_ping.first || event_queue_timeout.first) {
1,903✔
1694
        dbg_printf("%s", "# main_loop\n");
1695

1696
        /* timeout event ? */
1697
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,553✔
1698
            event = ev_dequeue(&event_queue_timeout);
101✔
1699
            h = event->host;
101✔
1700

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

1703
            stats_add(h, event->ping_index, 0, -1);
101✔
1704

1705
            if (per_recv_flag) {
101✔
1706
                if (json_flag)
25✔
1707
                    printf("{\"timeout\": {");
6✔
1708

1709
                if (timestamp_flag)
25✔
UNCOV
1710
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1711

1712
                if (json_flag)
25✔
1713
                {
1714
                  printf("\"host\": \"%s\", ", h->host);
6✔
1715
                  printf("\"seq\": %d", event->ping_index);
6✔
1716
                }
2✔
1717
                else {
1718
                    printf("%-*s : [%d], timed out",
19✔
1719
                        max_hostname_len, h->host, event->ping_index);
5✔
1720
                }
1721
                if (h->num_recv > 0) {
25✔
NEW
1722
                    if (json_flag)
×
NEW
1723
                        printf(", \"rttAvg\": %s", sprint_tm(h->total_time / h->num_recv));
×
1724
                    else
NEW
1725
                        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1726
                }
1727
                else {
1728
                    if (json_flag)
25✔
1729
                        printf(", \"rttAvg\": \"NaN\"");
6✔
1730
                    else
1731
                        printf(" (NaN avg, ");
19✔
1732
                }
1733
                if (h->num_recv <= h->num_sent) {
25✔
1734
                    if (json_flag) {
25✔
1735
                        printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
6✔
1736
                    }
2✔
1737
                    else {
1738
                        printf("%d%% loss)",
19✔
1739
                            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1740
                    }
1741
                }
7✔
1742
                else {
NEW
1743
                    if (json_flag) {
×
NEW
1744
                        printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
1745
                    }
1746
                    else {
NEW
1747
                        printf("%d%% return)",
×
NEW
1748
                            (h->num_recv_total * 100) / h->num_sent);
×
1749
                    }
1750
                }
1751

1752
                if (json_flag)
25✔
1753
                    printf("}}");
6✔
1754
                
1755
                printf("\n");
25✔
1756
            }
7✔
1757

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

1768
            /* note: we process first timeout events, because we might need to
1769
             * wait to process ping events, while we for sure never need to
1770
             * wait for timeout events.
1771
             */
1772
            continue;
101✔
1773
        }
1774

1775
        /* ping event ? */
1776
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,709✔
1777
            /* Make sure that we don't ping more than once every "interval" */
1778
            lt = current_time_ns - last_send_time;
1,151✔
1779
            if (lt < interval)
1,151✔
1780
                goto wait_for_reply;
264✔
1781

1782
            /* Dequeue the event */
1783
            event = ev_dequeue(&event_queue_ping);
887✔
1784
            h = event->host;
887✔
1785

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

1788
            /* Send the ping */
1789
            send_ping(h, event->ping_index);
887✔
1790

1791
            /* Loop and count mode: schedule next ping */
1792
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
887✔
1793
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
333✔
1794
            }
107✔
1795
        }
257✔
1796

1797
    wait_for_reply:
629✔
1798

1799
        /* When is the next ping next event? */
1800
        wait_time_ns = -1;
1,452✔
1801
        if (event_queue_ping.first) {
1,452✔
1802
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,026✔
1803
            if (wait_time_ns < 0)
1,026✔
1804
                wait_time_ns = 0;
556✔
1805
            /* make sure that we wait enough, so that the inter-ping delay is
1806
             * bigger than 'interval' */
1807
            if (wait_time_ns < interval) {
1,026✔
1808
                lt = current_time_ns - last_send_time;
558✔
1809
                if (lt < interval) {
558✔
1810
                    wait_time_ns = interval - lt;
558✔
1811
                }
202✔
1812
            }
202✔
1813

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

1817
        /* When is the next timeout event? */
1818
        if (event_queue_timeout.first) {
1,452✔
1819
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
931✔
1820
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
931✔
1821
                wait_time_ns = wait_time_timeout;
423✔
1822
                if (wait_time_ns < 0) {
423✔
1823
                    wait_time_ns = 0;
1✔
1824
                }
1825
            }
110✔
1826

1827
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1828
        }
274✔
1829

1830
        /* When is the next report due? */
1831
        if (report_interval && (loop_flag || count_flag)) {
1,452✔
1832
            int64_t wait_time_next_report = next_report_time - current_time_ns;
214✔
1833
            if (wait_time_next_report < wait_time_ns) {
214✔
1834
                wait_time_ns = wait_time_next_report;
72✔
1835
                if (wait_time_ns < 0) {
72✔
1836
                    wait_time_ns = 0;
×
1837
                }
1838
            }
24✔
1839

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

1843
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1844
        if (wait_time_ns == -1) {
1,452✔
1845
            break;
14✔
1846
        }
1847

1848
        /* end of loop was requested by interrupt signal handler */
1849
        if (finish_requested) {
1,438✔
1850
            break;
3✔
1851
        }
1852

1853
        /* Receive replies */
1854
        /* (this is what sleeps during each loop iteration) */
1855
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1856
        if (wait_for_reply(wait_time_ns)) {
1,435✔
1857
            while (wait_for_reply(0))
1,355✔
1858
                ; /* process other replies in the queue */
1859
        }
238✔
1860

1861
        update_current_time();
1,435✔
1862

1863
        if (status_snapshot) {
1,435✔
1864
            status_snapshot = 0;
×
NEW
1865
            if (json_flag)
×
NEW
1866
                print_per_system_splits_json();
×
1867
            else
NEW
1868
                print_per_system_splits();
×
1869
        }
1870

1871
        /* Print report */
1872
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,435✔
1873
            if (netdata_flag) {
45✔
1874
                print_netdata();
3✔
1875
            }
1✔
1876
            else if (json_flag) {
42✔
1877
                print_per_system_splits_json();
3✔
1878
            }
1✔
1879
            else {
1880
                print_per_system_splits();
39✔
1881
            }
1882

1883
            while (current_time_ns >= next_report_time) {
90✔
1884
                next_report_time += report_interval;
45✔
1885
            }
1886
        }
15✔
1887
    }
1888
}
367✔
1889

1890
/************************************************************
1891

1892
  Function: signal_handler
1893

1894
*************************************************************
1895

1896
  Inputs:  int signum
1897

1898
  Description:
1899

1900
  SIGQUIT signal handler - set flag and return
1901
  SIGINT signal handler - set flag and return
1902

1903
************************************************************/
1904

1905
void signal_handler(int signum)
×
1906
{
1907
    switch (signum) {
×
1908
    case SIGINT:
1909
        finish_requested = 1;
×
1910
        break;
×
1911

1912
    case SIGQUIT:
1913
        status_snapshot = 1;
×
1914
        break;
×
1915
    }
1916
}
×
1917

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

1920
  Function: update_current_time
1921

1922
*************************************************************/
1923

1924
void update_current_time()
4,618✔
1925
{
1926
    clock_gettime(CLOCKID, &current_time);
4,618✔
1927
    current_time_ns = timespec_ns(&current_time);
4,618✔
1928
}
4,618✔
1929

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

1932
  Function: finish
1933

1934
*************************************************************
1935

1936
  Inputs:  void (none)
1937

1938
  Description:
1939

1940
  Main program clean up and exit point
1941

1942
************************************************************/
1943

1944
void finish()
367✔
1945
{
1946
    int i;
1947
    HOST_ENTRY *h;
1948

1949
    update_current_time();
367✔
1950
    end_time = current_time_ns;
367✔
1951

1952
    /* tot up unreachables */
1953
    for (i = 0; i < num_hosts; i++) {
936✔
1954
        h = table[i];
569✔
1955

1956
        if (!h->num_recv) {
569✔
1957
            num_unreachable++;
86✔
1958

1959
            if (verbose_flag || unreachable_flag) {
86✔
1960
                printf("%s", h->host);
44✔
1961

1962
                if (verbose_flag)
44✔
1963
                    printf(" is unreachable");
41✔
1964

1965
                printf("\n");
44✔
1966
            }
2✔
1967
        }
14✔
1968
    }
155✔
1969

1970
    if (count_flag || loop_flag) {
367✔
1971
        if (json_flag)
156✔
1972
            print_per_system_stats_json();
56✔
1973
        else
1974
            print_per_system_stats();
100✔
1975
    }
46✔
1976
#if defined(DEBUG) || defined(_DEBUG)
1977
    else if (print_per_system_flag) {
1978
        if (json_flag)
1979
            print_per_system_stats_json();
1980
        else
1981
            print_per_system_stats();
1982
    }
1983
#endif /* DEBUG || _DEBUG */
1984

1985
    if (stats_flag) {
367✔
1986
        if (json_flag)
12✔
1987
            print_global_stats_json();
3✔
1988
        else
1989
            print_global_stats();
9✔
1990
    }
4✔
1991

1992
    if (min_reachable) {
367✔
1993
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
1994
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1995
            exit(0);
6✔
1996
        }
1997
        else {
1998
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
1999
            exit(1);
3✔
2000
        }
2001
    }
2002

2003
    if (num_noaddress)
358✔
2004
        exit(2);
3✔
2005
    else if (num_alive != num_hosts)
355✔
2006
        exit(1);
47✔
2007

2008
    exit(0);
308✔
2009
}
2010

2011
/************************************************************
2012

2013
  Function: print_per_system_stats
2014

2015
*************************************************************
2016

2017
  Inputs:  void (none)
2018

2019
  Description:
2020

2021

2022
************************************************************/
2023

2024
void print_per_system_stats(void)
100✔
2025
{
2026
    int i, j, avg, outage_ms;
2027
    HOST_ENTRY *h;
2028
    int64_t resp;
2029

2030
    if (verbose_flag || per_recv_flag)
100✔
2031
        fprintf(stderr, "\n");
59✔
2032

2033
    for (i = 0; i < num_hosts; i++) {
234✔
2034
        h = table[i];
134✔
2035
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
2036

2037
        if (report_all_rtts_flag) {
134✔
2038
            for (j = 0; j < h->num_sent; j++) {
248✔
2039
                if ((resp = h->resp_times[j]) >= 0)
204✔
2040
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2041
                else
2042
                    fprintf(stderr, " -");
8✔
2043
            }
64✔
2044

2045
            fprintf(stderr, "\n");
44✔
2046
        }
12✔
2047
        else {
2048
            if (h->num_recv <= h->num_sent) {
90✔
2049
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
2050
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
2051

2052
                if (outage_flag) {
90✔
2053
                    /* Time outage total */
2054
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
2055
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
2056
                }
3✔
2057
            }
26✔
2058
            else {
2059
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2060
                    h->num_sent, h->num_recv,
2061
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2062
            }
2063

2064
            if (h->num_recv) {
90✔
2065
                avg = h->total_time / h->num_recv;
79✔
2066
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
2067
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
2068
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
2069
            }
23✔
2070

2071
            fprintf(stderr, "\n");
90✔
2072
        }
2073
    }
38✔
2074
}
100✔
2075

2076
/************************************************************
2077

2078
  Function: print_per_system_stats_json
2079

2080
*************************************************************
2081

2082
  Inputs:  void (none)
2083

2084
  Description:
2085

2086

2087
************************************************************/
2088

2089
void print_per_system_stats_json(void)
56✔
2090
{
2091
    int i, j, avg, outage_ms;
2092
    HOST_ENTRY *h;
2093
    int64_t resp;
2094

2095
    for (i = 0; i < num_hosts; i++) {
118✔
2096
        h = table[i];
62✔
2097

2098
        if (report_all_rtts_flag)
62✔
2099
            fprintf(stdout, "{\"vSum\": {");
18✔
2100
        else
2101
            fprintf(stdout, "{\"summary\": {");
44✔
2102

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

2105
        if (report_all_rtts_flag) {
62✔
2106
            fprintf(stdout, "\"values\": [");
18✔
2107
            for (j = 0; j < h->num_sent; j++) {
42✔
2108
                if (j > 0)
24✔
2109
                  fprintf(stdout, ", ");
6✔
2110
                
2111
                if ((resp = h->resp_times[j]) >= 0)
24✔
2112
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2113
                else
2114
                    fprintf(stdout, "null");
3✔
2115
            }
8✔
2116

2117
            fprintf(stdout, "]}");
18✔
2118
        }
6✔
2119
        else {
2120
            if (h->num_recv <= h->num_sent) {
44✔
2121
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
44✔
2122
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
44✔
2123
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
44✔
2124

2125
                if (outage_flag) {
44✔
2126
                    /* Time outage total */
NEW
2127
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
NEW
2128
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2129
                }
2130
            }
14✔
2131
            else {
NEW
2132
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
NEW
2133
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
NEW
2134
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2135
            }
2136

2137
            if (h->num_recv) {
44✔
2138
                avg = h->total_time / h->num_recv;
41✔
2139
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
41✔
2140
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
41✔
2141
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
41✔
2142
            }
13✔
2143

2144
            fprintf(stdout, "}");
44✔
2145
        }
2146
        fprintf(stdout, "}\n");
62✔
2147
    }
20✔
2148
}
56✔
2149

2150
/************************************************************
2151

2152
  Function: print_netdata
2153

2154
*************************************************************
2155

2156
  Inputs:  void (none)
2157

2158
  Description:
2159

2160

2161
************************************************************/
2162

2163
void print_netdata(void)
3✔
2164
{
2165
    static int sent_charts = 0;
2166

2167
    int i;
2168
    int64_t avg;
2169
    HOST_ENTRY *h;
2170

2171
    for (i = 0; i < num_hosts; i++) {
6✔
2172
        h = table[i];
3✔
2173

2174
        if (!sent_charts) {
3✔
2175
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2176
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2177
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2178
        }
1✔
2179

2180
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2181
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2182
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2183
        printf("END\n");
3✔
2184

2185
        if (!sent_charts) {
3✔
2186
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2187
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2188
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2189
        }
1✔
2190

2191
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2192
        /*
2193
        if( h->num_recv_i <= h->num_sent_i )
2194
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2195
        else
2196
            printf("SET lost = 0\n");
2197
*/
2198

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

2202
        if (!sent_charts) {
3✔
2203
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2204
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2205
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2206
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2207
        }
1✔
2208

2209
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2210
        if (h->num_recv_i) {
3✔
2211
            avg = h->total_time_i / h->num_recv_i;
3✔
2212
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2213
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2214
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2215
        }
1✔
2216
        printf("END\n");
3✔
2217

2218
        stats_reset_interval(h);
3✔
2219
    }
1✔
2220

2221
    sent_charts = 1;
3✔
2222
}
3✔
2223

2224
/************************************************************
2225

2226
  Function: print_per_system_splits
2227

2228
*************************************************************
2229

2230
  Inputs:  void (none)
2231

2232
  Description:
2233

2234

2235
************************************************************/
2236

2237
void print_per_system_splits(void)
39✔
2238
{
2239
    int i, avg, outage_ms_i;
2240
    HOST_ENTRY *h;
2241
    struct tm *curr_tm;
2242

2243
    if (verbose_flag || per_recv_flag)
39✔
2244
        fprintf(stderr, "\n");
×
2245

2246
    update_current_time();
39✔
2247
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2248
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2249
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2250

2251
    for (i = 0; i < num_hosts; i++) {
78✔
2252
        h = table[i];
39✔
2253
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2254

2255
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2256
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2257
                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✔
2258

2259
            if (outage_flag) {
39✔
2260
                /* Time outage  */
2261
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2262
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2263
            }
4✔
2264
        }
13✔
2265
        else {
2266
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2267
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2268
        }
2269

2270
        if (h->num_recv_i) {
39✔
2271
            avg = h->total_time_i / h->num_recv_i;
27✔
2272
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2273
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2274
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2275
        }
9✔
2276

2277
        fprintf(stderr, "\n");
39✔
2278
        if (!cumulative_stats_flag) {
39✔
2279
            stats_reset_interval(h);
27✔
2280
        }
9✔
2281
    }
13✔
2282
}
39✔
2283

2284
/************************************************************
2285

2286
  Function: print_per_system_splits_json
2287

2288
*************************************************************
2289

2290
  Inputs:  void (none)
2291

2292
  Description:
2293

2294

2295
************************************************************/
2296

2297
void print_per_system_splits_json(void)
3✔
2298
{
2299
    int i, avg, outage_ms_i;
2300
    HOST_ENTRY *h;
2301

2302
    update_current_time();
3✔
2303

2304
    for (i = 0; i < num_hosts; i++) {
6✔
2305
        h = table[i];
3✔
2306
        fprintf(stdout, "{\"intSum\": {");
3✔
2307
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2308
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2309

2310
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2311
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2312
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2313
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2314

2315
            if (outage_flag) {
3✔
2316
                /* Time outage  */
NEW
2317
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
NEW
2318
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2319
            }
2320
        }
1✔
2321
        else {
NEW
2322
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
NEW
2323
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
NEW
2324
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2325
        }
2326

2327
        if (h->num_recv_i) {
3✔
2328
            avg = h->total_time_i / h->num_recv_i;
3✔
2329
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2330
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2331
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2332
        }
1✔
2333

2334
        fprintf(stdout, "}}\n");
3✔
2335
        if (!cumulative_stats_flag) {
3✔
2336
            stats_reset_interval(h);
3✔
2337
        }
1✔
2338
    }
1✔
2339
}
3✔
2340

2341
/************************************************************
2342

2343
  Function: print_global_stats
2344

2345
*************************************************************
2346

2347
  Inputs:  void (none)
2348

2349
  Description:
2350

2351

2352
************************************************************/
2353

2354
void print_global_stats(void)
9✔
2355
{
2356
    fprintf(stderr, "\n");
9✔
2357
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2358
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2359
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2360
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2361
    fprintf(stderr, "\n");
9✔
2362
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2363
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2364
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2365
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2366
    fprintf(stderr, "\n");
9✔
2367

2368
    if (total_replies == 0) {
9✔
2369
        min_reply = 0;
3✔
2370
        max_reply = 0;
3✔
2371
        total_replies = 1;
3✔
2372
        sum_replies = 0;
3✔
2373
    }
1✔
2374

2375
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2376
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2377
        sprint_tm(sum_replies / total_replies));
3✔
2378
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2379
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2380
        (end_time - start_time) / 1e9);
9✔
2381
    fprintf(stderr, "\n");
9✔
2382
}
9✔
2383

2384
/************************************************************
2385

2386
  Function: print_global_stats_json
2387

2388
*************************************************************
2389

2390
  Inputs:  void (none)
2391

2392
  Description:
2393

2394

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

2397
void print_global_stats_json(void)
3✔
2398
{
2399
    fprintf(stdout, "{\"globalSum\": {");
3✔
2400
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2401
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2402
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2403
    fprintf(stdout, "\"unknown addresses\": %d, ", num_noaddress);
3✔
2404
    fprintf(stdout, "\"timeouts (waiting for response)\": %d, ", num_timeout);
3✔
2405
    fprintf(stdout, "\"ICMP Echos sent\": %d, ", num_pingsent);
3✔
2406
    fprintf(stdout, "\"ICMP Echo Replies received\": %d, ", num_pingreceived);
3✔
2407
    fprintf(stdout, "\"other ICMP received\": %d, ", num_othericmprcvd);
3✔
2408

2409
    if (total_replies == 0) {
3✔
NEW
2410
        min_reply = 0;
×
NEW
2411
        max_reply = 0;
×
NEW
2412
        total_replies = 1;
×
NEW
2413
        sum_replies = 0;
×
2414
    }
2415

2416
    fprintf(stdout, "\"ms (min round trip time)\": %s, ", sprint_tm(min_reply));
3✔
2417
    fprintf(stdout, "\"ms (avg round trip time)\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2418
    fprintf(stdout, "\"ms (max round trip time)\": %s, ", sprint_tm(max_reply));
3✔
2419
    fprintf(stdout, "\"sec (elapsed real time)\": %.3f", (end_time - start_time) / 1e9);
3✔
2420
    fprintf(stdout, "}}\n");
3✔
2421
}
3✔
2422

2423
/************************************************************
2424

2425
  Function: send_ping
2426

2427
*************************************************************
2428

2429
  Inputs:  int s, HOST_ENTRY *h
2430

2431
  Description:
2432

2433
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2434
  will be added on by the kernel.  The ID field is our UNIX process ID,
2435
  and the sequence number is an index into an array of outstanding
2436
  ping requests. The sequence number will later be used to quickly
2437
  figure out who the ping reply came from.
2438

2439
************************************************************/
2440

2441
int send_ping(HOST_ENTRY *h, int index)
917✔
2442
{
2443
    int n;
2444
    int myseq;
2445
    int ret = 1;
917✔
2446
    uint8_t proto = ICMP_ECHO;
917✔
2447

2448
    update_current_time();
917✔
2449
    h->last_send_time = current_time_ns;
917✔
2450
    myseq = seqmap_add(h->i, index, current_time_ns);
917✔
2451

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

2454
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
917✔
2455
        if(icmp_request_typ == 13)
794✔
2456
            proto = ICMP_TSTAMP;
18✔
2457
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
794✔
2458
    }
248✔
2459
#ifdef IPV6
2460
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2461
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2462
    }
15✔
2463
#endif
2464
    else {
2465
        return 0;
×
2466
    }
2467

2468
    /* error sending? */
2469
    if (
654✔
2470
        (n < 0)
263✔
2471
#if defined(EHOSTDOWN)
2472
        && errno != EHOSTDOWN
297✔
2473
#endif
2474
    ) {
2475
        if (verbose_flag) {
34✔
2476
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2477
        }
2478
        else {
2479
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2480
        }
2481

2482
        h->num_sent++;
34✔
2483
        h->num_sent_i++;
34✔
2484
        if (!loop_flag)
34✔
2485
            h->resp_times[index] = RESP_ERROR;
34✔
2486

2487
        ret = 0;
34✔
2488
    }
2489
    else {
2490
        /* schedule timeout */
2491
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
883✔
2492

2493
        /* mark this trial as outstanding */
2494
        if (!loop_flag) {
883✔
2495
            h->resp_times[index] = RESP_WAITING;
883✔
2496
        }
263✔
2497
    }
2498

2499
    num_pingsent++;
917✔
2500
    last_send_time = h->last_send_time;
917✔
2501

2502
    return (ret);
917✔
2503
}
263✔
2504

2505
int socket_can_read(struct timeval *timeout)
2,790✔
2506
{
2507
    int nfound;
2508
    fd_set readset;
2509
    int socketmax;
2510

2511
#ifndef IPV6
2512
    socketmax = socket4;
2513
#else
2514
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,790✔
2515
#endif
2516

2517
select_again:
2,107✔
2518
    FD_ZERO(&readset);
36,502✔
2519
    if (socket4 >= 0)
2,790✔
2520
        FD_SET(socket4, &readset);
2,790✔
2521
#ifdef IPV6
2522
    if (socket6 >= 0)
2,790✔
2523
        FD_SET(socket6, &readset);
2,790✔
2524
#endif
2525

2526
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,790✔
2527
    if (nfound < 0) {
2,790✔
2528
        if (errno == EINTR) {
×
2529
            /* interrupted system call: redo the select */
2530
            goto select_again;
×
2531
        }
2532
        else {
2533
            perror("select");
×
2534
        }
2535
    }
2536

2537
    if (nfound > 0) {
2,790✔
2538
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,355✔
2539
            return socket4;
1,175✔
2540
        }
2541
#ifdef IPV6
2542
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
180✔
2543
            return socket6;
180✔
2544
        }
2545
#endif
2546
    }
2547

2548
    return -1;
1,435✔
2549
}
683✔
2550

2551
int receive_packet(int64_t wait_time,
2,790✔
2552
#if HAVE_SO_TIMESTAMPNS
2553
    int64_t *reply_timestamp,
2554
#else
2555
    int64_t *reply_timestamp __attribute__((unused)),
2556
#endif
2557
    struct sockaddr *reply_src_addr,
2558
    size_t reply_src_addr_len,
2559
    char *reply_buf,
2560
    size_t reply_buf_len,
2561
    int *ip_header_tos,
2562
    int *ip_header_ttl)
2563
{
2564
    struct timeval to;
2565
    int s = 0;
2,790✔
2566
    int recv_len;
2567
    static unsigned char msg_control[128];
2568
    struct iovec msg_iov = {
4,156✔
2569
        reply_buf,
683✔
2570
        reply_buf_len
683✔
2571
    };
2572
    struct msghdr recv_msghdr = {0};
2,790✔
2573
    recv_msghdr.msg_name = reply_src_addr;
2,790✔
2574
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,790✔
2575
    recv_msghdr.msg_iov = &msg_iov;
2,790✔
2576
    recv_msghdr.msg_iovlen = 1;
2,790✔
2577
    recv_msghdr.msg_control = &msg_control;
2,790✔
2578
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,790✔
2579
    struct cmsghdr *cmsg;
2580

2581
    /* Wait for a socket to become ready */
2582
    if (wait_time) {
2,790✔
2583
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,434✔
2584
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,434✔
2585
    }
430✔
2586
    else {
2587
        to.tv_sec = 0;
1,356✔
2588
        to.tv_usec = 0;
1,356✔
2589
    }
2590
    s = socket_can_read(&to);
2,790✔
2591
    if (s == -1) {
2,790✔
2592
        return 0; /* timeout */
1,435✔
2593
    }
2594

2595
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,355✔
2596
    if (recv_len <= 0) {
1,355✔
2597
        return 0;
×
2598
    }
2599

2600
    /* ancilliary data */
2601
    {
2602
#if HAVE_SO_TIMESTAMPNS
2603
        struct timespec reply_timestamp_ts;
2604
        struct timeval reply_timestamp_tv;
2605
#endif
2606
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,355✔
2607
             cmsg != NULL;
2,469✔
2608
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,114✔
2609
#if HAVE_SO_TIMESTAMPNS
2610
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,114✔
2611
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,102✔
2612
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,102✔
2613
            }
2614
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,114✔
2615
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2616
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2617
            }
2618
#endif
2619
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,114✔
2620
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2621
            }
2622
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,114✔
2623
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2624
            }
2625
#ifdef IPV6
2626
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,114✔
2627
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
4✔
2628
            }
2629
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,114✔
2630
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
4✔
2631
            }
2632
#endif
2633
        }
2634
    }
2635

2636
#if defined(DEBUG) || defined(_DEBUG)
2637
    if (randomly_lose_flag) {
2638
        if ((random() & 0x07) <= lose_factor)
2639
            return 0;
2640
    }
2641
#endif
2642

2643
    return recv_len;
1,355✔
2644
}
683✔
2645

2646
/* stats_add: update host statistics for a single packet that was received (or timed out)
2647
 * h: host entry to update
2648
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2649
 * success: 1 if response received, 0 otherwise
2650
 * latency: response time, in ns
2651
 */
2652
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
883✔
2653
{
2654
    /* sent count - we update only on receive/timeout, so that we don't get
2655
     * weird loss percentage, just because a packet was note recived yet.
2656
     */
2657
    h->num_sent++;
883✔
2658
    h->num_sent_i++;
883✔
2659

2660
    if (!success) {
883✔
2661
        if (!loop_flag && index >= 0) {
101✔
2662
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2663
        }
25✔
2664
        num_timeout++;
101✔
2665
        return;
101✔
2666
    }
2667

2668
    /* received count */
2669
    h->num_recv++;
782✔
2670
    h->num_recv_i++;
782✔
2671

2672
    /* maximum */
2673
    if (!h->max_reply || latency > h->max_reply) {
782✔
2674
        h->max_reply = latency;
625✔
2675
    }
177✔
2676
    if (!h->max_reply_i || latency > h->max_reply_i) {
782✔
2677
        h->max_reply_i = latency;
637✔
2678
    }
180✔
2679

2680
    /* minimum */
2681
    if (!h->min_reply || latency < h->min_reply) {
782✔
2682
        h->min_reply = latency;
520✔
2683
    }
166✔
2684
    if (!h->min_reply_i || latency < h->min_reply_i) {
782✔
2685
        h->min_reply_i = latency;
551✔
2686
    }
176✔
2687

2688
    /* total time (for average) */
2689
    h->total_time += latency;
782✔
2690
    h->total_time_i += latency;
782✔
2691

2692
    /* response time per-packet (count mode) */
2693
    if (!loop_flag && index >= 0) {
782✔
2694
        h->resp_times[index] = latency;
782✔
2695
    }
238✔
2696
}
263✔
2697

2698
/* stats_reset_interval: reset interval statistics
2699
 * h: host entry to update
2700
 */
2701
void stats_reset_interval(HOST_ENTRY *h)
33✔
2702
{
2703
    h->num_sent_i = 0;
33✔
2704
    h->num_recv_i = 0;
33✔
2705
    h->max_reply_i = 0;
33✔
2706
    h->min_reply_i = 0;
33✔
2707
    h->total_time_i = 0;
33✔
2708
}
33✔
2709

2710
int decode_icmp_ipv4(
1,175✔
2711
    struct sockaddr *response_addr,
2712
    size_t response_addr_len,
2713
    char *reply_buf,
2714
    size_t reply_buf_len,
2715
    unsigned short *id,
2716
    unsigned short *seq,
2717
    int *ip_header_tos,
2718
    int *ip_header_ttl,
2719
    uint32_t *ip_header_otime_ms,
2720
    uint32_t *ip_header_rtime_ms,
2721
    uint32_t *ip_header_ttime_ms)
2722
{
2723
    struct icmp *icp;
2724
    int hlen = 0;
1,175✔
2725

2726
    if (!using_sock_dgram4) {
1,175✔
2727
        struct ip *ip = (struct ip *)reply_buf;
1,167✔
2728
        *ip_header_tos = ip->ip_tos;
1,167✔
2729
        *ip_header_ttl = ip->ip_ttl;
1,167✔
2730

2731
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2732
        /* The alpha headers are decidedly broken.
2733
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2734
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2735
         */
2736
        hlen = (ip->ip_vhl & 0x0F) << 2;
2737
#else
2738
        hlen = ip->ip_hl << 2;
1,167✔
2739
#endif
2740
    }
223✔
2741

2742
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,175✔
2743
        /* too short */
2744
        if (verbose_flag) {
×
2745
            char buf[INET6_ADDRSTRLEN];
2746
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2747
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2748
        }
2749
        return -1;
×
2750
    }
2751

2752
    icp = (struct icmp *)(reply_buf + hlen);
1,175✔
2753

2754
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,175✔
2755
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
496✔
2756
        /* Handle other ICMP packets */
2757
        struct icmp *sent_icmp;
2758
        SEQMAP_VALUE *seqmap_value;
2759
        char addr_ascii[INET6_ADDRSTRLEN];
2760
        HOST_ENTRY *h;
2761

2762
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2763
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
918✔
2764
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2765
            return -1;
×
2766
        }
2767

2768
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
472✔
2769

2770
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
472✔
2771
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
2772
            sent_icmp->icmp_id != ident4) {
×
2773
            /* not caused by us */
2774
            return -1;
472✔
2775
        }
2776

2777
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2778
        if (seqmap_value == NULL) {
×
2779
            return -1;
×
2780
        }
2781

2782
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2783

2784
        switch (icp->icmp_type) {
×
2785
        case ICMP_UNREACH:
2786
            h = table[seqmap_value->host_nr];
×
2787
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2788
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2789
                    addr_ascii, h->host);
2790
            }
2791
            else {
2792
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2793
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2794
            }
2795

2796
            print_warning("\n");
×
2797
            num_othericmprcvd++;
×
2798
            break;
×
2799

2800
        case ICMP_SOURCEQUENCH:
2801
        case ICMP_REDIRECT:
2802
        case ICMP_TIMXCEED:
2803
        case ICMP_PARAMPROB:
2804
            h = table[seqmap_value->host_nr];
×
2805
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2806
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2807
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2808
            }
2809
            else {
2810
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2811
                    icp->icmp_type, addr_ascii, h->host);
×
2812
            }
2813
            print_warning("\n");
×
2814
            num_othericmprcvd++;
×
2815
            break;
×
2816
        }
2817

2818
        return -1;
×
2819
    }
2820

2821
    *id = icp->icmp_id;
703✔
2822
    *seq = ntohs(icp->icmp_seq);
703✔
2823
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
703✔
2824

2825
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2826
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
2827
            if (verbose_flag) {
×
2828
                char buf[INET6_ADDRSTRLEN];
2829
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2830
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2831
            }
2832
            return -1;
×
2833
        }
2834

2835
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
2836
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
2837
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
2838
    }
2839

2840
    return hlen;
703✔
2841
}
223✔
2842

2843
#ifdef IPV6
2844
int decode_icmp_ipv6(
180✔
2845
    struct sockaddr *response_addr,
2846
    size_t response_addr_len,
2847
    char *reply_buf,
2848
    size_t reply_buf_len,
2849
    unsigned short *id,
2850
    unsigned short *seq)
2851
{
2852
    struct icmp6_hdr *icp;
2853

2854
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
180✔
2855
        if (verbose_flag) {
×
2856
            char buf[INET6_ADDRSTRLEN];
2857
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2858
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2859
        }
2860
        return 0; /* too short */
×
2861
    }
2862

2863
    icp = (struct icmp6_hdr *)reply_buf;
180✔
2864

2865
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
180✔
2866
        /* Handle other ICMP packets */
2867
        struct icmp6_hdr *sent_icmp;
2868
        SEQMAP_VALUE *seqmap_value;
2869
        char addr_ascii[INET6_ADDRSTRLEN];
2870
        HOST_ENTRY *h;
2871

2872
        /* reply icmp packet (ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2873
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
89✔
2874
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2875
            return 0;
×
2876
        }
2877

2878
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
89✔
2879

2880
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
89✔
2881
            /* not caused by us */
2882
            return 0;
89✔
2883
        }
2884

2885
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2886
        if (seqmap_value == NULL) {
×
2887
            return 0;
×
2888
        }
2889

2890
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2891

2892
        switch (icp->icmp6_type) {
×
2893
        case ICMP_UNREACH:
2894
            h = table[seqmap_value->host_nr];
×
2895
            if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
×
2896
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2897
                    addr_ascii, h->host);
2898
            }
2899
            else {
2900
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2901
                    icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2902
            }
2903

2904
            print_warning("\n");
×
2905
            num_othericmprcvd++;
×
2906
            break;
×
2907

2908
        case ICMP_SOURCEQUENCH:
2909
        case ICMP_REDIRECT:
2910
        case ICMP_TIMXCEED:
2911
        case ICMP_PARAMPROB:
2912
            h = table[seqmap_value->host_nr];
×
2913
            if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
×
2914
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2915
                    icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
×
2916
            }
2917
            else {
2918
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2919
                    icp->icmp6_type, addr_ascii, h->host);
×
2920
            }
2921
            print_warning("\n");
×
2922
            num_othericmprcvd++;
×
2923
            break;
×
2924
        }
2925

2926
        return 0;
×
2927
    }
2928

2929
    *id = icp->icmp6_id;
91✔
2930
    *seq = ntohs(icp->icmp6_seq);
91✔
2931

2932
    return 1;
91✔
2933
}
30✔
2934
#endif
2935

2936
int wait_for_reply(int64_t wait_time)
2,790✔
2937
{
2938
    int result;
2939
    static char buffer[RECV_BUFSIZE];
2940
    struct sockaddr_storage response_addr;
2941
    int n, avg;
2942
    HOST_ENTRY *h;
2943
    int64_t this_reply;
2944
    int this_count;
2945
    int64_t recv_time = 0;
2,790✔
2946
    SEQMAP_VALUE *seqmap_value;
2947
    unsigned short id;
2948
    unsigned short seq;
2949
    int ip_header_tos = -1;
2,790✔
2950
    int ip_header_ttl = -1;
2,790✔
2951
    // ICMP Timestamp
2952
    uint32_t ip_header_otime_ms = 0x80000000U;
2,790✔
2953
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,790✔
2954
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,790✔
2955

2956
    /* Receive packet */
2957
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,790✔
2958
        &recv_time, /* reply_timestamp */
2959
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2960
        sizeof(response_addr), /* reply_src_addr_len */
2961
        buffer, /* reply_buf */
2962
        sizeof(buffer), /* reply_buf_len */
2963
        &ip_header_tos, /* TOS resp. TC byte */
2964
        &ip_header_ttl /* TTL resp. hop limit */
2965
    );
2966

2967
    if (result <= 0) {
2,790✔
2968
        return 0;
1,435✔
2969
    }
2970

2971
    update_current_time();
1,355✔
2972
    if (recv_time == 0)
1,355✔
2973
        recv_time = current_time_ns;
253✔
2974

2975
    /* Process ICMP packet and retrieve id/seq */
2976
    if (response_addr.ss_family == AF_INET) {
1,355✔
2977
        int ip_hlen = decode_icmp_ipv4(
1,175✔
2978
            (struct sockaddr *)&response_addr,
2979
            sizeof(response_addr),
2980
            buffer,
2981
            sizeof(buffer),
2982
            &id,
2983
            &seq,
2984
            &ip_header_tos,
2985
            &ip_header_ttl,
2986
            &ip_header_otime_ms,
2987
            &ip_header_rtime_ms,
2988
            &ip_header_ttime_ms);
2989
        if (ip_hlen < 0) {
1,175✔
2990
            return 1;
472✔
2991
        }
2992
        if (id != ident4) {
703✔
2993
            return 1; /* packet received, but not the one we are looking for! */
×
2994
        }
2995
        if (!using_sock_dgram4) {
703✔
2996
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2997
             * with fping with IPv6 hosts */
2998
            result -= ip_hlen;
695✔
2999
        }
223✔
3000
    }
223✔
3001
#ifdef IPV6
3002
    else if (response_addr.ss_family == AF_INET6) {
180✔
3003
        if (!decode_icmp_ipv6(
180✔
3004
                (struct sockaddr *)&response_addr,
3005
                sizeof(response_addr),
3006
                buffer,
3007
                sizeof(buffer),
3008
                &id,
3009
                &seq)) {
3010
            return 1;
89✔
3011
        }
3012
        if (id != ident6) {
91✔
3013
            return 1; /* packet received, but not the one we are looking for! */
×
3014
        }
3015
    }
15✔
3016
#endif
3017
    else {
3018
        return 1;
×
3019
    }
3020

3021
    seqmap_value = seqmap_fetch(seq, current_time_ns);
794✔
3022
    if (seqmap_value == NULL) {
794✔
3023
        return 1;
×
3024
    }
3025

3026
    /* find corresponding host_entry */
3027
    n = seqmap_value->host_nr;
794✔
3028
    h = table[n];
794✔
3029
    this_count = seqmap_value->ping_count;
794✔
3030
    this_reply = recv_time - seqmap_value->ping_ts;
794✔
3031

3032
    /* update stats that include invalid replies */
3033
    h->num_recv_total++;
794✔
3034
    num_pingreceived++;
794✔
3035

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

3038
    /* optionally require reply source equal to target address */
3039
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
794✔
3040
        dbg_printf("%s\n", "discarding reply from wrong source address");
3041
        return 1;
12✔
3042
    }
3043

3044
    /* discard duplicates */
3045
    if (!loop_flag && h->resp_times[this_count] >= 0) {
782✔
3046
        if (!per_recv_flag) {
×
3047
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3048
                h->host, this_count, result, sprint_tm(this_reply));
3049

3050
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3051
                char buf[INET6_ADDRSTRLEN];
3052
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3053
                fprintf(stderr, " [<- %s]", buf);
×
3054
            }
3055
            fprintf(stderr, "\n");
×
3056
        }
3057
        return 1;
×
3058
    }
3059

3060
    /* discard reply if delay is larger than timeout
3061
     * (see also: github #32) */
3062
    if (this_reply > h->timeout) {
782✔
3063
        return 1;
×
3064
    }
3065

3066
    /* update stats */
3067
    stats_add(h, this_count, 1, this_reply);
782✔
3068
    // TODO: move to stats_add?
3069
    if (!max_reply || this_reply > max_reply)
782✔
3070
        max_reply = this_reply;
495✔
3071
    if (!min_reply || this_reply < min_reply)
782✔
3072
        min_reply = this_reply;
437✔
3073
    sum_replies += this_reply;
782✔
3074
    total_replies++;
782✔
3075

3076
    /* initialize timeout to initial timeout (without backoff) */
3077
    h->timeout = timeout;
782✔
3078

3079
    /* remove timeout event */
3080
    struct event *timeout_event = host_get_timeout_event(h, this_count);
782✔
3081
    if (timeout_event) {
782✔
3082
        ev_remove(&event_queue_timeout, timeout_event);
782✔
3083
    }
238✔
3084

3085
    /* print "is alive" */
3086
    if (h->num_recv == 1) {
782✔
3087
        num_alive++;
483✔
3088
        if (fast_reachable && num_alive >= min_reachable)
483✔
3089
                finish_requested = 1;
3✔
3090

3091
        if (verbose_flag || alive_flag) {
483✔
3092
            printf("%s", h->host);
296✔
3093

3094
            if (verbose_flag)
296✔
3095
                printf(" is alive");
276✔
3096
        }
84✔
3097
    }
141✔
3098

3099
    /* print received ping (unless --quiet) */
3100
    if (per_recv_flag) {
782✔
3101
        if (json_flag)
186✔
3102
            printf("{\"resp\": {");
35✔
3103

3104
        if (timestamp_flag)
186✔
3105
            print_timestamp_format(recv_time, timestamp_format_flag);
45✔
3106

3107
        avg = h->total_time / h->num_recv;
186✔
3108
        if (json_flag) {
186✔
3109
            printf("\"host\": \"%s\", ", h->host);
35✔
3110
            printf("\"seq\": %d, ", this_count);
35✔
3111
            printf("\"size\": %d, ", result);
35✔
3112
            printf("\"rtt\": %s, ", sprint_tm(this_reply));
35✔
3113
            printf("\"rttAvg\": %s", sprint_tm(avg));
35✔
3114
        }
11✔
3115
        else {
3116
            printf("%-*s : [%d], %d bytes, %s ms", max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
151✔
3117
            printf(" (%s avg, ", sprint_tm(avg));
151✔
3118
        }
3119

3120
        if (h->num_recv <= h->num_sent) {
186✔
3121
            if (json_flag)
186✔
3122
                printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
35✔
3123
            else
3124
                printf("%d%% loss)", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
3125
        }
56✔
3126
        else {
NEW
3127
            if (json_flag)
×
NEW
3128
                printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
3129
            else
NEW
3130
                printf("%d%% return)", (h->num_recv_total * 100) / h->num_sent);
×
3131
        }
3132
    }
56✔
3133

3134
    if (verbose_flag || alive_flag || per_recv_flag) {
782✔
3135

3136
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
482✔
3137
            char buf[INET6_ADDRSTRLEN];
3138
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3139
            fprintf(stderr, " [<- %s]", buf);
6✔
3140
        }
3141

3142
        if (icmp_request_typ == 13) {
482✔
3143
            if (json_flag) {
16✔
3144
              printf(", \"timestamps\": {");
2✔
3145
              printf("\"originate\": %u, ", ip_header_otime_ms);
2✔
3146
              printf("\"receive\": %u, ", ip_header_rtime_ms);
2✔
3147
              printf("\"transmit\": %u, ", ip_header_ttime_ms);
2✔
3148
              printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
3149
            }
3150
            else {
3151
                printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
3152
                      alive_flag ? "" : ",",
14✔
3153
                      ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
3154
                      ms_since_midnight_utc(recv_time));
3155
            }
3156
        }
3157

3158
        if(print_tos_flag) {
482✔
3159
            if(ip_header_tos != -1) {
49✔
3160
                if (json_flag)
49✔
3161
                    printf(", \"tos\": %d", ip_header_tos);
6✔
3162
                else
3163
                    printf(" (TOS %d)", ip_header_tos);
43✔
3164
            }
13✔
3165
            else {
NEW
3166
                if (json_flag)
×
NEW
3167
                    printf(", \"tos\": -1");
×
3168
                else
NEW
3169
                    printf(" (TOS unknown)");
×
3170
            }
3171
        }
13✔
3172

3173
        if (print_ttl_flag) {
482✔
3174
          if(ip_header_ttl != -1) {
49✔
3175
              if (json_flag)
49✔
3176
                  printf(", \"ttl\": %d", ip_header_ttl);
6✔
3177
              else
3178
                  printf(" (TTL %d)", ip_header_ttl);
43✔
3179
          }
13✔
3180
          else {
NEW
3181
              if (json_flag)
×
NEW
3182
                  printf(", \"ttl\": -1");
×
3183
              else
NEW
3184
                  printf(" (TTL unknown)");
×
3185
          }
3186
        }
13✔
3187

3188
        if (elapsed_flag && !per_recv_flag)
482✔
3189
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
3190

3191
        if (json_flag)
482✔
3192
            printf("}}");
35✔
3193

3194
        printf("\n");
482✔
3195
    }
140✔
3196

3197
    return 1;
782✔
3198
}
683✔
3199

3200
/************************************************************
3201

3202
  Function: add_name
3203

3204
*************************************************************
3205

3206
  Inputs:  char* name
3207

3208
  Description:
3209

3210
  process input name for addition to target list
3211
  name can turn into multiple targets via multiple interfaces (-m)
3212
  or via NIS groups
3213

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

3216
void add_name(char *name)
603✔
3217
{
3218
    struct addrinfo *res0, *res, hints;
3219
    int ret_ga;
3220
    char *printname;
3221
    char namebuf[256];
3222
    char addrbuf[256];
3223

3224
    /* getaddrinfo */
3225
    memset(&hints, 0, sizeof(struct addrinfo));
603✔
3226
    hints.ai_flags = AI_UNUSABLE;
603✔
3227
    hints.ai_socktype = SOCK_RAW;
603✔
3228
    hints.ai_family = hints_ai_family;
603✔
3229
    if (hints_ai_family == AF_INET) {
603✔
3230
        hints.ai_protocol = IPPROTO_ICMP;
81✔
3231
    }
21✔
3232
#ifdef IPV6
3233
    else if (hints_ai_family == AF_INET6) {
522✔
3234
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
3235
    }
1✔
3236
#endif
3237
    else {
3238
        hints.ai_socktype = SOCK_STREAM;
491✔
3239
        hints.ai_protocol = 0;
491✔
3240
    }
3241
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
603✔
3242
    if (ret_ga) {
603✔
3243
        if (!quiet_flag)
29✔
3244
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3245
        num_noaddress++;
29✔
3246

3247
        // Handle JSON output for invalid hosts
3248
        if (json_flag) {
29✔
3249
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}", name, gai_strerror(ret_ga));
3✔
3250
            return;
21✔
3251
        }
3252

3253
        return;
26✔
3254
    }
3255

3256
    /* NOTE: we could/should loop with res on all addresses like this:
3257
     * for (res = res0; res; res = res->ai_next) {
3258
     * We don't do it yet, however, because is is an incompatible change
3259
     * (need to implement a separate option for this)
3260
     */
3261
    for (res = res0; res; res = res->ai_next) {
574✔
3262
        /* name_flag: addr -> name lookup requested) */
3263
        if (name_flag || rdns_flag) {
576✔
3264
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3265
            if (name_flag) {
3✔
3266
                /* Was it a numerical address? Only then do a rdns-query */
3267
                struct addrinfo *nres;
3268
                hints.ai_flags = AI_NUMERICHOST;
×
3269
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3270
                    do_rdns = 1;
×
3271
                    freeaddrinfo(nres);
×
3272
                }
3273
            }
3274

3275
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3276
                printname = namebuf;
3✔
3277
            }
1✔
3278
            else {
3279
                printname = name;
×
3280
            }
3281
        }
1✔
3282
        else {
3283
            printname = name;
571✔
3284
        }
3285

3286
        /* addr_flag: name -> addr lookup requested */
3287
        if (addr_flag) {
574✔
3288
            int ret;
3289
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3290
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3291
            if (ret) {
3✔
3292
                if (!quiet_flag) {
×
3293
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3294
                }
3295
                continue;
×
3296
            }
3297

3298
            if (name_flag || rdns_flag) {
3✔
3299
                char nameaddrbuf[512 + 3];
3300
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3301
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3302
            }
3303
            else {
3304
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3305
            }
3306
        }
1✔
3307
        else {
3308
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
571✔
3309
        }
3310

3311
        if (!multif_flag) {
574✔
3312
            break;
574✔
3313
        }
3314
    }
3315

3316
    freeaddrinfo(res0);
574✔
3317
}
165✔
3318

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

3321
  Function: add_addr
3322

3323
*************************************************************
3324

3325
  Description:
3326

3327
  add single address to list of hosts to be pinged
3328

3329
************************************************************/
3330

3331
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
574✔
3332
{
3333
    HOST_ENTRY *p;
3334
    int n;
3335
    int64_t *i;
3336

3337
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
574✔
3338
    if (!p)
574✔
3339
        crash_and_burn("can't allocate HOST_ENTRY");
×
3340

3341
    p->name = strdup(name);
574✔
3342
    p->host = strdup(host);
574✔
3343
    memcpy(&p->saddr, ipaddr, ipaddr_len);
574✔
3344
    p->saddr_len = ipaddr_len;
574✔
3345
    p->timeout = timeout;
574✔
3346
    p->min_reply = 0;
574✔
3347

3348
    if (netdata_flag) {
574✔
3349
        char *s = p->name;
3✔
3350
        while (*s) {
30✔
3351
            if (!isalnum(*s))
27✔
3352
                *s = '_';
9✔
3353
            s++;
27✔
3354
        }
3355
    }
1✔
3356

3357
    if (strlen(p->host) > max_hostname_len)
574✔
3358
        max_hostname_len = strlen(p->host);
381✔
3359

3360
    /* array for response time results */
3361
    if (!loop_flag) {
574✔
3362
        i = (int64_t *)malloc(trials * sizeof(int64_t));
574✔
3363
        if (!i)
574✔
3364
            crash_and_burn("can't allocate resp_times array");
×
3365

3366
        for (n = 1; n < trials; n++)
2,287✔
3367
            i[n] = RESP_UNUSED;
1,713✔
3368

3369
        p->resp_times = i;
574✔
3370
    }
156✔
3371

3372
    /* allocate event storage */
3373
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
574✔
3374
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
574✔
3375

3376
    /* schedule first ping */
3377
    host_add_ping_event(p, 0, current_time_ns);
574✔
3378

3379
    num_hosts++;
574✔
3380
}
574✔
3381

3382
/************************************************************
3383

3384
  Function: crash_and_burn
3385

3386
*************************************************************
3387

3388
  Inputs:  char* message
3389

3390
  Description:
3391

3392
************************************************************/
3393

3394
void crash_and_burn(char *message)
×
3395
{
3396
    fprintf(stderr, "%s: %s\n", prog, message);
×
3397
    exit(4);
×
3398
}
3399

3400
/************************************************************
3401

3402
  Function: errno_crash_and_burn
3403

3404
*************************************************************
3405

3406
  Inputs:  char* message
3407

3408
  Description:
3409

3410
************************************************************/
3411

3412
void errno_crash_and_burn(char *message)
8✔
3413
{
3414
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3415
    exit(4);
8✔
3416
}
3417

3418
/************************************************************
3419

3420
  Function: print_warning
3421

3422
  Description: fprintf(stderr, ...), unless running with -q
3423

3424
*************************************************************/
3425

3426
void print_warning(char *format, ...)
54✔
3427
{
3428
    va_list args;
3429
    if (!quiet_flag) {
54✔
3430
        va_start(args, format);
54✔
3431
        vfprintf(stderr, format, args);
54✔
3432
        va_end(args);
54✔
3433
    }
8✔
3434
}
54✔
3435

3436
/************************************************************
3437

3438
  Function: sprint_tm
3439

3440
*************************************************************
3441

3442
  render nanosecond int64_t value into milliseconds string with three digits of
3443
  precision.
3444

3445
************************************************************/
3446

3447
const char *sprint_tm(int64_t ns)
1,083✔
3448
{
3449
    static char buf[10];
3450
    double t = (double)ns / 1e6;
1,083✔
3451

3452
    if (t < 0.0) {
1,083✔
3453
        /* negative (unexpected) */
3454
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3455
    }
3456
    else if (t < 1.0) {
1,083✔
3457
        /* <= 0.99 ms */
3458
        snprintf(buf, sizeof(buf), "%.3f", t);
1,082✔
3459
    }
334✔
3460
    else if (t < 10.0) {
1✔
3461
        /* 1.00 - 9.99 ms */
3462
        snprintf(buf, sizeof(buf), "%.2f", t);
1✔
3463
    }
1✔
3464
    else if (t < 100.0) {
×
3465
        /* 10.0 - 99.9 ms */
3466
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3467
    }
3468
    else if (t < 1000000.0) {
×
3469
        /* 100 - 1'000'000 ms */
3470
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3471
    }
3472
    else {
3473
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3474
    }
3475

3476
    return (buf);
1,083✔
3477
}
3478

3479
/************************************************************
3480

3481
  Function: addr_cmp
3482

3483
*************************************************************/
3484
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
504✔
3485
{
3486
    if (a->sa_family != b->sa_family) {
504✔
3487
        return a->sa_family - b->sa_family;
×
3488
    }
3489
    else {
3490
        if (a->sa_family == AF_INET) {
504✔
3491
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
440✔
3492
        }
3493
        else if (a->sa_family == AF_INET6) {
64✔
3494
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3495
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3496
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3497
        }
3498
    }
3499

3500
    return 0;
×
3501
}
142✔
3502

3503
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
907✔
3504
{
3505
    struct event *event = &h->event_storage_ping[index % event_storage_count];
907✔
3506
    event->host = h;
907✔
3507
    event->ping_index = index;
907✔
3508
    event->ev_time = ev_time;
907✔
3509
    ev_enqueue(&event_queue_ping, event);
907✔
3510

3511
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3512
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3513
}
907✔
3514

3515
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
883✔
3516
{
3517
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
883✔
3518
    event->host = h;
883✔
3519
    event->ping_index = index;
883✔
3520
    event->ev_time = ev_time;
883✔
3521
    ev_enqueue(&event_queue_timeout, event);
883✔
3522

3523
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3524
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3525
}
883✔
3526

3527
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
782✔
3528
{
3529
    return &h->event_storage_timeout[index % event_storage_count];
782✔
3530
}
3531

3532
/************************************************************
3533

3534
  Function: ev_enqueue
3535

3536
  Enqueue an event
3537

3538
  The queue is sorted by event->ev_time, so that queue->first always points to
3539
  the earliest event.
3540

3541
  We start scanning the queue from the tail, because we assume
3542
  that new events mostly get inserted with a event time higher
3543
  than the others.
3544

3545
*************************************************************/
3546
void ev_enqueue(struct event_queue *queue, struct event *event)
1,790✔
3547
{
3548
    struct event *i;
3549
    struct event *i_prev;
3550

3551
    /* Empty list */
3552
    if (queue->last == NULL) {
1,790✔
3553
        event->ev_next = NULL;
1,395✔
3554
        event->ev_prev = NULL;
1,395✔
3555
        queue->first = event;
1,395✔
3556
        queue->last = event;
1,395✔
3557
        return;
1,395✔
3558
    }
3559

3560
    /* Insert on tail? */
3561
    if (event->ev_time - queue->last->ev_time >= 0) {
395✔
3562
        event->ev_next = NULL;
395✔
3563
        event->ev_prev = queue->last;
395✔
3564
        queue->last->ev_next = event;
395✔
3565
        queue->last = event;
395✔
3566
        return;
395✔
3567
    }
3568

3569
    /* Find insertion point */
3570
    i = queue->last;
×
3571
    while (1) {
3572
        i_prev = i->ev_prev;
×
3573
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3574
            event->ev_prev = i_prev;
×
3575
            event->ev_next = i;
×
3576
            i->ev_prev = event;
×
3577
            if (i_prev != NULL) {
×
3578
                i_prev->ev_next = event;
×
3579
            }
3580
            else {
3581
                queue->first = event;
×
3582
            }
3583
            return;
×
3584
        }
3585
        i = i_prev;
×
3586
    }
3587
}
526✔
3588

3589
/************************************************************
3590

3591
  Function: ev_dequeue
3592

3593
*************************************************************/
3594
struct event *ev_dequeue(struct event_queue *queue)
988✔
3595
{
3596
    struct event *dequeued;
3597

3598
    if (queue->first == NULL) {
988✔
3599
        return NULL;
×
3600
    }
3601
    dequeued = queue->first;
988✔
3602
    ev_remove(queue, dequeued);
988✔
3603

3604
    return dequeued;
988✔
3605
}
282✔
3606

3607
/************************************************************
3608

3609
  Function: ev_remove
3610

3611
*************************************************************/
3612
void ev_remove(struct event_queue *queue, struct event *event)
1,770✔
3613
{
3614
    if (queue->first == event) {
1,770✔
3615
        queue->first = event->ev_next;
1,761✔
3616
    }
517✔
3617
    if (queue->last == event) {
1,770✔
3618
        queue->last = event->ev_prev;
1,396✔
3619
    }
403✔
3620
    if (event->ev_prev) {
1,770✔
3621
        event->ev_prev->ev_next = event->ev_next;
9✔
3622
    }
3✔
3623
    if (event->ev_next) {
1,770✔
3624
        event->ev_next->ev_prev = event->ev_prev;
374✔
3625
    }
117✔
3626
    event->ev_prev = NULL;
1,770✔
3627
    event->ev_next = NULL;
1,770✔
3628
}
1,770✔
3629

3630
/************************************************************
3631

3632
  Function: print_human_readable_time from current_time_ns
3633

3634
*************************************************************/
3635
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
45✔
3636
{
3637
    char time_buffer[100];
3638
    time_t current_time_s;
3639
    struct tm *local_time;
3640

3641
    current_time_s = current_time_ns / 1000000000;
45✔
3642
    local_time = localtime(&current_time_s);
45✔
3643
    switch(timestamp_format) {
45✔
3644
        case 1:
6✔
3645
            // timestamp-format ctime
3646
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
9✔
3647
            if (json_flag)
9✔
3648
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3649
            else
3650
                printf("[%s] ", time_buffer);
6✔
3651
            break;
9✔
3652
        case 2:
6✔
3653
            // timestamp-format iso
3654
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
9✔
3655
            if (json_flag)
9✔
3656
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3657
            else
3658
                printf("[%s] ", time_buffer);
6✔
3659
            break;
9✔
3660
        case 3:
6✔
3661
            // timestamp-format rfc3339
3662
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
9✔
3663
            if (json_flag)
9✔
3664
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3665
            else
3666
                printf("[%s] ", time_buffer);
6✔
3667
            break;
9✔
3668
        default:
12✔
3669
            if (json_flag)
18✔
3670
                printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9);
6✔
3671
            else
3672
                printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3673
    }
6✔
3674
}
45✔
3675

3676
/************************************************************
3677

3678
  Function: ms_since_midnight_utc
3679

3680
*************************************************************
3681

3682
  Input: int64_t: current UTC time in ns
3683

3684
  Output: uint32_t: current time in ms since midnight UTC
3685

3686
  Description:
3687

3688
  Return ICMP Timestamp value corresponding to the given time value.
3689
  The given time value must be in UTC.
3690

3691
*************************************************************/
3692
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
3693
{
3694
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
3695
}
3696

3697
/************************************************************
3698

3699
  Function: usage
3700

3701
*************************************************************
3702

3703
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3704

3705
  Description:
3706

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

3709
void usage(int is_error)
115✔
3710
{
3711
    FILE *out = is_error ? stderr : stdout;
115✔
3712
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
115✔
3713
    fprintf(out, "\n");
115✔
3714
    fprintf(out, "Probing options:\n");
115✔
3715
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
115✔
3716
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
115✔
3717
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
115✔
3718
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
115✔
3719
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
115✔
3720
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
115✔
3721
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
115✔
3722
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
115✔
3723
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
115✔
3724
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
115✔
3725
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
115✔
3726
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
115✔
3727
#ifdef SO_BINDTODEVICE
3728
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
78✔
3729
#endif
3730
#ifdef SO_MARK
3731
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
78✔
3732
#endif
3733
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
115✔
3734
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
115✔
3735
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
115✔
3736
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
115✔
3737
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
115✔
3738
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
115✔
3739
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
115✔
3740
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
115✔
3741
    fprintf(out, "   -S, --src=IP       set source address\n");
115✔
3742
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
115✔
3743
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
115✔
3744
    fprintf(out, "       --check-source discard replies not from target address\n");
115✔
3745
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
115✔
3746
    fprintf(out, "\n");
115✔
3747
    fprintf(out, "Output options:\n");
115✔
3748
    fprintf(out, "   -a, --alive        show targets that are alive\n");
115✔
3749
    fprintf(out, "   -A, --addr         show targets by address\n");
115✔
3750
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
115✔
3751
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
115✔
3752
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
115✔
3753
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
115✔
3754
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
115✔
3755
    fprintf(out, "   -J, --json         output in JSON format (-c or -C required)\n");
115✔
3756
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
115✔
3757
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
115✔
3758
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
115✔
3759
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
115✔
3760
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
115✔
3761
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
115✔
3762
    fprintf(out, "   -s, --stats        print final stats\n");
115✔
3763
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
115✔
3764
    fprintf(out, "   -v, --version      show version\n");
115✔
3765
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
115✔
3766
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
115✔
3767
    fprintf(out, "       --print-tos    show received TOS value\n");
115✔
3768
    fprintf(out, "       --print-ttl    show IP TTL value\n");
115✔
3769
    exit(is_error);
115✔
3770
}
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