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

schweikert / fping / 14685306113

26 Apr 2025 09:32PM UTC coverage: 87.301% (-0.6%) from 87.902%
14685306113

Pull #386

github

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

168 of 204 new or added lines in 1 file covered. (82.35%)

2 existing lines in 1 file now uncovered.

1533 of 1756 relevant lines covered (87.3%)

328.95 hits per line

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

87.44
/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
char *get_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)
686✔
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();
686✔
488
    int tos = 0;
686✔
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];
686✔
497
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
686✔
498
        usage(0);
3✔
499
    }
1✔
500

501
    socket4 = open_ping_socket_ipv4(&socktype4);
684✔
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);
486✔
507
#endif
508

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

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

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

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

535
    /* get command line options */
536

537
    struct optparse_long longopts[] = {
684✔
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,653✔
592
        switch (c) {
1,075✔
593
        case '0':
86✔
594
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
108✔
595
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
15✔
596
                  timestamp_format_flag = 1;
3✔
597
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
13✔
598
                  timestamp_format_flag = 2;
3✔
599
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
10✔
600
                  timestamp_format_flag = 3;
3✔
601
                }else{
1✔
602
                  usage(1);
6✔
603
                }
604
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
98✔
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;
102✔
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':
84✔
702
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
118✔
703
                usage(1);
6✔
704

705
            count_flag = 1;
114✔
706
            break;
114✔
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':
16✔
794
            timestamp_flag = 1;
24✔
795
            break;
24✔
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':
36✔
908
            json_flag = 1;
53✔
909
            break;
53✔
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())) {
528✔
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)) {
528✔
962
        crash_and_burn("can't create socket (must run as root?)");
×
963
    }
964
#endif
965

966
    if (ttl > 255) {
526✔
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) {
523✔
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) {
520✔
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) {
517✔
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)
514✔
988
        && getuid()) {
134✔
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) {
508✔
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)) {
505✔
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) {
499✔
1009
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1010
        exit(1);
9✔
1011
    }
1012

1013
    if (count_flag) {
490✔
1014
        if (verbose_flag)
150✔
1015
            per_recv_flag = 1;
85✔
1016

1017
        alive_flag = unreachable_flag = verbose_flag = 0;
150✔
1018
    }
44✔
1019

1020
    if (loop_flag) {
490✔
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)
490✔
1028
        verbose_flag = 0;
37✔
1029

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

1032
    /* auto-tune default timeout for count/loop modes
1033
     * see also github #32 */
1034
    if (loop_flag || count_flag) {
506✔
1035
        if (!timeout_flag) {
150✔
1036
            timeout = perhost_interval;
145✔
1037
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
145✔
1038
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1039
            }
1040
        }
43✔
1041
    }
44✔
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) {
506✔
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;
368✔
1131
        if (socket4 >= 0) {
368✔
1132
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
368✔
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) {
368✔
1140
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
368✔
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();
506✔
1151
    start_time = current_time_ns;
506✔
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];
506✔
1157
    argc -= optparse_state.optind;
506✔
1158

1159
    /* calculate how many ping can be in-flight per host */
1160
    if (count_flag) {
506✔
1161
        event_storage_count = count;
165✔
1162
    }
59✔
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))
506✔
1179
        usage(1);
19✔
1180

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

1185
    if (*argv && !generate_flag) {
488✔
1186
        while (*argv) {
617✔
1187
            add_name(*argv);
339✔
1188
            ++argv;
339✔
1189
        }
1190
    }
72✔
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) {
408✔
1293
        exit(num_noaddress ? 2 : 1);
36✔
1294
    }
1295

1296
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
372✔
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)) {
370✔
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;
368✔
1308
        int i = 0;
368✔
1309
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
368✔
1310
        if (!table)
368✔
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) {
926✔
1315
            table[i] = cursor->host;
558✔
1316
            cursor->host->i = i;
558✔
1317
            i++;
558✔
1318
        }
152✔
1319
    }
1320

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

1326
#ifdef USE_SIGACTION
1327
    memset(&act, 0, sizeof(act));
368✔
1328
    act.sa_handler = signal_handler;
368✔
1329
    sigemptyset(&act.sa_mask);
368✔
1330
    sigaddset(&act.sa_mask, SIGINT);
368✔
1331
    sigaddset(&act.sa_mask, SIGQUIT);
368✔
1332
    act.sa_flags = SA_RESTART;
368✔
1333
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
368✔
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);
344✔
1341

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

1346
    last_send_time = 0;
344✔
1347

1348
    seqmap_init();
344✔
1349

1350
    /* main loop */
1351
    main_loop();
344✔
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();
344✔
1361

1362
    return 0;
80✔
1363
}
1364

1365
static inline int64_t timespec_ns(struct timespec *a)
5,645✔
1366
{
1367
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,645✔
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()
356✔
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,881✔
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,542✔
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✔
NEW
1683
                    char *print_timestamp_format = get_timestamp_format(current_time_ns, timestamp_format_flag);
×
NEW
1684
                    if (print_timestamp_format != NULL) {
×
NEW
1685
                            if (json_flag)
×
NEW
1686
                                printf("\"timestamp\": \"%s\", ", print_timestamp_format);
×
1687
                            else
NEW
1688
                                printf("[%s] ", print_timestamp_format);
×
NEW
1689
                        free(print_timestamp_format);
×
1690
                    }
1691
                }
1692
                if (json_flag)
25✔
1693
                {
1694
                  printf("\"host\": \"%s\", ", h->host);
6✔
1695
                  printf("\"seq\": %d", event->ping_index);
6✔
1696
                }
2✔
1697
                else {
1698
                    printf("%-*s : [%d], timed out",
19✔
1699
                        max_hostname_len, h->host, event->ping_index);
5✔
1700
                }
1701
                if (h->num_recv > 0) {
25✔
NEW
1702
                    if (json_flag)
×
NEW
1703
                        printf(", \"rttAvg\": %s", sprint_tm(h->total_time / h->num_recv));
×
1704
                    else
NEW
1705
                        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1706
                }
1707
                else {
1708
                    if (json_flag)
25✔
1709
                        printf(", \"rttAvg\": \"NaN\"");
6✔
1710
                    else
1711
                        printf(" (NaN avg, ");
19✔
1712
                }
1713
                if (h->num_recv <= h->num_sent) {
25✔
1714
                    if (json_flag) {
25✔
1715
                        printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
6✔
1716
                    }
2✔
1717
                    else {
1718
                        printf("%d%% loss)",
19✔
1719
                            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1720
                    }
1721
                }
7✔
1722
                else {
NEW
1723
                    if (json_flag) {
×
NEW
1724
                        printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
1725
                    }
1726
                    else {
NEW
1727
                        printf("%d%% return)",
×
NEW
1728
                            (h->num_recv_total * 100) / h->num_sent);
×
1729
                    }
1730
                }
1731

1732
                if (json_flag)
25✔
1733
                    printf("}}");
6✔
1734
                
1735
                printf("\n");
25✔
1736
            }
7✔
1737

1738
            /* do we need to send a retry? */
1739
            if (!loop_flag && !count_flag) {
101✔
1740
                if (h->num_sent < retry + 1) {
52✔
1741
                    if (backoff_flag) {
30✔
1742
                        h->timeout *= backoff;
30✔
1743
                    }
6✔
1744
                    send_ping(h, event->ping_index);
30✔
1745
                }
6✔
1746
            }
10✔
1747

1748
            /* note: we process first timeout events, because we might need to
1749
             * wait to process ping events, while we for sure never need to
1750
             * wait for timeout events.
1751
             */
1752
            continue;
101✔
1753
        }
1754

1755
        /* ping event ? */
