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

schweikert / fping / 12518190572

27 Dec 2024 03:40PM UTC coverage: 87.602% (-0.1%) from 87.747%
12518190572

push

github

schweikert
Prepare 5.3-rc3

1286 of 1468 relevant lines covered (87.6%)

274.19 hits per line

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

87.8
/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 "fping.h"
38
#include "config.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 131070 /* maximum number of hosts that -g can generate */
143

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

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

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

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

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

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

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

218
#define ICMP_UNREACH_MAXTYPE 15
219

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

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

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

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

267
#define EV_TYPE_PING 1
268
#define EV_TYPE_TIMEOUT 2
269

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

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

283
/*** globals ***/
284

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

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

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

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

313
unsigned int debugging = 0;
314

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

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

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

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

376
unsigned int fwmark = 0;
377

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

380
/*** forward declarations ***/
381

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

416
/************************************************************
417

418
  Function: p_setsockopt
419

420
*************************************************************
421

422
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
423

424
  Description:
425

426
  Elevates privileges to p_uid when required, calls
427
  setsockopt, and drops privileges back.
428

429
************************************************************/
430

431
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
12✔
432
    const void *optval, socklen_t optlen)
433
{
434
    const uid_t saved_uid = geteuid();
12✔
435
    int res;
436

437
    if (p_uid != saved_uid && seteuid(p_uid)) {
12✔
438
        perror("cannot elevate privileges for setsockopt");
×
439
    }
440

441
    res = setsockopt(sockfd, level, optname, optval, optlen);
12✔
442

443
    if (p_uid != saved_uid && seteuid(saved_uid)) {
12✔
444
        perror("fatal error: could not drop privileges after setsockopt");
×
445
        /* continuing would be a security hole */
446
        exit(4);
×
447
    }
448

449
    return res;
12✔
450
}
451

452
/************************************************************
453

454
  Function: main
455

456
*************************************************************
457

458
  Inputs:  int argc, char** argv
459

460
  Description:
461

462
  Main program entry point
463

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

466
int main(int argc, char **argv)
496✔
467
{
468
/* Debug: CPU Performance */
469
#if defined(DEBUG) || defined(_DEBUG)
470
    clock_t perf_cpu_start, perf_cpu_end;
471
    double perf_cpu_time_used;
472
    perf_cpu_start = clock();
473
#endif /* DEBUG || _DEBUG */
474

475
    int c;
476
    const uid_t suid = geteuid();
496✔
477
    int tos = 0;
496✔
478
    struct optparse optparse_state;
479
#ifdef USE_SIGACTION
480
    struct sigaction act;
481
#endif
482

483
    /* pre-parse -h/--help, so that we also can output help information
484
     * without trying to open the socket, which might fail */
485
    prog = argv[0];
496✔
486
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
496✔
487
        usage(0);
3✔
488
    }
1✔
489

490
    socket4 = open_ping_socket_ipv4(&socktype4);
494✔
491
#ifdef __linux__
492
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
493
     * structure is missing in the message.
494
     */
495
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
336✔
496
#endif
497

498
#ifdef IPV6
499
    socket6 = open_ping_socket_ipv6(&socktype6);
494✔
500
    /* if called (sym-linked) via 'fping6', imply '-6'
501
     * for backward compatibility */
502
    if (strstr(prog, "fping6")) {
494✔
503
        hints_ai_family = AF_INET6;
×
504
    }
505
#endif
506

507
    memset(&src_addr, 0, sizeof(src_addr));
494✔
508
#ifdef IPV6
509
    memset(&src_addr6, 0, sizeof(src_addr6));
494✔
510
#endif
511

512
    if (!suid && suid != getuid()) {
494✔
513
        /* *temporarily* drop privileges */
514
        if (seteuid(getuid()) == -1)
458✔
515
            perror("cannot setuid");
×
516
    }
132✔
517

518
    optparse_init(&optparse_state, argv);
494✔
519
    ident4 = ident6 = htons(getpid() & 0xFFFF);
494✔
520
    verbose_flag = 1;
494✔
521
    backoff_flag = 1;
494✔
522
    opterr = 1;
494✔
523

524
    /* get command line options */
525

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

578
    float opt_value_float;
579
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
1,371✔
580
        switch (c) {
810✔
581
        case '0':
74✔
582
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
92✔
583
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
15✔
584
                  timestamp_format_flag = 1;
3✔
585
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
13✔
586
                  timestamp_format_flag = 2;
3✔
587
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
10✔
588
                  timestamp_format_flag = 3;
3✔
589
                }else{
1✔
590
                  usage(1);
6✔
591
                }
592
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
82✔
593
                check_source_flag = 1;
9✔
594
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
69✔
595
#ifdef IPV6
596
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
25✔
597
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
598
                    exit(1);
2✔
599
                }
600
                hints_ai_family = AF_INET;
23✔
601
#endif
602
                icmp_request_typ = 13;
23✔
603
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
23✔
604
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
46✔
605
                print_tos_flag = 1;
23✔
606
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
25✔
607
                print_ttl_flag = 1;
20✔
608
            } else {
4✔
609
                usage(1);
×
610
            }
611
            break;
86✔
612
        case '4':
26✔
613
#ifdef IPV6
614
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
36✔
615
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
616
                exit(1);
3✔
617
            }
618
            hints_ai_family = AF_INET;
33✔
619
#endif
620
            break;
33✔
621
        case '6':
22✔
622
#ifdef IPV6
623
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
25✔
624
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
625
                exit(1);
5✔
626
            }
627
            hints_ai_family = AF_INET6;
20✔
628
#else
629
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
630
            exit(1);
631
#endif
632
            break;
20✔
633
        case 'M':
2✔
634
#ifdef IP_MTU_DISCOVER
635
            if (socket4 >= 0) {
2✔
636
                int val = IP_PMTUDISC_DO;
2✔
637
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
638
                    perror("setsockopt IP_MTU_DISCOVER");
639
                }
640
            }
641
#ifdef IPV6
642
            if (socket6 >= 0) {
2✔
643
                int val = IPV6_PMTUDISC_DO;
2✔
644
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
645
                    perror("setsockopt IPV6_MTU_DISCOVER");
646
                }
647
            }
648
#endif
649
#else
650
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
651
            exit(1);
652
#endif
653
            break;
2✔
654

655
        case 't':
14✔
656
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
19✔
657
                usage(1);
6✔
658
            if (opt_value_float < 0) {
15✔
659
                usage(1);
3✔
660
            }
1✔
661
            timeout = opt_value_float * 1000000;
13✔
662
            timeout_flag = 1;
13✔
663
            break;
13✔
664

665
        case 'r':
10✔
666
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
14✔
667
                usage(1);
6✔
668
            break;
10✔
669

670
        case 'i':
12✔
671
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
18✔
672
                usage(1);
6✔
673
            if (opt_value_float < 0) {
14✔
674
                usage(1);
3✔
675
            }
1✔
676
            interval = opt_value_float * 1000000;
12✔
677
            break;
12✔
678

679
        case 'p':
68✔
680
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
99✔
681
                usage(1);
6✔
682
            if (opt_value_float < 0) {
95✔
683
                usage(1);
3✔
684
            }
1✔
685
            perhost_interval = opt_value_float * 1000000;
93✔
686

687
            break;
93✔
688

689
        case 'c':
60✔
690
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
85✔
691
                usage(1);
6✔
692

693
            count_flag = 1;
81✔
694
            break;
81✔
695

696
        case 'C':
24✔
697
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
33✔
698
                usage(1);
6✔
699

700
            count_flag = 1;
29✔
701
            report_all_rtts_flag = 1;
29✔
702
            break;
29✔
703

704
        case 'b':
14✔
705
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
21✔
706
                usage(1);
6✔
707
            size_flag = 1;
17✔
708
            break;
17✔
709

710
        case 'h':
2✔
711
            usage(0);
3✔
712
            break;
1✔
713

714
        case 'q':
12✔
715
            verbose_flag = 0;
17✔
716
            quiet_flag = 1;
17✔
717
            break;
17✔
718

719
        case 'Q':
22✔
720
            verbose_flag = 0;
33✔
721
            quiet_flag = 1;
33✔
722
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
33✔
723
                usage(1);
6✔
724
            if (opt_value_float < 0) {
29✔
725
                usage(1);
3✔
726
            }
1✔
727
            report_interval = opt_value_float * 1e9;
27✔
728

729
            /* recognize keyword(s) after number, ignore everything else */
730
            {
731
                char *comma = strchr(optparse_state.optarg, ',');
27✔
732
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
27✔
733
                    cumulative_stats_flag = 1;
6✔
734
                }
2✔
735
            }
736

737
            break;
27✔
738

739
        case 'e':
6✔
740
            elapsed_flag = 1;
8✔
741
            break;
8✔
742

743
        case 'm':
744
            multif_flag = 1;
×
745
            break;
×
746

747
        case 'N':
2✔
748
            netdata_flag = 1;
3✔
749
            break;
3✔
750

751
        case 'n':
4✔
752
            name_flag = 1;
6✔
753
            if (rdns_flag) {
6✔
754
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
755
                exit(1);
3✔
756
            }
757
            break;
3✔
758

759
        case 'd':
6✔
760
            rdns_flag = 1;
9✔
761
            if (name_flag) {
9✔
762
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
763
                exit(1);
3✔
764
            }
765
            break;
6✔
766

767
        case 'A':
2✔
768
            addr_flag = 1;
3✔
769
            break;
3✔
770

771
        case 'B':
10✔
772
            if (!(backoff = atof(optparse_state.optarg)))
12✔
773
                usage(1);
6✔
774

775
            break;
8✔
776

777
        case 's':
6✔
778
            stats_flag = 1;
9✔
779
            break;
9✔
780

781
        case 'D':
14✔
782
            timestamp_flag = 1;
21✔
783
            break;
21✔
784

785
        case 'R':
4✔
786
            random_data_flag = 1;
5✔
787
            break;
5✔
788

789
        case 'l':
2✔
790
            loop_flag = 1;
3✔
791
            backoff_flag = 0;
3✔
792
            break;
3✔
793

794
        case 'u':
8✔
795
            unreachable_flag = 1;
12✔
796
            break;
12✔
797

798
        case 'a':
18✔
799
            alive_flag = 1;
26✔
800
            break;
26✔
801

802
        case 'H':
8✔
803
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
12✔
804
                usage(1);
6✔
805
            break;
8✔
806

807
#if defined(DEBUG) || defined(_DEBUG)
808
        case 'z':
