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

schweikert / fping / 11777176665

11 Nov 2024 10:58AM UTC coverage: 87.637% (-0.1%) from 87.762%
11777176665

push

github

gsnw-sebast
Update CHANGELOG.md for ICMP Timestamp

1276 of 1456 relevant lines covered (87.64%)

256.82 hits per line

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

87.84
/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
/* maxima and minima */
148
#ifdef FPING_SAFE_LIMITS
149
#define MIN_INTERVAL 1 /* in millisec */
150
#define MIN_PERHOST_INTERVAL 10 /* in millisec */
151
#else
152
#define MIN_INTERVAL 0
153
#define MIN_PERHOST_INTERVAL 0
154
#endif
155

156
/* response time array flags */
157
#define RESP_WAITING -1
158
#define RESP_UNUSED -2
159
#define RESP_ERROR -3
160
#define RESP_TIMEOUT -4
161

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

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

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

215
#define ICMP_UNREACH_MAXTYPE 15
216

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

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

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

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

264
#define EV_TYPE_PING 1
265
#define EV_TYPE_TIMEOUT 2
266

267
struct event {
268
    struct event *ev_prev;
269
    struct event *ev_next;
270
    int64_t ev_time;
271
    struct host_entry *host;
272
    int ping_index;
273
};
274

275
struct event_queue {
276
    struct event *first;
277
    struct event *last;
278
};
279

280
/*** globals ***/
281

282
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
283

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

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

307
volatile sig_atomic_t status_snapshot = 0;
308
volatile sig_atomic_t finish_requested = 0;
309

310
unsigned int debugging = 0;
311

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

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

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

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

372
unsigned int fwmark = 0;
373

374
char *filename = NULL; /* file containing hosts to ping */
375

376
/*** forward declarations ***/
377

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

411
/************************************************************
412

413
  Function: p_setsockopt
414

415
*************************************************************
416

417
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
418

419
  Description:
420

421
  Elevates privileges to p_uid when required, calls
422
  setsockopt, and drops privileges back.
423

424
************************************************************/
425

426
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
12✔
427
    const void *optval, socklen_t optlen)
428
{
429
    const uid_t saved_uid = geteuid();
12✔
430
    int res;
431

432
    if (p_uid != saved_uid && seteuid(p_uid)) {
12✔
433
        perror("cannot elevate privileges for setsockopt");
×
434
    }
435

436
    res = setsockopt(sockfd, level, optname, optval, optlen);
12✔
437

438
    if (p_uid != saved_uid && seteuid(saved_uid)) {
12✔
439
        perror("fatal error: could not drop privileges after setsockopt");
×
440
        /* continuing would be a security hole */
441
        exit(4);
×
442
    }
443

444
    return res;
12✔
445
}
446

447
/************************************************************
448

449
  Function: main
450

451
*************************************************************
452

453
  Inputs:  int argc, char** argv
454

455
  Description:
456

457
  Main program entry point
458

459
************************************************************/
460

461
int main(int argc, char **argv)
467✔
462
{
463
/* Debug: CPU Performance */
464
#if defined(DEBUG) || defined(_DEBUG)
465
    clock_t perf_cpu_start, perf_cpu_end;
466
    double perf_cpu_time_used;
467
    perf_cpu_start = clock();
468
#endif /* DEBUG || _DEBUG */
469

470
    int c;
471
    const uid_t suid = geteuid();
467✔
472
    int tos = 0;
467✔
473
    struct optparse optparse_state;
474
#ifdef USE_SIGACTION
475
    struct sigaction act;
476
#endif
477

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

485
    socket4 = open_ping_socket_ipv4(&socktype4);
465✔
486
#ifdef __linux__
487
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
488
     * structure is missing in the message.
489
     */
490
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
316✔
491
#endif
492

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

502
    memset(&src_addr, 0, sizeof(src_addr));
465✔
503
#ifdef IPV6
504
    memset(&src_addr6, 0, sizeof(src_addr6));
465✔
505
#endif
506

507
    if (!suid && suid != getuid()) {
465✔
508
        /* *temporarily* drop privileges */
509
        if (seteuid(getuid()) == -1)
434✔
510
            perror("cannot setuid");
×
511
    }
128✔
512

513
    optparse_init(&optparse_state, argv);
465✔
514
    ident4 = ident6 = htons(getpid() & 0xFFFF);
465✔
515
    verbose_flag = 1;
465✔
516
    backoff_flag = 1;
465✔
517
    opterr = 1;
465✔
518

519
    /* get command line options */
520

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

573
    float opt_value_float;
574
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
1,209✔
575
        switch (c) {
687✔
576
        case '0':
42✔
577
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
54✔
578
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
15✔
579
                  timestamp_format_flag = 1;
3✔
580
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
13✔
581
                  timestamp_format_flag = 2;
3✔
582
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
10✔
583
                  timestamp_format_flag = 3;
3✔
584
                }else{
1✔
585
                  usage(1);
6✔
586
                }
587
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
44✔
588
                check_source_flag = 1;
9✔
589
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
31✔
590
                icmp_request_typ = 13;
7✔
591
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
24✔
592
                print_tos_flag = 1;
13✔
593
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
13✔
594
                print_ttl_flag = 1;
10✔
595
            } else {
2✔
596
                usage(1);
×
597
            }
598
            break;
50✔
599
        case '4':
20✔
600
#ifdef IPV6
601
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
28✔
602
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
603
                exit(1);
3✔
604
            }
605
            hints_ai_family = AF_INET;
25✔
606
#endif
607
            break;
25✔
608
        case '6':
18✔
609
#ifdef IPV6
610
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
21✔
611
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
612
                exit(1);
3✔
613
            }
614
            hints_ai_family = AF_INET6;
18✔
615
#else
616
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
617
            exit(1);
618
#endif
619
            break;
18✔
620
        case 'M':
2✔
621
#ifdef IP_MTU_DISCOVER
622
            if (socket4 >= 0) {
2✔
623
                int val = IP_PMTUDISC_DO;
2✔
624
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
625
                    perror("setsockopt IP_MTU_DISCOVER");
626
                }
627
            }
628
#ifdef IPV6
629
            if (socket6 >= 0) {
2✔
630
                int val = IPV6_PMTUDISC_DO;
2✔
631
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
632
                    perror("setsockopt IPV6_MTU_DISCOVER");
633
                }
634
            }
635
#endif
636
#else
637
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
638
            exit(1);
639
#endif
640
            break;
2✔
641

642
        case 't':
14✔
643
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
16✔
644
                usage(1);
6✔
645
            if (opt_value_float < 0) {
12✔
646
                usage(1);
3✔
647
            }
1✔
648
            timeout = opt_value_float * 1000000;
10✔
649
            timeout_flag = 1;
10✔
650
            break;
10✔
651

652
        case 'r':
10✔
653
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
12✔
654
                usage(1);
6✔
655
            break;
8✔
656

657
        case 'i':
12✔
658
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
15✔
659
                usage(1);
6✔
660
            if (opt_value_float < 0) {
11✔
661
                usage(1);
3✔
662
            }
1✔
663
            interval = opt_value_float * 1000000;
9✔
664
            break;
9✔
665

666
        case 'p':
62✔
667
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
88✔
668
                usage(1);
6✔
669
            if (opt_value_float < 0) {
84✔
670
                usage(1);
3✔
671
            }
1✔
672
            perhost_interval = opt_value_float * 1000000;
82✔
673

674
            break;
82✔
675

676
        case 'c':
56✔
677
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
78✔
678
                usage(1);
6✔
679

680
            count_flag = 1;
74✔
681
            break;
74✔
682

683
        case 'C':
22✔
684
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
28✔
685
                usage(1);
6✔
686

687
            count_flag = 1;
24✔
688
            report_all_rtts_flag = 1;
24✔
689
            break;
24✔
690

691
        case 'b':
10✔
692
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
12✔
693
                usage(1);
6✔
694
            if (icmp_request_typ > 0)
8✔
695
                usage(1);
3✔
696

697
            break;
6✔
698

699
        case 'h':
2✔
700
            usage(0);
3✔
701
            break;
1✔
702

703
        case 'q':
12✔
704
            verbose_flag = 0;
17✔
705
            quiet_flag = 1;
17✔
706
            break;
17✔
707

708
        case 'Q':
22✔
709
            verbose_flag = 0;
30✔
710
            quiet_flag = 1;
30✔
711
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
30✔
712
                usage(1);
6✔
713
            if (opt_value_float < 0) {
26✔
714
                usage(1);
3✔
715
            }
1✔
716
            report_interval = opt_value_float * 1e9;
24✔
717

718
            /* recognize keyword(s) after number, ignore everything else */
719
            {
720
                char *comma = strchr(optparse_state.optarg, ',');
24✔
721
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
24✔
722
                    cumulative_stats_flag = 1;
6✔
723
                }
2✔
724
            }
725

726
            break;
24✔
727

728
        case 'e':
4✔
729
            elapsed_flag = 1;
6✔
730
            break;
6✔
731

732
        case 'm':
733
            multif_flag = 1;
×
734
            break;
×
735

736
        case 'N':
2✔
737
            netdata_flag = 1;
3✔
738
            break;
3✔
739

740
        case 'n':
4✔
741
            name_flag = 1;
6✔
742
            if (rdns_flag) {
6✔
743
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
744
                exit(1);
3✔
745
            }
746
            break;
3✔
747

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

756
        case 'A':
2✔
757
            addr_flag = 1;
3✔
758
            break;
3✔
759

760
        case 'B':
10✔
761
            if (!(backoff = atof(optparse_state.optarg)))
12✔
762
                usage(1);
6✔
763

764
            break;
8✔
765

766
        case 's':
6✔
767
            stats_flag = 1;
9✔
768
            break;
9✔
769

770
        case 'D':
14✔
771
            timestamp_flag = 1;
21✔
772
            break;
21✔
773

774
        case 'R':
4✔
775
            random_data_flag = 1;
5✔
776
            break;
5✔
777

778
        case 'l':
2✔
779
            loop_flag = 1;
3✔
780
            backoff_flag = 0;
3✔
781
            break;
3✔
782

783
        case 'u':
8✔
784
            unreachable_flag = 1;
12✔
785
            break;
12✔
786

787
        case 'a':
16✔
788
            alive_flag = 1;
24✔
789
            break;
24✔
790

791
        case 'H':
8✔
792
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
10✔
793
                usage(1);
6✔
794
            break;
6✔
795

796
#if defined(DEBUG) || defined(_DEBUG)
797
        case 'z':