1756
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,695✔
1757
            /* Make sure that we don't ping more than once every "interval" */
1758
            lt = current_time_ns - last_send_time;
1,138✔
1759
            if (lt < interval)
1,138✔
1760
                goto wait_for_reply;
262✔
1761

1762
            /* Dequeue the event */
1763
            event = ev_dequeue(&event_queue_ping);
876✔
1764
            h = event->host;
876✔
1765

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

1768
            /* Send the ping */
1769
            send_ping(h, event->ping_index);
876✔
1770

1771
            /* Loop and count mode: schedule next ping */
1772
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
876✔
1773
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
333✔
1774
            }
107✔
1775
        }
254✔
1776

1777
    wait_for_reply:
621✔
1778

1779
        /* When is the next ping next event? */
1780
        wait_time_ns = -1;
1,441✔
1781
        if (event_queue_ping.first) {
1,441✔
1782
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,026✔
1783
            if (wait_time_ns < 0)
1,026✔
1784
                wait_time_ns = 0;
551✔
1785
            /* make sure that we wait enough, so that the inter-ping delay is
1786
             * bigger than 'interval' */
1787
            if (wait_time_ns < interval) {
1,026✔
1788
                lt = current_time_ns - last_send_time;
557✔
1789
                if (lt < interval) {
557✔
1790
                    wait_time_ns = interval - lt;
556✔
1791
                }
200✔
1792
            }
201✔
1793

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

1797
        /* When is the next timeout event? */
1798
        if (event_queue_timeout.first) {
1,441✔
1799
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
919✔
1800
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
919✔
1801
                wait_time_ns = wait_time_timeout;
410✔
1802
                if (wait_time_ns < 0) {
410✔
1803
                    wait_time_ns = 0;
×
1804
                }
1805
            }
105✔
1806

1807
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1808
        }
270✔
1809

1810
        /* When is the next report due? */
1811
        if (report_interval && (loop_flag || count_flag)) {
1,441✔
1812
            int64_t wait_time_next_report = next_report_time - current_time_ns;
215✔
1813
            if (wait_time_next_report < wait_time_ns) {
215✔
1814
                wait_time_ns = wait_time_next_report;
72✔
1815
                if (wait_time_ns < 0) {
72✔
1816
                    wait_time_ns = 0;
×
1817
                }
1818
            }
24✔
1819

1820
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1821
        }
71✔
1822

1823
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1824
        if (wait_time_ns == -1) {
1,441✔
1825
            break;
14✔
1826
        }
1827

1828
        /* end of loop was requested by interrupt signal handler */
1829
        if (finish_requested) {
1,427✔
1830
            break;
3✔
1831
        }
1832

1833
        /* Receive replies */
1834
        /* (this is what sleeps during each loop iteration) */
1835
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1836
        if (wait_for_reply(wait_time_ns)) {
1,424✔
1837
            while (wait_for_reply(0))
1,338✔
1838
                ; /* process other replies in the queue */
1839
        }
235✔
1840

1841
        update_current_time();
1,424✔
1842

1843
        if (status_snapshot) {
1,424✔
1844
            status_snapshot = 0;
×
NEW
1845
            if (json_flag)
×
NEW
1846
                print_per_system_splits_json();
×
1847
            else
NEW
1848
                print_per_system_splits();
×
1849
        }
1850

1851
        /* Print report */
1852
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,424✔
1853
            if (netdata_flag) {
45✔
1854
                print_netdata();
3✔
1855
            }
1✔
1856
            else if (json_flag) {
42✔
1857
                print_per_system_splits_json();
3✔
1858
            }
1✔
1859
            else {
1860
                print_per_system_splits();
39✔
1861
            }
1862

1863
            while (current_time_ns >= next_report_time) {
90✔
1864
                next_report_time += report_interval;
45✔
1865
            }
1866
        }
15✔
1867
    }
1868
}
356✔
1869

1870
/************************************************************
1871

1872
  Function: signal_handler
1873

1874
*************************************************************
1875

1876
  Inputs:  int signum
1877

1878
  Description:
1879

1880
  SIGQUIT signal handler - set flag and return
1881
  SIGINT signal handler - set flag and return
1882

1883
************************************************************/
1884

1885
void signal_handler(int signum)
×
1886
{
1887
    switch (signum) {
×
1888
    case SIGINT:
1889
        finish_requested = 1;
×
1890
        break;
×
1891

1892
    case SIGQUIT:
1893
        status_snapshot = 1;
×
1894
        break;
×
1895
    }
1896
}
×
1897

1898
/************************************************************
1899

1900
  Function: update_current_time
1901

1902
*************************************************************/
1903

1904
void update_current_time()
4,557✔
1905
{
1906
    clock_gettime(CLOCKID, &current_time);
4,557✔
1907
    current_time_ns = timespec_ns(&current_time);
4,557✔
1908
}
4,557✔
1909

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

1912
  Function: finish
1913

1914
*************************************************************
1915

1916
  Inputs:  void (none)
1917

1918
  Description:
1919

1920
  Main program clean up and exit point
1921

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

1924
void finish()
356✔
1925
{
1926
    int i;
1927
    HOST_ENTRY *h;
1928

1929
    update_current_time();
356✔
1930
    end_time = current_time_ns;
356✔
1931

1932
    /* tot up unreachables */
1933
    for (i = 0; i < num_hosts; i++) {
914✔
1934
        h = table[i];
558✔
1935

1936
        if (!h->num_recv) {
558✔
1937
            num_unreachable++;
86✔
1938

1939
            if (verbose_flag || unreachable_flag) {
86✔
1940
                printf("%s", h->host);
44✔
1941

1942
                if (verbose_flag)
44✔
1943
                    printf(" is unreachable");
41✔
1944

1945
                printf("\n");
44✔
1946
            }
2✔
1947
        }
14✔
1948
    }
152✔
1949

1950
    if (count_flag || loop_flag) {
356✔
1951
        if (json_flag)
147✔
1952
            print_per_system_stats_json();
47✔
1953
        else
1954
            print_per_system_stats();
100✔
1955
    }
43✔
1956
#if defined(DEBUG) || defined(_DEBUG)
1957
    else if (print_per_system_flag) {
1958
        if (json_flag)
1959
            print_per_system_stats_json();
1960
        else
1961
            print_per_system_stats();
1962
    }
1963
#endif /* DEBUG || _DEBUG */
1964

1965
    if (stats_flag) {
356✔
1966
        if (json_flag)
12✔
1967
            print_global_stats_json();
3✔
1968
        else
1969
            print_global_stats();
9✔
1970
    }
4✔
1971

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

1983
    if (num_noaddress)
347✔
1984
        exit(2);
3✔
1985
    else if (num_alive != num_hosts)
344✔
1986
        exit(1);
47✔
1987

1988
    exit(0);
297✔
1989
}
1990

1991
/************************************************************
1992

1993
  Function: print_per_system_stats
1994

1995
*************************************************************
1996

1997
  Inputs:  void (none)
1998

1999
  Description:
2000

2001

2002
************************************************************/
2003

2004
void print_per_system_stats(void)
100✔
2005
{
2006
    int i, j, avg, outage_ms;
2007
    HOST_ENTRY *h;
2008
    int64_t resp;
2009

2010
    if (verbose_flag || per_recv_flag)
100✔
2011
        fprintf(stderr, "\n");
59✔
2012

2013
    for (i = 0; i < num_hosts; i++) {
234✔
2014
        h = table[i];
134✔
2015
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
2016

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

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

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

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

2051
            fprintf(stderr, "\n");
90✔
2052
        }
2053
    }
38✔
2054
}
100✔
2055