809
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
810
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
811
                    usage(1);
812

813
            break;
814
#endif /* DEBUG || _DEBUG */
815

816
        case 'v':
4✔
817
            printf("%s: Version %s\n", prog, VERSION);
6✔
818
            exit(0);
6✔
819

820
        case 'x':
8✔
821
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
10✔
822
                usage(1);
6✔
823
            break;
6✔
824

825
        case 'X':
6✔
826
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
827
                usage(1);
6✔
828
            fast_reachable = 1;
5✔
829
            break;
5✔
830

831
        case 'f':
8✔
832
            filename = optparse_state.optarg;
12✔
833
            break;
12✔
834
#ifdef SO_MARK
835
        case 'k':
10✔
836
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
10✔
837
                usage(1);
4✔
838

839
            if (socket4 >= 0)
6✔
840
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
841
                    perror("fwmark ipv4");
4✔
842

843
#ifdef IPV6
844
            if (socket6 >= 0)
6✔
845
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
846
                    perror("fwmark ipv6");
4✔
847
#endif
848

849
            break;
6✔
850
#endif
851

852
        case 'g':
44✔
853
            /* use IP list generation */
854
            /* mutually exclusive with using file input or command line targets */
855
            generate_flag = 1;
62✔
856
            break;
62✔
857

858
        case 'S':
10✔
859
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
860
                src_addr_set = 1;
6✔
861
                break;
6✔
862
            }
863
#ifdef IPV6
864
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
865
                src_addr6_set = 1;
4✔
866
                break;
4✔
867
            }
868
#endif
869
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
870
            exit(1);
3✔
871

872
        case 'I':
873
#ifdef SO_BINDTODEVICE
874
            if (socket4 >= 0) {
875
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
876
                    perror("binding to specific interface (SO_BINTODEVICE)");
877
                    exit(1);
878
                }
879
            }
880
#ifdef IPV6
881
            if (socket6 >= 0) {
882
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
883
                    perror("binding to specific interface (SO_BINTODEVICE), IPV6");
884
                    exit(1);
885
                }
886
            }
887
#endif
888
#else
889
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
890
            exit(3);
891
            ;
892
#endif
893
            break;
894

895
        case 'T':
2✔
896
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
897
            break;
3✔
898

899
        case 'O':
10✔
900
            if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
15✔
901
                if (socket4 >= 0) {
9✔
902
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
903
                        perror("setting type of service octet IP_TOS");
×
904
                    }
905
                }
3✔
906
#if defined(IPV6) && defined(IPV6_TCLASS)
907
                if (socket6 >= 0) {
9✔
908
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
909
                        perror("setting type of service octet IPV6_TCLASS");
×
910
                    }
911
                }
3✔
912
#endif
913
            }
3✔
914
            else {
915
                usage(1);
6✔
916
            }
917
            break;
11✔
918

919
        case 'o':
6✔
920
            outage_flag = 1;
9✔
921
            break;
9✔
922

923
        case '?':
4✔
924
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
925
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
926
            exit(1);
6✔
927
            break;
928
        }
929
    }
404✔
930

931
    /* permanently drop privileges */
932
    if (suid != getuid() && setuid(getuid())) {
335✔
933
        perror("fatal: failed to permanently drop privileges");
×
934
        /* continuing would be a security hole */
935
        exit(4);
×
936
    }
937

938
    /* validate various option settings */
939

940
#ifndef IPV6
941
    if (socket4 < 0) {
942
        crash_and_burn("can't create socket (must run as root?)");
943
    }
944
#else
945
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
335✔
946
        crash_and_burn("can't create socket (must run as root?)");
×
947
    }
948
#endif
949

950
    if (ttl > 255) {
335✔
951
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
952
        exit(1);
3✔
953
    }
954

955
    if (unreachable_flag && alive_flag) {
332✔
956
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
957
        exit(1);
3✔
958
    }
959

960
    if (count_flag && loop_flag) {
329✔
961
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
962
        exit(1);
3✔
963
    }
964

965
#ifdef FPING_SAFE_LIMITS
966
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
326✔
967
        && getuid()) {
92✔
968
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
969
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
970
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
971
        exit(1);
6✔
972
    }
973
#endif
974

975
    if (ping_data_size > MAX_PING_DATA) {
320✔
976
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
977
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
978
        exit(1);
3✔
979
    }
980

981
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
317✔
982
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
983
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
984
        exit(1);
6✔
985
    }
986

987
    if (icmp_request_typ == 13 && size_flag != 0) {
311✔
988
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
989
        exit(1);
9✔
990
    }
991

992
    if (count_flag) {
302✔
993
        if (verbose_flag)
100✔
994
            per_recv_flag = 1;
59✔
995

996
        alive_flag = unreachable_flag = verbose_flag = 0;
100✔
997
    }
28✔
998

999
    if (loop_flag) {
302✔
1000
        if (!report_interval)
×
1001
            per_recv_flag = 1;
×
1002

1003
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1004
    }
1005

1006
    if (alive_flag || unreachable_flag || min_reachable)
302✔
1007
        verbose_flag = 0;
29✔
1008

1009
    trials = (count > retry + 1) ? count : retry + 1;
306✔
1010

1011
    /* auto-tune default timeout for count/loop modes
1012
     * see also github #32 */
1013
    if (loop_flag || count_flag) {
306✔
1014
        if (!timeout_flag) {
100✔
1015
            timeout = perhost_interval;
95✔
1016
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
95✔
1017
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1018
            }
1019
        }
27✔
1020
    }
28✔
1021

1022
#if defined(DEBUG) || defined(_DEBUG)
1023
    if (debugging & DBG_TRACE)
1024
        trace_flag = 1;
1025

1026
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1027
        randomly_lose_flag = 1;
1028
        lose_factor = 1; /* ie, 1/4 */
1029
    }
1030

1031
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1032
        randomly_lose_flag = 1;
1033
        lose_factor = 5; /* ie, 3/4 */
1034
    }
1035

1036
    if (debugging & DBG_PRINT_PER_SYSTEM)
1037
        print_per_system_flag = 1;
1038

1039
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1040
        report_all_rtts_flag = 1;
1041

1042
    if (trace_flag) {
1043
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1044
            prog, count, retry, interval / 1e6);
1045
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1046
            perhost_interval / 1e6, timeout / 1e6);
1047
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1048
            ping_data_size, trials);
1049

1050
        if (verbose_flag)
1051
            fprintf(stderr, "  verbose_flag set\n");
1052
        if (multif_flag)
1053
            fprintf(stderr, "  multif_flag set\n");
1054
        if (name_flag)
1055
            fprintf(stderr, "  name_flag set\n");
1056
        if (addr_flag)
1057
            fprintf(stderr, "  addr_flag set\n");
1058
        if (stats_flag)
1059
            fprintf(stderr, "  stats_flag set\n");
1060
        if (unreachable_flag)
1061
            fprintf(stderr, "  unreachable_flag set\n");
1062
        if (alive_flag)
1063
            fprintf(stderr, "  alive_flag set\n");
1064
        if (elapsed_flag)
1065
            fprintf(stderr, "  elapsed_flag set\n");
1066
        if (version_flag)
1067
            fprintf(stderr, "  version_flag set\n");
1068
        if (count_flag)
1069
            fprintf(stderr, "  count_flag set\n");
1070
        if (loop_flag)
1071
            fprintf(stderr, "  loop_flag set\n");
1072
        if (backoff_flag)
1073
            fprintf(stderr, "  backoff_flag set\n");
1074
        if (per_recv_flag)
1075
            fprintf(stderr, "  per_recv_flag set\n");
1076
        if (report_all_rtts_flag)
1077
            fprintf(stderr, "  report_all_rtts_flag set\n");
1078
        if (randomly_lose_flag)
1079
            fprintf(stderr, "  randomly_lose_flag set\n");
1080
        if (print_per_system_flag)
1081
            fprintf(stderr, "  print_per_system_flag set\n");
1082
        if (outage_flag)
1083
            fprintf(stderr, "  outage_flag set\n");
1084
        if (netdata_flag)
1085
            fprintf(stderr, "  netdata_flag set\n");
1086
    }
1087
#endif /* DEBUG || _DEBUG */
1088

1089
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1090
    if (ttl > 0) {
306✔
1091
        if (socket4 >= 0) {
3✔
1092
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1093
                perror("setting time to live");
×
1094
            }
1095
        }
1✔
1096
#ifdef IPV6
1097
        if (socket6 >= 0) {
3✔
1098
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1099
                perror("setting time to live");
×
1100
            }
1101
        }
1✔
1102
#endif
1103
    }
1✔
1104

1105
#if HAVE_SO_TIMESTAMPNS
1106
    {
1107
        int opt = 1;
222✔
1108
        if (socket4 >= 0) {
222✔
1109
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
222✔
1110
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1111
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1112
                }
1113
            }
1114
        }
1115
#ifdef IPV6
1116
        if (socket6 >= 0) {
222✔
1117
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
222✔
1118
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1119
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1120
                }
1121
            }
1122
        }
1123
#endif
1124
    }
1125
#endif
1126

1127
    update_current_time();
306✔
1128
    start_time = current_time_ns;
306✔
1129

1130
    /* handle host names supplied on command line or in a file */
1131
    /* if the generate_flag is on, then generate the IP list */
1132

1133
    argv = &argv[optparse_state.optind];
306✔
1134
    argc -= optparse_state.optind;
306✔
1135

1136
    /* calculate how many ping can be in-flight per host */
1137
    if (count_flag) {
306✔
1138
        event_storage_count = count;
105✔
1139
    }
33✔
1140
    else if (loop_flag) {
201✔
1141
        if (perhost_interval > timeout) {
×
1142
            event_storage_count = 1;
×
1143
        }
1144
        else {
1145
            event_storage_count = 1 + timeout / perhost_interval;
×
1146
        }
1147
    }
1148
    else {
1149
        event_storage_count = 1;
201✔
1150
    }
1151

1152
    /* file and generate are mutually exclusive */
1153
    /* file and command line are mutually exclusive */
1154
    /* generate requires command line parameters beyond the switches */
1155
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
306✔
1156
        usage(1);
9✔
1157

1158
    /* if no conditions are specified, then assume input from stdin */
1159
    if (!*argv && !filename && !generate_flag)
298✔
1160
        filename = "-";
3✔
1161

1162
    if (*argv && !generate_flag) {
298✔
1163
        while (*argv) {
512✔
1164
            add_name(*argv);
285✔
1165
            ++argv;
285✔
1166
        }
1167
    }