798
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
799
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
800
                    usage(1);
801

802
            break;
803
#endif /* DEBUG || _DEBUG */
804

805
        case 'v':
4✔
806
            printf("%s: Version %s\n", prog, VERSION);
6✔
807
            exit(0);
6✔
808

809
        case 'x':
8✔
810
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
10✔
811
                usage(1);
6✔
812
            break;
6✔
813

814
        case 'X':
6✔
815
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
816
                usage(1);
6✔
817
            fast_reachable = 1;
5✔
818
            break;
5✔
819

820
        case 'f':
8✔
821
            filename = optparse_state.optarg;
12✔
822
            break;
12✔
823
#ifdef SO_MARK
824
        case 'k':
10✔
825
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
10✔
826
                usage(1);
4✔
827

828
            if (socket4 >= 0)
6✔
829
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
830
                    perror("fwmark ipv4");
4✔
831

832
#ifdef IPV6
833
            if (socket6 >= 0)
6✔
834
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
835
                    perror("fwmark ipv6");
4✔
836
#endif
837

838
            break;
6✔
839
#endif
840

841
        case 'g':
44✔
842
            /* use IP list generation */
843
            /* mutually exclusive with using file input or command line targets */
844
            generate_flag = 1;
62✔
845
            break;
62✔
846

847
        case 'S':
10✔
848
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
849
                src_addr_set = 1;
6✔
850
                break;
6✔
851
            }
852
#ifdef IPV6
853
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
854
                src_addr6_set = 1;
4✔
855
                break;
4✔
856
            }
857
#endif
858
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
859
            exit(1);
3✔
860

861
        case 'I':
862
#ifdef SO_BINDTODEVICE
863
            if (socket4 >= 0) {
864
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
865
                    perror("binding to specific interface (SO_BINTODEVICE)");
866
                    exit(1);
867
                }
868
            }
869
#ifdef IPV6
870
            if (socket6 >= 0) {
871
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
872
                    perror("binding to specific interface (SO_BINTODEVICE), IPV6");
873
                    exit(1);
874
                }
875
            }
876
#endif
877
#else
878
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
879
            exit(3);
880
            ;
881
#endif
882
            break;
883

884
        case 'T':
2✔
885
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
886
            break;
3✔
887

888
        case 'O':
10✔
889
            if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
15✔
890
                if (socket4 >= 0) {
9✔
891
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
892
                        perror("setting type of service octet IP_TOS");
×
893
                    }
894
                }
3✔
895
#if defined(IPV6) && defined(IPV6_TCLASS)
896
                if (socket6 >= 0) {
9✔
897
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
898
                        perror("setting type of service octet IPV6_TCLASS");
×
899
                    }
900
                }
3✔
901
#endif
902
            }
3✔
903
            else {
904
                usage(1);
6✔
905
            }
906
            break;
11✔
907

908
        case 'o':
6✔
909
            outage_flag = 1;
9✔
910
            break;
9✔
911

912
        case '?':
4✔
913
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
914
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
915
            exit(1);
6✔
916
            break;
917
        }
918
    }
364✔
919

920
    /* permanently drop privileges */
921
    if (suid != getuid() && setuid(getuid())) {
312✔
922
        perror("fatal: failed to permanently drop privileges");
×
923
        /* continuing would be a security hole */
924
        exit(4);
×
925
    }
926

927
    /* validate various option settings */
928

929
#ifndef IPV6
930
    if (socket4 < 0) {
931
        crash_and_burn("can't create socket (must run as root?)");
932
    }
933
#else
934
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
312✔
935
        crash_and_burn("can't create socket (must run as root?)");
×
936
    }
937
#endif
938

939
    if (ttl > 255) {
312✔
940
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
941
        exit(1);
3✔
942
    }
943

944
    if (unreachable_flag && alive_flag) {
309✔
945
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
946
        exit(1);
3✔
947
    }
948

949
    if (count_flag && loop_flag) {
306✔
950
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
951
        exit(1);
3✔
952
    }
953

954
#ifdef FPING_SAFE_LIMITS
955
    if ((interval < (int64_t)MIN_INTERVAL * 1000000 || perhost_interval < (int64_t)MIN_PERHOST_INTERVAL * 1000000)
303✔
956
        && getuid()) {
87✔
957
        fprintf(stderr, "%s: these options are too risky for mere mortals.\n", prog);
6✔
958
        fprintf(stderr, "%s: You need -i >= %u and -p >= %u\n",
8✔
959
            prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL);
2✔
960
        exit(1);
6✔
961
    }
962
#endif
963

964
    if (ping_data_size > MAX_PING_DATA) {
297✔
965
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
966
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
967
        exit(1);
3✔
968
    }
969

970
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
294✔
971
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
972
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
973
        exit(1);
6✔
974
    }
975

976
    if (count_flag) {
288✔
977
        if (verbose_flag)
92✔
978
            per_recv_flag = 1;
51✔
979

980
        alive_flag = unreachable_flag = verbose_flag = 0;
92✔
981
    }
26✔
982

983
    if (loop_flag) {
288✔
984
        if (!report_interval)
×
985
            per_recv_flag = 1;
×
986

987
        alive_flag = unreachable_flag = verbose_flag = 0;
×
988
    }
989

990
    if (alive_flag || unreachable_flag || min_reachable)
288✔
991
        verbose_flag = 0;
27✔
992

993
    trials = (count > retry + 1) ? count : retry + 1;
292✔
994

995
    /* auto-tune default timeout for count/loop modes
996
     * see also github #32 */
997
    if (loop_flag || count_flag) {
292✔
998
        if (!timeout_flag) {
92✔
999
            timeout = perhost_interval;
87✔
1000
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
87✔
1001
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1002
            }
1003
        }
25✔
1004
    }
26✔
1005

1006
#if defined(DEBUG) || defined(_DEBUG)
1007
    if (debugging & DBG_TRACE)
1008
        trace_flag = 1;
1009

1010
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1011
        randomly_lose_flag = 1;
1012
        lose_factor = 1; /* ie, 1/4 */
1013
    }
1014

1015
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1016
        randomly_lose_flag = 1;
1017
        lose_factor = 5; /* ie, 3/4 */
1018
    }
1019

1020
    if (debugging & DBG_PRINT_PER_SYSTEM)
1021
        print_per_system_flag = 1;
1022

1023
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1024
        report_all_rtts_flag = 1;
1025

1026
    if (trace_flag) {
1027
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1028
            prog, count, retry, interval / 1e6);
1029
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1030
            perhost_interval / 1e6, timeout / 1e6);
1031
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1032
            ping_data_size, trials);
1033

1034
        if (verbose_flag)
1035
            fprintf(stderr, "  verbose_flag set\n");
1036
        if (multif_flag)
1037
            fprintf(stderr, "  multif_flag set\n");
1038
        if (name_flag)
1039
            fprintf(stderr, "  name_flag set\n");
1040
        if (addr_flag)
1041
            fprintf(stderr, "  addr_flag set\n");
1042
        if (stats_flag)
1043
            fprintf(stderr, "  stats_flag set\n");
1044
        if (unreachable_flag)
1045
            fprintf(stderr, "  unreachable_flag set\n");
1046
        if (alive_flag)
1047
            fprintf(stderr, "  alive_flag set\n");
1048
        if (elapsed_flag)
1049
            fprintf(stderr, "  elapsed_flag set\n");
1050
        if (version_flag)
1051
            fprintf(stderr, "  version_flag set\n");
1052
        if (count_flag)
1053
            fprintf(stderr, "  count_flag set\n");
1054
        if (loop_flag)
1055
            fprintf(stderr, "  loop_flag set\n");
1056
        if (backoff_flag)
1057
            fprintf(stderr, "  backoff_flag set\n");
1058
        if (per_recv_flag)
1059
            fprintf(stderr, "  per_recv_flag set\n");
1060
        if (report_all_rtts_flag)
1061
            fprintf(stderr, "  report_all_rtts_flag set\n");
1062
        if (randomly_lose_flag)
1063
            fprintf(stderr, "  randomly_lose_flag set\n");
1064
        if (print_per_system_flag)
1065
            fprintf(stderr, "  print_per_system_flag set\n");
1066
        if (outage_flag)
1067
            fprintf(stderr, "  outage_flag set\n");
1068
        if (netdata_flag)
1069
            fprintf(stderr, "  netdata_flag set\n");
1070
    }
1071
#endif /* DEBUG || _DEBUG */
1072

1073
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1074
    if (ttl > 0) {
292✔
1075
        if (socket4 >= 0) {
3✔
1076
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1077
                perror("setting time to live");
×
1078
            }
1079
        }
1✔
1080
#ifdef IPV6
1081
        if (socket6 >= 0) {
3✔
1082
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1083
                perror("setting time to live");
×
1084
            }
1085
        }
1✔
1086
#endif
1087
    }
1✔
1088

1089
#if HAVE_SO_TIMESTAMPNS
1090
    {
1091
        int opt = 1;
210✔
1092
        if (socket4 >= 0) {
210✔
1093
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
210✔
1094
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1095
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1096
                }
1097
            }
1098
        }
1099
#ifdef IPV6
1100
        if (socket6 >= 0) {
210✔
1101
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
210✔
1102
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1103
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1104
                }
1105
            }
1106
        }
1107
#endif
1108
    }
1109
#endif
1110

1111
    update_current_time();
292✔
1112
    start_time = current_time_ns;
292✔
1113

1114
    /* handle host names supplied on command line or in a file */
1115
    /* if the generate_flag is on, then generate the IP list */
1116

1117
    argv = &argv[optparse_state.optind];
292✔
1118
    argc -= optparse_state.optind;
292✔
1119

1120
    /* calculate how many ping can be in-flight per host */
1121
    if (count_flag) {
292✔
1122
        event_storage_count = count;
97✔
1123
    }
31✔
1124
    else if (loop_flag) {
195✔
1125
        if (perhost_interval > timeout) {
×
1126
            event_storage_count = 1;
×
1127
        }
1128
        else {
1129
            event_storage_count = 1 + timeout / perhost_interval;
×
1130
        }
1131
    }
1132
    else {
1133
        event_storage_count = 1;
195✔
1134
    }
1135

1136
    /* file and generate are mutually exclusive */
1137
    /* file and command line are mutually exclusive */
1138
    /* generate requires command line parameters beyond the switches */
1139
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
292✔
1140
        usage(1);
9✔
1141

1142
    /* if no conditions are specified, then assume input from stdin */
1143
    if (!*argv && !filename && !generate_flag)