2056
/************************************************************
2057

2058
  Function: print_per_system_stats_json
2059

2060
*************************************************************
2061

2062
  Inputs:  void (none)
2063

2064
  Description:
2065

2066

2067
************************************************************/
2068

2069
void print_per_system_stats_json(void)
47✔
2070
{
2071
    int i, j, avg, outage_ms;
2072
    HOST_ENTRY *h;
2073
    int64_t resp;
2074

2075
    for (i = 0; i < num_hosts; i++) {
100✔
2076
        h = table[i];
53✔
2077

2078
        if (report_all_rtts_flag)
53✔
2079
            fprintf(stdout, "{\"vSum\": {");
18✔
2080
        else
2081
            fprintf(stdout, "{\"summary\": {");
35✔
2082

2083
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
53✔
2084

2085
        if (report_all_rtts_flag) {
53✔
2086
            fprintf(stdout, "\"values\": [");
18✔
2087
            for (j = 0; j < h->num_sent; j++) {
42✔
2088
                if (j > 0)
24✔
2089
                  fprintf(stdout, ", ");
6✔
2090
                
2091
                if ((resp = h->resp_times[j]) >= 0)
24✔
2092
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2093
                else
2094
                    fprintf(stdout, "null");
3✔
2095
            }
8✔
2096

2097
            fprintf(stdout, "]}");
18✔
2098
        }
6✔
2099
        else {
2100
            if (h->num_recv <= h->num_sent) {
35✔
2101
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
35✔
2102
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
35✔
2103
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
35✔
2104

2105
                if (outage_flag) {
35✔
2106
                    /* Time outage total */
NEW
2107
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
NEW
2108
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2109
                }
2110
            }
11✔
2111
            else {
NEW
2112
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
NEW
2113
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
NEW
2114
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2115
            }
2116

2117
            if (h->num_recv) {
35✔
2118
                avg = h->total_time / h->num_recv;
32✔
2119
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
32✔
2120
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
32✔
2121
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
32✔
2122
            }
10✔
2123

2124
            fprintf(stdout, "}");
35✔
2125
        }
2126
        fprintf(stdout, "}\n");
53✔
2127
    }
17✔
2128
}
47✔
2129

2130
/************************************************************
2131

2132
  Function: print_netdata
2133

2134
*************************************************************
2135

2136
  Inputs:  void (none)
2137

2138
  Description:
2139

2140

2141
************************************************************/
2142

2143
void print_netdata(void)
3✔
2144
{
2145
    static int sent_charts = 0;
2146

2147
    int i;
2148
    int64_t avg;
2149
    HOST_ENTRY *h;
2150

2151
    for (i = 0; i < num_hosts; i++) {
6✔
2152
        h = table[i];
3✔
2153

2154
        if (!sent_charts) {
3✔
2155
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2156
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2157
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2158
        }
1✔
2159

2160
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2161
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2162
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2163
        printf("END\n");
3✔
2164

2165
        if (!sent_charts) {
3✔
2166
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2167
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2168
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2169
        }
1✔
2170

2171
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2172
        /*
2173
        if( h->num_recv_i <= h->num_sent_i )
2174
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2175
        else
2176
            printf("SET lost = 0\n");
2177
*/
2178

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

2182
        if (!sent_charts) {
3✔
2183
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2184
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2185
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2186
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2187
        }
1✔
2188

2189
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2190
        if (h->num_recv_i) {
3✔
2191
            avg = h->total_time_i / h->num_recv_i;
3✔
2192
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2193
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2194
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2195
        }
1✔
2196
        printf("END\n");
3✔
2197

2198
        stats_reset_interval(h);
3✔
2199
    }
1✔
2200

2201
    sent_charts = 1;
3✔
2202
}
3✔
2203

2204
/************************************************************
2205

2206
  Function: print_per_system_splits
2207

2208
*************************************************************
2209

2210
  Inputs:  void (none)
2211

2212
  Description:
2213

2214

2215
************************************************************/
2216

2217
void print_per_system_splits(void)
39✔
2218
{
2219
    int i, avg, outage_ms_i;
2220
    HOST_ENTRY *h;
2221
    struct tm *curr_tm;
2222

2223
    if (verbose_flag || per_recv_flag)
39✔
2224
        fprintf(stderr, "\n");
×
2225

2226
    update_current_time();
39✔
2227
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2228
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2229
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2230

2231
    for (i = 0; i < num_hosts; i++) {
78✔
2232
        h = table[i];
39✔
2233
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2234

2235
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2236
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2237
                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✔
2238

2239
            if (outage_flag) {
39✔
2240
                /* Time outage  */
2241
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2242
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2243
            }
4✔
2244
        }
13✔
2245
        else {
2246
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2247
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2248
        }
2249

2250
        if (h->num_recv_i) {
39✔
2251
            avg = h->total_time_i / h->num_recv_i;
27✔
2252
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2253
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2254
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2255
        }
9✔
2256

2257
        fprintf(stderr, "\n");
39✔
2258
        if (!cumulative_stats_flag) {
39✔
2259
            stats_reset_interval(h);
27✔
2260
        }
9✔
2261
    }
13✔
2262
}
39✔
2263

2264
/************************************************************
2265

2266
  Function: print_per_system_splits_json
2267

2268
*************************************************************
2269

2270
  Inputs:  void (none)
2271

2272
  Description:
2273

2274

2275
************************************************************/
2276

2277
void print_per_system_splits_json(void)
3✔
2278
{
2279
    int i, avg, outage_ms_i;
2280
    HOST_ENTRY *h;
2281

2282
    update_current_time();
3✔
2283

2284
    for (i = 0; i < num_hosts; i++) {
6✔
2285
        h = table[i];
3✔
2286
        fprintf(stdout, "{\"intSum\": {");
3✔
2287
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2288
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2289

2290
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2291
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2292
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2293
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2294

2295
            if (outage_flag) {
3✔
2296
                /* Time outage  */
NEW
2297
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
NEW
2298
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2299
            }
2300
        }
1✔
2301
        else {
NEW
2302
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
NEW
2303
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
NEW
2304
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2305
        }
2306

2307
        if (h->num_recv_i) {
3✔
2308
            avg = h->total_time_i / h->num_recv_i;
3✔
2309
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2310
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2311
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2312
        }
1✔
2313

2314
        fprintf(stdout, "}}\n");
3✔
2315
        if (!cumulative_stats_flag) {
3✔
2316
            stats_reset_interval(h);
3✔
2317
        }
1✔
2318
    }
1✔
2319
}
3✔
2320

2321
/************************************************************
2322

2323
  Function: print_global_stats
2324

2325
*************************************************************
2326

2327
  Inputs:  void (none)
2328

2329
  Description:
2330

2331

2332
************************************************************/
2333

2334
void print_global_stats(void)
9✔
2335
{
2336
    fprintf(stderr, "\n");
9✔
2337
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2338
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2339
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2340
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2341
    fprintf(stderr, "\n");
9✔
2342
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2343
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2344
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2345
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2346
    fprintf(stderr, "\n");
9✔
2347

2348
    if (total_replies == 0) {
9✔
2349
        min_reply = 0;
3✔
2350
        max_reply = 0;
3✔
2351
        total_replies = 1;
3✔
2352
        sum_replies = 0;
3✔
2353
    }
1✔
2354

2355
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2356
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2357
        sprint_tm(sum_replies / total_replies));
3✔
2358
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2359
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2360
        (end_time - start_time) / 1e9);
9✔
2361
    fprintf(stderr, "\n");
9✔
2362
}
9✔
2363

2364
/************************************************************
2365

2366
  Function: print_global_stats_json
2367

2368
*************************************************************
2369

2370
  Inputs:  void (none)
2371

2372
  Description:
2373

2374

2375
************************************************************/
2376