57✔
1168
    else if (filename) {
71✔
1169
        FILE *ping_file;
1170
        char line[132];
1171
        char host[132];
1172

1173
        if (strcmp(filename, "-") == 0)
15✔
1174
            ping_file = fdopen(0, "r");
6✔
1175
        else
1176
            ping_file = fopen(filename, "r");
9✔
1177

1178
        if (!ping_file)
15✔
1179
            errno_crash_and_burn("fopen");
3✔
1180

1181
        while (fgets(line, sizeof(line), ping_file)) {
43✔
1182
            if (sscanf(line, "%s", host) != 1)
30✔
1183
                continue;
3✔
1184

1185
            if ((!*host) || (host[0] == '#')) /* magic to avoid comments */
27✔
1186
                continue;
3✔
1187

1188
            add_name(host);
24✔
1189
        }
1190

1191
        fclose(ping_file);
13✔
1192
    }
5✔
1193
    else if (*argv && generate_flag) {
56✔
1194
        if (argc == 1) {
56✔
1195
            /* one target: we expect a cidr range (n.n.n.n/m) */
1196
            add_cidr(argv[0]);
32✔
1197
        }
10✔
1198
        else if (argc == 2) {
24✔
1199
            add_range(argv[0], argv[1]);
21✔
1200
        }
5✔
1201
        else {
1202
            usage(1);
3✔
1203
        }
1204
    }
16✔
1205
    else {
1206
        usage(1);
×
1207
    }
1208

1209
    if (!num_hosts) {
268✔
1210
        exit(num_noaddress ? 2 : 1);
17✔
1211
    }
1212

1213
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
251✔
1214
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
16✔
1215
    }
2✔
1216
#ifdef IPV6
1217
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
249✔
1218
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
14✔
1219
    }
1220
#endif
1221

1222
    /* allocate and initialize array to map host nr to host_entry */
1223
    {
1224
        struct event *cursor = event_queue_ping.first;
247✔
1225
        int i = 0;
247✔
1226
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
247✔
1227
        if (!table)
247✔
1228
            crash_and_burn("Can't malloc array of hosts");
×
1229
        /* initialize table of hosts. we know that we have ping events scheduled
1230
         * for each of them */
1231
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
582✔
1232
            table[i] = cursor->host;
335✔
1233
            cursor->host->i = i;
335✔
1234
            i++;
335✔
1235
        }
93✔
1236
    }
1237

1238
    init_ping_buffer_ipv4(ping_data_size);
247✔
1239
#ifdef IPV6
1240
    init_ping_buffer_ipv6(ping_data_size);
247✔
1241
#endif
1242

1243
#ifdef USE_SIGACTION
1244
    memset(&act, 0, sizeof(act));
247✔
1245
    act.sa_handler = signal_handler;
247✔
1246
    sigemptyset(&act.sa_mask);
247✔
1247
    sigaddset(&act.sa_mask, SIGINT);
247✔
1248
    sigaddset(&act.sa_mask, SIGQUIT);
247✔
1249
    act.sa_flags = SA_RESTART;
247✔
1250
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
247✔
1251
        crash_and_burn("failure to set signal handler");
24✔
1252
    }
24✔
1253
#else
1254
    signal(SIGINT, signal_handler);
1255
    signal(SIGQUIT, signal_handler);
1256
#endif
1257
    setlinebuf(stdout);
223✔
1258

1259
    if (report_interval) {
223✔
1260
        next_report_time = current_time_ns + report_interval;
24✔
1261
    }
8✔
1262

1263
    last_send_time = 0;
223✔
1264

1265
    seqmap_init();
223✔
1266

1267
    /* main loop */
1268
    main_loop();
223✔
1269

1270
/* Debug: CPU Performance */
1271
#if defined(DEBUG) || defined(_DEBUG)
1272
    perf_cpu_end = clock();
1273
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1274
    printf("[DEBUG] CPU time used: %f sec", perf_cpu_time_used);
1275
#endif /* DEBUG || _DEBUG */
1276

1277
    finish();
223✔
1278

1279
    return 0;
49✔
1280
}
1281

1282
static inline int64_t timespec_ns(struct timespec *a)
4,133✔
1283
{
1284
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
4,133✔
1285
}
1286

1287
void add_cidr(char *addr)
31✔
1288
{
1289
    char *addr_end;
1290
    char *mask_str;
1291
    unsigned long mask;
1292
    unsigned long bitmask;
1293
    int ret;
1294
    struct addrinfo addr_hints;
1295
    struct addrinfo *addr_res;
1296
    unsigned long net_addr;
1297
    unsigned long net_last;
1298

1299
    /* Split address from mask */
1300
    addr_end = strchr(addr, '/');
31✔
1301
    if (addr_end == NULL) {
31✔
1302
        usage(1);
3✔
1303
    }
1✔
1304
    *addr_end = '\0';
29✔
1305
    mask_str = addr_end + 1;
29✔
1306
    mask = atoi(mask_str);
29✔
1307

1308
    /* parse address (IPv4 only) */
1309
    memset(&addr_hints, 0, sizeof(struct addrinfo));
29✔
1310
    addr_hints.ai_family = AF_UNSPEC;
29✔
1311
    addr_hints.ai_flags = AI_NUMERICHOST;
29✔
1312
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
29✔
1313
    if (ret) {
29✔
1314
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
3✔
1315
        exit(1);
3✔
1316
    }
1317
    if (addr_res->ai_family != AF_INET) {
26✔
1318
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
5✔
1319
        exit(1);
5✔
1320
    }
1321
    net_addr = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
21✔
1322
    freeaddrinfo(addr_res);
21✔
1323

1324
    /* check mask */
1325
    if (mask < 1 || mask > 32) {
21✔
1326
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %s)\n", prog, mask_str);
6✔
1327
        exit(1);
6✔
1328
    }
1329

1330
    /* convert mask integer from 1 to 32 to a bitmask */
1331
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
15✔
1332

1333
    /* calculate network range */
1334
    net_addr &= bitmask;
15✔
1335
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
15✔
1336

1337
    /* exclude network and broadcast address for regular prefixes */
1338
    if (mask < 31) {
15✔
1339
        net_last--;
9✔
1340
        net_addr++;
9✔
1341
    }
3✔
1342

1343
    /* add all hosts in that network (net_addr and net_last inclusive) */
1344
    add_addr_range_ipv4(net_addr, net_last);
15✔
1345
}
13✔
1346

1347
void add_range(char *start, char *end)
21✔
1348
{
1349
    struct addrinfo addr_hints;
1350
    struct addrinfo *addr_res;
1351
    unsigned long start_long;
1352
    unsigned long end_long;
1353
    int ret;
1354

1355
    /* parse start address (IPv4 only) */
1356
    memset(&addr_hints, 0, sizeof(struct addrinfo));
21✔
1357
    addr_hints.ai_family = AF_UNSPEC;
21✔
1358
    addr_hints.ai_flags = AI_NUMERICHOST;
21✔
1359
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
21✔
1360
    if (ret) {
21✔
1361
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
3✔
1362
        exit(1);
3✔
1363
    }
1364
    if (addr_res->ai_family != AF_INET) {
18✔
1365
        freeaddrinfo(addr_res);
4✔
1366
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
4✔
1367
        exit(1);
4✔
1368
    }
1369
    start_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
14✔
1370

1371
    /* parse end address (IPv4 only) */
1372
    memset(&addr_hints, 0, sizeof(struct addrinfo));
14✔
1373
    addr_hints.ai_family = AF_UNSPEC;
14✔
1374
    addr_hints.ai_flags = AI_NUMERICHOST;
14✔
1375
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
14✔
1376
    if (ret) {
14✔
1377
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
3✔
1378
        exit(1);
3✔
1379
    }
1380
    if (addr_res->ai_family != AF_INET) {
11✔
1381
        freeaddrinfo(addr_res);
2✔
1382
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
2✔
1383
        exit(1);
2✔
1384
    }
1385
    end_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
9✔
1386
    freeaddrinfo(addr_res);
9✔
1387

1388
    /* add IPv4 addresses from closed interval [start_long,end_long] */
1389
    add_addr_range_ipv4(start_long, end_long);
9✔
1390
}
7✔
1391

1392
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
24✔
1393
{
1394
    /* check if generator limit is exceeded */
1395
    if (end_long >= start_long + MAX_GENERATE) {
24✔
1396
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1397
        exit(1);
6✔
1398
    }
1399

1400
    /* generate */
1401
    for (; start_long <= end_long; start_long++) {
66✔
1402
        struct in_addr in_addr_tmp;
1403
        char buffer[20];
1404
        in_addr_tmp.s_addr = htonl(start_long);
48✔
1405
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
48✔
1406
        add_name(buffer);
48✔
1407
    }
16✔
1408
}
18✔
1409

1410
void main_loop()
235✔
1411
{
1412
    int64_t lt;
1413
    int64_t wait_time_ns;
1414
    struct event *event;
1415
    struct host_entry *h;
1416

1417
    while (event_queue_ping.first || event_queue_timeout.first) {
1,410✔
1418
        dbg_printf("%s", "# main_loop\n");
1419

1420
        /* timeout event ? */
1421
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,180✔
1422
            event = ev_dequeue(&event_queue_timeout);
89✔
1423
            h = event->host;
89✔
1424

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

1427
            stats_add(h, event->ping_index, 0, -1);
89✔
1428

1429
            if (per_recv_flag) {
89✔
1430
                if (timestamp_flag) {
19✔
1431
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1432
                }
1433
                printf("%-*s : [%d], timed out",
19✔
1434
                    max_hostname_len, h->host, event->ping_index);
5✔
1435
                if (h->num_recv > 0) {
19✔
1436
                    printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1437
                }
1438
                else {
1439
                    printf(" (NaN avg, ");
19✔
1440
                }
1441
                if (h->num_recv <= h->num_sent) {
19✔
1442
                    printf("%d%% loss)",
19✔
1443
                        ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1444
                }
5✔
1445
                else {
1446
                    printf("%d%% return)",
×
1447
                        (h->num_recv_total * 100) / h->num_sent);
×
1448
                }
1449
                printf("\n");
19✔
1450
            }
5✔
1451

1452
            /* do we need to send a retry? */
1453
            if (!loop_flag && !count_flag) {
89✔
1454
                if (h->num_sent < retry + 1) {
46✔
1455
                    if (backoff_flag) {
30✔
1456
                        h->timeout *= backoff;
30✔
1457
                    }
6✔
1458
                    send_ping(h, event->ping_index);
30✔
1459
                }
6✔
1460
            }
10✔
1461

1462
            /* note: we process first timeout events, because we might need to
1463
             * wait to process ping events, while we for sure never need to
1464
             * wait for timeout events.
1465
             */
1466
            continue;
89✔
1467
        }
1468

1469
        /* ping event ? */
1470
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,280✔
1471
            /* Make sure that we don't ping more than once every "interval" */
1472
            lt = current_time_ns - last_send_time;
812✔
1473
            if (lt < interval)
812✔
1474
                goto wait_for_reply;
177✔
1475

1476
            /* Dequeue the event */
1477
            event = ev_dequeue(&event_queue_ping);
635✔
1478
            h = event->host;
635✔
1479

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

1482
            /* Send the ping */
1483
            send_ping(h, event->ping_index);
635✔
1484

1485
            /* Loop and count mode: schedule next ping */
1486
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
635✔
1487
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
315✔
1488
            }
101✔
1489
        }