284✔
1144
        filename = "-";
3✔
1145

1146
    if (*argv && !generate_flag) {
284✔
1147
        while (*argv) {
478✔
1148
            add_name(*argv);
265✔
1149
            ++argv;
265✔
1150
        }
1151
    }
55✔
1152
    else if (filename) {
71✔
1153
        FILE *ping_file;
1154
        char line[132];
1155
        char host[132];
1156

1157
        if (strcmp(filename, "-") == 0)
15✔
1158
            ping_file = fdopen(0, "r");
6✔
1159
        else
1160
            ping_file = fopen(filename, "r");
9✔
1161

1162
        if (!ping_file)
15✔
1163
            errno_crash_and_burn("fopen");
3✔
1164

1165
        while (fgets(line, sizeof(line), ping_file)) {
43✔
1166
            if (sscanf(line, "%s", host) != 1)
30✔
1167
                continue;
3✔
1168

1169
            if ((!*host) || (host[0] == '#')) /* magic to avoid comments */
27✔
1170
                continue;
3✔
1171

1172
            add_name(host);
24✔
1173
        }
1174

1175
        fclose(ping_file);
13✔
1176
    }
5✔
1177
    else if (*argv && generate_flag) {
56✔
1178
        if (argc == 1) {
56✔
1179
            /* one target: we expect a cidr range (n.n.n.n/m) */
1180
            add_cidr(argv[0]);
32✔
1181
        }
10✔
1182
        else if (argc == 2) {
24✔
1183
            add_range(argv[0], argv[1]);
21✔
1184
        }
5✔
1185
        else {
1186
            usage(1);
3✔
1187
        }
1188
    }
16✔
1189
    else {
1190
        usage(1);
×
1191
    }
1192

1193
    if (!num_hosts) {
254✔
1194
        exit(num_noaddress ? 2 : 1);
15✔
1195
    }
1196

1197
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
239✔
1198
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
16✔
1199
    }
2✔
1200
#ifdef IPV6
1201
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
237✔
1202
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
14✔
1203
    }
1204
#endif
1205

1206
    /* allocate and initialize array to map host nr to host_entry */
1207
    {
1208
        struct event *cursor = event_queue_ping.first;
235✔
1209
        int i = 0;
235✔
1210
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
235✔
1211
        if (!table)
235✔
1212
            crash_and_burn("Can't malloc array of hosts");
×
1213
        /* initialize table of hosts. we know that we have ping events scheduled
1214
         * for each of them */
1215
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
552✔
1216
            table[i] = cursor->host;
317✔
1217
            cursor->host->i = i;
317✔
1218
            i++;
317✔
1219
        }
89✔
1220
    }
1221

1222
    init_ping_buffer_ipv4(ping_data_size);
235✔
1223
#ifdef IPV6
1224
    init_ping_buffer_ipv6(ping_data_size);
235✔
1225
#endif
1226

1227
#ifdef USE_SIGACTION
1228
    memset(&act, 0, sizeof(act));
235✔
1229
    act.sa_handler = signal_handler;
235✔
1230
    sigemptyset(&act.sa_mask);
235✔
1231
    sigaddset(&act.sa_mask, SIGINT);
235✔
1232
    sigaddset(&act.sa_mask, SIGQUIT);
235✔
1233
    act.sa_flags = SA_RESTART;
235✔
1234
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
235✔
1235
        crash_and_burn("failure to set signal handler");
24✔
1236
    }
24✔
1237
#else
1238
    signal(SIGINT, signal_handler);
1239
    signal(SIGQUIT, signal_handler);
1240
#endif
1241
    setlinebuf(stdout);
211✔
1242

1243
    if (report_interval) {
211✔
1244
        next_report_time = current_time_ns + report_interval;
24✔
1245
    }
8✔
1246

1247
    last_send_time = 0;
211✔
1248

1249
    seqmap_init();
211✔
1250

1251
    /* main loop */
1252
    main_loop();
211✔
1253

1254
/* Debug: CPU Performance */
1255
#if defined(DEBUG) || defined(_DEBUG)
1256
    perf_cpu_end = clock();
1257
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1258
    printf("[DEBUG] CPU time used: %f sec", perf_cpu_time_used);
1259
#endif /* DEBUG || _DEBUG */
1260

1261
    finish();
211✔
1262

1263
    return 0;
47✔
1264
}
1265

1266
static inline int64_t timespec_ns(struct timespec *a)
3,924✔
1267
{
1268
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
3,924✔
1269
}
1270

1271
void add_cidr(char *addr)
31✔
1272
{
1273
    char *addr_end;
1274
    char *mask_str;
1275
    unsigned long mask;
1276
    unsigned long bitmask;
1277
    int ret;
1278
    struct addrinfo addr_hints;
1279
    struct addrinfo *addr_res;
1280
    unsigned long net_addr;
1281
    unsigned long net_last;
1282

1283
    /* Split address from mask */
1284
    addr_end = strchr(addr, '/');
31✔
1285
    if (addr_end == NULL) {
31✔
1286
        usage(1);
3✔
1287
    }
1✔
1288
    *addr_end = '\0';
29✔
1289
    mask_str = addr_end + 1;
29✔
1290
    mask = atoi(mask_str);
29✔
1291

1292
    /* parse address (IPv4 only) */
1293
    memset(&addr_hints, 0, sizeof(struct addrinfo));
29✔
1294
    addr_hints.ai_family = AF_UNSPEC;
29✔
1295
    addr_hints.ai_flags = AI_NUMERICHOST;
29✔
1296
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
29✔
1297
    if (ret) {
29✔
1298
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
3✔
1299
        exit(1);
3✔
1300
    }
1301
    if (addr_res->ai_family != AF_INET) {
26✔
1302
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
5✔
1303
        exit(1);
5✔
1304
    }
1305
    net_addr = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
21✔
1306
    freeaddrinfo(addr_res);
21✔
1307

1308
    /* check mask */
1309
    if (mask < 1 || mask > 32) {
21✔
1310
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %s)\n", prog, mask_str);
6✔
1311
        exit(1);
6✔
1312
    }
1313

1314
    /* convert mask integer from 1 to 32 to a bitmask */
1315
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
15✔
1316

1317
    /* calculate network range */
1318
    net_addr &= bitmask;
15✔
1319
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
15✔
1320

1321
    /* exclude network and broadcast address for regular prefixes */
1322
    if (mask < 31) {
15✔
1323
        net_last--;
9✔
1324
        net_addr++;
9✔
1325
    }
3✔
1326

1327
    /* add all hosts in that network (net_addr and net_last inclusive) */
1328
    add_addr_range_ipv4(net_addr, net_last);
15✔
1329
}
13✔
1330

1331
void add_range(char *start, char *end)
21✔
1332
{
1333
    struct addrinfo addr_hints;
1334
    struct addrinfo *addr_res;
1335
    unsigned long start_long;
1336
    unsigned long end_long;
1337
    int ret;
1338

1339
    /* parse start address (IPv4 only) */
1340
    memset(&addr_hints, 0, sizeof(struct addrinfo));
21✔
1341
    addr_hints.ai_family = AF_UNSPEC;
21✔
1342
    addr_hints.ai_flags = AI_NUMERICHOST;
21✔
1343
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
21✔
1344
    if (ret) {
21✔
1345
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
3✔
1346
        exit(1);
3✔
1347
    }
1348
    if (addr_res->ai_family != AF_INET) {
18✔
1349
        freeaddrinfo(addr_res);
4✔
1350
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
4✔
1351
        exit(1);
4✔
1352
    }
1353
    start_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
14✔
1354

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

1372
    /* add IPv4 addresses from closed interval [start_long,end_long] */
1373
    add_addr_range_ipv4(start_long, end_long);
9✔
1374
}
7✔
1375

1376
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
24✔
1377
{
1378
    /* check if generator limit is exceeded */
1379
    if (end_long >= start_long + MAX_GENERATE) {
24✔
1380
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1381
        exit(1);
6✔
1382
    }
1383

1384
    /* generate */
1385
    for (; start_long <= end_long; start_long++) {
66✔
1386
        struct in_addr in_addr_tmp;
1387
        char buffer[20];
1388
        in_addr_tmp.s_addr = htonl(start_long);
48✔
1389
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
48✔
1390
        add_name(buffer);
48✔
1391
    }
16✔
1392
}
18✔
1393

1394
void main_loop()
223✔
1395
{
1396
    int64_t lt;
1397
    int64_t wait_time_ns;
1398
    struct event *event;
1399
    struct host_entry *h;
1400

1401
    while (event_queue_ping.first || event_queue_timeout.first) {
1,351✔
1402
        dbg_printf("%s", "# main_loop\n");
1403

1404
        /* timeout event ? */
1405
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,133✔
1406
            event = ev_dequeue(&event_queue_timeout);
89✔
1407
            h = event->host;
89✔
1408

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

1411
            stats_add(h, event->ping_index, 0, -1);
89✔
1412

1413
            if (per_recv_flag) {
89✔
1414
                if (timestamp_flag) {
19✔
1415
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1416
                }
1417
                printf("%-*s : [%d], timed out",
19✔
1418
                    max_hostname_len, h->host, event->ping_index);
5✔
1419
                if (h->num_recv > 0) {
19✔
1420
                    printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1421
                }
1422
                else {
1423
                    printf(" (NaN avg, ");
19✔
1424
                }
1425
                if (h->num_recv <= h->num_sent) {
19✔
1426
                    printf("%d%% loss)",
19✔
1427
                        ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1428
                }
5✔
1429
                else {
1430
                    printf("%d%% return)",
×
1431
                        (h->num_recv_total * 100) / h->num_sent);
×
1432
                }
1433
                printf("\n");
19✔
1434
            }
5✔
1435

1436
            /* do we need to send a retry? */
1437
            if (!loop_flag && !count_flag) {
89✔
1438
                if (h->num_sent < retry + 1) {
46✔
1439
                    if (backoff_flag) {
30✔
1440
                        h->timeout *= backoff;
30✔
1441
                    }
6✔
1442
                    send_ping(h, event->ping_index);
30✔
1443
                }
6✔
1444
            }
10✔
1445

1446
            /* note: we process first timeout events, because we might need to
1447
             * wait to process ping events, while we for sure never need to
1448
             * wait for timeout events.
1449
             */
1450
            continue;
89✔
1451
        }
1452

1453
        /* ping event ? */
1454
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,225✔
1455
            /* Make sure that we don't ping more than once every "interval" */
1456
            lt = current_time_ns - last_send_time;
763✔
1457
            if (lt < interval)
763✔
1458
                goto wait_for_reply;
160✔
1459

1460
            /* Dequeue the event */
1461
            event = ev_dequeue(&event_queue_ping);
603✔
1462
            h = event->host;
603✔
1463

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

1466
            /* Send the ping */
1467
            send_ping(h, event->ping_index);
603✔
1468

1469
            /* Loop and count mode: schedule next ping */
1470
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
603✔
1471
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
301✔
1472
            }
97✔
1473
        }