2377
void print_global_stats_json(void)
3✔
2378
{
2379
    fprintf(stdout, "{\"globalSum\": {");
3✔
2380
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2381
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2382
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2383
    fprintf(stdout, "\"unknown addresses\": %d, ", num_noaddress);
3✔
2384
    fprintf(stdout, "\"timeouts (waiting for response)\": %d, ", num_timeout);
3✔
2385
    fprintf(stdout, "\"ICMP Echos sent\": %d, ", num_pingsent);
3✔
2386
    fprintf(stdout, "\"ICMP Echo Replies received\": %d, ", num_pingreceived);
3✔
2387
    fprintf(stdout, "\"other ICMP received\": %d, ", num_othericmprcvd);
3✔
2388

2389
    if (total_replies == 0) {
3✔
NEW
2390
        min_reply = 0;
×
NEW
2391
        max_reply = 0;
×
NEW
2392
        total_replies = 1;
×
NEW
2393
        sum_replies = 0;
×
2394
    }
2395

2396
    fprintf(stdout, "\"ms (min round trip time)\": %s, ", sprint_tm(min_reply));
3✔
2397
    fprintf(stdout, "\"ms (avg round trip time)\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2398
    fprintf(stdout, "\"ms (max round trip time)\": %s, ", sprint_tm(max_reply));
3✔
2399
    fprintf(stdout, "\"sec (elapsed real time)\": %.3f", (end_time - start_time) / 1e9);
3✔
2400
    fprintf(stdout, "}}\n");
3✔
2401
}
3✔
2402

2403
/************************************************************
2404

2405
  Function: send_ping
2406

2407
*************************************************************
2408

2409
  Inputs:  int s, HOST_ENTRY *h
2410

2411
  Description:
2412

2413
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2414
  will be added on by the kernel.  The ID field is our UNIX process ID,
2415
  and the sequence number is an index into an array of outstanding
2416
  ping requests. The sequence number will later be used to quickly
2417
  figure out who the ping reply came from.
2418

2419
************************************************************/
2420

2421
int send_ping(HOST_ENTRY *h, int index)
906✔
2422
{
2423
    int n;
2424
    int myseq;
2425
    int ret = 1;
906✔
2426
    uint8_t proto = ICMP_ECHO;
906✔
2427

2428
    update_current_time();
906✔
2429
    h->last_send_time = current_time_ns;
906✔
2430
    myseq = seqmap_add(h->i, index, current_time_ns);
906✔
2431

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

2434
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
906✔
2435
        if(icmp_request_typ == 13)
783✔
2436
            proto = ICMP_TSTAMP;
18✔
2437
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
783✔
2438
    }
245✔
2439
#ifdef IPV6
2440
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2441
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2442
    }
15✔
2443
#endif
2444
    else {
2445
        return 0;
×
2446
    }
2447

2448
    /* error sending? */
2449
    if (
646✔
2450
        (n < 0)
260✔
2451
#if defined(EHOSTDOWN)
2452
        && errno != EHOSTDOWN
294✔
2453
#endif
2454
    ) {
2455
        if (verbose_flag) {
34✔
2456
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2457
        }
2458
        else {
2459
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2460
        }
2461

2462
        h->num_sent++;
34✔
2463
        h->num_sent_i++;
34✔
2464
        if (!loop_flag)
34✔
2465
            h->resp_times[index] = RESP_ERROR;
34✔
2466

2467
        ret = 0;
34✔
2468
    }
2469
    else {
2470
        /* schedule timeout */
2471
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
872✔
2472

2473
        /* mark this trial as outstanding */
2474
        if (!loop_flag) {
872✔
2475
            h->resp_times[index] = RESP_WAITING;
872✔
2476
        }
260✔
2477
    }
2478

2479
    num_pingsent++;
906✔
2480
    last_send_time = h->last_send_time;
906✔
2481

2482
    return (ret);
906✔
2483
}
260✔
2484

2485
int socket_can_read(struct timeval *timeout)
2,762✔
2486
{
2487
    int nfound;
2488
    fd_set readset;
2489
    int socketmax;
2490

2491
#ifndef IPV6
2492
    socketmax = socket4;
2493
#else
2494
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,762✔
2495
#endif
2496

2497
select_again:
2,085✔
2498
    FD_ZERO(&readset);
36,122✔
2499
    if (socket4 >= 0)
2,762✔
2500
        FD_SET(socket4, &readset);
2,762✔
2501
#ifdef IPV6
2502
    if (socket6 >= 0)
2,762✔
2503
        FD_SET(socket6, &readset);
2,762✔
2504
#endif
2505

2506
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,762✔
2507
    if (nfound < 0) {
2,762✔
2508
        if (errno == EINTR) {
×
2509
            /* interrupted system call: redo the select */
2510
            goto select_again;
×
2511
        }
2512
        else {
2513
            perror("select");
×
2514
        }
2515
    }
2516

2517
    if (nfound > 0) {
2,762✔
2518
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,338✔
2519
            return socket4;
1,158✔
2520
        }
2521
#ifdef IPV6
2522
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
180✔
2523
            return socket6;
180✔
2524
        }
2525
#endif
2526
    }
2527

2528
    return -1;
1,424✔
2529
}
677✔
2530

2531
int receive_packet(int64_t wait_time,
2,762✔
2532
#if HAVE_SO_TIMESTAMPNS
2533
    int64_t *reply_timestamp,
2534
#else
2535
    int64_t *reply_timestamp __attribute__((unused)),
2536
#endif
2537
    struct sockaddr *reply_src_addr,
2538
    size_t reply_src_addr_len,
2539
    char *reply_buf,
2540
    size_t reply_buf_len)
2541
{
2542
    struct timeval to;
2543
    int s = 0;
2,762✔
2544
    int recv_len;
2545
    static unsigned char msg_control[40];
2546
    struct iovec msg_iov = {
4,116✔
2547
        reply_buf,
677✔
2548
        reply_buf_len
677✔
2549
    };
2550
    struct msghdr recv_msghdr = {0};
2,762✔
2551
    recv_msghdr.msg_name = reply_src_addr;
2,762✔
2552
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,762✔
2553
    recv_msghdr.msg_iov = &msg_iov;
2,762✔
2554
    recv_msghdr.msg_iovlen = 1;
2,762✔
2555
    recv_msghdr.msg_control = &msg_control;
2,762✔
2556
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,762✔
2557
#if HAVE_SO_TIMESTAMPNS
2558
    struct cmsghdr *cmsg;
2559
#endif
2560

2561
    /* Wait for a socket to become ready */
2562
    if (wait_time) {
2,762✔
2563
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,424✔
2564
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,424✔
2565
    }
427✔
2566
    else {
2567
        to.tv_sec = 0;
1,338✔
2568
        to.tv_usec = 0;
1,338✔
2569
    }
2570
    s = socket_can_read(&to);
2,762✔
2571
    if (s == -1) {
2,762✔
2572
        return 0; /* timeout */
1,424✔
2573
    }
2574

2575
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,338✔
2576
    if (recv_len <= 0) {
1,338✔
2577
        return 0;
×
2578
    }
2579

2580
#if HAVE_SO_TIMESTAMPNS
2581
    /* ancilliary data */
2582
    {
2583
        struct timespec reply_timestamp_ts;
2584
        struct timeval reply_timestamp_tv;
2585
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,088✔
2586
             cmsg != NULL;
2,176✔
2587
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,088✔
2588
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,088✔
2589
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,088✔
2590
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,088✔
2591
            }
2592
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,088✔
2593
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2594
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2595
            }
2596
        }
2597
    }
2598
#endif
2599

2600
#if defined(DEBUG) || defined(_DEBUG)
2601
    if (randomly_lose_flag) {
2602
        if ((random() & 0x07) <= lose_factor)
2603
            return 0;
2604
    }