189✔
1490

1491
    wait_for_reply:
442✔
1492

1493
        /* When is the next ping next event? */
1494
        wait_time_ns = -1;
1,091✔
1495
        if (event_queue_ping.first) {
1,091✔
1496
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
801✔
1497
            if (wait_time_ns < 0)
801✔
1498
                wait_time_ns = 0;
364✔
1499
            /* make sure that we wait enough, so that the inter-ping delay is
1500
             * bigger than 'interval' */
1501
            if (wait_time_ns < interval) {
801✔
1502
                lt = current_time_ns - last_send_time;
368✔
1503
                if (lt < interval) {
368✔
1504
                    wait_time_ns = interval - lt;
368✔
1505
                }
142✔
1506
            }
142✔
1507

1508
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1509
        }
253✔
1510

1511
        /* When is the next timeout event? */
1512
        if (event_queue_timeout.first) {
1,091✔
1513
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
702✔
1514
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
702✔
1515
                wait_time_ns = wait_time_timeout;
299✔
1516
                if (wait_time_ns < 0) {
299✔
1517
                    wait_time_ns = 0;
×
1518
                }
1519
            }
75✔
1520

1521
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1522
        }
204✔
1523

1524
        /* When is the next report due? */
1525
        if (report_interval && (loop_flag || count_flag)) {
1,091✔
1526
            int64_t wait_time_next_report = next_report_time - current_time_ns;
203✔
1527
            if (wait_time_next_report < wait_time_ns) {
203✔
1528
                wait_time_ns = wait_time_next_report;
69✔
1529
                if (wait_time_ns < 0) {
69✔
1530
                    wait_time_ns = 0;
×
1531
                }
1532
            }
23✔
1533

1534
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1535
        }
65✔
1536

1537
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1538
        if (wait_time_ns == -1) {
1,091✔
1539
            break;
2✔
1540
        }
1541

1542
        /* end of loop was requested by interrupt signal handler */
1543
        if (finish_requested) {
1,089✔
1544
            break;
3✔
1545
        }
1546

1547
        /* Receive replies */
1548
        /* (this is what sleeps during each loop iteration) */
1549
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1550
        if (wait_for_reply(wait_time_ns)) {
1,086✔
1551
            while (wait_for_reply(0))
997✔
1552
                ; /* process other replies in the queue */
1553
        }
172✔
1554

1555
        update_current_time();
1,086✔
1556

1557
        if (status_snapshot) {
1,086✔
1558
            status_snapshot = 0;
×
1559
            print_per_system_splits();
×
1560
        }
1561

1562
        /* Print report */
1563
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,086✔
1564
            if (netdata_flag)
42✔
1565
                print_netdata();
3✔
1566
            else
1567
                print_per_system_splits();
39✔
1568

1569
            while (current_time_ns >= next_report_time) {
84✔
1570
                next_report_time += report_interval;
42✔
1571
            }
1572
        }
14✔
1573
    }
1574
}
235✔
1575

1576
/************************************************************
1577

1578
  Function: signal_handler
1579

1580
*************************************************************
1581

1582
  Inputs:  int signum
1583

1584
  Description:
1585

1586
  SIGQUIT signal handler - set flag and return
1587
  SIGINT signal handler - set flag and return
1588

1589
************************************************************/
1590

1591
void signal_handler(int signum)
×
1592
{
1593
    switch (signum) {
×
1594
    case SIGINT:
1595
        finish_requested = 1;
×
1596
        break;
×
1597

1598
    case SIGQUIT:
1599
        status_snapshot = 1;
×
1600
        break;
×
1601
    }
1602
}
×
1603

1604
/************************************************************
1605

1606
  Function: update_current_time
1607

1608
*************************************************************/
1609

1610
void update_current_time()
3,323✔
1611
{
1612
    clock_gettime(CLOCKID, &current_time);
3,323✔
1613
    current_time_ns = timespec_ns(&current_time);
3,323✔
1614
}
3,323✔
1615

1616
/************************************************************
1617

1618
  Function: finish
1619

1620
*************************************************************
1621

1622
  Inputs:  void (none)
1623

1624
  Description:
1625

1626
  Main program clean up and exit point
1627

1628
************************************************************/
1629

1630
void finish()
235✔
1631
{
1632
    int i;
1633
    HOST_ENTRY *h;
1634

1635
    update_current_time();
235✔
1636
    end_time = current_time_ns;
235✔
1637

1638
    /* tot up unreachables */
1639
    for (i = 0; i < num_hosts; i++) {
570✔
1640
        h = table[i];
335✔
1641

1642
        if (!h->num_recv) {
335✔
1643
            num_unreachable++;
46✔
1644

1645
            if (verbose_flag || unreachable_flag) {
46✔
1646
                printf("%s", h->host);
10✔
1647

1648
                if (verbose_flag)
10✔
1649
                    printf(" is unreachable");
7✔
1650

1651
                printf("\n");
10✔
1652
            }
2✔
1653
        }
12✔
1654
    }
93✔
1655

1656
    if (count_flag || loop_flag)
235✔
1657
        print_per_system_stats();
100✔
1658
#if defined(DEBUG) || defined(_DEBUG)
1659
    else if (print_per_system_flag)
1660
        print_per_system_stats();
1661
#endif /* DEBUG || _DEBUG */
1662

1663
    if (stats_flag)
235✔
1664
        print_global_stats();
9✔
1665

1666
    if (min_reachable) {
235✔
1667
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
1668
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1669
            exit(0);
6✔
1670
        }
1671
        else {
1672
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
1673
            exit(1);
3✔
1674
        }
1675
    }
1676

1677
    if (num_noaddress)
226✔
1678
        exit(2);
3✔
1679
    else if (num_alive != num_hosts)
223✔
1680
        exit(1);
25✔
1681

1682
    exit(0);
198✔
1683
}
1684

1685
/************************************************************
1686

1687
  Function: print_per_system_stats
1688

1689
*************************************************************
1690

1691
  Inputs:  void (none)
1692

1693
  Description:
1694

1695

1696
************************************************************/
1697

1698
void print_per_system_stats(void)
100✔
1699
{
1700
    int i, j, avg, outage_ms;
1701
    HOST_ENTRY *h;
1702
    int64_t resp;
1703

1704
    if (verbose_flag || per_recv_flag)
100✔
1705
        fprintf(stderr, "\n");
59✔
1706

1707
    for (i = 0; i < num_hosts; i++) {
234✔
1708
        h = table[i];
134✔
1709
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
1710

1711
        if (report_all_rtts_flag) {
134✔
1712
            for (j = 0; j < h->num_sent; j++) {
248✔
1713
                if ((resp = h->resp_times[j]) >= 0)
204✔
1714
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
1715
                else
1716
                    fprintf(stderr, " -");
8✔
1717
            }
64✔
1718

1719
            fprintf(stderr, "\n");
44✔
1720
        }
12✔
1721
        else {
1722
            if (h->num_recv <= h->num_sent) {
90✔
1723
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
1724
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
1725

1726
                if (outage_flag) {
90✔
1727
                    /* Time outage total */
1728
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
1729
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
1730
                }
3✔
1731
            }
26✔
1732
            else {
1733
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
1734
                    h->num_sent, h->num_recv,
1735
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
1736
            }
1737

1738
            if (h->num_recv) {
90✔
1739
                avg = h->total_time / h->num_recv;
79✔
1740
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
1741
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
1742
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
1743
            }
23✔
1744

1745
            fprintf(stderr, "\n");
90✔
1746
        }
1747
    }
38✔
1748
}
100✔
1749

1750
/************************************************************
1751

1752
  Function: print_netdata
1753

1754
*************************************************************
1755

1756
  Inputs:  void (none)
1757

1758
  Description:
1759

1760

1761
************************************************************/
1762

1763
void print_netdata(void)
3✔
1764
{
1765
    static int sent_charts = 0;
1766

1767
    int i;
1768
    int64_t avg;
1769
    HOST_ENTRY *h;
1770

1771
    for (i = 0; i < num_hosts; i++) {
6✔
1772
        h = table[i];
3✔
1773

1774
        if (!sent_charts) {
3✔
1775
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1776
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
1777
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
1778
        }
1✔
1779

1780
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
1781
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
1782
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
1783
        printf("END\n");
3✔
1784

1785
        if (!sent_charts) {
3✔
1786
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1787
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
1788
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
1789
        }
1✔
1790

1791
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
1792
        /*
1793
        if( h->num_recv_i <= h->num_sent_i )
1794
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
1795
        else
1796
            printf("SET lost = 0\n");
1797
*/
1798

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

1802
        if (!sent_charts) {
3✔
1803
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1804
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
1805
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
1806
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
1807
        }
1✔
1808

1809
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
1810
        if (h->num_recv_i) {
3✔
1811
            avg = h->total_time_i / h->num_recv_i;
3✔
1812
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
1813
            printf("SET avg = %" PRId64 "\n", avg);
3✔
1814
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
1815
        }
1✔
1816
        printf("END\n");
3✔
1817

1818
        stats_reset_interval(h);
3✔
1819
    }
1✔
1820

1821
    sent_charts = 1;
3✔
1822
}
3✔
1823

1824
/************************************************************
1825

1826
  Function: print_per_system_splits
1827

1828
*************************************************************
1829

1830
  Inputs:  void (none)
1831

1832
  Description:
1833

1834

1835
************************************************************/
1836