181✔
1474

1475
    wait_for_reply:
424✔
1476

1477
        /* When is the next ping next event? */
1478
        wait_time_ns = -1;
1,044✔
1479
        if (event_queue_ping.first) {
1,044✔
1480
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
764✔
1481
            if (wait_time_ns < 0)
764✔
1482
                wait_time_ns = 0;
329✔
1483
            /* make sure that we wait enough, so that the inter-ping delay is
1484
             * bigger than 'interval' */
1485
            if (wait_time_ns < interval) {
764✔
1486
                lt = current_time_ns - last_send_time;
331✔
1487
                if (lt < interval) {
331✔
1488
                    wait_time_ns = interval - lt;
329✔
1489
                }
119✔
1490
            }
121✔
1491

1492
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1493
        }
244✔
1494

1495
        /* When is the next timeout event? */
1496
        if (event_queue_timeout.first) {
1,044✔
1497
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
671✔
1498
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
671✔
1499
                wait_time_ns = wait_time_timeout;
289✔
1500
                if (wait_time_ns < 0) {
289✔
1501
                    wait_time_ns = 0;
1✔
1502
                }
1503
            }
73✔
1504

1505
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1506
        }
195✔
1507

1508
        /* When is the next report due? */
1509
        if (report_interval && (loop_flag || count_flag)) {
1,044✔
1510
            int64_t wait_time_next_report = next_report_time - current_time_ns;
206✔
1511
            if (wait_time_next_report < wait_time_ns) {
206✔
1512
                wait_time_ns = wait_time_next_report;
69✔
1513
                if (wait_time_ns < 0) {
69✔
1514
                    wait_time_ns = 0;
×
1515
                }
1516
            }
23✔
1517

1518
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1519
        }
68✔
1520

1521
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1522
        if (wait_time_ns == -1) {
1,044✔
1523
            break;
2✔
1524
        }
1525

1526
        /* end of loop was requested by interrupt signal handler */
1527
        if (finish_requested) {
1,042✔
1528
            break;
3✔
1529
        }
1530

1531
        /* Receive replies */
1532
        /* (this is what sleeps during each loop iteration) */
1533
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1534
        if (wait_for_reply(wait_time_ns)) {
1,039✔
1535
            while (wait_for_reply(0))
941✔
1536
                ; /* process other replies in the queue */
1537
        }
164✔
1538

1539
        update_current_time();
1,039✔
1540

1541
        if (status_snapshot) {
1,039✔
1542
            status_snapshot = 0;
×
1543
            print_per_system_splits();
×
1544
        }
1545

1546
        /* Print report */
1547
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,039✔
1548
            if (netdata_flag)
42✔
1549
                print_netdata();
3✔
1550
            else
1551
                print_per_system_splits();
39✔
1552

1553
            while (current_time_ns >= next_report_time) {
84✔
1554
                next_report_time += report_interval;
42✔
1555
            }
1556
        }
14✔
1557
    }
1558
}
223✔
1559

1560
/************************************************************
1561

1562
  Function: signal_handler
1563

1564
*************************************************************
1565

1566
  Inputs:  int signum
1567

1568
  Description:
1569

1570
  SIGQUIT signal handler - set flag and return
1571
  SIGINT signal handler - set flag and return
1572

1573
************************************************************/
1574

1575
void signal_handler(int signum)
×
1576
{
1577
    switch (signum) {
×
1578
    case SIGINT:
1579
        finish_requested = 1;
×
1580
        break;
×
1581

1582
    case SIGQUIT:
1583
        status_snapshot = 1;
×
1584
        break;
×
1585
    }
1586
}
×
1587

1588
/************************************************************
1589

1590
  Function: update_current_time
1591

1592
*************************************************************/
1593

1594
void update_current_time()
3,162✔
1595
{
1596
    clock_gettime(CLOCKID, &current_time);
3,162✔
1597
    current_time_ns = timespec_ns(&current_time);
3,162✔
1598
}
3,162✔
1599

1600
/************************************************************
1601

1602
  Function: finish
1603

1604
*************************************************************
1605

1606
  Inputs:  void (none)
1607

1608
  Description:
1609

1610
  Main program clean up and exit point
1611

1612
************************************************************/
1613

1614
void finish()
223✔
1615
{
1616
    int i;
1617
    HOST_ENTRY *h;
1618

1619
    update_current_time();
223✔
1620
    end_time = current_time_ns;
223✔
1621

1622
    /* tot up unreachables */
1623
    for (i = 0; i < num_hosts; i++) {
540✔
1624
        h = table[i];
317✔
1625

1626
        if (!h->num_recv) {
317✔
1627
            num_unreachable++;
46✔
1628

1629
            if (verbose_flag || unreachable_flag) {
46✔
1630
                printf("%s", h->host);
10✔
1631

1632
                if (verbose_flag)
10✔
1633
                    printf(" is unreachable");
7✔
1634

1635
                printf("\n");
10✔
1636
            }
2✔
1637
        }
12✔
1638
    }
89✔
1639

1640
    if (count_flag || loop_flag)
223✔
1641
        print_per_system_stats();
92✔
1642
#if defined(DEBUG) || defined(_DEBUG)
1643
    else if (print_per_system_flag)
1644
        print_per_system_stats();
1645
#endif /* DEBUG || _DEBUG */
1646

1647
    if (stats_flag)
223✔
1648
        print_global_stats();
9✔
1649

1650
    if (min_reachable) {
223✔
1651
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
1652
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1653
            exit(0);
6✔
1654
        }
1655
        else {
1656
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
1657
            exit(1);
3✔
1658
        }
1659
    }
1660

1661
    if (num_noaddress)
214✔
1662
        exit(2);
3✔
1663
    else if (num_alive != num_hosts)
211✔
1664
        exit(1);
25✔
1665

1666
    exit(0);
186✔
1667
}
1668

1669
/************************************************************
1670

1671
  Function: print_per_system_stats
1672

1673
*************************************************************
1674

1675
  Inputs:  void (none)
1676

1677
  Description:
1678

1679

1680
************************************************************/
1681

1682
void print_per_system_stats(void)
92✔
1683
{
1684
    int i, j, avg, outage_ms;
1685
    HOST_ENTRY *h;
1686
    int64_t resp;
1687

1688
    if (verbose_flag || per_recv_flag)
92✔
1689
        fprintf(stderr, "\n");
51✔
1690

1691
    for (i = 0; i < num_hosts; i++) {
212✔
1692
        h = table[i];
120✔
1693
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
120✔
1694

1695
        if (report_all_rtts_flag) {
120✔
1696
            for (j = 0; j < h->num_sent; j++) {
230✔
1697
                if ((resp = h->resp_times[j]) >= 0)
192✔
1698
                    fprintf(stderr, " %s", sprint_tm(resp));
184✔
1699
                else
1700
                    fprintf(stderr, " -");
8✔
1701
            }
60✔
1702

1703
            fprintf(stderr, "\n");
38✔
1704
        }
10✔
1705
        else {
1706
            if (h->num_recv <= h->num_sent) {
82✔
1707
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
106✔
1708
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
82✔
1709

1710
                if (outage_flag) {
82✔
1711
                    /* Time outage total */
1712
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
1713
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
1714
                }
3✔
1715
            }
24✔
1716
            else {
1717
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
1718
                    h->num_sent, h->num_recv,
1719
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
1720
            }
1721

1722
            if (h->num_recv) {
82✔
1723
                avg = h->total_time / h->num_recv;
71✔
1724
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
71✔
1725
                fprintf(stderr, "/%s", sprint_tm(avg));
71✔
1726
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
71✔
1727
            }
21✔
1728

1729
            fprintf(stderr, "\n");
82✔
1730
        }
1731
    }
34✔
1732
}
92✔
1733

1734
/************************************************************
1735

1736
  Function: print_netdata
1737

1738
*************************************************************
1739

1740
  Inputs:  void (none)
1741

1742
  Description:
1743

1744

1745
************************************************************/
1746

1747
void print_netdata(void)
3✔
1748
{
1749
    static int sent_charts = 0;
1750

1751
    int i;
1752
    int64_t avg;
1753
    HOST_ENTRY *h;
1754

1755
    for (i = 0; i < num_hosts; i++) {
6✔
1756
        h = table[i];
3✔
1757

1758
        if (!sent_charts) {
3✔
1759
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1760
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
1761
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
1762
        }
1✔
1763

1764
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
1765
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
1766
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
1767
        printf("END\n");
3✔
1768

1769
        if (!sent_charts) {
3✔
1770
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1771
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
1772
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
1773
        }
1✔
1774

1775
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
1776
        /*
1777
        if( h->num_recv_i <= h->num_sent_i )
1778
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
1779
        else
1780
            printf("SET lost = 0\n");
1781
*/
1782

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

1786
        if (!sent_charts) {
3✔
1787
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1788
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
1789
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
1790
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
1791
        }
1✔
1792

1793
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
1794
        if (h->num_recv_i) {
3✔
1795
            avg = h->total_time_i / h->num_recv_i;
3✔
1796
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
1797
            printf("SET avg = %" PRId64 "\n", avg);
3✔
1798
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
1799
        }
1✔
1800
        printf("END\n");
3✔
1801

1802
        stats_reset_interval(h);
3✔
1803
    }
1✔
1804

1805
    sent_charts = 1;
3✔
1806
}
3✔
1807

1808
/************************************************************
1809

1810
  Function: print_per_system_splits
1811

1812
*************************************************************
1813

1814
  Inputs:  void (none)
1815

1816
  Description:
1817

1818

1819
************************************************************/
1820