2605
#endif
2606

2607
    return recv_len;
1,338✔
2608
}
677✔
2609

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

2624
    if (!success) {
872✔
2625
        if (!loop_flag && index >= 0) {
101✔
2626
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2627
        }
25✔
2628
        num_timeout++;
101✔
2629
        return;
101✔
2630
    }
2631

2632
    /* received count */
2633
    h->num_recv++;
771✔
2634
    h->num_recv_i++;
771✔
2635

2636
    /* maximum */
2637
    if (!h->max_reply || latency > h->max_reply) {
771✔
2638
        h->max_reply = latency;
613✔
2639
    }
178✔
2640
    if (!h->max_reply_i || latency > h->max_reply_i) {
771✔
2641
        h->max_reply_i = latency;
628✔
2642
    }
181✔
2643

2644
    /* minimum */
2645
    if (!h->min_reply || latency < h->min_reply) {
771✔
2646
        h->min_reply = latency;
511✔
2647
    }
157✔
2648
    if (!h->min_reply_i || latency < h->min_reply_i) {
771✔
2649
        h->min_reply_i = latency;
539✔
2650
    }
165✔
2651

2652
    /* total time (for average) */
2653
    h->total_time += latency;
771✔
2654
    h->total_time_i += latency;
771✔
2655

2656
    /* response time per-packet (count mode) */
2657
    if (!loop_flag && index >= 0) {
771✔
2658
        h->resp_times[index] = latency;
771✔
2659
    }
235✔
2660
}
260✔
2661

2662
/* stats_reset_interval: reset interval statistics
2663
 * h: host entry to update
2664
 */
2665
void stats_reset_interval(HOST_ENTRY *h)
33✔
2666
{
2667
    h->num_sent_i = 0;
33✔
2668
    h->num_recv_i = 0;
33✔
2669
    h->max_reply_i = 0;
33✔
2670
    h->min_reply_i = 0;
33✔
2671
    h->total_time_i = 0;
33✔
2672
}
33✔
2673

2674
int decode_icmp_ipv4(
1,158✔
2675
    struct sockaddr *response_addr,
2676
    size_t response_addr_len,
2677
    char *reply_buf,
2678
    size_t reply_buf_len,
2679
    unsigned short *id,
2680
    unsigned short *seq,
2681
    int *ip_header_tos,
2682
    int *ip_header_ttl,
2683
    uint32_t *ip_header_otime_ms,
2684
    uint32_t *ip_header_rtime_ms,
2685
    uint32_t *ip_header_ttime_ms)
2686
{
2687
    struct icmp *icp;
2688
    int hlen = 0;
1,158✔
2689

2690
    if (!using_sock_dgram4) {
1,158✔
2691
        struct ip *ip = (struct ip *)reply_buf;
1,152✔
2692
        *ip_header_tos = ip->ip_tos;
1,152✔
2693
        *ip_header_ttl = ip->ip_ttl;
1,152✔
2694

2695
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2696
        /* The alpha headers are decidedly broken.
2697
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2698
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2699
         */
2700
        hlen = (ip->ip_vhl & 0x0F) << 2;
2701
#else
2702
        hlen = ip->ip_hl << 2;
1,152✔
2703
#endif
2704
    }
220✔
2705

2706
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,158✔
2707
        /* too short */
2708
        if (verbose_flag) {
×
2709
            char buf[INET6_ADDRSTRLEN];
2710
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2711
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2712
        }
2713
        return -1;
×
2714
    }
2715

2716
    icp = (struct icmp *)(reply_buf + hlen);
1,158✔
2717

2718
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,158✔
2719
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
488✔
2720
        /* Handle other ICMP packets */
2721
        struct icmp *sent_icmp;
2722
        SEQMAP_VALUE *seqmap_value;
2723
        char addr_ascii[INET6_ADDRSTRLEN];
2724
        HOST_ENTRY *h;
2725

2726
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2727
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
906✔
2728
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2729
            return -1;
×
2730
        }
2731

2732
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
466✔
2733

2734
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
466✔
2735
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
2736
            sent_icmp->icmp_id != ident4) {
×
2737
            /* not caused by us */
2738
            return -1;
466✔
2739
        }
2740

2741
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2742
        if (seqmap_value == NULL) {
×
2743
            return -1;
×
2744
        }
2745

2746
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2747

2748
        switch (icp->icmp_type) {
×
2749
        case ICMP_UNREACH:
2750
            h = table[seqmap_value->host_nr];
×
2751
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2752
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2753
                    addr_ascii, h->host);
2754
            }
2755
            else {
2756
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2757
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2758
            }
2759

2760
            print_warning("\n");
×
2761
            num_othericmprcvd++;
×
2762
            break;
×
2763

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

2782
        return -1;
×
2783
    }
2784

2785
    *id = icp->icmp_id;
692✔
2786
    *seq = ntohs(icp->icmp_seq);
692✔
2787
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
692✔
2788

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

2799
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
2800
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
2801
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
2802
    }
2803

2804
    return hlen;
692✔
2805
}
220✔
2806

2807
#ifdef IPV6
2808
int decode_icmp_ipv6(
180✔
2809
    struct sockaddr *response_addr,
2810
    size_t response_addr_len,
2811
    char *reply_buf,
2812
    size_t reply_buf_len,
2813
    unsigned short *id,
2814
    unsigned short *seq)
2815
{
2816
    struct icmp6_hdr *icp;
2817

2818
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
180✔
2819
        if (verbose_flag) {
×
2820
            char buf[INET6_ADDRSTRLEN];
2821
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2822
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2823
        }
2824
        return 0; /* too short */
×
2825
    }
2826

2827
    icp = (struct icmp6_hdr *)reply_buf;
180✔
2828

2829
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
180✔
2830
        /* Handle other ICMP packets */
2831
        struct icmp6_hdr *sent_icmp;
2832
        SEQMAP_VALUE *seqmap_value;
2833
        char addr_ascii[INET6_ADDRSTRLEN];
2834
        HOST_ENTRY *h;
2835

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

2842
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
89✔
2843

2844
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
89✔
2845
            /* not caused by us */
2846
            return 0;
89✔
2847
        }
2848

2849
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2850
        if (seqmap_value == NULL) {
×
2851
            return 0;
×
2852
        }
2853

2854
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2855

2856
        switch (icp->icmp6_type) {
×
2857
        case ICMP_UNREACH:
2858
            h = table[seqmap_value->host_nr];
×
2859
            if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
×
2860
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2861
                    addr_ascii, h->host);
2862
            }
2863
            else {
2864
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2865
                    icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2866
            }
2867

2868
            print_warning("\n");
×
2869
            num_othericmprcvd++;
×
2870
            break;
×
2871

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

2890
        return 0;
×
2891
    }
2892

2893
    *id = icp->icmp6_id;
91✔
2894
    *seq = ntohs(icp->icmp6_seq);
91✔
2895

2896
    return 1;
91✔
2897
}
30✔
2898
#endif
2899

2900
int wait_for_reply(int64_t wait_time)
2,762✔
2901
{
2902
    int result;
2903
    static char buffer[RECV_BUFSIZE];
2904
    struct sockaddr_storage response_addr;
2905
    int n, avg;
2906
    HOST_ENTRY *h;
2907
    int64_t this_reply;
2908
    int this_count;
2909
    int64_t recv_time = 0;
2,762✔
2910
    SEQMAP_VALUE *seqmap_value;
2911
    unsigned short id;
2912
    unsigned short seq;
2913
    int ip_header_tos = -1;
2,762✔
2914
    int ip_header_ttl = -1;
2,762✔
2915
    // ICMP Timestamp
2916
    uint32_t ip_header_otime_ms = 0x80000000U;
2,762✔
2917
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,762✔
2918
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,762✔
2919

2920
    /* Receive packet */
2921
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,762✔
2922
        &recv_time, /* reply_timestamp */
2923
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2924
        sizeof(response_addr), /* reply_src_addr_len */
2925
        buffer, /* reply_buf */
2926
        sizeof(buffer) /* reply_buf_len */
2927
    );