1837
void print_per_system_splits(void)
39✔
1838
{
1839
    int i, avg, outage_ms_i;
1840
    HOST_ENTRY *h;
1841
    struct tm *curr_tm;
1842

1843
    if (verbose_flag || per_recv_flag)
39✔
1844
        fprintf(stderr, "\n");
×
1845

1846
    update_current_time();
39✔
1847
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
1848
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
1849
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
1850

1851
    for (i = 0; i < num_hosts; i++) {
78✔
1852
        h = table[i];
39✔
1853
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
1854

1855
        if (h->num_recv_i <= h->num_sent_i) {
39✔
1856
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
1857
                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✔
1858

1859
            if (outage_flag) {
39✔
1860
                /* Time outage  */
1861
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
1862
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
1863
            }
4✔
1864
        }
13✔
1865
        else {
1866
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
1867
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
1868
        }
1869

1870
        if (h->num_recv_i) {
39✔
1871
            avg = h->total_time_i / h->num_recv_i;
27✔
1872
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
1873
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
1874
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
1875
        }
9✔
1876

1877
        fprintf(stderr, "\n");
39✔
1878
        if (!cumulative_stats_flag) {
39✔
1879
            stats_reset_interval(h);
27✔
1880
        }
9✔
1881
    }
13✔
1882
}
39✔
1883

1884
/************************************************************
1885

1886
  Function: print_global_stats
1887

1888
*************************************************************
1889

1890
  Inputs:  void (none)
1891

1892
  Description:
1893

1894

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

1897
void print_global_stats(void)
9✔
1898
{
1899
    fprintf(stderr, "\n");
9✔
1900
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
1901
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
1902
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
1903
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
1904
    fprintf(stderr, "\n");
9✔
1905
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
1906
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
1907
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
1908
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
1909
    fprintf(stderr, "\n");
9✔
1910

1911
    if (total_replies == 0) {
9✔
1912
        min_reply = 0;
3✔
1913
        max_reply = 0;
3✔
1914
        total_replies = 1;
3✔
1915
        sum_replies = 0;
3✔
1916
    }
1✔
1917

1918
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
1919
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
1920
        sprint_tm(sum_replies / total_replies));
3✔
1921
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
1922
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
1923
        (end_time - start_time) / 1e9);
9✔
1924
    fprintf(stderr, "\n");
9✔
1925
}
9✔
1926

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

1929
  Function: send_ping
1930

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

1933
  Inputs:  int s, HOST_ENTRY *h
1934

1935
  Description:
1936

1937
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
1938
  will be added on by the kernel.  The ID field is our UNIX process ID,
1939
  and the sequence number is an index into an array of outstanding
1940
  ping requests. The sequence number will later be used to quickly
1941
  figure out who the ping reply came from.
1942

1943
************************************************************/
1944

1945
int send_ping(HOST_ENTRY *h, int index)
665✔
1946
{
1947
    int n;
1948
    int myseq;
1949
    int ret = 1;
665✔
1950
    uint8_t proto = ICMP_ECHO;
665✔
1951

1952
    update_current_time();
665✔
1953
    h->last_send_time = current_time_ns;
665✔
1954
    myseq = seqmap_add(h->i, index, current_time_ns);
665✔
1955

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

1958
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
665✔
1959
        if(icmp_request_typ == 13)
584✔
1960
            proto = ICMP_TSTAMP;
14✔
1961
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
584✔
1962
    }
180✔
1963
#ifdef IPV6
1964
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
81✔
1965
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
81✔
1966
    }
15✔
1967
#endif
1968
    else {
1969
        return 0;
×
1970
    }
1971

1972
    /* error sending? */
1973
    if (
470✔
1974
        (n < 0)
195✔
1975
#if defined(EHOSTDOWN)
1976
        && errno != EHOSTDOWN
201✔
1977
#endif
1978
    ) {
1979
        if (verbose_flag) {
6✔
1980
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
×
1981
        }
1982
        else {
1983
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
1984
        }
1985

1986
        h->num_sent++;
6✔
1987
        h->num_sent_i++;
6✔
1988
        if (!loop_flag)
6✔
1989
            h->resp_times[index] = RESP_ERROR;
6✔
1990

1991
        ret = 0;
6✔
1992
    }
1993
    else {
1994
        /* schedule timeout */
1995
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
659✔
1996

1997
        /* mark this trial as outstanding */
1998
        if (!loop_flag) {
659✔
1999
            h->resp_times[index] = RESP_WAITING;
659✔
2000
        }
195✔
2001
    }
2002

2003
    num_pingsent++;
665✔
2004
    last_send_time = h->last_send_time;
665✔
2005

2006
    return (ret);
665✔
2007
}
195✔
2008

2009
int socket_can_read(struct timeval *timeout)
2,083✔
2010
{
2011
    int nfound;
2012
    fd_set readset;
2013
    int socketmax;
2014

2015
#ifndef IPV6
2016
    socketmax = socket4;
2017
#else
2018
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,083✔
2019
#endif
2020

2021
select_again:
1,572✔
2022
    FD_ZERO(&readset);
14,659✔
2023
    if (socket4 >= 0)
2,083✔
2024
        FD_SET(socket4, &readset);
2,083✔
2025
#ifdef IPV6
2026
    if (socket6 >= 0)
2,083✔
2027
        FD_SET(socket6, &readset);
2,083✔
2028
#endif
2029

2030
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,083✔
2031
    if (nfound < 0) {
2,083✔
2032
        if (errno == EINTR) {
×
2033
            /* interrupted system call: redo the select */
2034
            goto select_again;
×
2035
        }
2036
        else {
2037
            perror("select");
×
2038
        }
2039
    }
2040

2041
    if (nfound > 0) {
2,083✔
2042
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
997✔
2043
            return socket4;
837✔
2044
        }
2045
#ifdef IPV6
2046
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
160✔
2047
            return socket6;
160✔
2048
        }
2049
#endif
2050
    }
2051

2052
    return -1;
1,086✔
2053
}
511✔
2054

2055
int receive_packet(int64_t wait_time,
2,083✔
2056
#if HAVE_SO_TIMESTAMPNS
2057
    int64_t *reply_timestamp,
2058
#else
2059
    int64_t *reply_timestamp __attribute__((unused)),
2060
#endif
2061
    struct sockaddr *reply_src_addr,
2062
    size_t reply_src_addr_len,
2063
    char *reply_buf,
2064
    size_t reply_buf_len)
2065
{
2066
    struct timeval to;
2067
    int s = 0;
2,083✔
2068
    int recv_len;
2069
    static unsigned char msg_control[40];
2070
    struct iovec msg_iov = {
3,105✔
2071
        reply_buf,
511✔
2072
        reply_buf_len
511✔
2073
    };
2074
    struct msghdr recv_msghdr = {0};
2,083✔
2075
    recv_msghdr.msg_name = reply_src_addr;
2,083✔
2076
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,083✔
2077
    recv_msghdr.msg_iov = &msg_iov;
2,083✔
2078
    recv_msghdr.msg_iovlen = 1;
2,083✔
2079
    recv_msghdr.msg_control = &msg_control;
2,083✔
2080
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,083✔
2081
#if HAVE_SO_TIMESTAMPNS
2082
    struct cmsghdr *cmsg;
2083
#endif
2084

2085
    /* Wait for a socket to become ready */
2086
    if (wait_time) {
2,083✔
2087
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,086✔
2088
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,086✔
2089
    }
324✔
2090
    else {
2091
        to.tv_sec = 0;
997✔
2092
        to.tv_usec = 0;
997✔
2093
    }
2094
    s = socket_can_read(&to);
2,083✔
2095
    if (s == -1) {
2,083✔
2096
        return 0; /* timeout */
1,086✔
2097
    }
2098

2099
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
997✔
2100
    if (recv_len <= 0) {
997✔
2101
        return 0;
×
2102
    }
2103

2104
#if HAVE_SO_TIMESTAMPNS
2105
    /* ancilliary data */
2106
    {
2107
        struct timespec reply_timestamp_ts;
2108
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,215✔
2109
             cmsg != NULL;
810✔
2110
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
810✔
2111
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
810✔
2112
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
810✔
2113
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
810✔
2114
            }
2115
        }
2116
    }
2117
#endif
2118

2119
#if defined(DEBUG) || defined(_DEBUG)
2120
    if (randomly_lose_flag) {
2121
        if ((random() & 0x07) <= lose_factor)
2122
            return 0;
2123
    }
2124
#endif
2125

2126
    return recv_len;
997✔
2127
}
511✔
2128

2129
/* stats_add: update host statistics for a single packet that was received (or timed out)
2130
 * h: host entry to update
2131
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2132
 * success: 1 if response received, 0 otherwise
2133
 * latency: response time, in ns
2134
 */
2135
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
659✔
2136
{
2137
    /* sent count - we update only on receive/timeout, so that we don't get
2138
     * weird loss percentage, just because a packet was note recived yet.
2139
     */
2140
    h->num_sent++;
659✔
2141
    h->num_sent_i++;
659✔
2142

2143
    if (!success) {
659✔
2144
        if (!loop_flag && index >= 0) {
89✔
2145
            h->resp_times[index] = RESP_TIMEOUT;
89✔
2146
        }
23✔
2147
        num_timeout++;
89✔
2148
        return;
89✔
2149
    }
2150

2151
    /* received count */
2152
    h->num_recv++;
570✔
2153
    h->num_recv_i++;
570✔
2154

2155
    /* maximum */
2156
    if (!h->max_reply || latency > h->max_reply) {
570✔
2157
        h->max_reply = latency;
397✔
2158
    }
114✔
2159
    if (!h->max_reply_i || latency > h->max_reply_i) {
570✔
2160
        h->max_reply_i = latency;
418✔
2161
    }
123✔
2162

2163
    /* minimum */
2164
    if (!h->min_reply || latency < h->min_reply) {
570✔
2165
        h->min_reply = latency;
329✔
2166
    }
92✔
2167
    if (!h->min_reply_i || latency < h->min_reply_i) {
570✔
2168
        h->min_reply_i = latency;
354✔
2169
    }
101✔
2170

2171
    /* total time (for average) */
2172
    h->total_time += latency;
570✔
2173
    h->total_time_i += latency;
570✔
2174

2175
    /* response time per-packet (count mode) */
2176
    if (!loop_flag && index >= 0) {
570✔
2177
        h->resp_times[index] = latency;
570✔
2178
    }
172✔
2179
}
195✔
2180

2181
/* stats_reset_interval: reset interval statistics
2182
 * h: host entry to update
2183
 */
