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

schweikert / fping / 14760462647

30 Apr 2025 05:23PM UTC coverage: 87.764% (-0.1%) from 87.902%
14760462647

Pull #386

github

web-flow
Merge 91a05cce7 into 913982a8c
Pull Request #386: New option -J / --json for the JSON output

167 of 196 new or added lines in 1 file covered. (85.2%)

1 existing line in 1 file now uncovered.

1535 of 1749 relevant lines covered (87.76%)

334.1 hits per line

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

87.94
/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
int socket4 = -1;
301
int socktype4 = -1;
302
int using_sock_dgram4 = 0;
303
#ifndef IPV6
304
int hints_ai_family = AF_INET;
305
#else
306
int socket6 = -1;
307
int socktype6 = -1;
308
int hints_ai_family = AF_UNSPEC;
309
#endif
310

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

314
unsigned int debugging = 0;
315

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

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

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

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

377
unsigned int fwmark = 0;
378

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

381
/*** forward declarations ***/
382

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

427
/************************************************************
428

429
  Function: p_setsockopt
430

431
*************************************************************
432

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

435
  Description:
436

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

440
************************************************************/
441

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

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

452
    res = setsockopt(sockfd, level, optname, optval, optlen);
18✔
453

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

460
    return res;
18✔
461
}
462

463
/************************************************************
464

465
  Function: main
466

467
*************************************************************
468

469
  Inputs:  int argc, char** argv
470

471
  Description:
472

473
  Main program entry point
474

475
************************************************************/
476

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

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

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

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

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

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

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

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

535
    /* get command line options */
536

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

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

667
        case 't':
40✔
668
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
42✔
669
                usage(1);
6✔
670
            if (opt_value_float < 0) {
38✔
671
                usage(1);
3✔
672
            }
1✔
673
            timeout = opt_value_float * 1000000;
36✔
674
            timeout_flag = 1;
36✔
675
            break;
36✔
676

677
        case 'r':
36✔
678
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
38✔
679
                usage(1);
6✔
680
            break;
34✔
681

682
        case 'i':
30✔
683
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
684
                usage(1);
6✔
685
            if (opt_value_float < 0) {
29✔
686
                usage(1);
3✔
687
            }
1✔
688
            interval = opt_value_float * 1000000;
27✔
689
            break;
27✔
690

691
        case 'p':
70✔
692
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
99✔
693
                usage(1);
6✔
694
            if (opt_value_float < 0) {
95✔
695
                usage(1);
3✔
696
            }
1✔
697
            perhost_interval = opt_value_float * 1000000;
93✔
698

699
            break;
93✔
700

701
        case 'c':
90✔
702
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
127✔
703
                usage(1);
6✔
704

705
            count_flag = 1;
123✔
706
            break;
123✔
707

708
        case 'C':
34✔
709
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
46✔
710
                usage(1);
6✔
711

712
            count_flag = 1;
42✔
713
            report_all_rtts_flag = 1;
42✔
714
            break;
42✔
715

716
        case 'b':
14✔
717
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
19✔
718
                usage(1);
6✔
719
            size_flag = 1;
15✔
720
            break;
15✔
721

722
        case 'h':
2✔
723
            usage(0);
3✔
724
            break;
1✔
725

726
        case 'q':
26✔
727
            verbose_flag = 0;
38✔
728
            quiet_flag = 1;
38✔
729
            break;
38✔
730

731
        case 'Q':
24✔
732
            verbose_flag = 0;
33✔
733
            quiet_flag = 1;
33✔
734
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
735
                usage(1);
6✔
736
            if (opt_value_float < 0) {
29✔
737
                usage(1);
3✔
738
            }
1✔
739
            report_interval = opt_value_float * 1e9;
27✔
740

741
            /* recognize keyword(s) after number, ignore everything else */
742
            {
743
                char *comma = strchr(optparse_state.optarg, ',');
27✔
744
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
27✔
745
                    cumulative_stats_flag = 1;
6✔
746
                }
2✔
747
            }
748

749
            break;
27✔
750

751
        case 'e':
6✔
752
            elapsed_flag = 1;
8✔
753
            break;
8✔
754

755
        case 'm':
756
            multif_flag = 1;
×
757
            break;
×
758

759
        case 'N':
2✔
760
            netdata_flag = 1;
3✔
761
            break;
3✔
762

763
        case 'n':
4✔
764
            name_flag = 1;
6✔
765
            if (rdns_flag) {
6✔
766
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
767
                exit(1);
3✔
768
            }
769
            break;
3✔
770

771
        case 'd':
6✔
772
            rdns_flag = 1;
9✔
773
            if (name_flag) {
9✔
774
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
775
                exit(1);
3✔
776
            }
777
            break;
6✔
778

779
        case 'A':
2✔
780
            addr_flag = 1;
3✔
781
            break;
3✔
782

783
        case 'B':
10✔
784
            if (!(backoff = atof(optparse_state.optarg)))
12✔
785
                usage(1);
6✔
786

787
            break;
8✔
788

789
        case 's':
8✔
790
            stats_flag = 1;
12✔
791
            break;
12✔
792

793
        case 'D':
22✔
794
            timestamp_flag = 1;
33✔
795
            break;
33✔
796

797
        case 'R':
4✔
798
            random_data_flag = 1;
5✔
799
            break;
5✔
800

801
        case 'l':
2✔
802
            loop_flag = 1;
3✔
803
            backoff_flag = 0;
3✔
804
            break;
3✔
805

806
        case 'u':
8✔
807
            unreachable_flag = 1;
12✔
808
            break;
12✔
809

810
        case 'a':
18✔
811
            alive_flag = 1;
26✔
812
            break;
26✔
813

814
        case 'H':
8✔
815
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
10✔
816
                usage(1);
6✔
817
            break;
6✔
818

819
#if defined(DEBUG) || defined(_DEBUG)
820
        case 'z':
821
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
822
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
823
                    usage(1);
824

825
            break;
826
#endif /* DEBUG || _DEBUG */
827

828
        case 'v':
4✔
829
            printf("%s: Version %s\n", prog, VERSION);
6✔
830
            exit(0);
6✔
831

832
        case 'x':
8✔
833
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
10✔
834
                usage(1);
6✔
835
            break;
6✔
836

837
        case 'X':
6✔
838
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
839
                usage(1);
6✔
840
            fast_reachable = 1;
5✔
841
            break;
5✔
842

843
        case 'f':
18✔
844
            filename = optparse_state.optarg;
27✔
845
            break;
27✔
846
#ifdef SO_MARK
847
        case 'k':
8✔
848
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
8✔
849
                usage(1);
4✔
850

851
            if (socket4 >= 0)
4✔
852
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
4✔
853
                    perror("fwmark ipv4");
2✔
854

855
#ifdef IPV6
856
            if (socket6 >= 0)
4✔
857
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
4✔
858
                    perror("fwmark ipv6");
2✔
859
#endif
860

861
            break;
4✔
862
#endif
863

864
        case 'g':
100✔
865
            /* use IP list generation */
866
            /* mutually exclusive with using file input or command line targets */
867
            generate_flag = 1;
120✔
868
            break;
120✔
869

870
        case 'S':
10✔
871
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
872
                src_addr_set = 1;
6✔
873
                break;
6✔
874
            }
875
#ifdef IPV6
876
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
877
                src_addr6_set = 1;
4✔
878
                break;
4✔
879
            }
880
#endif
881
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
882
            exit(1);
3✔
883

884
        case 'I':
6✔
885
#ifdef SO_BINDTODEVICE
886
            if (socket4 >= 0) {
6✔
887
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
888
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
889
                    exit(1);
2✔
890
                }
891
            }
892
#ifdef IPV6
893
            if (socket6 >= 0) {
4✔
894
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
895
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
896
                    exit(1);
897
                }
898
            }
899
#endif
900
#else
901
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
1✔
902
            exit(3);
1✔
903
            ;
904
#endif
905
            break;
4✔
906

907
        case 'J':
42✔
908
            json_flag = 1;
62✔
909
            break;
62✔
910

911
        case 'T':
2✔
912
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
913
            break;
3✔
914

915
        case 'O':
10✔
916
            if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
15✔
917
                if (socket4 >= 0) {
9✔
918
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
919
                        perror("setting type of service octet IP_TOS");
×
920
                    }
921
                }
3✔
922
#if defined(IPV6) && defined(IPV6_TCLASS)
923
                if (socket6 >= 0) {
9✔
924
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
925
                        perror("setting type of service octet IPV6_TCLASS");
×
926
                    }
927
                }
3✔
928
#endif
929
            }
3✔
930
            else {
931
                usage(1);
6✔
932
            }
933
            break;
11✔
934

935
        case 'o':
6✔
936
            outage_flag = 1;
9✔
937
            break;
9✔
938

939
        case '?':
4✔
940
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
941
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
942
            exit(1);
6✔
943
            break;
944
        }
945
    }
946

947
    /* permanently drop privileges */
948
    if (suid != getuid() && setuid(getuid())) {
537✔
949
        perror("fatal: failed to permanently drop privileges");
×
950
        /* continuing would be a security hole */
951
        exit(4);
×
952
    }
953

954
    /* validate various option settings */
955

956
#ifndef IPV6
957
    if (socket4 < 0) {
958
        crash_and_burn("can't create socket (must run as root?)");
959
    }
960
#else
961
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
537✔
962
        crash_and_burn("can't create socket (must run as root?)");
×
963
    }
964
#endif
965

966
    if (ttl > 255) {
535✔
967
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
968
        exit(1);
3✔
969
    }
970

971
    if (unreachable_flag && alive_flag) {
532✔
972
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
973
        exit(1);
3✔
974
    }
975

976
    if (count_flag && loop_flag) {
529✔
977
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
978
        exit(1);
3✔
979
    }
980

981
    if (json_flag && !count_flag) {
526✔
982
        fprintf(stderr, "%s: with -J or --json required -c or -C\n", prog);
3✔
983
        exit(1);
3✔
984
    }
985

986
#ifdef FPING_SAFE_LIMITS
987
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
523✔
988
        && getuid()) {
137✔
989
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
990
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
991
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
992
        exit(1);
6✔
993
    }
994
#endif
995

996
    if (ping_data_size > MAX_PING_DATA) {
517✔
997
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
998
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
999
        exit(1);
3✔
1000
    }
1001

1002
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
514✔
1003
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1004
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1005
        exit(1);
6✔
1006
    }
1007

1008
    if (icmp_request_typ == 13 && size_flag != 0) {
508✔
1009
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1010
        exit(1);
9✔
1011
    }
1012

1013
    if (count_flag) {
499✔
1014
        if (verbose_flag)
159✔
1015
            per_recv_flag = 1;
94✔
1016

1017
        alive_flag = unreachable_flag = verbose_flag = 0;
159✔
1018
    }
47✔
1019

1020
    if (loop_flag) {
499✔
1021
        if (!report_interval)
×
1022
            per_recv_flag = 1;
×
1023

1024
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1025
    }
1026

1027
    if (alive_flag || unreachable_flag || min_reachable)
499✔
1028
        verbose_flag = 0;
37✔
1029

1030
    trials = (count > retry + 1) ? count : retry + 1;
515✔
1031

1032
    /* auto-tune default timeout for count/loop modes
1033
     * see also github #32 */
1034
    if (loop_flag || count_flag) {
515✔
1035
        if (!timeout_flag) {
159✔
1036
            timeout = perhost_interval;
154✔
1037
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
154✔
1038
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1039
            }
1040
        }
46✔
1041
    }
47✔
1042

1043
#if defined(DEBUG) || defined(_DEBUG)
1044
    if (debugging & DBG_TRACE)