1821
void print_per_system_splits(void)
39✔
1822
{
1823
    int i, avg, outage_ms_i;
1824
    HOST_ENTRY *h;
1825
    struct tm *curr_tm;
1826

1827
    if (verbose_flag || per_recv_flag)
39✔
1828
        fprintf(stderr, "\n");
×
1829

1830
    update_current_time();
39✔
1831
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
1832
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
1833
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
1834

1835
    for (i = 0; i < num_hosts; i++) {
78✔
1836
        h = table[i];
39✔
1837
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
1838

1839
        if (h->num_recv_i <= h->num_sent_i) {
39✔
1840
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
1841
                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✔
1842

1843
            if (outage_flag) {
39✔
1844
                /* Time outage  */
1845
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
1846
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
1847
            }
4✔
1848
        }
13✔
1849
        else {
1850
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
1851
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
1852
        }
1853

1854
        if (h->num_recv_i) {
39✔
1855
            avg = h->total_time_i / h->num_recv_i;
27✔
1856
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
1857
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
1858
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
1859
        }
9✔
1860

1861
        fprintf(stderr, "\n");
39✔
1862
        if (!cumulative_stats_flag) {
39✔
1863
            stats_reset_interval(h);
27✔
1864
        }
9✔
1865
    }
13✔
1866
}
39✔
1867

1868
/************************************************************
1869

1870
  Function: print_global_stats
1871

1872
*************************************************************
1873

1874
  Inputs:  void (none)
1875

1876
  Description:
1877

1878

1879
************************************************************/
1880

1881
void print_global_stats(void)
9✔
1882
{
1883
    fprintf(stderr, "\n");
9✔
1884
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
1885
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
1886
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
1887
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
1888
    fprintf(stderr, "\n");
9✔
1889
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
1890
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
1891
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
1892
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
1893
    fprintf(stderr, "\n");
9✔
1894

1895
    if (total_replies == 0) {
9✔
1896
        min_reply = 0;
3✔
1897
        max_reply = 0;
3✔
1898
        total_replies = 1;
3✔
1899
        sum_replies = 0;
3✔
1900
    }
1✔
1901

1902
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
1903
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
1904
        sprint_tm(sum_replies / total_replies));
3✔
1905
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
1906
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
1907
        (end_time - start_time) / 1e9);
9✔
1908
    fprintf(stderr, "\n");
9✔
1909
}
9✔
1910

1911
/************************************************************
1912

1913
  Function: send_ping
1914

1915
*************************************************************
1916

1917
  Inputs:  int s, HOST_ENTRY *h
1918

1919
  Description:
1920

1921
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
1922
  will be added on by the kernel.  The ID field is our UNIX process ID,
1923
  and the sequence number is an index into an array of outstanding
1924
  ping requests. The sequence number will later be used to quickly
1925
  figure out who the ping reply came from.
1926

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

1929
int send_ping(HOST_ENTRY *h, int index)
633✔
1930
{
1931
    int n;
1932
    int myseq;
1933
    int ret = 1;
633✔
1934
    uint8_t proto = ICMP_ECHO;
633✔
1935

1936
    update_current_time();
633✔
1937
    h->last_send_time = current_time_ns;
633✔
1938
    myseq = seqmap_add(h->i, index, current_time_ns);
633✔
1939

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

1942
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
633✔
1943
        if(icmp_request_typ == 13)
552✔
1944
            proto = ICMP_TSTAMP;
6✔
1945
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
552✔
1946
    }
172✔
1947
#ifdef IPV6
1948
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
81✔
1949
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
81✔
1950
    }
15✔
1951
#endif
1952
    else {
1953
        return 0;
×
1954
    }
1955

1956
    /* error sending? */
1957
    if (
446✔
1958
        (n < 0)
187✔
1959
#if defined(EHOSTDOWN)
1960
        && errno != EHOSTDOWN
193✔
1961
#endif
1962
    ) {
1963
        if (verbose_flag) {
6✔
1964
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
×
1965
        }
1966
        else {
1967
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
1968
        }
1969

1970
        h->num_sent++;
6✔
1971
        h->num_sent_i++;
6✔
1972
        if (!loop_flag)
6✔
1973
            h->resp_times[index] = RESP_ERROR;
6✔
1974

1975
        ret = 0;
6✔
1976
    }
1977
    else {
1978
        /* schedule timeout */
1979
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
627✔
1980

1981
        /* mark this trial as outstanding */
1982
        if (!loop_flag) {
627✔
1983
            h->resp_times[index] = RESP_WAITING;
627✔
1984
        }
187✔
1985
    }
1986

1987
    num_pingsent++;
633✔
1988
    last_send_time = h->last_send_time;
633✔
1989

1990
    return (ret);
633✔
1991
}
187✔
1992

1993
int socket_can_read(struct timeval *timeout)
1,980✔
1994
{
1995
    int nfound;
1996
    fd_set readset;
1997
    int socketmax;
1998

1999
#ifndef IPV6
2000
    socketmax = socket4;
2001
#else
2002
    socketmax = socket4 > socket6 ? socket4 : socket6;
1,980✔
2003
#endif
2004

2005
select_again:
1,488✔
2006
    FD_ZERO(&readset);
13,884✔
2007
    if (socket4 >= 0)
1,980✔
2008
        FD_SET(socket4, &readset);
1,980✔
2009
#ifdef IPV6
2010
    if (socket6 >= 0)
1,980✔
2011
        FD_SET(socket6, &readset);
1,980✔
2012
#endif
2013

2014
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
1,980✔
2015
    if (nfound < 0) {
1,980✔
2016
        if (errno == EINTR) {
×
2017
            /* interrupted system call: redo the select */
2018
            goto select_again;
×
2019
        }
2020
        else {
2021
            perror("select");
×
2022
        }
2023
    }
2024

2025
    if (nfound > 0) {
1,980✔
2026
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
941✔
2027
            return socket4;
781✔
2028
        }
2029
#ifdef IPV6
2030
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
160✔
2031
            return socket6;
160✔
2032
        }
2033
#endif
2034
    }
2035

2036
    return -1;
1,039✔
2037
}
492✔
2038

2039
int receive_packet(int64_t wait_time,
1,980✔
2040
#if HAVE_SO_TIMESTAMPNS
2041
    int64_t *reply_timestamp,
2042
#else
2043
    int64_t *reply_timestamp __attribute__((unused)),
2044
#endif
2045
    struct sockaddr *reply_src_addr,
2046
    size_t reply_src_addr_len,
2047
    char *reply_buf,
2048
    size_t reply_buf_len)
2049
{
2050
    struct timeval to;
2051
    int s = 0;
1,980✔
2052
    int recv_len;
2053
    static unsigned char msg_control[40];
2054
    struct iovec msg_iov = {
2,964✔
2055
        reply_buf,
492✔
2056
        reply_buf_len
492✔
2057
    };
2058
    struct msghdr recv_msghdr = {0};
1,980✔
2059
    recv_msghdr.msg_name = reply_src_addr;
1,980✔
2060
    recv_msghdr.msg_namelen = reply_src_addr_len;
1,980✔
2061
    recv_msghdr.msg_iov = &msg_iov;
1,980✔
2062
    recv_msghdr.msg_iovlen = 1;
1,980✔
2063
    recv_msghdr.msg_control = &msg_control;
1,980✔
2064
    recv_msghdr.msg_controllen = sizeof(msg_control);
1,980✔
2065
#if HAVE_SO_TIMESTAMPNS
2066
    struct cmsghdr *cmsg;
2067
#endif
2068

2069
    /* Wait for a socket to become ready */
2070
    if (wait_time) {
1,980✔
2071
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,038✔
2072
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,038✔
2073
    }
313✔
2074
    else {
2075
        to.tv_sec = 0;
942✔
2076
        to.tv_usec = 0;
942✔
2077
    }
2078
    s = socket_can_read(&to);
1,980✔
2079
    if (s == -1) {
1,980✔
2080
        return 0; /* timeout */
1,039✔
2081
    }
2082

2083
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
941✔
2084
    if (recv_len <= 0) {
941✔
2085
        return 0;
×
2086
    }
2087

2088
#if HAVE_SO_TIMESTAMPNS
2089
    /* ancilliary data */
2090
    {
2091
        struct timespec reply_timestamp_ts;
2092
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,143✔
2093
             cmsg != NULL;
762✔
2094
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
762✔
2095
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
762✔
2096
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
762✔
2097
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
762✔
2098
            }
2099
        }
2100
    }
2101
#endif
2102

2103
#if defined(DEBUG) || defined(_DEBUG)
2104
    if (randomly_lose_flag) {
2105
        if ((random() & 0x07) <= lose_factor)
2106
            return 0;
2107
    }
2108
#endif
2109

2110
    return recv_len;
941✔
2111
}
492✔
2112

2113
/* stats_add: update host statistics for a single packet that was received (or timed out)
2114
 * h: host entry to update
2115
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2116
 * success: 1 if response received, 0 otherwise
2117
 * latency: response time, in ns
2118
 */
2119
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
627✔
2120
{
2121
    /* sent count - we update only on receive/timeout, so that we don't get
2122
     * weird loss percentage, just because a packet was note recived yet.
2123
     */
2124
    h->num_sent++;
627✔
2125
    h->num_sent_i++;
627✔
2126

2127
    if (!success) {
627✔
2128
        if (!loop_flag && index >= 0) {
89✔
2129
            h->resp_times[index] = RESP_TIMEOUT;
89✔
2130
        }
23✔
2131
        num_timeout++;
89✔
2132
        return;
89✔
2133
    }
2134

2135
    /* received count */
2136
    h->num_recv++;
538✔
2137
    h->num_recv_i++;
538✔
2138

2139
    /* maximum */
2140
    if (!h->max_reply || latency > h->max_reply) {
538✔
2141
        h->max_reply = latency;
378✔
2142
    }
108✔
2143
    if (!h->max_reply_i || latency > h->max_reply_i) {
538✔
2144
        h->max_reply_i = latency;
396✔
2145
    }
113✔
2146

2147
    /* minimum */
2148
    if (!h->min_reply || latency < h->min_reply) {
538✔
2149
        h->min_reply = latency;
315✔
2150
    }
96✔
2151
    if (!h->min_reply_i || latency < h->min_reply_i) {
538✔
2152
        h->min_reply_i = latency;
337✔
2153
    }
101✔
2154

2155
    /* total time (for average) */
2156
    h->total_time += latency;
538✔
2157
    h->total_time_i += latency;
538✔
2158

2159
    /* response time per-packet (count mode) */
2160
    if (!loop_flag && index >= 0) {
538✔
2161
        h->resp_times[index] = latency;
538✔
2162
    }
164✔
2163
}
187✔
2164

2165
/* stats_reset_interval: reset interval statistics
2166
 * h: host entry to update
2167
 */