2184
void stats_reset_interval(HOST_ENTRY *h)
30✔
2185
{
2186
    h->num_sent_i = 0;
30✔
2187
    h->num_recv_i = 0;
30✔
2188
    h->max_reply_i = 0;
30✔
2189
    h->min_reply_i = 0;
30✔
2190
    h->total_time_i = 0;
30✔
2191
}
30✔
2192

2193
int decode_icmp_ipv4(
837✔
2194
    struct sockaddr *response_addr,
2195
    size_t response_addr_len,
2196
    char *reply_buf,
2197
    size_t reply_buf_len,
2198
    unsigned short *id,
2199
    unsigned short *seq,
2200
    int *ip_header_tos,
2201
    int *ip_header_ttl,
2202
    uint32_t *ip_header_otime_ms,
2203
    uint32_t *ip_header_rtime_ms,
2204
    uint32_t *ip_header_ttime_ms)
2205
{
2206
    struct icmp *icp;
2207
    int hlen = 0;
837✔
2208

2209
    if (!using_sock_dgram4) {
837✔
2210
        struct ip *ip = (struct ip *)reply_buf;
829✔
2211
        *ip_header_tos = ip->ip_tos;
829✔
2212
        *ip_header_ttl = ip->ip_ttl;
829✔
2213

2214
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2215
        /* The alpha headers are decidedly broken.
2216
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2217
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2218
         */
2219
        hlen = (ip->ip_vhl & 0x0F) << 2;
2220
#else
2221
        hlen = ip->ip_hl << 2;
829✔
2222
#endif
2223
    }
157✔
2224

2225
    if (reply_buf_len < hlen + ICMP_MINLEN) {
837✔
2226
        /* too short */
2227
        if (verbose_flag) {
×
2228
            char buf[INET6_ADDRSTRLEN];
2229
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2230
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2231
        }
2232
        return -1;
×
2233
    }
2234

2235
    icp = (struct icmp *)(reply_buf + hlen);
837✔
2236

2237
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
837✔
2238
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
358✔
2239
        /* Handle other ICMP packets */
2240
        struct icmp *sent_icmp;
2241
        SEQMAP_VALUE *seqmap_value;
2242
        char addr_ascii[INET6_ADDRSTRLEN];
2243
        HOST_ENTRY *h;
2244

2245
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2246
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
650✔
2247
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2248
            return -1;
×
2249
        }
2250

2251
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
336✔
2252

2253
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
336✔
2254
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
14✔
2255
            sent_icmp->icmp_id != ident4) {
×
2256
            /* not caused by us */
2257
            return -1;
336✔
2258
        }
2259

2260
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2261
        if (seqmap_value == NULL) {
×
2262
            return -1;
×
2263
        }
2264

2265
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2266

2267
        switch (icp->icmp_type) {
×
2268
        case ICMP_UNREACH:
2269
            h = table[seqmap_value->host_nr];
×
2270
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2271
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2272
                    addr_ascii, h->host);
2273
            }
2274
            else {
2275
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2276
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2277
            }
2278

2279
            print_warning("\n");
×
2280
            num_othericmprcvd++;
×
2281
            break;
×
2282

2283
        case ICMP_SOURCEQUENCH:
2284
        case ICMP_REDIRECT:
2285
        case ICMP_TIMXCEED:
2286
        case ICMP_PARAMPROB:
2287
            h = table[seqmap_value->host_nr];
×
2288
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2289
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2290
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2291
            }
2292
            else {
2293
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2294
                    icp->icmp_type, addr_ascii, h->host);
×
2295
            }
2296
            print_warning("\n");
×
2297
            num_othericmprcvd++;
×
2298
            break;
×
2299
        }
2300

2301
        return -1;
×
2302
    }
2303

2304
    *id = icp->icmp_id;
501✔
2305
    *seq = ntohs(icp->icmp_seq);
501✔
2306
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
501✔
2307

2308
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2309
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
14✔
2310
            if (verbose_flag) {
×
2311
                char buf[INET6_ADDRSTRLEN];
2312
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2313
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2314
            }
2315
            return -1;
×
2316
        }
2317

2318
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
14✔
2319
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
14✔
2320
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
14✔
2321
    }
2322

2323
    return hlen;
501✔
2324
}
157✔
2325

2326
#ifdef IPV6
2327
int decode_icmp_ipv6(
160✔
2328
    struct sockaddr *response_addr,
2329
    size_t response_addr_len,
2330
    char *reply_buf,
2331
    size_t reply_buf_len,
2332
    unsigned short *id,
2333
    unsigned short *seq)
2334
{
2335
    struct icmp6_hdr *icp;
2336

2337
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
160✔
2338
        if (verbose_flag) {
×
2339
            char buf[INET6_ADDRSTRLEN];
2340
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2341
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2342
        }
2343
        return 0; /* too short */
×
2344
    }
2345

2346
    icp = (struct icmp6_hdr *)reply_buf;
160✔
2347

2348
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
160✔
2349
        /* Handle other ICMP packets */
2350
        struct icmp6_hdr *sent_icmp;
2351
        SEQMAP_VALUE *seqmap_value;
2352
        char addr_ascii[INET6_ADDRSTRLEN];
2353
        HOST_ENTRY *h;
2354

2355
        /* reply icmp packet (ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2356
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
79✔
2357
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2358
            return 0;
×
2359
        }
2360

2361
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
79✔
2362

2363
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
79✔
2364
            /* not caused by us */
2365
            return 0;
79✔
2366
        }
2367

2368
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2369
        if (seqmap_value == NULL) {
×
2370
            return 0;
×
2371
        }
2372

2373
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2374

2375
        switch (icp->icmp6_type) {
×
2376
        case ICMP_UNREACH:
2377
            h = table[seqmap_value->host_nr];
×
2378
            if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
×
2379
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2380
                    addr_ascii, h->host);
2381
            }
2382
            else {
2383
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2384
                    icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2385
            }
2386

2387
            print_warning("\n");
×
2388
            num_othericmprcvd++;
×
2389
            break;
×
2390

2391
        case ICMP_SOURCEQUENCH:
2392
        case ICMP_REDIRECT:
2393
        case ICMP_TIMXCEED:
2394
        case ICMP_PARAMPROB:
2395
            h = table[seqmap_value->host_nr];
×
2396
            if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
×
2397
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2398
                    icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
×
2399
            }
2400
            else {
2401
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2402
                    icp->icmp6_type, addr_ascii, h->host);
×
2403
            }
2404
            print_warning("\n");
×
2405
            num_othericmprcvd++;
×
2406
            break;
×
2407
        }
2408

2409
        return 0;
×
2410
    }
2411

2412
    *id = icp->icmp6_id;
81✔
2413
    *seq = ntohs(icp->icmp6_seq);
81✔
2414

2415
    return 1;
81✔
2416
}
30✔
2417
#endif
2418

2419
int wait_for_reply(int64_t wait_time)
2,083✔
2420
{
2421
    int result;
2422
    static char buffer[RECV_BUFSIZE];
2423
    struct sockaddr_storage response_addr;
2424
    int n, avg;
2425
    HOST_ENTRY *h;
2426
    int64_t this_reply;
2427
    int this_count;
2428
    int64_t recv_time = 0;
2,083✔
2429
    SEQMAP_VALUE *seqmap_value;
2430
    unsigned short id;
2431
    unsigned short seq;
2432
    int ip_header_tos = -1;
2,083✔
2433
    int ip_header_ttl = -1;
2,083✔
2434
    // ICMP Timestamp
2435
    uint32_t ip_header_otime_ms = 0x80000000U;
2,083✔
2436
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,083✔
2437
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,083✔
2438

2439
    /* Receive packet */
2440
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,083✔
2441
        &recv_time, /* reply_timestamp */
2442
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2443
        sizeof(response_addr), /* reply_src_addr_len */
2444
        buffer, /* reply_buf */
2445
        sizeof(buffer) /* reply_buf_len */
2446
    );
2447

2448
    if (result <= 0) {
2,083✔
2449
        return 0;
1,086✔
2450
    }
2451

2452
    update_current_time();
997✔
2453
    if (recv_time == 0)
997✔
2454
        recv_time = current_time_ns;
187✔
2455

2456
    /* Process ICMP packet and retrieve id/seq */
2457
    if (response_addr.ss_family == AF_INET) {
997✔
2458
        int ip_hlen = decode_icmp_ipv4(
837✔
2459
            (struct sockaddr *)&response_addr,
2460
            sizeof(response_addr),
2461
            buffer,
2462
            sizeof(buffer),
2463
            &id,
2464
            &seq,
2465
            &ip_header_tos,
2466
            &ip_header_ttl,
2467
            &ip_header_otime_ms,
2468
            &ip_header_rtime_ms,
2469
            &ip_header_ttime_ms);
2470
        if (ip_hlen < 0) {
837✔
2471
            return 1;
336✔
2472
        }
2473
        if (id != ident4) {
501✔
2474
            return 1; /* packet received, but not the one we are looking for! */
×
2475
        }
2476
        if (!using_sock_dgram4) {
501✔
2477
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2478
             * with fping with IPv6 hosts */
2479
            result -= ip_hlen;
493✔
2480
        }
157✔
2481
    }
157✔
2482
#ifdef IPV6
2483
    else if (response_addr.ss_family == AF_INET6) {
160✔
2484
        if (!decode_icmp_ipv6(
160✔
2485
                (struct sockaddr *)&response_addr,
2486
                sizeof(response_addr),
2487
                buffer,
2488
                sizeof(buffer),
2489
                &id,
2490
                &seq)) {
2491
            return 1;
79✔
2492
        }
2493
        if (id != ident6) {
81✔
2494
            return 1; /* packet received, but not the one we are looking for! */
×
2495
        }
2496
    }
15✔
2497
#endif
2498
    else {
2499
        return 1;
×
2500
    }
2501

2502
    seqmap_value = seqmap_fetch(seq, current_time_ns);
582✔
2503
    if (seqmap_value == NULL) {
582✔
2504
        return 1;
×
2505
    }
2506

2507
    /* find corresponding host_entry */
2508
    n = seqmap_value->host_nr;
582✔
2509
    h = table[n];
582✔
2510
    this_count = seqmap_value->ping_count;
582✔
2511
    this_reply = recv_time - seqmap_value->ping_ts;
582✔
2512

2513
    /* update stats that include invalid replies */
2514
    h->num_recv_total++;
582✔
2515
    num_pingreceived++;
582✔
2516

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

2519
    /* optionally require reply source equal to target address */
2520
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
582✔
2521
        dbg_printf("discarding reply from wrong source address\n");
2522
        return 1;
12✔
2523
    }
2524