2928

2929
    if (result <= 0) {
2,762✔
2930
        return 0;
1,424✔
2931
    }
2932

2933
    update_current_time();
1,338✔
2934
    if (recv_time == 0)
1,338✔
2935
        recv_time = current_time_ns;
250✔
2936

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

2983
    seqmap_value = seqmap_fetch(seq, current_time_ns);
783✔
2984
    if (seqmap_value == NULL) {
783✔
2985
        return 1;
×
2986
    }
2987

2988
    /* find corresponding host_entry */
2989
    n = seqmap_value->host_nr;
783✔
2990
    h = table[n];
783✔
2991
    this_count = seqmap_value->ping_count;
783✔
2992
    this_reply = recv_time - seqmap_value->ping_ts;
783✔
2993

2994
    /* update stats that include invalid replies */
2995
    h->num_recv_total++;
783✔
2996
    num_pingreceived++;
783✔
2997

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

3000
    /* optionally require reply source equal to target address */
3001
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
783✔
3002
        dbg_printf("%s\n", "discarding reply from wrong source address");
3003
        return 1;
12✔
3004
    }
3005

3006
    /* discard duplicates */
3007
    if (!loop_flag && h->resp_times[this_count] >= 0) {
771✔
3008
        if (!per_recv_flag) {
×
3009
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3010
                h->host, this_count, result, sprint_tm(this_reply));
3011

3012
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3013
                char buf[INET6_ADDRSTRLEN];
3014
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3015
                fprintf(stderr, " [<- %s]", buf);
×
3016
            }
3017
            fprintf(stderr, "\n");
×
3018
        }
3019
        return 1;
×
3020
    }
3021

3022
    /* discard reply if delay is larger than timeout
3023
     * (see also: github #32) */
3024
    if (this_reply > h->timeout) {
771✔
3025
        return 1;
×
3026
    }
3027

3028
    /* update stats */
3029
    stats_add(h, this_count, 1, this_reply);
771✔
3030
    // TODO: move to stats_add?
3031
    if (!max_reply || this_reply > max_reply)
771✔
3032
        max_reply = this_reply;
487✔
3033
    if (!min_reply || this_reply < min_reply)
771✔
3034
        min_reply = this_reply;
424✔
3035
    sum_replies += this_reply;
771✔
3036
    total_replies++;
771✔
3037

3038
    /* initialize timeout to initial timeout (without backoff) */
3039
    h->timeout = timeout;
771✔
3040

3041
    /* remove timeout event */
3042
    struct event *timeout_event = host_get_timeout_event(h, this_count);
771✔
3043
    if (timeout_event) {
771✔
3044
        ev_remove(&event_queue_timeout, timeout_event);
771✔
3045
    }
235✔
3046

3047
    /* print "is alive" */
3048
    if (h->num_recv == 1) {
771✔
3049
        num_alive++;
472✔
3050
        if (fast_reachable && num_alive >= min_reachable)
472✔
3051
                finish_requested = 1;
3✔
3052

3053
        if (verbose_flag || alive_flag) {
472✔
3054
            printf("%s", h->host);
294✔
3055

3056
            if (verbose_flag)
294✔
3057
                printf(" is alive");
274✔
3058
        }
84✔
3059
    }
138✔
3060

3061
    /* print received ping (unless --quiet) */
3062
    if (per_recv_flag) {
771✔
3063
        if (json_flag)
177✔
3064
            printf("{\"resp\": {");
26✔
3065

3066
        if (timestamp_flag) {
177✔
3067
            char *print_timestamp_format = get_timestamp_format(current_time_ns, timestamp_format_flag);
36✔
3068
            if (print_timestamp_format != NULL) {
36✔
3069
                if (json_flag)
36✔
3070
                    printf("\"timestamp\": \"%s\", ", print_timestamp_format);
6✔
3071
                else
3072
                    printf("[%s] ", print_timestamp_format);
30✔
3073
                free(print_timestamp_format);
36✔
3074
            }
12✔
3075
        }
12✔
3076
        avg = h->total_time / h->num_recv;
177✔
3077
        if (json_flag) {
177✔
3078
            printf("\"host\": \"%s\", ", h->host);
26✔
3079
            printf("\"seq\": %d, ", this_count);
26✔
3080
            printf("\"size\": %d, ", result);
26✔
3081
            printf("\"rtt\": %s, ", sprint_tm(this_reply));
26✔
3082
            printf("\"rttAvg\": %s", sprint_tm(avg));
26✔
3083
        }
8✔
3084
        else {
3085
            printf("%-*s : [%d], %d bytes, %s ms", max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
151✔
3086
            printf(" (%s avg, ", sprint_tm(avg));
151✔
3087
        }
3088

3089
        if (h->num_recv <= h->num_sent) {
177✔
3090
            if (json_flag)
177✔
3091
                printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
26✔
3092
            else
3093
                printf("%d%% loss)", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
3094
        }
53✔
3095
        else {
NEW
3096
            if (json_flag)
×
NEW
3097
                printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
3098
            else
NEW
3099
                printf("%d%% return)", (h->num_recv_total * 100) / h->num_sent);
×
3100
        }
3101
    }
53✔
3102

3103
    if (verbose_flag || alive_flag || per_recv_flag) {
771✔
3104

3105
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
471✔
3106
            char buf[INET6_ADDRSTRLEN];
3107
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3108
            fprintf(stderr, " [<- %s]", buf);
6✔
3109
        }
3110

3111
        if (icmp_request_typ == 13) {
471✔
3112
            if (json_flag) {
16✔
3113
              printf(", \"timestamps\": {");
2✔
3114
              printf("\"originate\": %u, ", ip_header_otime_ms);
2✔
3115
              printf("\"receive\": %u, ", ip_header_rtime_ms);
2✔
3116
              printf("\"transmit\": %u, ", ip_header_ttime_ms);
2✔
3117
              printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
3118
            }
3119
            else {
3120
                printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
3121
                      alive_flag ? "" : ",",
14✔
3122
                      ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
3123
                      ms_since_midnight_utc(recv_time));
3124
            }
3125
        }
3126

3127
        if(print_tos_flag) {
471✔
3128
            if(ip_header_tos != -1) {
49✔
3129
                if (json_flag)
45✔
3130
                    printf(", \"tos\": %d", ip_header_tos);
6✔
3131
                else
3132
                    printf(" (TOS %d)", ip_header_tos);
39✔
3133
            }
13✔
3134
            else {
3135
                if (json_flag)
4✔
NEW
3136
                    printf(", \"tos\": -1");
×
3137
                else
3138
                    printf(" (TOS unknown)");
4✔
3139
            }
3140
        }
13✔
3141

3142
        if (print_ttl_flag) {
471✔
3143
          if(ip_header_ttl != -1) {
49✔
3144
              if (json_flag)
45✔
3145
                  printf(", \"ttl\": %d", ip_header_ttl);
6✔
3146
              else
3147
                  printf(" (TTL %d)", ip_header_ttl);
39✔
3148
          }
13✔
3149
          else {
3150
              if (json_flag)
4✔
NEW
3151
                  printf(", \"ttl\": -1");
×
3152
              else
3153
                  printf(" (TTL unknown)");
4✔
3154
          }
3155
        }
13✔
3156

3157
        if (elapsed_flag && !per_recv_flag)
471✔
3158
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
3159

3160
        if (json_flag)
471✔
3161
            printf("}}");
26✔
3162

3163
        printf("\n");
471✔
3164
    }