2168
void stats_reset_interval(HOST_ENTRY *h)
30✔
2169
{
2170
    h->num_sent_i = 0;
30✔
2171
    h->num_recv_i = 0;
30✔
2172
    h->max_reply_i = 0;
30✔
2173
    h->min_reply_i = 0;
30✔
2174
    h->total_time_i = 0;
30✔
2175
}
30✔
2176

2177
int decode_icmp_ipv4(
781✔
2178
    struct sockaddr *response_addr,
2179
    size_t response_addr_len,
2180
    char *reply_buf,
2181
    size_t reply_buf_len,
2182
    unsigned short *id,
2183
    unsigned short *seq,
2184
    int *ip_header_tos,
2185
    int *ip_header_ttl,
2186
    uint32_t *ip_header_otime_ms,
2187
    uint32_t *ip_header_rtime_ms,
2188
    uint32_t *ip_header_ttime_ms)
2189
{
2190
    struct icmp *icp;
2191
    int hlen = 0;
781✔
2192

2193
    if (!using_sock_dgram4) {
781✔
2194
        struct ip *ip = (struct ip *)reply_buf;
773✔
2195
        *ip_header_tos = ip->ip_tos;
773✔
2196
        *ip_header_ttl = ip->ip_ttl;
773✔
2197

2198
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2199
        /* The alpha headers are decidedly broken.
2200
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2201
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2202
         */
2203
        hlen = (ip->ip_vhl & 0x0F) << 2;
2204
#else
2205
        hlen = ip->ip_hl << 2;
773✔
2206
#endif
2207
    }
149✔
2208

2209
    if (reply_buf_len < hlen + ICMP_MINLEN) {
781✔
2210
        /* too short */
2211
        if (verbose_flag) {
×
2212
            char buf[INET6_ADDRSTRLEN];
2213
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2214
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2215
        }
2216
        return -1;
×
2217
    }
2218

2219
    icp = (struct icmp *)(reply_buf + hlen);
781✔
2220

2221
    if (icp->icmp_type != ICMP_ECHOREPLY && icp->icmp_type != ICMP_TSTAMPREPLY) {
781✔
2222
        /* Handle other ICMP packets */
2223
        struct icmp *sent_icmp;
2224
        SEQMAP_VALUE *seqmap_value;
2225
        char addr_ascii[INET6_ADDRSTRLEN];
2226
        HOST_ENTRY *h;
2227

2228
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2229
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
312✔
2230
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2231
            return -1;
×
2232
        }
2233

2234
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
312✔
2235

2236
        if ((sent_icmp->icmp_type != ICMP_ECHO && sent_icmp->icmp_type != ICMP_TSTAMP) || sent_icmp->icmp_id != ident4) {
312✔
2237
            /* not caused by us */
2238
            return -1;
312✔
2239
        }
2240

2241
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2242
        if (seqmap_value == NULL) {
×
2243
            return -1;
×
2244
        }
2245

2246
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2247

2248
        switch (icp->icmp_type) {
×
2249
        case ICMP_UNREACH:
2250
            h = table[seqmap_value->host_nr];
×
2251
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2252
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2253
                    addr_ascii, h->host);
2254
            }
2255
            else {
2256
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2257
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2258
            }
2259

2260
            print_warning("\n");
×
2261
            num_othericmprcvd++;
×
2262
            break;
×
2263

2264
        case ICMP_SOURCEQUENCH:
2265
        case ICMP_REDIRECT:
2266
        case ICMP_TIMXCEED:
2267
        case ICMP_PARAMPROB:
2268
            h = table[seqmap_value->host_nr];
×
2269
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2270
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2271
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2272
            }
2273
            else {
2274
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2275
                    icp->icmp_type, addr_ascii, h->host);
×
2276
            }
2277
            print_warning("\n");
×
2278
            num_othericmprcvd++;
×
2279
            break;
×
2280
        }
2281

2282
        return -1;
×
2283
    }
2284

2285
    *id = icp->icmp_id;
469✔
2286
    *seq = ntohs(icp->icmp_seq);
469✔
2287
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
469✔
2288

2289
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2290
        if (reply_buf_len < hlen + ICMP_MINLEN + 3 * sizeof(uint32_t)) {
6✔
2291
            if (verbose_flag) {
×
2292
                char buf[INET6_ADDRSTRLEN];
2293
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2294
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2295
            }
2296
            return -1;
×
2297
        }
2298

2299
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
6✔
2300
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
6✔
2301
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
6✔
2302
    }
2303

2304
    return hlen;
469✔
2305
}
149✔
2306

2307
#ifdef IPV6
2308
int decode_icmp_ipv6(
160✔
2309
    struct sockaddr *response_addr,
2310
    size_t response_addr_len,
2311
    char *reply_buf,
2312
    size_t reply_buf_len,
2313
    unsigned short *id,
2314
    unsigned short *seq)
2315
{
2316
    struct icmp6_hdr *icp;
2317

2318
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
160✔
2319
        if (verbose_flag) {
×
2320
            char buf[INET6_ADDRSTRLEN];
2321
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2322
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2323
        }
2324
        return 0; /* too short */
×
2325
    }
2326

2327
    icp = (struct icmp6_hdr *)reply_buf;
160✔
2328

2329
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
160✔
2330
        /* Handle other ICMP packets */
2331
        struct icmp6_hdr *sent_icmp;
2332
        SEQMAP_VALUE *seqmap_value;
2333
        char addr_ascii[INET6_ADDRSTRLEN];
2334
        HOST_ENTRY *h;
2335

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

2342
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
79✔
2343

2344
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
79✔
2345
            /* not caused by us */
2346
            return 0;
79✔
2347
        }
2348

2349
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2350
        if (seqmap_value == NULL) {
×
2351
            return 0;
×
2352
        }
2353

2354
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2355

2356
        switch (icp->icmp6_type) {
×
2357
        case ICMP_UNREACH:
2358
            h = table[seqmap_value->host_nr];
×
2359
            if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
×
2360
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2361
                    addr_ascii, h->host);
2362
            }
2363
            else {
2364
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2365
                    icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2366
            }
2367

2368
            print_warning("\n");
×
2369
            num_othericmprcvd++;
×
2370
            break;
×
2371

2372
        case ICMP_SOURCEQUENCH:
2373
        case ICMP_REDIRECT:
2374
        case ICMP_TIMXCEED:
2375
        case ICMP_PARAMPROB:
2376
            h = table[seqmap_value->host_nr];
×
2377
            if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
×
2378
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2379
                    icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
×
2380
            }
2381
            else {
2382
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2383
                    icp->icmp6_type, addr_ascii, h->host);
×
2384
            }
2385
            print_warning("\n");
×
2386
            num_othericmprcvd++;
×
2387
            break;
×
2388
        }
2389

2390
        return 0;
×
2391
    }
2392

2393
    *id = icp->icmp6_id;
81✔
2394
    *seq = ntohs(icp->icmp6_seq);
81✔
2395

2396
    return 1;
81✔
2397
}
30✔
2398
#endif
2399

2400
int wait_for_reply(int64_t wait_time)
1,980✔
2401
{
2402
    int result;
2403
    static char buffer[RECV_BUFSIZE];
2404
    struct sockaddr_storage response_addr;
2405
    int n, avg;
2406
    HOST_ENTRY *h;
2407
    int64_t this_reply;
2408
    int this_count;
2409
    int64_t recv_time = 0;
1,980✔
2410
    SEQMAP_VALUE *seqmap_value;
2411
    unsigned short id;
2412
    unsigned short seq;
2413
    int ip_header_tos = -1;
1,980✔
2414
    int ip_header_ttl = -1;
1,980✔
2415
    // ICMP Timestamp
2416
    uint32_t ip_header_otime_ms;
2417
    uint32_t ip_header_rtime_ms;
2418
    uint32_t ip_header_ttime_ms;
2419

2420
    /* Receive packet */
2421
    result = receive_packet(wait_time, /* max. wait time, in ns */
1,980✔
2422
        &recv_time, /* reply_timestamp */
2423
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2424
        sizeof(response_addr), /* reply_src_addr_len */
2425
        buffer, /* reply_buf */
2426
        sizeof(buffer) /* reply_buf_len */
2427
    );
2428

2429
    if (result <= 0) {
1,980✔
2430
        return 0;
1,039✔
2431
    }
2432

2433
    update_current_time();
941✔
2434
    if (recv_time == 0)
941✔
2435
        recv_time = current_time_ns;
179✔
2436

2437
    /* Process ICMP packet and retrieve id/seq */
2438
    if (response_addr.ss_family == AF_INET) {
941✔
2439
        int ip_hlen = decode_icmp_ipv4(
781✔
2440
            (struct sockaddr *)&response_addr,
2441
            sizeof(response_addr),
2442
            buffer,
2443
            sizeof(buffer),
2444
            &id,
2445
            &seq,
2446
            &ip_header_tos,
2447
            &ip_header_ttl,
2448
            &ip_header_otime_ms,
2449
            &ip_header_rtime_ms,
2450
            &ip_header_ttime_ms);
2451
        if (ip_hlen < 0) {
781✔
2452
            return 1;
312✔
2453
        }
2454
        if (id != ident4) {
469✔
2455
            return 1; /* packet received, but not the one we are looking for! */
×
2456
        }
2457
        if (!using_sock_dgram4) {
469✔
2458
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2459
             * with fping with IPv6 hosts */
2460
            result -= ip_hlen;
461✔
2461
        }
149✔
2462
    }
149✔
2463
#ifdef IPV6
2464
    else if (response_addr.ss_family == AF_INET6) {
160✔
2465
        if (!decode_icmp_ipv6(
160✔
2466
                (struct sockaddr *)&response_addr,
2467
                sizeof(response_addr),
2468
                buffer,
2469
                sizeof(buffer),
2470
                &id,
2471
                &seq)) {
2472
            return 1;
79✔
2473
        }
2474
        if (id != ident6) {
81✔
2475
            return 1; /* packet received, but not the one we are looking for! */
×
2476
        }
2477
    }
15✔
2478
#endif
2479
    else {
2480
        return 1;
×
2481
    }
2482

2483
    seqmap_value = seqmap_fetch(seq, current_time_ns);
550✔
2484
    if (seqmap_value == NULL) {
550✔
2485
        return 1;
×
2486
    }
2487

2488
    /* find corresponding host_entry */
2489
    n = seqmap_value->host_nr;
550✔
2490
    h = table[n];
550✔
2491
    this_count = seqmap_value->ping_count;
550✔
2492
    this_reply = recv_time - seqmap_value->ping_ts;
550✔
2493

2494
    /* update stats that include invalid replies */
2495
    h->num_recv_total++;
550✔
2496
    num_pingreceived++;
550✔
2497

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

2500
    /* optionally require reply source equal to target address */
2501
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
550✔
2502
        dbg_printf("discarding reply from wrong source address\n");
2503
        return 1;
12✔
2504
    }