2525
    /* discard duplicates */
2526
    if (!loop_flag && h->resp_times[this_count] >= 0) {
570✔
2527
        if (!per_recv_flag) {
×
2528
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2529
                h->host, this_count, result, sprint_tm(this_reply));
2530

2531
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2532
                char buf[INET6_ADDRSTRLEN];
2533
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2534
                fprintf(stderr, " [<- %s]", buf);
×
2535
            }
2536
            fprintf(stderr, "\n");
×
2537
        }
2538
        return 1;
×
2539
    }
2540

2541
    /* discard reply if delay is larger than timeout
2542
     * (see also: github #32) */
2543
    if (this_reply > h->timeout) {
570✔
2544
        return 1;
×
2545
    }
2546

2547
    /* update stats */
2548
    stats_add(h, this_count, 1, this_reply);
570✔
2549
    // TODO: move to stats_add?
2550
    if (!max_reply || this_reply > max_reply)
570✔
2551
        max_reply = this_reply;
345✔
2552
    if (!min_reply || this_reply < min_reply)
570✔
2553
        min_reply = this_reply;
276✔
2554
    sum_replies += this_reply;
570✔
2555
    total_replies++;
570✔
2556

2557
    /* initialize timeout to initial timeout (without backoff) */
2558
    h->timeout = timeout;
570✔
2559

2560
    /* remove timeout event */
2561
    struct event *timeout_event = host_get_timeout_event(h, this_count);
570✔
2562
    if (timeout_event) {
570✔
2563
        ev_remove(&event_queue_timeout, timeout_event);
570✔
2564
    }
172✔
2565

2566
    /* print "is alive" */
2567
    if (h->num_recv == 1) {
570✔
2568
        num_alive++;
289✔
2569
        if (fast_reachable && num_alive >= min_reachable)
289✔
2570
                finish_requested = 1;
3✔
2571

2572
        if (verbose_flag || alive_flag) {
289✔
2573
            printf("%s", h->host);
158✔
2574

2575
            if (verbose_flag)
158✔
2576
                printf(" is alive");
138✔
2577
        }
42✔
2578
    }
81✔
2579

2580
    /* print received ping (unless --quiet) */
2581
    if (per_recv_flag) {
570✔
2582
        if (timestamp_flag) {
151✔
2583
            print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2584
        }
10✔
2585
        avg = h->total_time / h->num_recv;
151✔
2586
        printf("%-*s : [%d], %d bytes, %s ms",
151✔
2587
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2588
        printf(" (%s avg, ", sprint_tm(avg));
151✔
2589

2590
        if (h->num_recv <= h->num_sent) {
151✔
2591
            printf("%d%% loss)",
151✔
2592
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2593
        }
45✔
2594
        else {
2595
            printf("%d%% return)",
×
2596
                (h->num_recv_total * 100) / h->num_sent);
×
2597
        }
2598
    }
45✔
2599

2600
    if (verbose_flag || alive_flag || per_recv_flag) {
570✔
2601

2602
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
309✔
2603
            char buf[INET6_ADDRSTRLEN];
2604
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2605
            fprintf(stderr, " [<- %s]", buf);
6✔
2606
        }
2607

2608
        if (icmp_request_typ == 13) {
309✔
2609
            printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2610
                   alive_flag ? "" : ",",
14✔
2611
                   ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
2612
                   ms_since_midnight_utc(recv_time));
2613
        }
2614

2615
        if(print_tos_flag) {
309✔
2616
            if(ip_header_tos != -1) {
43✔
2617
                printf(" (TOS %d)", ip_header_tos);
39✔
2618
            }
11✔
2619
            else {
2620
                printf(" (TOS unknown)");
4✔
2621
            }
2622
        }
11✔
2623

2624
        if (print_ttl_flag) {
309✔
2625
          if(ip_header_ttl != -1) {
43✔
2626
              printf(" (TTL %d)", ip_header_ttl);
39✔
2627
          }
11✔
2628
          else {
2629
              printf(" (TTL unknown)");
4✔
2630
          }
2631
        }
11✔
2632

2633
        if (elapsed_flag && !per_recv_flag)
309✔
2634
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2635

2636
        printf("\n");
309✔
2637
    }
87✔
2638

2639
    return 1;
570✔
2640
}
511✔
2641

2642
/************************************************************
2643

2644
  Function: add_name
2645

2646
*************************************************************
2647

2648
  Inputs:  char* name
2649

2650
  Description:
2651

2652
  process input name for addition to target list
2653
  name can turn into multiple targets via multiple interfaces (-m)
2654
  or via NIS groups
2655

2656
************************************************************/
2657

2658
void add_name(char *name)
357✔
2659
{
2660
    struct addrinfo *res0, *res, hints;
2661
    int ret_ga;
2662
    char *printname;
2663
    char namebuf[256];
2664
    char addrbuf[256];
2665

2666
    /* getaddrinfo */
2667
    memset(&hints, 0, sizeof(struct addrinfo));
357✔
2668
    hints.ai_flags = AI_UNUSABLE;
357✔
2669
    hints.ai_socktype = SOCK_RAW;
357✔
2670
    hints.ai_family = hints_ai_family;
357✔
2671
    if (hints_ai_family == AF_INET) {
357✔
2672
        hints.ai_protocol = IPPROTO_ICMP;
47✔
2673
    }
11✔
2674
#ifdef IPV6
2675
    else if (hints_ai_family == AF_INET6) {
310✔
2676
        hints.ai_protocol = IPPROTO_ICMPV6;
7✔
2677
    }
1✔
2678
#endif
2679
    else {
2680
        hints.ai_socktype = SOCK_STREAM;
303✔
2681
        hints.ai_protocol = 0;
303✔
2682
    }
2683
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
357✔
2684
    if (ret_ga) {
357✔
2685
        if (!quiet_flag)
17✔
2686
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
17✔
2687
        num_noaddress++;
17✔
2688
        return;
17✔
2689
    }
2690

2691
    /* NOTE: we could/should loop with res on all addresses like this:
2692
     * for (res = res0; res; res = res->ai_next) {
2693
     * We don't do it yet, however, because is is an incompatible change
2694
     * (need to implement a separate option for this)
2695
     */
2696
    for (res = res0; res; res = res->ai_next) {
340✔
2697
        /* name_flag: addr -> name lookup requested) */
2698
        if (name_flag || rdns_flag) {
342✔
2699
            int do_rdns = rdns_flag ? 1 : 0;
3✔
2700
            if (name_flag) {
3✔
2701
                /* Was it a numerical address? Only then do a rdns-query */
2702
                struct addrinfo *nres;
2703
                hints.ai_flags = AI_NUMERICHOST;
×
2704
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
2705
                    do_rdns = 1;
×
2706
                    freeaddrinfo(nres);
×
2707
                }
2708
            }
2709

2710
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2711
                printname = namebuf;
3✔
2712
            }
1✔
2713
            else {
2714
                printname = name;
×
2715
            }
2716
        }
1✔
2717
        else {
2718
            printname = name;
337✔
2719
        }
2720

2721
        /* addr_flag: name -> addr lookup requested */
2722
        if (addr_flag) {
340✔
2723
            int ret;
2724
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2725
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2726
            if (ret) {
3✔
2727
                if (!quiet_flag) {
×
2728
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2729
                }
2730
                continue;
×
2731
            }
2732

2733
            if (name_flag || rdns_flag) {
3✔
2734
                char nameaddrbuf[512 + 3];
2735
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
2736
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2737
            }
2738
            else {
2739
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2740
            }
2741
        }
1✔
2742
        else {
2743
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
337✔
2744
        }
2745

2746
        if (!multif_flag) {
340✔
2747
            break;
340✔
2748
        }
2749
    }
2750

2751
    freeaddrinfo(res0);
340✔
2752
}
99✔
2753

2754
/************************************************************
2755

2756
  Function: add_addr
2757

2758
*************************************************************
2759

2760
  Description:
2761

2762
  add single address to list of hosts to be pinged
2763

2764
************************************************************/
2765

2766
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
340✔
2767
{
2768
    HOST_ENTRY *p;
2769
    int n;
2770
    int64_t *i;
2771

2772
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
340✔
2773
    if (!p)
340✔
2774
        crash_and_burn("can't allocate HOST_ENTRY");
×
2775

2776
    p->name = strdup(name);
340✔
2777
    p->host = strdup(host);
340✔
2778
    memcpy(&p->saddr, ipaddr, ipaddr_len);
340✔
2779
    p->saddr_len = ipaddr_len;
340✔
2780
    p->timeout = timeout;
340✔
2781
    p->min_reply = 0;
340✔
2782

2783
    if (netdata_flag) {
340✔
2784
        char *s = p->name;
3✔
2785
        while (*s) {
30✔
2786
            if (!isalnum(*s))
27✔
2787
                *s = '_';
9✔
2788
            s++;
27✔
2789
        }
2790
    }
1✔
2791

2792
    if (strlen(p->host) > max_hostname_len)
340✔
2793
        max_hostname_len = strlen(p->host);
249✔
2794

2795
    /* array for response time results */
2796
    if (!loop_flag) {
340✔
2797
        i = (int64_t *)malloc(trials * sizeof(int64_t));
340✔
2798
        if (!i)
340✔
2799
            crash_and_burn("can't allocate resp_times array");
×
2800

2801
        for (n = 1; n < trials; n++)
1,447✔
2802
            i[n] = RESP_UNUSED;
1,107✔
2803

2804
        p->resp_times = i;
340✔
2805
    }
94✔
2806

2807
    /* allocate event storage */
2808
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
340✔
2809
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
340✔
2810

2811
    /* schedule first ping */
2812
    host_add_ping_event(p, 0, current_time_ns);
340✔
2813

2814
    num_hosts++;
340✔
2815
}
340✔
2816

2817
/************************************************************
2818

2819
  Function: crash_and_burn
2820

2821
*************************************************************
2822

2823
  Inputs:  char* message
2824

2825
  Description:
2826

2827
************************************************************/
2828

2829
void crash_and_burn(char *message)
×
2830
{
2831
    fprintf(stderr, "%s: %s\n", prog, message);
×
2832
    exit(4);
×
2833
}
2834

2835
/************************************************************
2836

2837
  Function: errno_crash_and_burn
2838

2839
*************************************************************
2840

2841
  Inputs:  char* message
2842

2843
  Description:
2844

2845
************************************************************/
2846

2847
void errno_crash_and_burn(char *message)
8✔
2848
{
2849
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
2850
    exit(4);
8✔
2851
}
2852