1045
        trace_flag = 1;
1046

1047
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1048
        randomly_lose_flag = 1;
1049
        lose_factor = 1; /* ie, 1/4 */
1050
    }
1051

1052
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1053
        randomly_lose_flag = 1;
1054
        lose_factor = 5; /* ie, 3/4 */
1055
    }
1056

1057
    if (debugging & DBG_PRINT_PER_SYSTEM)
1058
        print_per_system_flag = 1;
1059

1060
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1061
        report_all_rtts_flag = 1;
1062

1063
    if (trace_flag) {
1064
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1065
            prog, count, retry, interval / 1e6);
1066
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1067
            perhost_interval / 1e6, timeout / 1e6);
1068
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1069
            ping_data_size, trials);
1070

1071
        if (verbose_flag)
1072
            fprintf(stderr, "  verbose_flag set\n");
1073
        if (multif_flag)
1074
            fprintf(stderr, "  multif_flag set\n");
1075
        if (name_flag)
1076
            fprintf(stderr, "  name_flag set\n");
1077
        if (addr_flag)
1078
            fprintf(stderr, "  addr_flag set\n");
1079
        if (stats_flag)
1080
            fprintf(stderr, "  stats_flag set\n");
1081
        if (unreachable_flag)
1082
            fprintf(stderr, "  unreachable_flag set\n");
1083
        if (alive_flag)
1084
            fprintf(stderr, "  alive_flag set\n");
1085
        if (elapsed_flag)
1086
            fprintf(stderr, "  elapsed_flag set\n");
1087
        if (version_flag)
1088
            fprintf(stderr, "  version_flag set\n");
1089
        if (count_flag)
1090
            fprintf(stderr, "  count_flag set\n");
1091
        if (loop_flag)
1092
            fprintf(stderr, "  loop_flag set\n");
1093
        if (backoff_flag)
1094
            fprintf(stderr, "  backoff_flag set\n");
1095
        if (per_recv_flag)
1096
            fprintf(stderr, "  per_recv_flag set\n");
1097
        if (report_all_rtts_flag)
1098
            fprintf(stderr, "  report_all_rtts_flag set\n");
1099
        if (randomly_lose_flag)
1100
            fprintf(stderr, "  randomly_lose_flag set\n");
1101
        if (print_per_system_flag)
1102
            fprintf(stderr, "  print_per_system_flag set\n");
1103
        if (outage_flag)
1104
            fprintf(stderr, "  outage_flag set\n");
1105
        if (netdata_flag)
1106
            fprintf(stderr, "  netdata_flag set\n");
1107
        if (json_flag)
1108
            fprintf(stderr, "  json_flag set\n");
1109
    }
1110
#endif /* DEBUG || _DEBUG */
1111

1112
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1113
    if (ttl > 0) {
515✔
1114
        if (socket4 >= 0) {
3✔
1115
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1116
                perror("setting time to live");
×
1117
            }
1118
        }
1✔
1119
#ifdef IPV6
1120
        if (socket6 >= 0) {
3✔
1121
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1122
                perror("setting time to live");
×
1123
            }
1124
        }
1✔
1125
#endif
1126
    }
1✔
1127

1128
#if HAVE_SO_TIMESTAMPNS
1129
    {
1130
        int opt = 1;
374✔
1131
        if (socket4 >= 0) {
374✔
1132
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
374✔
1133
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1134
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1135
                }
1136
            }
1137
        }
1138
#ifdef IPV6
1139
        if (socket6 >= 0) {
374✔
1140
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
374✔
1141
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1142
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1143
                }
1144
            }
1145
        }
1146
#endif
1147
    }
1148
#endif
1149

1150
    update_current_time();
515✔
1151
    start_time = current_time_ns;
515✔
1152

1153
    /* handle host names supplied on command line or in a file */
1154
    /* if the generate_flag is on, then generate the IP list */
1155

1156
    argv = &argv[optparse_state.optind];
515✔
1157
    argc -= optparse_state.optind;
515✔
1158

1159
    /* calculate how many ping can be in-flight per host */
1160
    if (count_flag) {
515✔
1161
        event_storage_count = count;
174✔
1162
    }
62✔
1163
    else if (loop_flag) {
341✔
1164
        if (perhost_interval > timeout) {
×
1165
            event_storage_count = 1;
×
1166
        }
1167
        else {
1168
            event_storage_count = 1 + timeout / perhost_interval;
×
1169
        }
1170
    }
1171
    else {
1172
        event_storage_count = 1;
341✔
1173
    }
1174

1175
    /* file and generate are mutually exclusive */
1176
    /* file and command line are mutually exclusive */
1177
    /* generate requires command line parameters beyond the switches */
1178
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
515✔
1179
        usage(1);
19✔
1180

1181
    /* if no conditions are specified, then assume input from stdin */
1182
    if (!*argv && !filename && !generate_flag)
497✔
1183
        filename = "-";
69✔
1184

1185
    if (*argv && !generate_flag) {
497✔
1186
        while (*argv) {
635✔
1187
            add_name(*argv);
348✔
1188
            ++argv;
348✔
1189
        }
1190
    }
75✔
1191
    else if (filename) {
210✔
1192
        FILE *ping_file;
1193
        char line[MAX_TARGET_NAME_LEN + 1];
1194
        char host[MAX_TARGET_NAME_LEN + 1];
1195
        char scratch[MAX_TARGET_NAME_LEN + 1];
1196
        int skip, non_empty;
1197

1198
        if (strcmp(filename, "-") == 0)
96✔
1199
            ping_file = fdopen(0, "r");
72✔
1200
        else
1201
            ping_file = fopen(filename, "r");
24✔
1202

1203
        if (!ping_file)
96✔
1204
            errno_crash_and_burn("fopen");
3✔
1205

1206
        /*
1207
         * Read the first word of every non-comment line, skip everything else.
1208
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1209
         * character is a '#' are interpreted as comments and ignored.)
1210
        */
1211
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1212
            skip = non_empty = 0;
309✔
1213

1214
            /* skip over a prefix of the line where sscanf finds nothing */
1215
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1216
                continue;
99✔
1217
            }
1218

1219
            /* the first word of the line can indicate a comment line */
1220
            if (host[0] == '#') {
210✔
1221
                skip = 1; /* skip remainder of line */
42✔
1222
            } else {
14✔
1223
                non_empty = 1; /* we have something to add as a target name */
168✔
1224
                /*
1225
                 * We have found the start of a word.
1226
                 * This part of the line may contain all of the first word.
1227
                 */
1228
                if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
168✔
1229
                    char discard1[MAX_TARGET_NAME_LEN + 1];
1230
                    char discard2[MAX_TARGET_NAME_LEN + 1];
1231
                    if (sscanf(line, "%s%s", discard1, discard2) == 2) {
102✔
1232
                        skip = 1; /* a second word starts in this part */
6✔
1233
                    }
2✔
1234
                    if (isspace(line[sizeof(line) - 2])) {
102✔
1235
                        skip = 1; /* the first word ends in this part */
42✔
1236
                    }
14✔
1237
                }
34✔
1238
            }
1239
            /* read remainder of this input line */
1240
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
336✔
1241
                if (skip) {
150✔
1242
                    continue; /* skip rest of data in this input line */
102✔
1243
                }
1244
                if (isspace(line[0])) {
48✔
1245
                    skip = 1; /* first word ended in previous part */
6✔
1246
                    continue;
6✔
1247
                }
1248
                if ((sscanf(line, "%s", scratch) != 1) || (!*scratch)) {
42✔
1249
                    skip = 1; /* empty or blank part of line, skip the rest */
×
1250
                    continue;
×
1251
                }
1252
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
42✔
1253
                    fprintf(stderr, "%s: target name too long\n", prog);
24✔
1254
                    exit(1);
24✔
1255
                }
1256
                /* append remainder of word started in previous line part */
1257
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
18✔
1258
                /*
1259
                 * Since the "host" buffer is the same size as the "line"
1260
                 * buffer, a target name that fits into the "host" buffer
1261
                 * cannot use more than two consecutive line parts.
1262
                 * A target name that uses two consecutive line parts
1263
                 * and fits into the "host" buffer must end before the
1264
                 * end of the second "line" buffer.  Thus the rest of
1265
                 * the line can be skipped.
1266
                 */
1267
                skip = 1;
18✔
1268
            }
1269

1270
            if (non_empty)
186✔
1271
                add_name(host);
144✔
1272
        }
1273

1274
        fclose(ping_file);
70✔
1275
    }
24✔
1276
    else if (*argv && generate_flag) {
114✔
1277
        if (argc == 1) {
114✔
1278
            /* one target: we expect a cidr range (n.n.n.n/m) */
1279
            add_cidr(argv[0]);
59✔
1280
        }
11✔
1281
        else if (argc == 2) {
55✔
1282
            add_range(argv[0], argv[1]);
52✔
1283
        }
6✔
1284
        else {
1285
            usage(1);
3✔
1286
        }
1287
    }
18✔
1288
    else {
1289
        usage(1);
×
1290
    }
1291

1292
    if (!num_hosts) {
417✔
1293
        exit(num_noaddress ? 2 : 1);
36✔
1294
    }
1295

1296
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
381✔
1297
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
16✔
1298
    }
2✔
1299
#ifdef IPV6
1300
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
379✔
1301
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
14✔
1302
    }
1303
#endif
1304

1305
    /* allocate and initialize array to map host nr to host_entry */
1306
    {
1307
        struct event *cursor = event_queue_ping.first;
377✔
1308
        int i = 0;
377✔
1309
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
377✔
1310
        if (!table)
377✔
1311
            crash_and_burn("Can't malloc array of hosts");
×
1312
        /* initialize table of hosts. we know that we have ping events scheduled
1313
         * for each of them */
1314
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
944✔
1315
            table[i] = cursor->host;
567✔
1316
            cursor->host->i = i;
567✔
1317
            i++;
567✔
1318
        }
155✔
1319
    }
1320

1321
    init_ping_buffer_ipv4(ping_data_size);
377✔
1322
#ifdef IPV6
1323
    init_ping_buffer_ipv6(ping_data_size);
377✔
1324
#endif
1325

1326
#ifdef USE_SIGACTION
1327
    memset(&act, 0, sizeof(act));
377✔
1328
    act.sa_handler = signal_handler;
377✔
1329
    sigemptyset(&act.sa_mask);
377✔
1330
    sigaddset(&act.sa_mask, SIGINT);
377✔
1331
    sigaddset(&act.sa_mask, SIGQUIT);
377✔
1332
    act.sa_flags = SA_RESTART;
377✔
1333
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
377✔
1334
        crash_and_burn("failure to set signal handler");
24✔
1335
    }
24✔
1336
#else
1337
    signal(SIGINT, signal_handler);
1338
    signal(SIGQUIT, signal_handler);
1339
#endif
1340
    setlinebuf(stdout);
353✔
1341

1342
    if (report_interval) {
353✔
1343
        next_report_time = current_time_ns + report_interval;
27✔
1344
    }
9✔
1345

1346
    last_send_time = 0;
353✔
1347

1348
    seqmap_init();
353✔
1349

1350
    /* main loop */
1351
    main_loop();
353✔
1352

1353
/* Debug: CPU Performance */
1354
#if defined(DEBUG) || defined(_DEBUG)
1355
    perf_cpu_end = clock();
1356
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1357
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1358
#endif /* DEBUG || _DEBUG */
1359

1360
    finish();
353✔
1361

1362
    return 0;
83✔
1363
}
1364

1365
static inline int64_t timespec_ns(struct timespec *a)
5,707✔
1366
{
1367
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,707✔
1368
}
1369