2505

2506
    /* discard duplicates */
2507
    if (!loop_flag && h->resp_times[this_count] >= 0) {
538✔
2508
        if (!per_recv_flag) {
×
2509
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2510
                h->host, this_count, result, sprint_tm(this_reply));
2511

2512
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2513
                char buf[INET6_ADDRSTRLEN];
2514
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2515
                fprintf(stderr, " [<- %s]", buf);
×
2516
            }
2517
            fprintf(stderr, "\n");
×
2518
        }
2519
        return 1;
×
2520
    }
2521

2522
    /* discard reply if delay is larger than timeout
2523
     * (see also: github #32) */
2524
    if (this_reply > h->timeout) {
538✔
2525
        return 1;
×
2526
    }
2527

2528
    /* update stats */
2529
    stats_add(h, this_count, 1, this_reply);
538✔
2530
    // TODO: move to stats_add?
2531
    if (!max_reply || this_reply > max_reply)
538✔
2532
        max_reply = this_reply;
325✔
2533
    if (!min_reply || this_reply < min_reply)
538✔
2534
        min_reply = this_reply;
262✔
2535
    sum_replies += this_reply;
538✔
2536
    total_replies++;
538✔
2537

2538
    /* initialize timeout to initial timeout (without backoff) */
2539
    h->timeout = timeout;
538✔
2540

2541
    /* remove timeout event */
2542
    struct event *timeout_event = host_get_timeout_event(h, this_count);
538✔
2543
    if (timeout_event) {
538✔
2544
        ev_remove(&event_queue_timeout, timeout_event);
538✔
2545
    }
164✔
2546

2547
    /* print "is alive" */
2548
    if (h->num_recv == 1) {
538✔
2549
        num_alive++;
271✔
2550
        if (fast_reachable && num_alive >= min_reachable)
271✔
2551
                finish_requested = 1;
3✔
2552

2553
        if (verbose_flag || alive_flag) {
271✔
2554
            printf("%s", h->host);
154✔
2555

2556
            if (verbose_flag)
154✔
2557
                printf(" is alive");
136✔
2558

2559
            if(print_tos_flag) {
154✔
2560
                if(ip_header_tos != -1) {
13✔
2561
                    printf(" (TOS %d)", ip_header_tos);
9✔
2562
                }
3✔
2563
                else {
2564
                    printf(" (TOS unknown)");
4✔
2565
                }
2566
            }
3✔
2567

2568
            if (print_ttl_flag) {
154✔
2569
              if(ip_header_ttl != -1) {
13✔
2570
                  printf(" (TTL %d)", ip_header_ttl);
9✔
2571
              }
3✔
2572
              else {
2573
                  printf(" (TTL unknown)");
4✔
2574
              }
2575
            }
3✔
2576

2577
            if (icmp_request_typ == 13) {
154✔
2578
                printf(" (Timestamp Originate=%u Receive=%u Transmit=%u)", ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms);
2✔
2579
            }
2580

2581
            if (elapsed_flag)
154✔
2582
                printf(" (%s ms)", sprint_tm(this_reply));
6✔
2583

2584
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
154✔
2585
                char buf[INET6_ADDRSTRLEN];
2586
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
2✔
2587
                fprintf(stderr, " [<- %s]", buf);
2✔
2588
            }
2589

2590
            printf("\n");
154✔
2591
        }
42✔
2592
    }
77✔
2593

2594
    /* print received ping (unless --quiet) */
2595
    if (per_recv_flag) {
538✔
2596
        if (timestamp_flag) {
123✔
2597
            print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2598
        }
10✔
2599
        avg = h->total_time / h->num_recv;
123✔
2600
        printf("%-*s : [%d], %d bytes, %s ms",
123✔
2601
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
37✔
2602
        printf(" (%s avg, ", sprint_tm(avg));
123✔
2603

2604
        if (h->num_recv <= h->num_sent) {
123✔
2605
            printf("%d%% loss)",
123✔
2606
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
123✔
2607
        }
37✔
2608
        else {
2609
            printf("%d%% return)",
×
2610
                (h->num_recv_total * 100) / h->num_sent);
×
2611
        }
2612

2613
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
123✔
2614
            char buf[INET6_ADDRSTRLEN];
2615
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
4✔
2616
            fprintf(stderr, " [<- %s]", buf);
4✔
2617
        }
2618

2619
        if (icmp_request_typ == 13) {
123✔
2620
            printf(", ICMP timestamp: Originate=%u Receive=%u Transmit=%u", ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms);
4✔
2621
        }
2622

2623
        printf("\n");
123✔
2624
    }
37✔
2625

2626
    return 1;
538✔
2627
}
492✔
2628

2629
/************************************************************
2630

2631
  Function: add_name
2632

2633
*************************************************************
2634

2635
  Inputs:  char* name
2636

2637
  Description:
2638

2639
  process input name for addition to target list
2640
  name can turn into multiple targets via multiple interfaces (-m)
2641
  or via NIS groups
2642

2643
************************************************************/
2644

2645
void add_name(char *name)
337✔
2646
{
2647
    struct addrinfo *res0, *res, hints;
2648
    int ret_ga;
2649
    char *printname;
2650
    char namebuf[256];
2651
    char addrbuf[256];
2652

2653
    /* getaddrinfo */
2654
    memset(&hints, 0, sizeof(struct addrinfo));
337✔
2655
    hints.ai_flags = AI_UNUSABLE;
337✔
2656
    hints.ai_socktype = SOCK_RAW;
337✔
2657
    hints.ai_family = hints_ai_family;
337✔
2658
    if (hints_ai_family == AF_INET) {
337✔
2659
        hints.ai_protocol = IPPROTO_ICMP;
25✔
2660
    }
7✔
2661
#ifdef IPV6
2662
    else if (hints_ai_family == AF_INET6) {
312✔
2663
        hints.ai_protocol = IPPROTO_ICMPV6;
7✔
2664
    }
1✔
2665
#endif
2666
    else {
2667
        hints.ai_socktype = SOCK_STREAM;
305✔
2668
        hints.ai_protocol = 0;
305✔
2669
    }
2670
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
337✔
2671
    if (ret_ga) {
337✔
2672
        if (!quiet_flag)
15✔
2673
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
15✔
2674
        num_noaddress++;
15✔
2675
        return;
15✔
2676
    }
2677

2678
    /* NOTE: we could/should loop with res on all addresses like this:
2679
     * for (res = res0; res; res = res->ai_next) {
2680
     * We don't do it yet, however, because is is an incompatible change
2681
     * (need to implement a separate option for this)
2682
     */
2683
    for (res = res0; res; res = res->ai_next) {
322✔
2684
        /* name_flag: addr -> name lookup requested) */
2685
        if (name_flag || rdns_flag) {
324✔
2686
            int do_rdns = rdns_flag ? 1 : 0;
3✔
2687
            if (name_flag) {
3✔
2688
                /* Was it a numerical address? Only then do a rdns-query */
2689
                struct addrinfo *nres;
2690
                hints.ai_flags = AI_NUMERICHOST;
×
2691
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
2692
                    do_rdns = 1;
×
2693
                    freeaddrinfo(nres);
×
2694
                }
2695
            }
2696

2697
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2698
                printname = namebuf;
3✔
2699
            }
1✔
2700
            else {
2701
                printname = name;
×
2702
            }
2703
        }
1✔
2704
        else {
2705
            printname = name;
319✔
2706
        }
2707

2708
        /* addr_flag: name -> addr lookup requested */
2709
        if (addr_flag) {
322✔
2710
            int ret;
2711
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2712
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2713
            if (ret) {
3✔
2714
                if (!quiet_flag) {
×
2715
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2716
                }
2717
                continue;
×
2718
            }
2719

2720
            if (name_flag || rdns_flag) {
3✔
2721
                char nameaddrbuf[512 + 3];
2722
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
2723
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2724
            }
2725
            else {
2726
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2727
            }
2728
        }
1✔
2729
        else {
2730
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
319✔
2731
        }
2732

2733
        if (!multif_flag) {
322✔
2734
            break;
322✔
2735
        }
2736
    }
2737

2738
    freeaddrinfo(res0);
322✔
2739
}
95✔
2740

2741
/************************************************************
2742

2743
  Function: add_addr
2744

2745
*************************************************************
2746

2747
  Description:
2748

2749
  add single address to list of hosts to be pinged
2750

2751
************************************************************/
2752

2753
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
322✔
2754
{
2755
    HOST_ENTRY *p;
2756
    int n;
2757
    int64_t *i;
2758

2759
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
322✔
2760
    if (!p)
322✔
2761
        crash_and_burn("can't allocate HOST_ENTRY");
×
2762

2763
    p->name = strdup(name);
322✔
2764
    p->host = strdup(host);
322✔
2765
    memcpy(&p->saddr, ipaddr, ipaddr_len);
322✔
2766
    p->saddr_len = ipaddr_len;
322✔
2767
    p->timeout = timeout;
322✔
2768
    p->min_reply = 0;
322✔
2769

2770
    if (netdata_flag) {
322✔
2771
        char *s = p->name;
3✔
2772
        while (*s) {
30✔
2773
            if (!isalnum(*s))
27✔
2774
                *s = '_';
9✔
2775
            s++;
27✔
2776
        }
2777
    }
1✔
2778

2779
    if (strlen(p->host) > max_hostname_len)
322✔
2780
        max_hostname_len = strlen(p->host);
237✔
2781

2782
    /* array for response time results */
2783
    if (!loop_flag) {
322✔
2784
        i = (int64_t *)malloc(trials * sizeof(int64_t));
322✔
2785
        if (!i)
322✔
2786
            crash_and_burn("can't allocate resp_times array");
×
2787

2788
        for (n = 1; n < trials; n++)
1,375✔
2789
            i[n] = RESP_UNUSED;
1,053✔
2790

2791
        p->resp_times = i;
322✔
2792
    }
90✔
2793

2794
    /* allocate event storage */
2795
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
322✔
2796
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
322✔
2797

2798
    /* schedule first ping */
2799
    host_add_ping_event(p, 0, current_time_ns);
322✔
2800

2801
    num_hosts++;
322✔
2802
}
322✔
2803

2804
/************************************************************
2805

2806
  Function: crash_and_burn
2807

2808
*************************************************************
2809

2810
  Inputs:  char* message
2811

2812
  Description:
2813

2814
************************************************************/
2815