2853
/************************************************************
2854

2855
  Function: print_warning
2856

2857
  Description: fprintf(stderr, ...), unless running with -q
2858

2859
*************************************************************/
2860

2861
void print_warning(char *format, ...)
17✔
2862
{
2863
    va_list args;
2864
    if (!quiet_flag) {
17✔
2865
        va_start(args, format);
17✔
2866
        vfprintf(stderr, format, args);
17✔
2867
        va_end(args);
17✔
2868
    }
5✔
2869
}
17✔
2870

2871
/************************************************************
2872

2873
  Function: sprint_tm
2874

2875
*************************************************************
2876

2877
  render nanosecond int64_t value into milliseconds string with three digits of
2878
  precision.
2879

2880
************************************************************/
2881

2882
const char *sprint_tm(int64_t ns)
851✔
2883
{
2884
    static char buf[10];
2885
    double t = (double)ns / 1e6;
851✔
2886

2887
    if (t < 0.0) {
851✔
2888
        /* negative (unexpected) */
2889
        sprintf(buf, "%.2g", t);
×
2890
    }
2891
    else if (t < 1.0) {
851✔
2892
        /* <= 0.99 ms */
2893
        sprintf(buf, "%.3f", t);
851✔
2894
    }
261✔
2895
    else if (t < 10.0) {
×
2896
        /* 1.00 - 9.99 ms */
2897
        sprintf(buf, "%.2f", t);
×
2898
    }
2899
    else if (t < 100.0) {
×
2900
        /* 10.0 - 99.9 ms */
2901
        sprintf(buf, "%.1f", t);
×
2902
    }
2903
    else if (t < 1000000.0) {
×
2904
        /* 100 - 1'000'000 ms */
2905
        sprintf(buf, "%.0f", t);
×
2906
    }
2907
    else {
2908
        sprintf(buf, "%.3e", t);
×
2909
    }
2910

2911
    return (buf);
851✔
2912
}
2913

2914
/************************************************************
2915

2916
  Function: addr_cmp
2917

2918
*************************************************************/
2919
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
331✔
2920
{
2921
    if (a->sa_family != b->sa_family) {
331✔
2922
        return a->sa_family - b->sa_family;
×
2923
    }
2924
    else {
2925
        if (a->sa_family == AF_INET) {
331✔
2926
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
277✔
2927
        }
2928
        else if (a->sa_family == AF_INET6) {
54✔
2929
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
62✔
2930
                &((struct sockaddr_in6 *)b)->sin6_addr,
54✔
2931
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
2932
        }
2933
    }
2934

2935
    return 0;
×
2936
}
89✔
2937

2938
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
655✔
2939
{
2940
    struct event *event = &h->event_storage_ping[index % event_storage_count];
655✔
2941
    event->host = h;
655✔
2942
    event->ping_index = index;
655✔
2943
    event->ev_time = ev_time;
655✔
2944
    ev_enqueue(&event_queue_ping, event);
655✔
2945

2946
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
2947
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2948
}
655✔
2949

2950
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
659✔
2951
{
2952
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
659✔
2953
    event->host = h;
659✔
2954
    event->ping_index = index;
659✔
2955
    event->ev_time = ev_time;
659✔
2956
    ev_enqueue(&event_queue_timeout, event);
659✔
2957

2958
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
2959
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2960
}
659✔
2961

2962
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
570✔
2963
{
2964
    return &h->event_storage_timeout[index % event_storage_count];
570✔
2965
}
2966

2967
/************************************************************
2968

2969
  Function: ev_enqueue
2970

2971
  Enqueue an event
2972

2973
  The queue is sorted by event->ev_time, so that queue->first always points to
2974
  the earliest event.
2975

2976
  We start scanning the queue from the tail, because we assume
2977
  that new events mostly get inserted with a event time higher
2978
  than the others.
2979

2980
*************************************************************/
2981
void ev_enqueue(struct event_queue *queue, struct event *event)
1,314✔
2982
{
2983
    struct event *i;
2984
    struct event *i_prev;
2985

2986
    /* Empty list */
2987
    if (queue->last == NULL) {
1,314✔
2988
        event->ev_next = NULL;
1,024✔
2989
        event->ev_prev = NULL;
1,024✔
2990
        queue->first = event;
1,024✔
2991
        queue->last = event;
1,024✔
2992
        return;
1,024✔
2993
    }
2994

2995
    /* Insert on tail? */
2996
    if (event->ev_time - queue->last->ev_time >= 0) {
290✔
2997
        event->ev_next = NULL;
290✔
2998
        event->ev_prev = queue->last;
290✔
2999
        queue->last->ev_next = event;
290✔
3000
        queue->last = event;
290✔
3001
        return;
290✔
3002
    }
3003

3004
    /* Find insertion point */
3005
    i = queue->last;
×
3006
    while (1) {
3007
        i_prev = i->ev_prev;
×
3008
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3009
            event->ev_prev = i_prev;
×
3010
            event->ev_next = i;
×
3011
            i->ev_prev = event;
×
3012
            if (i_prev != NULL) {
×
3013
                i_prev->ev_next = event;
×
3014
            }
3015
            else {
3016
                queue->first = event;
×
3017
            }
3018
            return;
×
3019
        }
3020
        i = i_prev;
×
3021
    }
3022
}
390✔
3023

3024
/************************************************************
3025

3026
  Function: ev_dequeue
3027

3028
*************************************************************/
3029
struct event *ev_dequeue(struct event_queue *queue)
724✔
3030
{
3031
    struct event *dequeued;
3032

3033
    if (queue->first == NULL) {
724✔
3034
        return NULL;
×
3035
    }
3036
    dequeued = queue->first;
724✔
3037
    ev_remove(queue, dequeued);
724✔
3038

3039
    return dequeued;
724✔
3040
}
212✔
3041

3042
/************************************************************
3043

3044
  Function: ev_remove
3045

3046
*************************************************************/
3047
void ev_remove(struct event_queue *queue, struct event *event)
1,294✔
3048
{
3049
    if (queue->first == event) {
1,294✔
3050
        queue->first = event->ev_next;
1,285✔
3051
    }
381✔
3052
    if (queue->last == event) {
1,294✔
3053
        queue->last = event->ev_prev;
1,025✔
3054
    }
295✔
3055
    if (event->ev_prev) {
1,294✔
3056
        event->ev_prev->ev_next = event->ev_next;
9✔
3057
    }
3✔
3058
    if (event->ev_next) {
1,294✔
3059
        event->ev_next->ev_prev = event->ev_prev;
269✔
3060
    }
89✔
3061
    event->ev_prev = NULL;
1,294✔
3062
    event->ev_next = NULL;
1,294✔
3063
}
1,294✔
3064

3065
/************************************************************
3066

3067
  Function: print_human_readable_time from current_time_ns
3068

3069
*************************************************************/
3070
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
30✔
3071
{
3072
    char time_buffer[100];
3073
    time_t current_time_s;
3074
    struct tm *local_time;
3075

3076
    current_time_s = current_time_ns / 1000000000;
30✔
3077
    local_time = localtime(&current_time_s);
30✔
3078
    switch(timestamp_format) {
30✔
3079
        case 1:
4✔
3080
            // timestamp-format ctime
3081
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
6✔
3082
            printf("[%s] ", time_buffer);
6✔
3083
            break;
6✔
3084
        case 2:
4✔
3085
            // timestamp-format iso
3086
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
6✔
3087
            printf("[%s] ", time_buffer);
6✔
3088
            break;
6✔
3089
        case 3:
4✔
3090
            // timestamp-format rfc3339
3091
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
6✔
3092
            printf("[%s] ", time_buffer);
6✔
3093
            break;
6✔
3094
        default:
8✔
3095
            printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3096
    }
4✔
3097
}
30✔
3098

3099
/************************************************************
3100

3101
  Function: ms_since_midnight_utc
3102

3103
*************************************************************
3104

3105
  Input: int64_t: current UTC time in ns
3106

3107
  Output: uint32_t: current time in ms since midnight UTC
3108

3109
  Description:
3110

3111
  Return ICMP Timestamp value corresponding to the given time value.
3112
  The given time value must be in UTC.
3113

3114
*************************************************************/
3115
static uint32_t ms_since_midnight_utc(int64_t time_val)
14✔
3116
{
3117
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
14✔
3118
}
3119

3120
/************************************************************
3121

3122
  Function: usage
3123

3124
*************************************************************
3125

3126
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3127

3128
  Description:
3129

3130
************************************************************/
3131

3132
void usage(int is_error)
115✔
3133
{
3134
    FILE *out = is_error ? stderr : stdout;
115✔
3135
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
115✔
3136
    fprintf(out, "\n");
115✔
3137
    fprintf(out, "Probing options:\n");
115✔
3138
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
115✔
3139
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
115✔
3140
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
115✔
3141
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
115✔
3142
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
115✔
3143
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
115✔
3144
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
115✔
3145
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
115✔
3146
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
115✔
3147
    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✔
3148
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
115✔
3149
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
115✔
3150
#ifdef SO_BINDTODEVICE
3151
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
78✔
3152
#endif
3153
#ifdef SO_MARK
3154
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
78✔
3155
#endif
3156
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
115✔
3157
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
115✔
3158
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
115✔
3159
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
115✔
3160
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
115✔
3161
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
115✔
3162
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
115✔
3163
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
115✔
3164
    fprintf(out, "   -S, --src=IP       set source address\n");
115✔
3165
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
115✔
3166
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
115✔
3167
    fprintf(out, "       --check-source discard replies not from target address\n");
115✔
3168
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
115✔
3169
    fprintf(out, "\n");
115✔
3170
    fprintf(out, "Output options:\n");
115✔
3171
    fprintf(out, "   -a, --alive        show targets that are alive\n");
115✔
3172
    fprintf(out, "   -A, --addr         show targets by address\n");
115✔
3173
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
115✔
3174
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
115✔
3175
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
115✔
3176
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
115✔
3177
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
115✔
3178
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
115✔
3179
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
115✔
3180
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
115✔
3181
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
115✔
3182
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
115✔
3183
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
115✔
3184
    fprintf(out, "   -s, --stats        print final stats\n");
115✔
3185
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
115✔
3186
    fprintf(out, "   -v, --version      show version\n");
115✔
3187
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
115✔
3188
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
115✔
3189
    fprintf(out, "       --print-tos    show received TOS value\n");
115✔
3190
    fprintf(out, "       --print-ttl    show IP TTL value\n");
115✔
3191
    exit(is_error);
115✔
3192
}
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