1370
#if HAVE_SO_TIMESTAMPNS
1371
/* convert a struct timeval to nanoseconds */
1372
static inline int64_t timeval_ns(struct timeval *a)
1373
{
1374
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1375
}
1376
#endif /* HAVE_SO_TIMESTAMPNS */
1377

1378
void add_cidr(char *addr)
57✔
1379
{
1380
    char *addr_end;
1381
    char *mask_str;
1382
    unsigned long mask;
1383
    int ret;
1384
    struct addrinfo addr_hints;
1385
    struct addrinfo *addr_res;
1386
    unsigned long net_addr;
1387
#ifdef IPV6
1388
    uint64_t net_upper, net_lower;
1389
    char *scope_str;
1390
#endif /* IPV6 */
1391

1392
    /* Split address from mask */
1393
    addr_end = strrchr(addr, '/');
57✔
1394
    if (addr_end == NULL) {
57✔
1395
        usage(1);
3✔
1396
    }
1✔
1397
    mask_str = addr_end + 1;
55✔
1398

1399
#ifdef IPV6
1400
    /* IPv6 addresses can have a scope */
1401
    scope_str = strchr(addr, '%');
55✔
1402
    if (scope_str && mask_str < scope_str) {
55✔
1403
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1404
        exit(1);
2✔
1405
    }
1406
#endif /*IPV6 */
1407

1408
    *addr_end = '\0';
53✔
1409
    mask = atoi(mask_str);
53✔
1410

1411
    /* parse address */
1412
    memset(&addr_hints, 0, sizeof(struct addrinfo));
53✔
1413
    addr_hints.ai_family = hints_ai_family;
53✔
1414
    addr_hints.ai_flags = AI_NUMERICHOST;
53✔
1415
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
53✔
1416
    if (ret) {
53✔
1417
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1418
        exit(1);
7✔
1419
    }
1420
    if (addr_res->ai_family == AF_INET) {
46✔
1421
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
24✔
1422
        freeaddrinfo(addr_res);
24✔
1423
        add_cidr_ipv4(net_addr, mask);
24✔
1424
#ifdef IPV6
1425
    } else if (addr_res->ai_family == AF_INET6) {
30✔
1426
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1427
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1428
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1429
        freeaddrinfo(addr_res);
22✔
1430
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1431
#endif /* IPV6 */
1432
    } else {
1433
        freeaddrinfo(addr_res);
×
1434
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1435
        exit(1);
×
1436
    }
1437
}
30✔
1438

1439
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1440
{
1441
    unsigned long bitmask;
1442
    unsigned long net_last;
1443

1444
    /* check mask */
1445
    if (mask < 1 || mask > 32) {
24✔
1446
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1447
        exit(1);
6✔
1448
    }
1449

1450
    /* convert mask integer from 1 to 32 to a bitmask */
1451
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1452

1453
    /* calculate network range */
1454
    net_addr &= bitmask;
18✔
1455
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1456

1457
    /* exclude network and broadcast address for regular prefixes */
1458
    if (mask < 31) {
18✔
1459
        net_last--;
12✔
1460
        net_addr++;
12✔
1461
    }
4✔
1462

1463
    /* add all hosts in that network (net_addr and net_last inclusive) */
1464
    add_addr_range_ipv4(net_addr, net_last);
18✔
1465
}
16✔
1466

1467
#ifdef IPV6
1468
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1469
{
1470
    uint64_t bitmask_lower;
1471
    uint64_t last_lower;
1472

1473
    /* check mask -- 2^63 addresses should suffice for now */
1474
    if (mask < 65 || mask > 128) {
23✔
1475
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1476
        exit(1);
7✔
1477
    }
1478

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

1482
    /* calculate network range */
1483
    net_lower &= bitmask_lower;
16✔
1484
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1485

1486
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1487
}
12✔
1488
#endif /* IPV6 */
1489

1490
void add_range(char *start, char *end)
52✔
1491
{
1492
    struct addrinfo addr_hints;
1493
    struct addrinfo *addr_res;
1494
    unsigned long start_long;
1495
    unsigned long end_long;
1496
    int ret;
1497
#ifdef IPV6
1498
    uint64_t start_upper, start_lower;
1499
    uint64_t end_upper, end_lower;
1500
    char *start_scope_str, *end_scope_str;
1501

1502
    /*
1503
     * The compiler does not know that setting the address family hint to
1504
     * ensure that start and end are from the same address family also
1505
     * ensures that either start_long and end_long are initialized and used,
1506
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1507
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1508
     * supported to suppress compiler warnings.
1509
     */
1510
    start_long = -1;
52✔
1511
    end_long = 0;
52✔
1512
    start_upper = start_lower = -1;
52✔
1513
    end_upper = end_lower = 0;
52✔
1514
    start_scope_str = end_scope_str = NULL;
52✔
1515
#endif /* IPV6 */
1516

1517
    /* parse start address */
1518
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1519
    addr_hints.ai_family = hints_ai_family;
52✔
1520
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1521
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1522
    if (ret) {
52✔
1523
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1524
        exit(1);
9✔
1525
    }
1526
    /* start and end must be from the same address family */
1527
    hints_ai_family = addr_res->ai_family;
43✔
1528
    if (addr_res->ai_family == AF_INET) {
43✔
1529
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1530
        freeaddrinfo(addr_res);
17✔
1531
#ifdef IPV6
1532
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1533
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1534
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1535
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1536
        freeaddrinfo(addr_res);
26✔
1537
#endif /* IPV6 */
1538
    } else {
1539
        freeaddrinfo(addr_res);
×
1540
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1541
        exit(1);
×
1542
    }
1543

1544
    /* IPv6 addresses can have a scope */
1545
    if (hints_ai_family == AF_INET6) {
43✔
1546
        start_scope_str = strchr(start, '%');
26✔
1547
        end_scope_str = strchr(end, '%');
26✔
1548
        if ((!start_scope_str && end_scope_str) ||
26✔
1549
            (start_scope_str && !end_scope_str) ||
24✔
1550
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1551
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1552
                exit(1);
6✔
1553
        }
1554
    }
1555

1556
    /* parse end address */
1557
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1558
    addr_hints.ai_family = hints_ai_family;
37✔
1559
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1560
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1561
    if (ret) {
37✔
1562
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1563
        exit(1);
7✔
1564
    }
1565
    if (addr_res->ai_family == AF_INET) {
30✔
1566
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1567
        freeaddrinfo(addr_res);
12✔
1568
        add_addr_range_ipv4(start_long, end_long);
12✔
1569
#ifdef IPV6
1570
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1571
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1572
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1573
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1574
        freeaddrinfo(addr_res);
18✔
1575
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1576
#endif /* IPV6 */
1577
    } else {
1578
        freeaddrinfo(addr_res);
×
1579
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1580
        exit(1);
×
1581
    }
1582
}
24✔
1583

1584
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1585
{
1586
    /* check if generator limit is exceeded */
1587
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1588
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1589
        exit(1);
6✔
1590
    }
1591

1592
    /* generate */
1593
    for (; start_long <= end_long; start_long++) {
93✔
1594
        struct in_addr in_addr_tmp;
1595
        char buffer[20];
1596
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1597
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1598
        add_name(buffer);
69✔
1599
    }
23✔
1600
}
24✔
1601

1602
#ifdef IPV6
1603
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1604
{
1605
    int i;
1606
    uint64_t ret = 0;
134✔
1607
    for (i = 0; i < 8; i++) {
1,206✔
1608
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1609
    }
16✔
1610
    return ret;
134✔
1611
}
1612

1613
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1614
{
1615
    int i;
1616
    for (i = 0; i < 8; i++) {
720✔
1617
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1618
    }
1619
}
80✔
1620

1621
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1622
                         uint64_t end_upper, uint64_t end_lower,
1623
                         const char *scope_str)
1624
{
1625
    struct in6_addr in6_addr_tmp;
1626
    char buffer[100];
1627

1628
    /* prevent generating too many addresses */
1629
    if ((start_upper + 1 < end_upper) ||
34✔
1630
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1631
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1632
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1633
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1634
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1635
        exit(1);
8✔
1636
    }
1637

1638
    while ((start_upper < end_upper) ||
66✔
1639
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1640
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1641
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1642
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1643
        if (scope_str) {
40✔
1644
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1645
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1646
                exit(1);
×
1647
            }
1648
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1649
        }
1650
        add_name(buffer);
40✔
1651
        start_lower++;
40✔
1652
        if (start_lower == 0) {
40✔
1653
            start_upper++;
2✔
1654
        }
1655
    }
1656
}
26✔
1657
#endif /* IPv6 */
1658

1659
void main_loop()
365✔
1660
{
1661
    int64_t lt;
1662
    int64_t wait_time_ns;
1663
    struct event *event;
1664
    struct host_entry *h;
1665

1666
    while (event_queue_ping.first || event_queue_timeout.first) {
1,898✔
1667
        dbg_printf("%s", "# main_loop\n");
1668

1669
        /* timeout event ? */
1670
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,550✔
1671
            event = ev_dequeue(&event_queue_timeout);
101✔
1672
            h = event->host;
101✔
1673

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

1676
            stats_add(h, event->ping_index, 0, -1);
101✔
1677

1678
            if (per_recv_flag) {
101✔
1679
                if (json_flag)
25✔
1680
                    printf("{\"timeout\": {");
6✔
1681

1682
                if (timestamp_flag)
25✔
UNCOV
1683
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1684

1685
                if (json_flag)
25✔
1686
                {
1687
                  printf("\"host\": \"%s\", ", h->host);
6✔
1688
                  printf("\"seq\": %d", event->ping_index);
6✔
1689
                }
2✔
1690
                else {
1691
                    printf("%-*s : [%d], timed out",
19✔
1692
                        max_hostname_len, h->host, event->ping_index);
5✔
1693
                }
1694
                if (h->num_recv > 0) {
25✔
NEW
1695
                    if (json_flag)
×
NEW
1696
                        printf(", \"rttAvg\": %s", sprint_tm(h->total_time / h->num_recv));
×
1697
                    else
NEW
1698
                        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1699
                }
1700
                else {
1701
                    if (json_flag)
25✔
1702
                        printf(", \"rttAvg\": \"NaN\"");
6✔
1703
                    else
1704
                        printf(" (NaN avg, ");
19✔
1705
                }
1706
                if (h->num_recv <= h->num_sent) {
25✔
1707
                    if (json_flag) {
25✔
1708
                        printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
6✔
1709
                    }
2✔
1710
                    else {
1711
                        printf("%d%% loss)",
19✔
1712
                            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1713
                    }
1714
                }
7✔
1715
                else {
NEW
1716
                    if (json_flag) {
×
NEW
1717
                        printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
1718
                    }
1719
                    else {
NEW
1720
                        printf("%d%% return)",
×
NEW
1721
                            (h->num_recv_total * 100) / h->num_sent);
×
1722
                    }
1723
                }
1724

1725
                if (json_flag)
25✔
1726
                    printf("}}");
6✔
1727
                
1728
                printf("\n");
25✔
1729
            }
7✔
1730

1731
            /* do we need to send a retry? */
1732
            if (!loop_flag && !count_flag) {
101✔
1733
                if (h->num_sent < retry + 1) {
52✔
1734
                    if (backoff_flag) {
30✔
1735
                        h->timeout *= backoff;
30✔
1736
                    }
6✔
1737
                    send_ping(h, event->ping_index);
30✔
1738
                }
6✔
1739
            }
10✔
1740

1741
            /* note: we process first timeout events, because we might need to
1742
             * wait to process ping events, while we for sure never need to
1743
             * wait for timeout events.
1744
             */
1745
            continue;
101✔
1746
        }
1747

1748
        /* ping event ? */
1749
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,706✔
1750
            /* Make sure that we don't ping more than once every "interval" */
1751
            lt = current_time_ns - last_send_time;
1,146✔
1752
            if (lt < interval)
1,146✔
1753
                goto wait_for_reply;
261✔
1754

1755
            /* Dequeue the event */
1756
            event = ev_dequeue(&event_queue_ping);
885✔
1757
            h = event->host;
885✔
1758

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

1761
            /* Send the ping */
1762
            send_ping(h, event->ping_index);
885✔
1763

1764
            /* Loop and count mode: schedule next ping */
1765
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
885✔
1766
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
333✔
1767
            }
107✔
1768
        }