2816
void crash_and_burn(char *message)
×
2817
{
2818
    fprintf(stderr, "%s: %s\n", prog, message);
×
2819
    exit(4);
×
2820
}
2821

2822
/************************************************************
2823

2824
  Function: errno_crash_and_burn
2825

2826
*************************************************************
2827

2828
  Inputs:  char* message
2829

2830
  Description:
2831

2832
************************************************************/
2833

2834
void errno_crash_and_burn(char *message)
8✔
2835
{
2836
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
2837
    exit(4);
8✔
2838
}
2839

2840
/************************************************************
2841

2842
  Function: print_warning
2843

2844
  Description: fprintf(stderr, ...), unless running with -q
2845

2846
*************************************************************/
2847

2848
void print_warning(char *format, ...)
15✔
2849
{
2850
    va_list args;
2851
    if (!quiet_flag) {
15✔
2852
        va_start(args, format);
15✔
2853
        vfprintf(stderr, format, args);
15✔
2854
        va_end(args);
15✔
2855
    }
5✔
2856
}
15✔
2857

2858
/************************************************************
2859

2860
  Function: sprint_tm
2861

2862
*************************************************************
2863

2864
  render nanosecond int64_t value into milliseconds string with three digits of
2865
  precision.
2866

2867
************************************************************/
2868

2869
const char *sprint_tm(int64_t ns)
757✔
2870
{
2871
    static char buf[10];
2872
    double t = (double)ns / 1e6;
757✔
2873

2874
    if (t < 0.0) {
757✔
2875
        /* negative (unexpected) */
2876
        sprintf(buf, "%.2g", t);
×
2877
    }
2878
    else if (t < 1.0) {
757✔
2879
        /* <= 0.99 ms */
2880
        sprintf(buf, "%.3f", t);
757✔
2881
    }
235✔
2882
    else if (t < 10.0) {
×
2883
        /* 1.00 - 9.99 ms */
2884
        sprintf(buf, "%.2f", t);
×
2885
    }
2886
    else if (t < 100.0) {
×
2887
        /* 10.0 - 99.9 ms */
2888
        sprintf(buf, "%.1f", t);
×
2889
    }
2890
    else if (t < 1000000.0) {
×
2891
        /* 100 - 1'000'000 ms */
2892
        sprintf(buf, "%.0f", t);
×
2893
    }
2894
    else {
2895
        sprintf(buf, "%.3e", t);
×
2896
    }
2897

2898
    return (buf);
757✔
2899
}
2900

2901
/************************************************************
2902

2903
  Function: addr_cmp
2904

2905
*************************************************************/
2906
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
299✔
2907
{
2908
    if (a->sa_family != b->sa_family) {
299✔
2909
        return a->sa_family - b->sa_family;
×
2910
    }
2911
    else {
2912
        if (a->sa_family == AF_INET) {
299✔
2913
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
245✔
2914
        }
2915
        else if (a->sa_family == AF_INET6) {
54✔
2916
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
62✔
2917
                &((struct sockaddr_in6 *)b)->sin6_addr,
54✔
2918
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
2919
        }
2920
    }
2921

2922
    return 0;
×
2923
}
81✔
2924

2925
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
623✔
2926
{
2927
    struct event *event = &h->event_storage_ping[index % event_storage_count];
623✔
2928
    event->host = h;
623✔
2929
    event->ping_index = index;
623✔
2930
    event->ev_time = ev_time;
623✔
2931
    ev_enqueue(&event_queue_ping, event);
623✔
2932

2933
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
2934
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2935
}
623✔
2936

2937
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
627✔
2938
{
2939
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
627✔
2940
    event->host = h;
627✔
2941
    event->ping_index = index;
627✔
2942
    event->ev_time = ev_time;
627✔
2943
    ev_enqueue(&event_queue_timeout, event);
627✔
2944

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

2949
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
538✔
2950
{
2951
    return &h->event_storage_timeout[index % event_storage_count];
538✔
2952
}
2953

2954
/************************************************************
2955

2956
  Function: ev_enqueue
2957

2958
  Enqueue an event
2959

2960
  The queue is sorted by event->ev_time, so that queue->first always points to
2961
  the earliest event.
2962

2963
  We start scanning the queue from the tail, because we assume
2964
  that new events mostly get inserted with a event time higher
2965
  than the others.
2966

2967
*************************************************************/
2968
void ev_enqueue(struct event_queue *queue, struct event *event)
1,250✔
2969
{
2970
    struct event *i;
2971
    struct event *i_prev;
2972

2973
    /* Empty list */
2974
    if (queue->last == NULL) {
1,250✔
2975
        event->ev_next = NULL;
977✔
2976
        event->ev_prev = NULL;
977✔
2977
        queue->first = event;
977✔
2978
        queue->last = event;
977✔
2979
        return;
977✔
2980
    }
2981

2982
    /* Insert on tail? */
2983
    if (event->ev_time - queue->last->ev_time >= 0) {
273✔
2984
        event->ev_next = NULL;
273✔
2985
        event->ev_prev = queue->last;
273✔
2986
        queue->last->ev_next = event;
273✔
2987
        queue->last = event;
273✔
2988
        return;
273✔
2989
    }
2990

2991
    /* Find insertion point */
2992
    i = queue->last;
×
2993
    while (1) {
2994
        i_prev = i->ev_prev;
×
2995
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
2996
            event->ev_prev = i_prev;
×
2997
            event->ev_next = i;
×
2998
            i->ev_prev = event;
×
2999
            if (i_prev != NULL) {
×
3000
                i_prev->ev_next = event;
×
3001
            }
3002
            else {
3003
                queue->first = event;
×
3004
            }
3005
            return;
×
3006
        }
3007
        i = i_prev;
×
3008
    }
3009
}
374✔
3010

3011
/************************************************************
3012

3013
  Function: ev_dequeue
3014

3015
*************************************************************/
3016
struct event *ev_dequeue(struct event_queue *queue)
692✔
3017
{
3018
    struct event *dequeued;
3019

3020
    if (queue->first == NULL) {
692✔
3021
        return NULL;
×
3022
    }
3023
    dequeued = queue->first;
692✔
3024
    ev_remove(queue, dequeued);
692✔
3025

3026
    return dequeued;
692✔
3027
}
204✔
3028

3029
/************************************************************
3030

3031
  Function: ev_remove
3032

3033
*************************************************************/
3034
void ev_remove(struct event_queue *queue, struct event *event)
1,230✔
3035
{
3036
    if (queue->first == event) {
1,230✔
3037
        queue->first = event->ev_next;
1,221✔
3038
    }
365✔
3039
    if (queue->last == event) {
1,230✔
3040
        queue->last = event->ev_prev;
978✔
3041
    }
286✔
3042
    if (event->ev_prev) {
1,230✔
3043
        event->ev_prev->ev_next = event->ev_next;
9✔
3044
    }
3✔
3045
    if (event->ev_next) {
1,230✔
3046
        event->ev_next->ev_prev = event->ev_prev;
252✔
3047
    }
82✔
3048
    event->ev_prev = NULL;
1,230✔
3049
    event->ev_next = NULL;
1,230✔
3050
}
1,230✔
3051

3052
/************************************************************
3053

3054
  Function: print_human_readable_time from current_time_ns
3055

3056
*************************************************************/
3057
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
30✔
3058
{
3059
    char time_buffer[100];
3060
    time_t current_time_s;
3061
    struct tm *local_time;
3062

3063
    current_time_s = current_time_ns / 1000000000;
30✔
3064
    local_time = localtime(&current_time_s);
30✔
3065
    switch(timestamp_format) {
30✔
3066
        case 1:
4✔
3067
            // timestamp-format ctime
3068
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
6✔
3069
            printf("[%s] ", time_buffer);
6✔
3070
            break;
6✔
3071
        case 2:
4✔
3072
            // timestamp-format iso
3073
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
6✔
3074
            printf("[%s] ", time_buffer);
6✔
3075
            break;
6✔
3076
        case 3:
4✔
3077
            // timestamp-format rfc3339
3078
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
6✔
3079
            printf("[%s] ", time_buffer);
6✔
3080
            break;
6✔
3081
        default:
8✔
3082
            printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3083
    }
4✔
3084
}
30✔
3085

3086
/************************************************************
3087

3088
  Function: usage
3089

3090
*************************************************************
3091

3092
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3093

3094
  Description:
3095

3096
************************************************************/
3097

3098
void usage(int is_error)
118✔
3099
{
3100
    FILE *out = is_error ? stderr : stdout;
118✔
3101
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
118✔
3102
    fprintf(out, "\n");
118✔
3103
    fprintf(out, "Probing options:\n");
118✔
3104
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
118✔
3105
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
118✔
3106
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
118✔
3107
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
118✔
3108
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
118✔
3109
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
118✔
3110
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
118✔
3111
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
118✔
3112
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
118✔
3113
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
118✔
3114
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
118✔
3115
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
118✔
3116
#ifdef SO_BINDTODEVICE
3117
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
80✔
3118
#endif
3119
#ifdef SO_MARK
3120
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
80✔
3121
#endif
3122
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
118✔
3123
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
118✔
3124
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
118✔
3125
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
118✔
3126
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
118✔
3127
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
118✔
3128
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
118✔
3129
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
118✔
3130
    fprintf(out, "   -S, --src=IP       set source address\n");
118✔
3131
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
118✔
3132
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
118✔
3133
    fprintf(out, "       --check-source discard replies not from target address\n");
118✔
3134
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
118✔
3135
    fprintf(out, "\n");
118✔
3136
    fprintf(out, "Output options:\n");
118✔
3137
    fprintf(out, "   -a, --alive        show targets that are alive\n");
118✔
3138
    fprintf(out, "   -A, --addr         show targets by address\n");
118✔
3139
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
118✔
3140
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
118✔
3141
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
118✔
3142
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
118✔
3143
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
118✔
3144
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
118✔
3145
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
118✔
3146
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
118✔
3147
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
118✔
3148
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
118✔
3149
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
118✔
3150
    fprintf(out, "   -s, --stats        print final stats\n");
118✔
3151
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
118✔
3152
    fprintf(out, "   -v, --version      show version\n");
118✔
3153
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
118✔
3154
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
118✔
3155
    fprintf(out, "       --print-tos    show received TOS value\n");
118✔
3156
    fprintf(out, "       --print-ttl    show IP TTL value\n");
118✔
3157
    exit(is_error);
118✔
3158
}
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