137✔
3165

3166
    return 1;
771✔
3167
}
677✔
3168

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

3171
  Function: add_name
3172

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

3175
  Inputs:  char* name
3176

3177
  Description:
3178

3179
  process input name for addition to target list
3180
  name can turn into multiple targets via multiple interfaces (-m)
3181
  or via NIS groups
3182

3183
************************************************************/
3184

3185
void add_name(char *name)
592✔
3186
{
3187
    struct addrinfo *res0, *res, hints;
3188
    int ret_ga;
3189
    char *printname;
3190
    char namebuf[256];
3191
    char addrbuf[256];
3192

3193
    /* getaddrinfo */
3194
    memset(&hints, 0, sizeof(struct addrinfo));
592✔
3195
    hints.ai_flags = AI_UNUSABLE;
592✔
3196
    hints.ai_socktype = SOCK_RAW;
592✔
3197
    hints.ai_family = hints_ai_family;
592✔
3198
    if (hints_ai_family == AF_INET) {
592✔
3199
        hints.ai_protocol = IPPROTO_ICMP;
79✔
3200
    }
21✔
3201
#ifdef IPV6
3202
    else if (hints_ai_family == AF_INET6) {
513✔
3203
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
3204
    }
1✔
3205
#endif
3206
    else {
3207
        hints.ai_socktype = SOCK_STREAM;
482✔
3208
        hints.ai_protocol = 0;
482✔
3209
    }
3210
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
592✔
3211
    if (ret_ga) {
592✔
3212
        if (!quiet_flag)
29✔
3213
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3214
        num_noaddress++;
29✔
3215

3216
        // Handle JSON output for invalid hosts
3217
        if (json_flag) {
29✔
3218
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}", name, gai_strerror(ret_ga));
3✔
3219
            return;
21✔
3220
        }
3221

3222
        return;
26✔
3223
    }
3224

3225
    /* NOTE: we could/should loop with res on all addresses like this:
3226
     * for (res = res0; res; res = res->ai_next) {
3227
     * We don't do it yet, however, because is is an incompatible change
3228
     * (need to implement a separate option for this)
3229
     */
3230
    for (res = res0; res; res = res->ai_next) {
563✔
3231
        /* name_flag: addr -> name lookup requested) */
3232
        if (name_flag || rdns_flag) {
565✔
3233
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3234
            if (name_flag) {
3✔
3235
                /* Was it a numerical address? Only then do a rdns-query */
3236
                struct addrinfo *nres;
3237
                hints.ai_flags = AI_NUMERICHOST;
×
3238
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3239
                    do_rdns = 1;
×
3240
                    freeaddrinfo(nres);
×
3241
                }
3242
            }
3243

3244
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3245
                printname = namebuf;
3✔
3246
            }
1✔
3247
            else {
3248
                printname = name;
×
3249
            }
3250
        }
1✔
3251
        else {
3252
            printname = name;
560✔
3253
        }
3254

3255
        /* addr_flag: name -> addr lookup requested */
3256
        if (addr_flag) {
563✔
3257
            int ret;
3258
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3259
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3260
            if (ret) {
3✔
3261
                if (!quiet_flag) {
×
3262
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3263
                }
3264
                continue;
×
3265
            }
3266

3267
            if (name_flag || rdns_flag) {
3✔
3268
                char nameaddrbuf[512 + 3];
3269
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3270
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3271
            }
3272
            else {
3273
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3274
            }
3275
        }
1✔
3276
        else {
3277
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
560✔
3278
        }
3279

3280
        if (!multif_flag) {
563✔
3281
            break;
563✔
3282
        }
3283
    }
3284

3285
    freeaddrinfo(res0);
563✔
3286
}
162✔
3287

3288
/************************************************************
3289

3290
  Function: add_addr
3291

3292
*************************************************************
3293

3294
  Description:
3295

3296
  add single address to list of hosts to be pinged
3297

3298
************************************************************/
3299

3300
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
563✔
3301
{
3302
    HOST_ENTRY *p;
3303
    int n;
3304
    int64_t *i;
3305

3306
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
563✔
3307
    if (!p)
563✔
3308
        crash_and_burn("can't allocate HOST_ENTRY");
×
3309

3310
    p->name = strdup(name);
563✔
3311
    p->host = strdup(host);
563✔
3312
    memcpy(&p->saddr, ipaddr, ipaddr_len);
563✔
3313
    p->saddr_len = ipaddr_len;
563✔
3314
    p->timeout = timeout;
563✔
3315
    p->min_reply = 0;
563✔
3316

3317
    if (netdata_flag) {
563✔
3318
        char *s = p->name;
3✔
3319
        while (*s) {
30✔
3320
            if (!isalnum(*s))
27✔
3321
                *s = '_';
9✔
3322
            s++;
27✔
3323
        }
3324
    }
1✔
3325

3326
    if (strlen(p->host) > max_hostname_len)
563✔
3327
        max_hostname_len = strlen(p->host);
370✔
3328

3329
    /* array for response time results */
3330
    if (!loop_flag) {
563✔
3331
        i = (int64_t *)malloc(trials * sizeof(int64_t));
563✔
3332
        if (!i)
563✔
3333
            crash_and_burn("can't allocate resp_times array");
×
3334

3335
        for (n = 1; n < trials; n++)
2,243✔
3336
            i[n] = RESP_UNUSED;
1,680✔
3337

3338
        p->resp_times = i;
563✔
3339
    }
153✔
3340

3341
    /* allocate event storage */
3342
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
563✔
3343
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
563✔
3344

3345
    /* schedule first ping */
3346
    host_add_ping_event(p, 0, current_time_ns);
563✔
3347

3348
    num_hosts++;
563✔
3349
}
563✔
3350

3351
/************************************************************
3352

3353
  Function: crash_and_burn
3354

3355
*************************************************************
3356

3357
  Inputs:  char* message
3358

3359
  Description:
3360

3361
************************************************************/
3362

3363
void crash_and_burn(char *message)
×
3364
{
3365
    fprintf(stderr, "%s: %s\n", prog, message);
×
3366
    exit(4);
×
3367
}
3368

3369
/************************************************************
3370

3371
  Function: errno_crash_and_burn
3372

3373
*************************************************************
3374

3375
  Inputs:  char* message
3376

3377
  Description:
3378

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

3381
void errno_crash_and_burn(char *message)
8✔
3382
{
3383
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3384
    exit(4);
8✔
3385
}
3386

3387
/************************************************************
3388

3389
  Function: print_warning
3390

3391
  Description: fprintf(stderr, ...), unless running with -q
3392

3393
*************************************************************/
3394

3395
void print_warning(char *format, ...)
54✔
3396
{
3397
    va_list args;
3398
    if (!quiet_flag) {
54✔
3399
        va_start(args, format);
54✔
3400
        vfprintf(stderr, format, args);
54✔
3401
        va_end(args);
54✔
3402
    }
8✔
3403
}
54✔
3404

3405
/************************************************************
3406

3407
  Function: sprint_tm
3408

3409
*************************************************************
3410

3411
  render nanosecond int64_t value into milliseconds string with three digits of
3412
  precision.
3413

3414
************************************************************/
3415