257✔
1769

1770
    wait_for_reply:
628✔
1771

1772
        /* When is the next ping next event? */
1773
        wait_time_ns = -1;
1,449✔
1774
        if (event_queue_ping.first) {
1,449✔
1775
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,025✔
1776
            if (wait_time_ns < 0)
1,025✔
1777
                wait_time_ns = 0;
551✔
1778
            /* make sure that we wait enough, so that the inter-ping delay is
1779
             * bigger than 'interval' */
1780
            if (wait_time_ns < interval) {
1,025✔
1781
                lt = current_time_ns - last_send_time;
559✔
1782
                if (lt < interval) {
559✔
1783
                    wait_time_ns = interval - lt;
559✔
1784
                }
203✔
1785
            }
203✔
1786

1787
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1788
        }
323✔
1789

1790
        /* When is the next timeout event? */
1791
        if (event_queue_timeout.first) {
1,449✔
1792
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
928✔
1793
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
928✔
1794
                wait_time_ns = wait_time_timeout;
420✔
1795
                if (wait_time_ns < 0) {
420✔
1796
                    wait_time_ns = 0;
2✔
1797
                }
1798
            }
108✔
1799

1800
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1801
        }
272✔
1802

1803
        /* When is the next report due? */
1804
        if (report_interval && (loop_flag || count_flag)) {
1,449✔
1805
            int64_t wait_time_next_report = next_report_time - current_time_ns;
215✔
1806
            if (wait_time_next_report < wait_time_ns) {
215✔
1807
                wait_time_ns = wait_time_next_report;
72✔
1808
                if (wait_time_ns < 0) {
72✔
1809
                    wait_time_ns = 0;
×
1810
                }
1811
            }
24✔
1812

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

1816
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1817
        if (wait_time_ns == -1) {
1,449✔
1818
            break;
14✔
1819
        }
1820

1821
        /* end of loop was requested by interrupt signal handler */
1822
        if (finish_requested) {
1,435✔
1823
            break;
3✔
1824
        }
1825

1826
        /* Receive replies */
1827
        /* (this is what sleeps during each loop iteration) */
1828
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1829
        if (wait_for_reply(wait_time_ns)) {
1,432✔
1830
            while (wait_for_reply(0))
1,353✔
1831
                ; /* process other replies in the queue */
1832
        }
238✔
1833

1834
        update_current_time();
1,432✔
1835

1836
        if (status_snapshot) {
1,432✔
1837
            status_snapshot = 0;
×
NEW
1838
            if (json_flag)
×
NEW
1839
                print_per_system_splits_json();
×
1840
            else
NEW
1841
                print_per_system_splits();
×
1842
        }
1843

1844
        /* Print report */
1845
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,432✔
1846
            if (netdata_flag) {
45✔
1847
                print_netdata();
3✔
1848
            }
1✔
1849
            else if (json_flag) {
42✔
1850
                print_per_system_splits_json();
3✔
1851
            }
1✔
1852
            else {
1853
                print_per_system_splits();
39✔
1854
            }
1855

1856
            while (current_time_ns >= next_report_time) {
90✔
1857
                next_report_time += report_interval;
45✔
1858
            }
1859
        }
15✔
1860
    }
1861
}
365✔
1862

1863
/************************************************************
1864

1865
  Function: signal_handler
1866

1867
*************************************************************
1868

1869
  Inputs:  int signum
1870

1871
  Description:
1872

1873
  SIGQUIT signal handler - set flag and return
1874
  SIGINT signal handler - set flag and return
1875

1876
************************************************************/
1877

1878
void signal_handler(int signum)
×
1879
{
1880
    switch (signum) {
×
1881
    case SIGINT:
1882
        finish_requested = 1;
×
1883
        break;
×
1884

1885
    case SIGQUIT:
1886
        status_snapshot = 1;
×
1887
        break;
×
1888
    }
1889
}
×
1890

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

1893
  Function: update_current_time
1894

1895
*************************************************************/
1896

1897
void update_current_time()
4,607✔
1898
{
1899
    clock_gettime(CLOCKID, &current_time);
4,607✔
1900
    current_time_ns = timespec_ns(&current_time);
4,607✔
1901
}
4,607✔
1902

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

1905
  Function: finish
1906

1907
*************************************************************
1908

1909
  Inputs:  void (none)
1910

1911
  Description:
1912

1913
  Main program clean up and exit point
1914

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

1917
void finish()
365✔
1918
{
1919
    int i;
1920
    HOST_ENTRY *h;
1921

1922
    update_current_time();
365✔
1923
    end_time = current_time_ns;
365✔
1924

1925
    /* tot up unreachables */
1926
    for (i = 0; i < num_hosts; i++) {
932✔
1927
        h = table[i];
567✔
1928

1929
        if (!h->num_recv) {
567✔
1930
            num_unreachable++;
86✔
1931

1932
            if (verbose_flag || unreachable_flag) {
86✔
1933
                printf("%s", h->host);
44✔
1934

1935
                if (verbose_flag)
44✔
1936
                    printf(" is unreachable");
41✔
1937

1938
                printf("\n");
44✔
1939
            }
2✔
1940
        }
14✔
1941
    }
155✔
1942

1943
    if (count_flag || loop_flag) {
365✔
1944
        if (json_flag)
156✔
1945
            print_per_system_stats_json();
56✔
1946
        else
1947
            print_per_system_stats();
100✔
1948
    }
46✔
1949
#if defined(DEBUG) || defined(_DEBUG)
1950
    else if (print_per_system_flag) {
1951
        if (json_flag)
1952
            print_per_system_stats_json();
1953
        else
1954
            print_per_system_stats();
1955
    }
1956
#endif /* DEBUG || _DEBUG */
1957

1958
    if (stats_flag) {
365✔
1959
        if (json_flag)
12✔
1960
            print_global_stats_json();
3✔
1961
        else
1962
            print_global_stats();
9✔
1963
    }
4✔
1964

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

1976
    if (num_noaddress)
356✔
1977
        exit(2);
3✔
1978
    else if (num_alive != num_hosts)
353✔
1979
        exit(1);
47✔
1980

1981
    exit(0);
306✔
1982
}
1983

1984
/************************************************************
1985

1986
  Function: print_per_system_stats
1987

1988
*************************************************************
1989

1990
  Inputs:  void (none)
1991

1992
  Description:
1993

1994

1995
************************************************************/
1996

1997
void print_per_system_stats(void)
100✔
1998
{
1999
    int i, j, avg, outage_ms;
2000
    HOST_ENTRY *h;
2001
    int64_t resp;
2002

2003
    if (verbose_flag || per_recv_flag)
100✔
2004
        fprintf(stderr, "\n");
59✔
2005

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

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

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

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

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

2044
            fprintf(stderr, "\n");
90✔
2045
        }
2046
    }
38✔
2047
}
100✔
2048

2049
/************************************************************
2050

2051
  Function: print_per_system_stats_json
2052

2053
*************************************************************
2054

2055
  Inputs:  void (none)
2056

2057
  Description:
2058

2059

2060
************************************************************/
2061

2062
void print_per_system_stats_json(void)
56✔
2063
{
2064
    int i, j, avg, outage_ms;
2065
    HOST_ENTRY *h;
2066
    int64_t resp;
2067

2068
    for (i = 0; i < num_hosts; i++) {
118✔
2069
        h = table[i];
62✔
2070

2071
        if (report_all_rtts_flag)
62✔
2072
            fprintf(stdout, "{\"vSum\": {");
18✔
2073
        else
2074
            fprintf(stdout, "{\"summary\": {");
44✔
2075

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

2078
        if (report_all_rtts_flag) {
62✔
2079
            fprintf(stdout, "\"values\": [");
18✔
2080
            for (j = 0; j < h->num_sent; j++) {
42✔
2081
                if (j > 0)
24✔
2082
                  fprintf(stdout, ", ");
6✔
2083
                
2084
                if ((resp = h->resp_times[j]) >= 0)
24✔
2085
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2086
                else
2087
                    fprintf(stdout, "null");
3✔
2088
            }
8✔
2089

2090
            fprintf(stdout, "]}");
18✔
2091
        }
6✔
2092
        else {
2093
            if (h->num_recv <= h->num_sent) {
44✔
2094
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
44✔
2095
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
44✔
2096
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
44✔
2097

2098
                if (outage_flag) {
44✔
2099
                    /* Time outage total */
NEW
2100
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
NEW
2101
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2102
                }
2103
            }
14✔
2104
            else {
NEW
2105
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
NEW
2106
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
NEW
2107
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2108
            }
2109

2110
            if (h->num_recv) {
44✔
2111
                avg = h->total_time / h->num_recv;
41✔
2112
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
41✔
2113
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
41✔
2114
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
41✔
2115
            }
13✔
2116

2117
            fprintf(stdout, "}");
44✔
2118
        }
2119
        fprintf(stdout, "}\n");
62✔
2120
    }
20✔
2121
}
56✔
2122

2123
/************************************************************
2124

2125
  Function: print_netdata
2126

2127
*************************************************************
2128

2129
  Inputs:  void (none)
2130

2131
  Description:
2132

2133

2134
************************************************************/
2135

2136
void print_netdata(void)
3✔
2137
{
2138
    static int sent_charts = 0;
2139

2140
    int i;
2141
    int64_t avg;
2142
    HOST_ENTRY *h;
2143

2144
    for (i = 0; i < num_hosts; i++) {
6✔
2145
        h = table[i];
3✔
2146

2147
        if (!sent_charts) {
3✔
2148
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2149
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2150
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2151
        }
1✔
2152

2153
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2154
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2155
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2156
        printf("END\n");
3✔
2157

2158
        if (!sent_charts) {
3✔
2159
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2160
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2161
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2162
        }
1✔
2163

2164
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2165
        /*
2166
        if( h->num_recv_i <= h->num_sent_i )
2167
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2168
        else
2169
            printf("SET lost = 0\n");
2170
*/
2171

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

2175
        if (!sent_charts) {
3✔
2176
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2177
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2178
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2179
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2180
        }
1✔
2181

2182
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2183
        if (h->num_recv_i) {
3✔
2184
            avg = h->total_time_i / h->num_recv_i;
3✔
2185
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2186
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2187
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2188
        }
1✔
2189
        printf("END\n");
3✔
2190

2191
        stats_reset_interval(h);
3✔
2192
    }
1✔
2193

2194
    sent_charts = 1;
3✔
2195
}
3✔
2196

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

2199
  Function: print_per_system_splits
2200

2201
*************************************************************
2202

2203
  Inputs:  void (none)
2204

2205
  Description:
2206

2207

2208
************************************************************/
2209

2210
void print_per_system_splits(void)
39✔
2211
{
2212
    int i, avg, outage_ms_i;
2213
    HOST_ENTRY *h;
2214
    struct tm *curr_tm;
2215

2216
    if (verbose_flag || per_recv_flag)
39✔
2217
        fprintf(stderr, "\n");
×
2218

2219
    update_current_time();
39✔
2220
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2221
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2222
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2223

2224
    for (i = 0; i < num_hosts; i++) {
78✔
2225
        h = table[i];
39✔
2226
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2227

2228
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2229
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2230
                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✔
2231

2232
            if (outage_flag) {
39✔
2233
                /* Time outage  */
2234
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2235
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2236
            }
4✔
2237
        }
13✔
2238
        else {
2239
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2240
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2241
        }
2242

2243
        if (h->num_recv_i) {
39✔
2244
            avg = h->total_time_i / h->num_recv_i;
27✔
2245
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2246
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2247
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2248
        }
9✔
2249

2250
        fprintf(stderr, "\n");
39✔
2251
        if (!cumulative_stats_flag) {
39✔
2252
            stats_reset_interval(h);
27✔
2253
        }
9✔
2254
    }
13✔
2255
}
39✔
2256

2257
/************************************************************
2258

2259
  Function: print_per_system_splits_json
2260

2261
*************************************************************
2262

2263
  Inputs:  void (none)
2264

2265
  Description:
2266

2267

2268
************************************************************/
2269

2270
void print_per_system_splits_json(void)
3✔
2271
{
2272
    int i, avg, outage_ms_i;
2273
    HOST_ENTRY *h;
2274

2275
    update_current_time();
3✔
2276

2277
    for (i = 0; i < num_hosts; i++) {
6✔
2278
        h = table[i];
3✔
2279
        fprintf(stdout, "{\"intSum\": {");
3✔
2280
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2281
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2282

2283
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2284
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2285
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2286
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2287

2288
            if (outage_flag) {
3✔
2289
                /* Time outage  */
NEW
2290
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
NEW
2291
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2292
            }
2293
        }
1✔
2294
        else {
NEW
2295
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
NEW
2296
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
NEW
2297
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2298
        }
2299

2300
        if (h->num_recv_i) {
3✔
2301
            avg = h->total_time_i / h->num_recv_i;
3✔
2302
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2303
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2304
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2305
        }
1✔
2306

2307
        fprintf(stdout, "}}\n");
3✔
2308
        if (!cumulative_stats_flag) {
3✔
2309
            stats_reset_interval(h);
3✔
2310
        }
1✔
2311
    }
1✔
2312
}
3✔
2313

2314
/************************************************************
2315

2316
  Function: print_global_stats
2317

2318
*************************************************************
2319

2320
  Inputs:  void (none)
2321

2322
  Description:
2323

2324

2325
************************************************************/
2326

2327
void print_global_stats(void)
9✔
2328
{
2329
    fprintf(stderr, "\n");
9✔
2330
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2331
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2332
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2333
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2334
    fprintf(stderr, "\n");
9✔
2335
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2336
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2337
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2338
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2339
    fprintf(stderr, "\n");
9✔
2340

2341
    if (total_replies == 0) {
9✔
2342
        min_reply = 0;
3✔
2343
        max_reply = 0;
3✔
2344
        total_replies = 1;
3✔
2345
        sum_replies = 0;
3✔
2346
    }
1✔
2347

2348
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2349
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2350
        sprint_tm(sum_replies / total_replies));
3✔
2351
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2352
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2353
        (end_time - start_time) / 1e9);
9✔
2354
    fprintf(stderr, "\n");
9✔
2355
}
9✔
2356

2357
/************************************************************
2358

2359
  Function: print_global_stats_json
2360

2361
*************************************************************
2362

2363
  Inputs:  void (none)
2364

2365
  Description:
2366

2367

2368
************************************************************/
2369

2370
void print_global_stats_json(void)
3✔
2371
{
2372
    fprintf(stdout, "{\"globalSum\": {");
3✔
2373
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2374
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2375
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2376
    fprintf(stdout, "\"unknown addresses\": %d, ", num_noaddress);
3✔
2377
    fprintf(stdout, "\"timeouts (waiting for response)\": %d, ", num_timeout);
3✔
2378
    fprintf(stdout, "\"ICMP Echos sent\": %d, ", num_pingsent);
3✔
2379
    fprintf(stdout, "\"ICMP Echo Replies received\": %d, ", num_pingreceived);
3✔
2380
    fprintf(stdout, "\"other ICMP received\": %d, ", num_othericmprcvd);
3✔
2381

2382
    if (total_replies == 0) {
3✔
NEW
2383
        min_reply = 0;
×
NEW
2384
        max_reply = 0;
×
NEW
2385
        total_replies = 1;
×
NEW
2386
        sum_replies = 0;
×
2387
    }
2388

2389
    fprintf(stdout, "\"ms (min round trip time)\": %s, ", sprint_tm(min_reply));
3✔
2390
    fprintf(stdout, "\"ms (avg round trip time)\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2391
    fprintf(stdout, "\"ms (max round trip time)\": %s, ", sprint_tm(max_reply));
3✔
2392
    fprintf(stdout, "\"sec (elapsed real time)\": %.3f", (end_time - start_time) / 1e9);
3✔
2393
    fprintf(stdout, "}}\n");
3✔
2394
}
3✔
2395

2396
/************************************************************
2397

2398
  Function: send_ping
2399

2400
*************************************************************
2401

2402
  Inputs:  int s, HOST_ENTRY *h
2403

2404
  Description:
2405

2406
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2407
  will be added on by the kernel.  The ID field is our UNIX process ID,
2408
  and the sequence number is an index into an array of outstanding
2409
  ping requests. The sequence number will later be used to quickly
2410
  figure out who the ping reply came from.
2411

2412
************************************************************/
2413

2414
int send_ping(HOST_ENTRY *h, int index)
915✔
2415
{
2416
    int n;
2417
    int myseq;
2418
    int ret = 1;
915✔
2419
    uint8_t proto = ICMP_ECHO;
915✔
2420

2421
    update_current_time();
915✔
2422
    h->last_send_time = current_time_ns;
915✔
2423
    myseq = seqmap_add(h->i, index, current_time_ns);
915✔
2424

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

2427
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
915✔
2428
        if(icmp_request_typ == 13)
792✔
2429
            proto = ICMP_TSTAMP;
18✔
2430
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
792✔
2431
    }
248✔
2432
#ifdef IPV6
2433
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2434
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2435
    }
15✔
2436
#endif
2437
    else {
2438
        return 0;
×
2439
    }
2440

2441
    /* error sending? */
2442
    if (
652✔
2443
        (n < 0)
263✔
2444
#if defined(EHOSTDOWN)
2445
        && errno != EHOSTDOWN
297✔
2446
#endif
2447
    ) {
2448
        if (verbose_flag) {
34✔
2449
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2450
        }
2451
        else {
2452
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2453
        }
2454

2455
        h->num_sent++;
34✔
2456
        h->num_sent_i++;
34✔
2457
        if (!loop_flag)
34✔
2458
            h->resp_times[index] = RESP_ERROR;
34✔
2459

2460
        ret = 0;
34✔
2461
    }
2462
    else {
2463
        /* schedule timeout */
2464
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
881✔
2465

2466
        /* mark this trial as outstanding */
2467
        if (!loop_flag) {
881✔
2468
            h->resp_times[index] = RESP_WAITING;
881✔
2469
        }
263✔
2470
    }
2471

2472
    num_pingsent++;
915✔
2473
    last_send_time = h->last_send_time;
915✔
2474

2475
    return (ret);
915✔
2476
}
263✔
2477

2478
int socket_can_read(struct timeval *timeout)
2,785✔
2479
{
2480
    int nfound;
2481
    fd_set readset;
2482
    int socketmax;
2483

2484
#ifndef IPV6
2485
    socketmax = socket4;
2486
#else
2487
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,785✔
2488
#endif
2489

2490
select_again:
2,104✔
2491
    FD_ZERO(&readset);
36,449✔
2492
    if (socket4 >= 0)
2,785✔
2493
        FD_SET(socket4, &readset);
2,785✔
2494
#ifdef IPV6
2495
    if (socket6 >= 0)
2,785✔
2496
        FD_SET(socket6, &readset);
2,785✔
2497
#endif
2498

2499
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,785✔
2500
    if (nfound < 0) {
2,785✔
2501
        if (errno == EINTR) {
×
2502
            /* interrupted system call: redo the select */
2503
            goto select_again;
×
2504
        }
2505
        else {
2506
            perror("select");
×
2507
        }
2508
    }
2509

2510
    if (nfound > 0) {
2,785✔
2511
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,353✔
2512
            return socket4;
1,173✔
2513
        }
2514
#ifdef IPV6
2515
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
180✔
2516
            return socket6;
180✔
2517
        }
2518
#endif
2519
    }
2520

2521
    return -1;
1,432✔
2522
}
681✔
2523

2524
int receive_packet(int64_t wait_time,
2,785✔
2525
#if HAVE_SO_TIMESTAMPNS
2526
    int64_t *reply_timestamp,
2527
#else
2528
    int64_t *reply_timestamp __attribute__((unused)),
2529
#endif
2530
    struct sockaddr *reply_src_addr,
2531
    size_t reply_src_addr_len,
2532
    char *reply_buf,
2533
    size_t reply_buf_len)
2534
{
2535
    struct timeval to;
2536
    int s = 0;
2,785✔
2537
    int recv_len;
2538
    static unsigned char msg_control[40];
2539
    struct iovec msg_iov = {
4,147✔
2540
        reply_buf,
681✔
2541
        reply_buf_len
681✔
2542
    };
2543
    struct msghdr recv_msghdr = {0};
2,785✔
2544
    recv_msghdr.msg_name = reply_src_addr;
2,785✔
2545
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,785✔
2546
    recv_msghdr.msg_iov = &msg_iov;
2,785✔
2547
    recv_msghdr.msg_iovlen = 1;
2,785✔
2548
    recv_msghdr.msg_control = &msg_control;
2,785✔
2549
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,785✔
2550
#if HAVE_SO_TIMESTAMPNS
2551
    struct cmsghdr *cmsg;
2552
#endif
2553

2554
    /* Wait for a socket to become ready */
2555
    if (wait_time) {
2,785✔
2556
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,430✔
2557
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,430✔
2558
    }
428✔
2559
    else {
2560
        to.tv_sec = 0;
1,355✔
2561
        to.tv_usec = 0;
1,355✔
2562
    }
2563
    s = socket_can_read(&to);
2,785✔
2564
    if (s == -1) {
2,785✔
2565
        return 0; /* timeout */
1,432✔
2566
    }
2567

2568
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,353✔
2569
    if (recv_len <= 0) {
1,353✔
2570
        return 0;
×
2571
    }
2572

2573
#if HAVE_SO_TIMESTAMPNS
2574
    /* ancilliary data */
2575
    {
2576
        struct timespec reply_timestamp_ts;
2577
        struct timeval reply_timestamp_tv;
2578
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,100✔
2579
             cmsg != NULL;
2,200✔
2580
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,100✔
2581
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,100✔
2582
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,100✔
2583
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,100✔
2584
            }
2585
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,100✔
2586
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2587
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2588
            }
2589
        }
2590
    }
2591
#endif
2592

2593
#if defined(DEBUG) || defined(_DEBUG)
2594
    if (randomly_lose_flag) {
2595
        if ((random() & 0x07) <= lose_factor)
2596
            return 0;
2597
    }
2598
#endif
2599

2600
    return recv_len;
1,353✔
2601
}
681✔
2602

2603
/* stats_add: update host statistics for a single packet that was received (or timed out)
2604
 * h: host entry to update
2605
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2606
 * success: 1 if response received, 0 otherwise
2607
 * latency: response time, in ns
2608
 */