3416
const char *sprint_tm(int64_t ns)
1,038✔
3417
{
3418
    static char buf[10];
3419
    double t = (double)ns / 1e6;
1,038✔
3420

3421
    if (t < 0.0) {
1,038✔
3422
        /* negative (unexpected) */
3423
        sprintf(buf, "%.2g", t);
×
3424
    }
3425
    else if (t < 1.0) {
1,038✔
3426
        /* <= 0.99 ms */
3427
        sprintf(buf, "%.3f", t);
1,038✔
3428
    }
320✔
UNCOV
3429
    else if (t < 10.0) {
×
3430
        /* 1.00 - 9.99 ms */
UNCOV
3431
        sprintf(buf, "%.2f", t);
×
3432
    }
3433
    else if (t < 100.0) {
×
3434
        /* 10.0 - 99.9 ms */
3435
        sprintf(buf, "%.1f", t);
×
3436
    }
3437
    else if (t < 1000000.0) {
×
3438
        /* 100 - 1'000'000 ms */
3439
        sprintf(buf, "%.0f", t);
×
3440
    }
3441
    else {
3442
        sprintf(buf, "%.3e", t);
×
3443
    }
3444

3445
    return (buf);
1,038✔
3446
}
3447

3448
/************************************************************
3449

3450
  Function: addr_cmp
3451

3452
*************************************************************/
3453
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
493✔
3454
{
3455
    if (a->sa_family != b->sa_family) {
493✔
3456
        return a->sa_family - b->sa_family;
×
3457
    }
3458
    else {
3459
        if (a->sa_family == AF_INET) {
493✔
3460
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
429✔
3461
        }
3462
        else if (a->sa_family == AF_INET6) {
64✔
3463
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3464
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3465
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3466
        }
3467
    }
3468

3469
    return 0;
×
3470
}
139✔
3471

3472
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
896✔
3473
{
3474
    struct event *event = &h->event_storage_ping[index % event_storage_count];
896✔
3475
    event->host = h;
896✔
3476
    event->ping_index = index;
896✔
3477
    event->ev_time = ev_time;
896✔
3478
    ev_enqueue(&event_queue_ping, event);
896✔
3479

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

3484
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
872✔
3485
{
3486
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
872✔
3487
    event->host = h;
872✔
3488
    event->ping_index = index;
872✔
3489
    event->ev_time = ev_time;
872✔
3490
    ev_enqueue(&event_queue_timeout, event);
872✔
3491

3492
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3493
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3494
}
872✔
3495

3496
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
771✔
3497
{
3498
    return &h->event_storage_timeout[index % event_storage_count];
771✔
3499
}
3500

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

3503
  Function: ev_enqueue
3504

3505
  Enqueue an event
3506

3507
  The queue is sorted by event->ev_time, so that queue->first always points to
3508
  the earliest event.
3509

3510
  We start scanning the queue from the tail, because we assume
3511
  that new events mostly get inserted with a event time higher
3512
  than the others.
3513

3514
*************************************************************/
3515
void ev_enqueue(struct event_queue *queue, struct event *event)
1,768✔
3516
{
3517
    struct event *i;
3518
    struct event *i_prev;
3519

3520
    /* Empty list */
3521
    if (queue->last == NULL) {
1,768✔
3522
        event->ev_next = NULL;
1,375✔
3523
        event->ev_prev = NULL;
1,375✔
3524
        queue->first = event;
1,375✔
3525
        queue->last = event;
1,375✔
3526
        return;
1,375✔
3527
    }
3528

3529
    /* Insert on tail? */
3530
    if (event->ev_time - queue->last->ev_time >= 0) {
393✔
3531
        event->ev_next = NULL;
393✔
3532
        event->ev_prev = queue->last;
393✔
3533
        queue->last->ev_next = event;
393✔
3534
        queue->last = event;
393✔
3535
        return;
393✔
3536
    }
3537

3538
    /* Find insertion point */
3539
    i = queue->last;
×
3540
    while (1) {
3541
        i_prev = i->ev_prev;
×
3542
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3543
            event->ev_prev = i_prev;
×
3544
            event->ev_next = i;
×
3545
            i->ev_prev = event;
×
3546
            if (i_prev != NULL) {
×
3547
                i_prev->ev_next = event;
×
3548
            }
3549
            else {
3550
                queue->first = event;
×
3551
            }
3552
            return;
×
3553
        }
3554
        i = i_prev;
×
3555
    }
3556
}
520✔
3557

3558
/************************************************************
3559

3560
  Function: ev_dequeue
3561

3562
*************************************************************/
3563
struct event *ev_dequeue(struct event_queue *queue)
977✔
3564
{
3565
    struct event *dequeued;
3566

3567
    if (queue->first == NULL) {
977✔
3568
        return NULL;
×
3569
    }
3570
    dequeued = queue->first;
977✔
3571
    ev_remove(queue, dequeued);
977✔
3572

3573
    return dequeued;
977✔
3574
}
279✔
3575

3576
/************************************************************
3577

3578
  Function: ev_remove
3579

3580
*************************************************************/
3581
void ev_remove(struct event_queue *queue, struct event *event)
1,748✔
3582
{
3583
    if (queue->first == event) {
1,748✔
3584
        queue->first = event->ev_next;
1,739✔
3585
    }
511✔
3586
    if (queue->last == event) {
1,748✔
3587
        queue->last = event->ev_prev;
1,376✔
3588
    }
399✔
3589
    if (event->ev_prev) {
1,748✔
3590
        event->ev_prev->ev_next = event->ev_next;
9✔
3591
    }
3✔
3592
    if (event->ev_next) {
1,748✔
3593
        event->ev_next->ev_prev = event->ev_prev;
372✔
3594
    }
115✔
3595
    event->ev_prev = NULL;
1,748✔
3596
    event->ev_next = NULL;
1,748✔
3597
}
1,748✔
3598

3599
/************************************************************
3600

3601
  Function: return human_readable_time from current_time_ns as char
3602

3603
*************************************************************/
3604
char *get_timestamp_format(int64_t current_time_ns, int timestamp_format)
36✔
3605
{
3606
    char time_buffer[100];
3607
    char *time_result = malloc(120);
36✔
3608
    time_t current_time_s;
3609
    struct tm *local_time;
3610

3611
    if (time_result == NULL) {
36✔
NEW
3612
        return NULL;
×
3613
    }
3614

3615
    current_time_s = current_time_ns / 1000000000;
36✔
3616
    local_time = localtime(&current_time_s);
36✔
3617
    switch(timestamp_format) {
36✔
3618
        case 1:
4✔
3619
            // timestamp-format ctime
3620
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
6✔
3621
            snprintf(time_result, 120, "%s", time_buffer);
6✔
3622
            break;
6✔
3623
        case 2:
4✔
3624
            // timestamp-format iso
3625
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
6✔
3626
            snprintf(time_result, 120, "%s", time_buffer);
6✔
3627
            break;
6✔
3628
        case 3:
4✔
3629
            // timestamp-format rfc3339
3630
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
6✔
3631
            snprintf(time_result, 120, "%s", time_buffer);
6✔
3632
            break;
6✔
3633
        default:
12✔
3634
            snprintf(time_result, 120, "%.5f", (double)current_time_ns / 1e9);
18✔
3635
    }
6✔
3636

3637
    return time_result;
36✔
3638
}
12✔
3639

3640
/************************************************************
3641

3642
  Function: ms_since_midnight_utc
3643

3644
*************************************************************
3645

3646
  Input: int64_t: current UTC time in ns
3647

3648
  Output: uint32_t: current time in ms since midnight UTC
3649

3650
  Description:
3651

3652
  Return ICMP Timestamp value corresponding to the given time value.
3653
  The given time value must be in UTC.
3654

3655
*************************************************************/
3656
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
3657
{
3658
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
3659
}
3660

3661
/************************************************************
3662

3663
  Function: usage
3664

3665
*************************************************************
3666

3667
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3668

3669
  Description:
3670

3671
************************************************************/
3672

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