2609
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
881✔
2610
{
2611
    /* sent count - we update only on receive/timeout, so that we don't get
2612
     * weird loss percentage, just because a packet was note recived yet.
2613
     */
2614
    h->num_sent++;
881✔
2615
    h->num_sent_i++;
881✔
2616

2617
    if (!success) {
881✔
2618
        if (!loop_flag && index >= 0) {
101✔
2619
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2620
        }
25✔
2621
        num_timeout++;
101✔
2622
        return;
101✔
2623
    }
2624

2625
    /* received count */
2626
    h->num_recv++;
780✔
2627
    h->num_recv_i++;
780✔
2628

2629
    /* maximum */
2630
    if (!h->max_reply || latency > h->max_reply) {
780✔
2631
        h->max_reply = latency;
631✔
2632
    }
182✔
2633
    if (!h->max_reply_i || latency > h->max_reply_i) {
780✔
2634
        h->max_reply_i = latency;
648✔
2635
    }
189✔
2636

2637
    /* minimum */
2638
    if (!h->min_reply || latency < h->min_reply) {
780✔
2639
        h->min_reply = latency;
508✔
2640
    }
153✔
2641
    if (!h->min_reply_i || latency < h->min_reply_i) {
780✔
2642
        h->min_reply_i = latency;
539✔
2643
    }
163✔
2644

2645
    /* total time (for average) */
2646
    h->total_time += latency;
780✔
2647
    h->total_time_i += latency;
780✔
2648

2649
    /* response time per-packet (count mode) */
2650
    if (!loop_flag && index >= 0) {
780✔
2651
        h->resp_times[index] = latency;
780✔
2652
    }
238✔
2653
}
263✔
2654

2655
/* stats_reset_interval: reset interval statistics
2656
 * h: host entry to update
2657
 */
2658
void stats_reset_interval(HOST_ENTRY *h)
33✔
2659
{
2660
    h->num_sent_i = 0;
33✔
2661
    h->num_recv_i = 0;
33✔
2662
    h->max_reply_i = 0;
33✔
2663
    h->min_reply_i = 0;
33✔
2664
    h->total_time_i = 0;
33✔
2665
}
33✔
2666

2667
int decode_icmp_ipv4(
1,173✔
2668
    struct sockaddr *response_addr,
2669
    size_t response_addr_len,
2670
    char *reply_buf,
2671
    size_t reply_buf_len,
2672
    unsigned short *id,
2673
    unsigned short *seq,
2674
    int *ip_header_tos,
2675
    int *ip_header_ttl,
2676
    uint32_t *ip_header_otime_ms,
2677
    uint32_t *ip_header_rtime_ms,
2678
    uint32_t *ip_header_ttime_ms)
2679
{
2680
    struct icmp *icp;
2681
    int hlen = 0;
1,173✔
2682

2683
    if (!using_sock_dgram4) {
1,173✔
2684
        struct ip *ip = (struct ip *)reply_buf;
1,167✔
2685
        *ip_header_tos = ip->ip_tos;
1,167✔
2686
        *ip_header_ttl = ip->ip_ttl;
1,167✔
2687

2688
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2689
        /* The alpha headers are decidedly broken.
2690
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2691
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2692
         */
2693
        hlen = (ip->ip_vhl & 0x0F) << 2;
2694
#else
2695
        hlen = ip->ip_hl << 2;
1,167✔
2696
#endif
2697
    }
223✔
2698

2699
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,173✔
2700
        /* too short */
2701
        if (verbose_flag) {
×
2702
            char buf[INET6_ADDRSTRLEN];
2703
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2704
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2705
        }
2706
        return -1;
×
2707
    }
2708

2709
    icp = (struct icmp *)(reply_buf + hlen);
1,173✔
2710

2711
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,173✔
2712
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
494✔
2713
        /* Handle other ICMP packets */
2714
        struct icmp *sent_icmp;
2715
        SEQMAP_VALUE *seqmap_value;
2716
        char addr_ascii[INET6_ADDRSTRLEN];
2717
        HOST_ENTRY *h;
2718

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

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

2727
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
472✔
2728
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
2729
            sent_icmp->icmp_id != ident4) {
×
2730
            /* not caused by us */
2731
            return -1;
472✔
2732
        }
2733

2734
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2735
        if (seqmap_value == NULL) {
×
2736
            return -1;
×
2737
        }
2738

2739
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2740

2741
        switch (icp->icmp_type) {
×
2742
        case ICMP_UNREACH:
2743
            h = table[seqmap_value->host_nr];
×
2744
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2745
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2746
                    addr_ascii, h->host);
2747
            }
2748
            else {
2749
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2750
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2751
            }
2752

2753
            print_warning("\n");
×
2754
            num_othericmprcvd++;
×
2755
            break;
×
2756

2757
        case ICMP_SOURCEQUENCH:
2758
        case ICMP_REDIRECT:
2759
        case ICMP_TIMXCEED:
2760
        case ICMP_PARAMPROB:
2761
            h = table[seqmap_value->host_nr];
×
2762
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2763
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2764
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2765
            }
2766
            else {
2767
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2768
                    icp->icmp_type, addr_ascii, h->host);
×
2769
            }
2770
            print_warning("\n");
×
2771
            num_othericmprcvd++;
×
2772
            break;
×
2773
        }
2774

2775
        return -1;
×
2776
    }
2777

2778
    *id = icp->icmp_id;
701✔
2779
    *seq = ntohs(icp->icmp_seq);
701✔
2780
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
701✔
2781

2782
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2783
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
2784
            if (verbose_flag) {
×
2785
                char buf[INET6_ADDRSTRLEN];
2786
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2787
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2788
            }
2789
            return -1;
×
2790
        }
2791

2792
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
2793
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
2794
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
2795
    }
2796

2797
    return hlen;
701✔
2798
}
223✔
2799

2800
#ifdef IPV6
2801
int decode_icmp_ipv6(
180✔
2802
    struct sockaddr *response_addr,
2803
    size_t response_addr_len,
2804
    char *reply_buf,
2805
    size_t reply_buf_len,
2806
    unsigned short *id,
2807
    unsigned short *seq)
2808
{
2809
    struct icmp6_hdr *icp;
2810

2811
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
180✔
2812
        if (verbose_flag) {
×
2813
            char buf[INET6_ADDRSTRLEN];
2814
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2815
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2816
        }
2817
        return 0; /* too short */
×
2818
    }
2819

2820
    icp = (struct icmp6_hdr *)reply_buf;
180✔
2821

2822
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
180✔
2823
        /* Handle other ICMP packets */
2824
        struct icmp6_hdr *sent_icmp;
2825
        SEQMAP_VALUE *seqmap_value;
2826
        char addr_ascii[INET6_ADDRSTRLEN];
2827
        HOST_ENTRY *h;
2828

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

2835
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
89✔
2836

2837
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
89✔
2838
            /* not caused by us */
2839
            return 0;
89✔
2840
        }
2841

2842
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2843
        if (seqmap_value == NULL) {
×
2844
            return 0;
×
2845
        }
2846

2847
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2848

2849
        switch (icp->icmp6_type) {
×
2850
        case ICMP_UNREACH:
2851
            h = table[seqmap_value->host_nr];
×
2852
            if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
×
2853
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2854
                    addr_ascii, h->host);
2855
            }
2856
            else {
2857
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2858
                    icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2859
            }
2860

2861
            print_warning("\n");
×
2862
            num_othericmprcvd++;
×
2863
            break;
×
2864

2865
        case ICMP_SOURCEQUENCH:
2866
        case ICMP_REDIRECT:
2867
        case ICMP_TIMXCEED:
2868
        case ICMP_PARAMPROB:
2869
            h = table[seqmap_value->host_nr];
×
2870
            if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
×
2871
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2872
                    icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
×
2873
            }
2874
            else {
2875
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2876
                    icp->icmp6_type, addr_ascii, h->host);
×
2877
            }
2878
            print_warning("\n");
×
2879
            num_othericmprcvd++;
×
2880
            break;
×
2881
        }
2882

2883
        return 0;
×
2884
    }
2885

2886
    *id = icp->icmp6_id;
91✔
2887
    *seq = ntohs(icp->icmp6_seq);
91✔
2888

2889
    return 1;
91✔
2890
}
30✔
2891
#endif
2892

2893
int wait_for_reply(int64_t wait_time)
2,785✔
2894
{
2895
    int result;
2896
    static char buffer[RECV_BUFSIZE];
2897
    struct sockaddr_storage response_addr;
2898
    int n, avg;
2899
    HOST_ENTRY *h;
2900
    int64_t this_reply;
2901
    int this_count;
2902
    int64_t recv_time = 0;
2,785✔
2903
    SEQMAP_VALUE *seqmap_value;
2904
    unsigned short id;
2905
    unsigned short seq;
2906
    int ip_header_tos = -1;
2,785✔
2907
    int ip_header_ttl = -1;
2,785✔
2908
    // ICMP Timestamp
2909
    uint32_t ip_header_otime_ms = 0x80000000U;
2,785✔
2910
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,785✔
2911
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,785✔
2912

2913
    /* Receive packet */
2914
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,785✔
2915
        &recv_time, /* reply_timestamp */
2916
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2917
        sizeof(response_addr), /* reply_src_addr_len */
2918
        buffer, /* reply_buf */
2919
        sizeof(buffer) /* reply_buf_len */
2920
    );
2921

2922
    if (result <= 0) {
2,785✔
2923
        return 0;
1,432✔
2924
    }
2925

2926
    update_current_time();
1,353✔
2927
    if (recv_time == 0)
1,353✔
2928
        recv_time = current_time_ns;
253✔
2929

2930
    /* Process ICMP packet and retrieve id/seq */
2931
    if (response_addr.ss_family == AF_INET) {
1,353✔
2932
        int ip_hlen = decode_icmp_ipv4(
1,173✔
2933
            (struct sockaddr *)&response_addr,
2934
            sizeof(response_addr),
2935
            buffer,
2936
            sizeof(buffer),
2937
            &id,
2938
            &seq,
2939
            &ip_header_tos,
2940
            &ip_header_ttl,
2941
            &ip_header_otime_ms,
2942
            &ip_header_rtime_ms,
2943
            &ip_header_ttime_ms);
2944
        if (ip_hlen < 0) {
1,173✔
2945
            return 1;
472✔
2946
        }
2947
        if (id != ident4) {
701✔
2948
            return 1; /* packet received, but not the one we are looking for! */
×
2949
        }
2950
        if (!using_sock_dgram4) {
701✔
2951
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2952
             * with fping with IPv6 hosts */
2953
            result -= ip_hlen;
695✔
2954
        }
223✔
2955
    }
223✔
2956
#ifdef IPV6
2957
    else if (response_addr.ss_family == AF_INET6) {
180✔
2958
        if (!decode_icmp_ipv6(
180✔
2959
                (struct sockaddr *)&response_addr,
2960
                sizeof(response_addr),
2961
                buffer,
2962
                sizeof(buffer),
2963
                &id,
2964
                &seq)) {
2965
            return 1;
89✔
2966
        }
2967
        if (id != ident6) {
91✔
2968
            return 1; /* packet received, but not the one we are looking for! */
×
2969
        }
2970
    }
15✔
2971
#endif
2972
    else {
2973
        return 1;
×
2974
    }
2975

2976
    seqmap_value = seqmap_fetch(seq, current_time_ns);
792✔
2977
    if (seqmap_value == NULL) {
792✔
2978
        return 1;
×
2979
    }
2980

2981
    /* find corresponding host_entry */
2982
    n = seqmap_value->host_nr;
792✔
2983
    h = table[n];
792✔
2984
    this_count = seqmap_value->ping_count;
792✔
2985
    this_reply = recv_time - seqmap_value->ping_ts;
792✔
2986

2987
    /* update stats that include invalid replies */
2988
    h->num_recv_total++;
792✔
2989
    num_pingreceived++;
792✔
2990

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

2993
    /* optionally require reply source equal to target address */
2994
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
792✔
2995
        dbg_printf("%s\n", "discarding reply from wrong source address");
2996
        return 1;
12✔
2997
    }
2998

2999
    /* discard duplicates */
3000
    if (!loop_flag && h->resp_times[this_count] >= 0) {
780✔
3001
        if (!per_recv_flag) {
×
3002
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3003
                h->host, this_count, result, sprint_tm(this_reply));
3004

3005
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3006
                char buf[INET6_ADDRSTRLEN];
3007
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3008
                fprintf(stderr, " [<- %s]", buf);
×
3009
            }
3010
            fprintf(stderr, "\n");
×
3011
        }
3012
        return 1;
×
3013
    }
3014

3015
    /* discard reply if delay is larger than timeout
3016
     * (see also: github #32) */
3017
    if (this_reply > h->timeout) {
780✔
3018
        return 1;
×
3019
    }
3020

3021
    /* update stats */
3022
    stats_add(h, this_count, 1, this_reply);
780✔
3023
    // TODO: move to stats_add?
3024
    if (!max_reply || this_reply > max_reply)
780✔
3025
        max_reply = this_reply;
507✔
3026
    if (!min_reply || this_reply < min_reply)
780✔
3027
        min_reply = this_reply;
417✔
3028
    sum_replies += this_reply;
780✔
3029
    total_replies++;
780✔
3030

3031
    /* initialize timeout to initial timeout (without backoff) */
3032
    h->timeout = timeout;
780✔
3033

3034
    /* remove timeout event */
3035
    struct event *timeout_event = host_get_timeout_event(h, this_count);
780✔
3036
    if (timeout_event) {
780✔
3037
        ev_remove(&event_queue_timeout, timeout_event);
780✔
3038
    }
238✔
3039

3040
    /* print "is alive" */
3041
    if (h->num_recv == 1) {
780✔
3042
        num_alive++;
481✔
3043
        if (fast_reachable && num_alive >= min_reachable)
481✔
3044
                finish_requested = 1;
3✔
3045

3046
        if (verbose_flag || alive_flag) {
481✔
3047
            printf("%s", h->host);
294✔
3048

3049
            if (verbose_flag)
294✔
3050
                printf(" is alive");
274✔
3051
        }
84✔
3052
    }
141✔
3053

3054
    /* print received ping (unless --quiet) */
3055
    if (per_recv_flag) {
780✔
3056
        if (json_flag)
186✔
3057
            printf("{\"resp\": {");
35✔
3058

3059
        if (timestamp_flag)
186✔
3060
            print_timestamp_format(recv_time, timestamp_format_flag);
45✔
3061

3062
        avg = h->total_time / h->num_recv;
186✔
3063
        if (json_flag) {
186✔
3064
            printf("\"host\": \"%s\", ", h->host);
35✔
3065
            printf("\"seq\": %d, ", this_count);
35✔
3066
            printf("\"size\": %d, ", result);
35✔
3067
            printf("\"rtt\": %s, ", sprint_tm(this_reply));
35✔
3068
            printf("\"rttAvg\": %s", sprint_tm(avg));
35✔
3069
        }
11✔
3070
        else {
3071
            printf("%-*s : [%d], %d bytes, %s ms", max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
151✔
3072
            printf(" (%s avg, ", sprint_tm(avg));
151✔
3073
        }
3074

3075
        if (h->num_recv <= h->num_sent) {
186✔
3076
            if (json_flag)
186✔
3077
                printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
35✔
3078
            else
3079
                printf("%d%% loss)", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
3080
        }
56✔
3081
        else {
NEW
3082
            if (json_flag)
×
NEW
3083
                printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
3084
            else
NEW
3085
                printf("%d%% return)", (h->num_recv_total * 100) / h->num_sent);
×
3086
        }
3087
    }
56✔
3088

3089
    if (verbose_flag || alive_flag || per_recv_flag) {
780✔
3090

3091
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
480✔
3092
            char buf[INET6_ADDRSTRLEN];
3093
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3094
            fprintf(stderr, " [<- %s]", buf);
6✔
3095
        }
3096

3097
        if (icmp_request_typ == 13) {
480✔
3098
            if (json_flag) {
16✔
3099
              printf(", \"timestamps\": {");
2✔
3100
              printf("\"originate\": %u, ", ip_header_otime_ms);
2✔
3101
              printf("\"receive\": %u, ", ip_header_rtime_ms);
2✔
3102
              printf("\"transmit\": %u, ", ip_header_ttime_ms);
2✔
3103
              printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
3104
            }
3105
            else {
3106
                printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
3107
                      alive_flag ? "" : ",",
14✔
3108
                      ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
3109
                      ms_since_midnight_utc(recv_time));
3110
            }
3111
        }
3112

3113
        if(print_tos_flag) {
480✔
3114
            if(ip_header_tos != -1) {
49✔
3115
                if (json_flag)
45✔
3116
                    printf(", \"tos\": %d", ip_header_tos);
6✔
3117
                else
3118
                    printf(" (TOS %d)", ip_header_tos);
39✔
3119
            }
13✔
3120
            else {
3121
                if (json_flag)
4✔
NEW
3122
                    printf(", \"tos\": -1");
×
3123
                else
3124
                    printf(" (TOS unknown)");
4✔
3125
            }
3126
        }
13✔
3127

3128
        if (print_ttl_flag) {
480✔
3129
          if(ip_header_ttl != -1) {
49✔
3130
              if (json_flag)
45✔
3131
                  printf(", \"ttl\": %d", ip_header_ttl);
6✔
3132
              else
3133
                  printf(" (TTL %d)", ip_header_ttl);
39✔
3134
          }
13✔
3135
          else {
3136
              if (json_flag)
4✔
NEW
3137
                  printf(", \"ttl\": -1");
×
3138
              else
3139
                  printf(" (TTL unknown)");
4✔
3140
          }
3141
        }
13✔
3142

3143
        if (elapsed_flag && !per_recv_flag)
480✔
3144
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
3145

3146
        if (json_flag)
480✔
3147
            printf("}}");
35✔
3148

3149
        printf("\n");
480✔
3150
    }
140✔
3151

3152
    return 1;
780✔
3153
}
681✔
3154

3155
/************************************************************
3156

3157
  Function: add_name
3158

3159
*************************************************************
3160

3161
  Inputs:  char* name
3162

3163
  Description:
3164

3165
  process input name for addition to target list
3166
  name can turn into multiple targets via multiple interfaces (-m)
3167
  or via NIS groups
3168

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

3171
void add_name(char *name)
601✔
3172
{
3173
    struct addrinfo *res0, *res, hints;
3174
    int ret_ga;
3175
    char *printname;
3176
    char namebuf[256];
3177
    char addrbuf[256];
3178

3179
    /* getaddrinfo */
3180
    memset(&hints, 0, sizeof(struct addrinfo));
601✔
3181
    hints.ai_flags = AI_UNUSABLE;
601✔
3182
    hints.ai_socktype = SOCK_RAW;
601✔
3183
    hints.ai_family = hints_ai_family;
601✔
3184
    if (hints_ai_family == AF_INET) {
601✔
3185
        hints.ai_protocol = IPPROTO_ICMP;
79✔
3186
    }
21✔
3187
#ifdef IPV6
3188
    else if (hints_ai_family == AF_INET6) {
522✔
3189
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
3190
    }
1✔
3191
#endif
3192
    else {
3193
        hints.ai_socktype = SOCK_STREAM;
491✔
3194
        hints.ai_protocol = 0;
491✔
3195
    }
3196
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
601✔
3197
    if (ret_ga) {
601✔
3198
        if (!quiet_flag)
29✔
3199
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3200
        num_noaddress++;
29✔
3201

3202
        // Handle JSON output for invalid hosts
3203
        if (json_flag) {
29✔
3204
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}", name, gai_strerror(ret_ga));
3✔
3205
            return;
21✔
3206
        }
3207

3208
        return;
26✔
3209
    }
3210

3211
    /* NOTE: we could/should loop with res on all addresses like this:
3212
     * for (res = res0; res; res = res->ai_next) {
3213
     * We don't do it yet, however, because is is an incompatible change
3214
     * (need to implement a separate option for this)
3215
     */
3216
    for (res = res0; res; res = res->ai_next) {
572✔
3217
        /* name_flag: addr -> name lookup requested) */
3218
        if (name_flag || rdns_flag) {
574✔
3219
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3220
            if (name_flag) {
3✔
3221
                /* Was it a numerical address? Only then do a rdns-query */
3222
                struct addrinfo *nres;
3223
                hints.ai_flags = AI_NUMERICHOST;
×
3224
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3225
                    do_rdns = 1;
×
3226
                    freeaddrinfo(nres);
×
3227
                }
3228
            }
3229

3230
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3231
                printname = namebuf;
3✔
3232
            }
1✔
3233
            else {
3234
                printname = name;
×
3235
            }
3236
        }
1✔
3237
        else {
3238
            printname = name;
569✔
3239
        }
3240

3241
        /* addr_flag: name -> addr lookup requested */
3242
        if (addr_flag) {
572✔
3243
            int ret;
3244
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3245
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3246
            if (ret) {
3✔
3247
                if (!quiet_flag) {
×
3248
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3249
                }
3250
                continue;
×
3251
            }
3252

3253
            if (name_flag || rdns_flag) {
3✔
3254
                char nameaddrbuf[512 + 3];
3255
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3256
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3257
            }
3258
            else {
3259
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3260
            }
3261
        }
1✔
3262
        else {
3263
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
569✔
3264
        }
3265

3266
        if (!multif_flag) {
572✔
3267
            break;
572✔
3268
        }
3269
    }
3270

3271
    freeaddrinfo(res0);
572✔
3272
}
165✔
3273

3274
/************************************************************
3275

3276
  Function: add_addr
3277

3278
*************************************************************
3279

3280
  Description:
3281

3282
  add single address to list of hosts to be pinged
3283

3284
************************************************************/
3285

3286
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
572✔
3287
{
3288
    HOST_ENTRY *p;
3289
    int n;
3290
    int64_t *i;
3291

3292
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
572✔
3293
    if (!p)
572✔
3294
        crash_and_burn("can't allocate HOST_ENTRY");
×
3295

3296
    p->name = strdup(name);
572✔
3297
    p->host = strdup(host);
572✔
3298
    memcpy(&p->saddr, ipaddr, ipaddr_len);
572✔
3299
    p->saddr_len = ipaddr_len;
572✔
3300
    p->timeout = timeout;
572✔
3301
    p->min_reply = 0;
572✔
3302

3303
    if (netdata_flag) {
572✔
3304
        char *s = p->name;
3✔
3305
        while (*s) {
30✔
3306
            if (!isalnum(*s))
27✔
3307
                *s = '_';
9✔
3308
            s++;
27✔
3309
        }
3310
    }
1✔
3311

3312
    if (strlen(p->host) > max_hostname_len)
572✔
3313
        max_hostname_len = strlen(p->host);
379✔
3314

3315
    /* array for response time results */
3316
    if (!loop_flag) {
572✔
3317
        i = (int64_t *)malloc(trials * sizeof(int64_t));
572✔
3318
        if (!i)
572✔
3319
            crash_and_burn("can't allocate resp_times array");
×
3320

3321
        for (n = 1; n < trials; n++)
2,279✔
3322
            i[n] = RESP_UNUSED;
1,707✔
3323

3324
        p->resp_times = i;
572✔
3325
    }
156✔
3326

3327
    /* allocate event storage */
3328
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
572✔
3329
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
572✔
3330

3331
    /* schedule first ping */
3332
    host_add_ping_event(p, 0, current_time_ns);
572✔
3333

3334
    num_hosts++;
572✔
3335
}
572✔
3336

3337
/************************************************************
3338

3339
  Function: crash_and_burn
3340

3341
*************************************************************
3342

3343
  Inputs:  char* message
3344

3345
  Description:
3346

3347
************************************************************/
3348

3349
void crash_and_burn(char *message)
×
3350
{
3351
    fprintf(stderr, "%s: %s\n", prog, message);
×
3352
    exit(4);
×
3353
}
3354

3355
/************************************************************
3356

3357
  Function: errno_crash_and_burn
3358

3359
*************************************************************
3360

3361
  Inputs:  char* message
3362

3363
  Description:
3364

3365
************************************************************/
3366

3367
void errno_crash_and_burn(char *message)
8✔
3368
{
3369
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3370
    exit(4);
8✔
3371
}
3372

3373
/************************************************************
3374

3375
  Function: print_warning
3376

3377
  Description: fprintf(stderr, ...), unless running with -q
3378

3379
*************************************************************/
3380

3381
void print_warning(char *format, ...)
54✔
3382
{
3383
    va_list args;
3384
    if (!quiet_flag) {
54✔
3385
        va_start(args, format);
54✔
3386
        vfprintf(stderr, format, args);
54✔
3387
        va_end(args);
54✔
3388
    }
8✔
3389
}
54✔
3390

3391
/************************************************************
3392

3393
  Function: sprint_tm
3394

3395
*************************************************************
3396

3397
  render nanosecond int64_t value into milliseconds string with three digits of
3398
  precision.
3399

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

3402
const char *sprint_tm(int64_t ns)
1,083✔
3403
{
3404
    static char buf[10];
3405
    double t = (double)ns / 1e6;
1,083✔
3406

3407
    if (t < 0.0) {
1,083✔
3408
        /* negative (unexpected) */
3409
        sprintf(buf, "%.2g", t);
×
3410
    }
3411
    else if (t < 1.0) {
1,083✔
3412
        /* <= 0.99 ms */
3413
        sprintf(buf, "%.3f", t);
1,079✔
3414
    }
331✔
3415
    else if (t < 10.0) {
4✔
3416
        /* 1.00 - 9.99 ms */
3417
        sprintf(buf, "%.2f", t);
4✔
3418
    }
4✔
3419
    else if (t < 100.0) {
×
3420
        /* 10.0 - 99.9 ms */
3421
        sprintf(buf, "%.1f", t);
×
3422
    }
3423
    else if (t < 1000000.0) {
×
3424
        /* 100 - 1'000'000 ms */
3425
        sprintf(buf, "%.0f", t);
×
3426
    }
3427
    else {
3428
        sprintf(buf, "%.3e", t);
×
3429
    }
3430

3431
    return (buf);
1,083✔
3432
}
3433

3434
/************************************************************
3435

3436
  Function: addr_cmp
3437

3438
*************************************************************/
3439
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
502✔
3440
{
3441
    if (a->sa_family != b->sa_family) {
502✔
3442
        return a->sa_family - b->sa_family;
×
3443
    }
3444
    else {
3445
        if (a->sa_family == AF_INET) {
502✔
3446
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
438✔
3447
        }
3448
        else if (a->sa_family == AF_INET6) {
64✔
3449
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3450
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3451
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3452
        }
3453
    }
3454

3455
    return 0;
×
3456
}
142✔
3457

3458
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
905✔
3459
{
3460
    struct event *event = &h->event_storage_ping[index % event_storage_count];
905✔
3461
    event->host = h;
905✔
3462
    event->ping_index = index;
905✔
3463
    event->ev_time = ev_time;
905✔
3464
    ev_enqueue(&event_queue_ping, event);
905✔
3465

3466
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3467
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3468
}
905✔
3469

3470
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
881✔
3471
{
3472
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
881✔
3473
    event->host = h;
881✔
3474
    event->ping_index = index;
881✔
3475
    event->ev_time = ev_time;
881✔
3476
    ev_enqueue(&event_queue_timeout, event);
881✔
3477

3478
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3479
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3480
}
881✔
3481

3482
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
780✔
3483
{
3484
    return &h->event_storage_timeout[index % event_storage_count];
780✔
3485
}
3486

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

3489
  Function: ev_enqueue
3490

3491
  Enqueue an event
3492

3493
  The queue is sorted by event->ev_time, so that queue->first always points to
3494
  the earliest event.
3495

3496
  We start scanning the queue from the tail, because we assume
3497
  that new events mostly get inserted with a event time higher
3498
  than the others.
3499

3500
*************************************************************/
3501
void ev_enqueue(struct event_queue *queue, struct event *event)
1,786✔
3502
{
3503
    struct event *i;
3504
    struct event *i_prev;
3505

3506
    /* Empty list */
3507
    if (queue->last == NULL) {
1,786✔
3508
        event->ev_next = NULL;
1,390✔
3509
        event->ev_prev = NULL;
1,390✔
3510
        queue->first = event;
1,390✔
3511
        queue->last = event;
1,390✔
3512
        return;
1,390✔
3513
    }
3514

3515
    /* Insert on tail? */
3516
    if (event->ev_time - queue->last->ev_time >= 0) {
396✔
3517
        event->ev_next = NULL;
396✔
3518
        event->ev_prev = queue->last;
396✔
3519
        queue->last->ev_next = event;
396✔
3520
        queue->last = event;
396✔
3521
        return;
396✔
3522
    }
3523

3524
    /* Find insertion point */
3525
    i = queue->last;
×
3526
    while (1) {
3527
        i_prev = i->ev_prev;
×
3528
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3529
            event->ev_prev = i_prev;
×
3530
            event->ev_next = i;
×
3531
            i->ev_prev = event;
×
3532
            if (i_prev != NULL) {
×
3533
                i_prev->ev_next = event;
×
3534
            }
3535
            else {
3536
                queue->first = event;
×
3537
            }
3538
            return;
×
3539
        }
3540
        i = i_prev;
×
3541
    }
3542
}
526✔
3543

3544
/************************************************************
3545

3546
  Function: ev_dequeue
3547

3548
*************************************************************/
3549
struct event *ev_dequeue(struct event_queue *queue)
986✔
3550
{
3551
    struct event *dequeued;
3552

3553
    if (queue->first == NULL) {
986✔
3554
        return NULL;
×
3555
    }
3556
    dequeued = queue->first;
986✔
3557
    ev_remove(queue, dequeued);
986✔
3558

3559
    return dequeued;
986✔
3560
}
282✔
3561

3562
/************************************************************
3563

3564
  Function: ev_remove
3565

3566
*************************************************************/
3567
void ev_remove(struct event_queue *queue, struct event *event)
1,766✔
3568
{
3569
    if (queue->first == event) {
1,766✔
3570
        queue->first = event->ev_next;
1,757✔
3571
    }
517✔
3572
    if (queue->last == event) {
1,766✔
3573
        queue->last = event->ev_prev;
1,391✔
3574
    }
403✔
3575
    if (event->ev_prev) {
1,766✔
3576
        event->ev_prev->ev_next = event->ev_next;
9✔
3577
    }
3✔
3578
    if (event->ev_next) {
1,766✔
3579
        event->ev_next->ev_prev = event->ev_prev;
375✔
3580
    }
117✔
3581
    event->ev_prev = NULL;
1,766✔
3582
    event->ev_next = NULL;
1,766✔
3583
}
1,766✔
3584

3585
/************************************************************
3586

3587
  Function: print_human_readable_time from current_time_ns
3588

3589
*************************************************************/
3590
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
45✔
3591
{
3592
    char time_buffer[100];
3593
    time_t current_time_s;
3594
    struct tm *local_time;
3595

3596
    current_time_s = current_time_ns / 1000000000;
45✔
3597
    local_time = localtime(&current_time_s);
45✔
3598
    switch(timestamp_format) {
45✔
3599
        case 1:
6✔
3600
            // timestamp-format ctime
3601
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
9✔
3602
            if (json_flag)
9✔
3603
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3604
            else
3605
                printf("[%s] ", time_buffer);
6✔
3606
            break;
9✔
3607
        case 2:
6✔
3608
            // timestamp-format iso
3609
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
9✔
3610
            if (json_flag)
9✔
3611
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3612
            else
3613
                printf("[%s] ", time_buffer);
6✔
3614
            break;
9✔
3615
        case 3:
6✔
3616
            // timestamp-format rfc3339
3617
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
9✔
3618
            if (json_flag)
9✔
3619
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3620
            else
3621
                printf("[%s] ", time_buffer);
6✔
3622
            break;
9✔
3623
        default:
12✔
3624
            if (json_flag)
18✔
3625
                printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9);
6✔
3626
            else
3627
                printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3628
    }
6✔
3629
}
45✔
3630

3631
/************************************************************
3632

3633
  Function: ms_since_midnight_utc
3634

3635
*************************************************************
3636

3637
  Input: int64_t: current UTC time in ns
3638

3639
  Output: uint32_t: current time in ms since midnight UTC
3640

3641
  Description:
3642

3643
  Return ICMP Timestamp value corresponding to the given time value.
3644
  The given time value must be in UTC.
3645

3646
*************************************************************/
3647
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
3648
{
3649
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
3650
}
3651

3652
/************************************************************
3653

3654
  Function: usage
3655

3656
*************************************************************
3657

3658
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3659

3660
  Description:
3661

3662
************************************************************/
3663

3664
void usage(int is_error)
115✔
3665
{
3666
    FILE *out = is_error ? stderr : stdout;
115✔
3667
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
115✔
3668
    fprintf(out, "\n");
115✔
3669
    fprintf(out, "Probing options:\n");
115✔
3670
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
115✔
3671
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
115✔
3672
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
115✔
3673
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
115✔
3674
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
115✔
3675
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
115✔
3676
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
115✔
3677
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
115✔
3678
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
115✔
3679
    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✔
3680
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
115✔
3681
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
115✔
3682
#ifdef SO_BINDTODEVICE
3683
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
78✔
3684
#endif
3685
#ifdef SO_MARK
3686
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
78✔
3687
#endif
3688
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
115✔
3689
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
115✔
3690
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
115✔
3691
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
115✔
3692
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
115✔
3693
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
115✔
3694
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
115✔
3695
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
115✔
3696
    fprintf(out, "   -S, --src=IP       set source address\n");
115✔
3697
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
115✔
3698
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
115✔
3699
    fprintf(out, "       --check-source discard replies not from target address\n");
115✔
3700
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
115✔
3701
    fprintf(out, "\n");
115✔
3702
    fprintf(out, "Output options:\n");
115✔
3703
    fprintf(out, "   -a, --alive        show targets that are alive\n");
115✔
3704
    fprintf(out, "   -A, --addr         show targets by address\n");
115✔
3705
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
115✔
3706
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
115✔
3707
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
115✔
3708
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
115✔
3709
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
115✔
3710
    fprintf(out, "   -J, --json         output in JSON format (-c or -C required)\n");
115✔
3711
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
115✔
3712
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
115✔
3713
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
115✔
3714
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
115✔
3715
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
115✔
3716
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
115✔
3717
    fprintf(out, "   -s, --stats        print final stats\n");
115✔
3718
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
115✔
3719
    fprintf(out, "   -v, --version      show version\n");
115✔
3720
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
115✔
3721
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
115✔
3722
    fprintf(out, "       --print-tos    show received TOS value\n");
115✔
3723
    fprintf(out, "       --print-ttl    show IP TTL value\n");
115✔
3724
    exit(is_error);
115✔
3725
}
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