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

schweikert / fping / 13090545081

01 Feb 2025 04:48PM UTC coverage: 87.642% (+0.03%) from 87.611%
13090545081

Pull #378

github

web-flow
Merge 5411732a0 into 511aa3720
Pull Request #378: Fix for GitHub issue #377

25 of 29 new or added lines in 1 file covered. (86.21%)

111 existing lines in 1 file now uncovered.

1312 of 1497 relevant lines covered (87.64%)

317.94 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
#define MAX_TARGET_NAME_LEN 255 /* maximum target name length read from file */
144

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

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

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

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

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

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

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

219
#define ICMP_UNREACH_MAXTYPE 15
220

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

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

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

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

268
#define EV_TYPE_PING 1
269
#define EV_TYPE_TIMEOUT 2
270

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

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

284
/*** globals ***/
285

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

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

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

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

314
unsigned int debugging = 0;
315

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

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

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

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

377
unsigned int fwmark = 0;
378

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

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

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

417
/************************************************************
418

419
  Function: p_setsockopt
420

421
*************************************************************
422

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

425
  Description:
426

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

430
************************************************************/
431

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

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

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

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

450
    return res;
12✔
451
}
452

453
/************************************************************
454

455
  Function: main
456

457
*************************************************************
458

459
  Inputs:  int argc, char** argv
460

461
  Description:
462

463
  Main program entry point
464

465
************************************************************/
466

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

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

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

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

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

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

513
    if (!suid && suid != getuid()) {
560✔
514
        /* *temporarily* drop privileges */
515
        if (seteuid(getuid()) == -1)
524✔
516
            perror("cannot setuid");
×
517
    }
154✔
518

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

525
    /* get command line options */
526

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

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

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

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

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

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

688
            break;
93✔
689

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

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

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

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

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

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

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

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

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

738
            break;
27✔
739

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

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

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

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

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

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

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

776
            break;
8✔
777

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

850
            break;
6✔
851
#endif
852

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

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

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

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

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

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

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

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

939
    /* validate various option settings */
940

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

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

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

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

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

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

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

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

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

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

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

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

1007
    if (alive_flag || unreachable_flag || min_reachable)
366✔
1008
        verbose_flag = 0;
37✔
1009

1010
    trials = (count > retry + 1) ? count : retry + 1;
382✔
1011

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

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

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

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

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

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

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

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

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

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

1128
    update_current_time();
382✔
1129
    start_time = current_time_ns;
382✔
1130

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

1134
    argv = &argv[optparse_state.optind];
382✔
1135
    argc -= optparse_state.optind;
382✔
1136

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

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

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

1163
    if (*argv && !generate_flag) {
364✔
1164
        while (*argv) {
512✔
1165
            add_name(*argv);
285✔
1166
            ++argv;
285✔
1167
        }
1168
    }
57✔
1169
    else if (filename) {
137✔
1170
        FILE *ping_file;
1171
        char line[MAX_TARGET_NAME_LEN + 1];
1172
        char host[MAX_TARGET_NAME_LEN + 1];
1173
        char scratch[MAX_TARGET_NAME_LEN + 1];
1174
        int skip = 0;
81✔
1175

1176
        if (strcmp(filename, "-") == 0)
81✔
1177
            ping_file = fdopen(0, "r");
57✔
1178
        else
1179
            ping_file = fopen(filename, "r");
24✔
1180

1181
        if (!ping_file)
81✔
1182
            errno_crash_and_burn("fopen");
3✔
1183

1184
        /*
1185
         * Read the first word of every non-comment line, skip everything else.
1186
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1187
         * character is a '#' are interpreted as comments and ignored.)
1188
        */
1189
        while (fgets(line, sizeof(line), ping_file)) {
256✔
1190
            if (sscanf(line, "%s", host) != 1)
195✔
1191
                continue;
36✔
1192

1193
            if ((!*host) || (host[0] == '#')) { /* magic to avoid comments */
159✔
1194
                skip = 1;
6✔
1195
                continue;
6✔
1196
            }
1197

1198
            /*
1199
             * Handle non-empty input lines longer than the line buffer.
1200
             * We have found at least the start of a word if we get here.
1201
             */
1202
            if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
153✔
1203
                char discard1[MAX_TARGET_NAME_LEN + 1];
1204
                char discard2[MAX_TARGET_NAME_LEN + 1];
1205
                if (sscanf(line, "%s%s", discard1, discard2) == 2) {
87✔
1206
                    skip = 1; /* a second word starts in this part */
6✔
1207
                }
2✔
1208
                if (isspace(line[sizeof(line) - 2])) {
87✔
1209
                    skip = 1; /* the first word ends in this part */
42✔
1210
                }
14✔
1211
            }
29✔
1212
            /* read remainder of this input line */
1213
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
234✔
1214
                if (skip)
99✔
1215
                    continue; /* skip rest of data in this input line */
66✔
1216
                if (isspace(line[0])) {
33✔
1217
                    skip = 1; /* the first word ended in previous part */
6✔
1218
                    continue;
6✔
1219
                }
1220
                if (sscanf(line, "%s", scratch) != 1) {
27✔
NEW
1221
                    skip = 1; /* empty or blank part of line, skip the rest */
×
NEW
1222
                    continue;
×
1223
                }
1224
                if ((!*scratch) || (scratch[0] == '#')) {
27✔
NEW
1225
                    skip = 1; /* empty, or comment; skip the rest */
×
NEW
1226
                    continue;
×
1227
                }
1228
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
27✔
1229
                    fprintf(stderr, "%s: target name too long\n", prog);
18✔
1230
                    exit(1);
18✔
1231
                }
1232
                /* append remainder of word started in previous line part */
1233
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
9✔
1234
                skip = 1; /* first word is complete, skip the rest */
9✔
1235
            }
1236
            skip = 0; /* complete input line has been read */
135✔
1237

1238
            add_name(host);
135✔
1239
        }
1240

1241
        fclose(ping_file);
61✔
1242
    }
21✔
1243
    else if (*argv && generate_flag) {
56✔
1244
        if (argc == 1) {
56✔
1245
            /* one target: we expect a cidr range (n.n.n.n/m) */
1246
            add_cidr(argv[0]);
32✔
1247
        }
10✔
1248
        else if (argc == 2) {
24✔
1249
            add_range(argv[0], argv[1]);
21✔
1250
        }
5✔
1251
        else {
1252
            usage(1);
3✔
1253
        }
1254
    }
16✔
1255
    else {
UNCOV
1256
        usage(1);
×
1257
    }
1258

1259
    if (!num_hosts) {
316✔
1260
        exit(num_noaddress ? 2 : 1);
20✔
1261
    }
1262

1263
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
296✔
1264
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
16✔
1265
    }
2✔
1266
#ifdef IPV6
1267
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
294✔
1268
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
14✔
1269
    }
1270
#endif
1271

1272
    /* allocate and initialize array to map host nr to host_entry */
1273
    {
1274
        struct event *cursor = event_queue_ping.first;
292✔
1275
        int i = 0;
292✔
1276
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
292✔
1277
        if (!table)
292✔
UNCOV
1278
            crash_and_burn("Can't malloc array of hosts");
×
1279
        /* initialize table of hosts. we know that we have ping events scheduled
1280
         * for each of them */
1281
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
738✔
1282
            table[i] = cursor->host;
446✔
1283
            cursor->host->i = i;
446✔
1284
            i++;
446✔
1285
        }
130✔
1286
    }
1287

1288
    init_ping_buffer_ipv4(ping_data_size);
292✔
1289
#ifdef IPV6
1290
    init_ping_buffer_ipv6(ping_data_size);
292✔
1291
#endif
1292

1293
#ifdef USE_SIGACTION
1294
    memset(&act, 0, sizeof(act));
292✔
1295
    act.sa_handler = signal_handler;
292✔
1296
    sigemptyset(&act.sa_mask);
292✔
1297
    sigaddset(&act.sa_mask, SIGINT);
292✔
1298
    sigaddset(&act.sa_mask, SIGQUIT);
292✔
1299
    act.sa_flags = SA_RESTART;
292✔
1300
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
292✔
1301
        crash_and_burn("failure to set signal handler");
24✔
1302
    }
24✔
1303
#else
1304
    signal(SIGINT, signal_handler);
1305
    signal(SIGQUIT, signal_handler);
1306
#endif
1307
    setlinebuf(stdout);
268✔
1308

1309
    if (report_interval) {
268✔
1310
        next_report_time = current_time_ns + report_interval;
24✔
1311
    }
8✔
1312

1313
    last_send_time = 0;
268✔
1314

1315
    seqmap_init();
268✔
1316

1317
    /* main loop */
1318
    main_loop();
268✔
1319

1320
/* Debug: CPU Performance */
1321
#if defined(DEBUG) || defined(_DEBUG)
1322
    perf_cpu_end = clock();
1323
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1324
    printf("[DEBUG] CPU time used: %f sec", perf_cpu_time_used);
1325
#endif /* DEBUG || _DEBUG */
1326

1327
    finish();
268✔
1328

1329
    return 0;
64✔
1330
}
1331

1332
static inline int64_t timespec_ns(struct timespec *a)
4,869✔
1333
{
1334
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
4,869✔
1335
}
1336

1337
#if HAVE_SO_TIMESTAMPNS
1338
/* convert a struct timeval to nanoseconds */
1339
static inline int64_t timeval_ns(struct timeval *a)
1340
{
1341
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1342
}
1343
#endif /* HAVE_SO_TIMESTAMPNS */
1344

1345
void add_cidr(char *addr)
31✔
1346
{
1347
    char *addr_end;
1348
    char *mask_str;
1349
    unsigned long mask;
1350
    unsigned long bitmask;
1351
    int ret;
1352
    struct addrinfo addr_hints;
1353
    struct addrinfo *addr_res;
1354
    unsigned long net_addr;
1355
    unsigned long net_last;
1356

1357
    /* Split address from mask */
1358
    addr_end = strchr(addr, '/');
31✔
1359
    if (addr_end == NULL) {
31✔
1360
        usage(1);
3✔
1361
    }
1✔
1362
    *addr_end = '\0';
29✔
1363
    mask_str = addr_end + 1;
29✔
1364
    mask = atoi(mask_str);
29✔
1365

1366
    /* parse address (IPv4 only) */
1367
    memset(&addr_hints, 0, sizeof(struct addrinfo));
29✔
1368
    addr_hints.ai_family = AF_UNSPEC;
29✔
1369
    addr_hints.ai_flags = AI_NUMERICHOST;
29✔
1370
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
29✔
1371
    if (ret) {
29✔
1372
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
3✔
1373
        exit(1);
3✔
1374
    }
1375
    if (addr_res->ai_family != AF_INET) {
26✔
1376
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
5✔
1377
        exit(1);
5✔
1378
    }
1379
    net_addr = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
21✔
1380
    freeaddrinfo(addr_res);
21✔
1381

1382
    /* check mask */
1383
    if (mask < 1 || mask > 32) {
21✔
1384
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %s)\n", prog, mask_str);
6✔
1385
        exit(1);
6✔
1386
    }
1387

1388
    /* convert mask integer from 1 to 32 to a bitmask */
1389
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
15✔
1390

1391
    /* calculate network range */
1392
    net_addr &= bitmask;
15✔
1393
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
15✔
1394

1395
    /* exclude network and broadcast address for regular prefixes */
1396
    if (mask < 31) {
15✔
1397
        net_last--;
9✔
1398
        net_addr++;
9✔
1399
    }
3✔
1400

1401
    /* add all hosts in that network (net_addr and net_last inclusive) */
1402
    add_addr_range_ipv4(net_addr, net_last);
15✔
1403
}
13✔
1404

1405
void add_range(char *start, char *end)
21✔
1406
{
1407
    struct addrinfo addr_hints;
1408
    struct addrinfo *addr_res;
1409
    unsigned long start_long;
1410
    unsigned long end_long;
1411
    int ret;
1412

1413
    /* parse start address (IPv4 only) */
1414
    memset(&addr_hints, 0, sizeof(struct addrinfo));
21✔
1415
    addr_hints.ai_family = AF_UNSPEC;
21✔
1416
    addr_hints.ai_flags = AI_NUMERICHOST;
21✔
1417
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
21✔
1418
    if (ret) {
21✔
1419
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
3✔
1420
        exit(1);
3✔
1421
    }
1422
    if (addr_res->ai_family != AF_INET) {
18✔
1423
        freeaddrinfo(addr_res);
4✔
1424
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
4✔
1425
        exit(1);
4✔
1426
    }
1427
    start_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
14✔
1428

1429
    /* parse end address (IPv4 only) */
1430
    memset(&addr_hints, 0, sizeof(struct addrinfo));
14✔
1431
    addr_hints.ai_family = AF_UNSPEC;
14✔
1432
    addr_hints.ai_flags = AI_NUMERICHOST;
14✔
1433
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
14✔
1434
    if (ret) {
14✔
1435
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
3✔
1436
        exit(1);
3✔
1437
    }
1438
    if (addr_res->ai_family != AF_INET) {
11✔
1439
        freeaddrinfo(addr_res);
2✔
1440
        fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
2✔
1441
        exit(1);
2✔
1442
    }
1443
    end_long = ntohl(((struct sockaddr_in *)addr_res->ai_addr)->sin_addr.s_addr);
9✔
1444
    freeaddrinfo(addr_res);
9✔
1445

1446
    /* add IPv4 addresses from closed interval [start_long,end_long] */
1447
    add_addr_range_ipv4(start_long, end_long);
9✔
1448
}
7✔
1449

1450
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
24✔
1451
{
1452
    /* check if generator limit is exceeded */
1453
    if (end_long >= start_long + MAX_GENERATE) {
24✔
1454
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1455
        exit(1);
6✔
1456
    }
1457

1458
    /* generate */
1459
    for (; start_long <= end_long; start_long++) {
66✔
1460
        struct in_addr in_addr_tmp;
1461
        char buffer[20];
1462
        in_addr_tmp.s_addr = htonl(start_long);
48✔
1463
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
48✔
1464
        add_name(buffer);
48✔
1465
    }
16✔
1466
}
18✔
1467

1468
void main_loop()
280✔
1469
{
1470
    int64_t lt;
1471
    int64_t wait_time_ns;
1472
    struct event *event;
1473
    struct host_entry *h;
1474

1475
    while (event_queue_ping.first || event_queue_timeout.first) {
1,636✔
1476
        dbg_printf("%s", "# main_loop\n");
1477

1478
        /* timeout event ? */
1479
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,361✔
1480
            event = ev_dequeue(&event_queue_timeout);
89✔
1481
            h = event->host;
89✔
1482

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

1485
            stats_add(h, event->ping_index, 0, -1);
89✔
1486

1487
            if (per_recv_flag) {
89✔
1488
                if (timestamp_flag) {
19✔
UNCOV
1489
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1490
                }
1491
                printf("%-*s : [%d], timed out",
19✔
1492
                    max_hostname_len, h->host, event->ping_index);
5✔
1493
                if (h->num_recv > 0) {
19✔
UNCOV
1494
                    printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1495
                }
1496
                else {
1497
                    printf(" (NaN avg, ");
19✔
1498
                }
1499
                if (h->num_recv <= h->num_sent) {
19✔
1500
                    printf("%d%% loss)",
19✔
1501
                        ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1502
                }
5✔
1503
                else {
UNCOV
1504
                    printf("%d%% return)",
×
1505
                        (h->num_recv_total * 100) / h->num_sent);
×
1506
                }
1507
                printf("\n");
19✔
1508
            }
5✔
1509

1510
            /* do we need to send a retry? */
1511
            if (!loop_flag && !count_flag) {
89✔
1512
                if (h->num_sent < retry + 1) {
46✔
1513
                    if (backoff_flag) {
30✔
1514
                        h->timeout *= backoff;
30✔
1515
                    }
6✔
1516
                    send_ping(h, event->ping_index);
30✔
1517
                }
6✔
1518
            }
10✔
1519

1520
            /* note: we process first timeout events, because we might need to
1521
             * wait to process ping events, while we for sure never need to
1522
             * wait for timeout events.
1523
             */
1524
            continue;
89✔
1525
        }
1526

1527
        /* ping event ? */
1528
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,498✔
1529
            /* Make sure that we don't ping more than once every "interval" */
1530
            lt = current_time_ns - last_send_time;
990✔
1531
            if (lt < interval)
990✔
1532
                goto wait_for_reply;
244✔
1533

1534
            /* Dequeue the event */
1535
            event = ev_dequeue(&event_queue_ping);
746✔
1536
            h = event->host;
746✔
1537

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

1540
            /* Send the ping */
1541
            send_ping(h, event->ping_index);
746✔
1542

1543
            /* Loop and count mode: schedule next ping */
1544
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
746✔
1545
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
315✔
1546
            }
101✔
1547
        }
226✔
1548

1549
    wait_for_reply:
517✔
1550

1551
        /* When is the next ping next event? */
1552
        wait_time_ns = -1;
1,272✔
1553
        if (event_queue_ping.first) {
1,272✔
1554
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
937✔
1555
            if (wait_time_ns < 0)
937✔
1556
                wait_time_ns = 0;
498✔
1557
            /* make sure that we wait enough, so that the inter-ping delay is
1558
             * bigger than 'interval' */
1559
            if (wait_time_ns < interval) {
937✔
1560
                lt = current_time_ns - last_send_time;
501✔
1561
                if (lt < interval) {
501✔
1562
                    wait_time_ns = interval - lt;
500✔
1563
                }
186✔
1564
            }
187✔
1565

1566
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1567
        }
301✔
1568

1569
        /* When is the next timeout event? */
1570
        if (event_queue_timeout.first) {
1,272✔
1571
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
815✔
1572
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
815✔
1573
                wait_time_ns = wait_time_timeout;
345✔
1574
                if (wait_time_ns < 0) {
345✔
1575
                    wait_time_ns = 0;
1✔
1576
                }
1577
            }
90✔
1578

1579
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1580
        }
242✔
1581

1582
        /* When is the next report due? */
1583
        if (report_interval && (loop_flag || count_flag)) {
1,272✔
1584
            int64_t wait_time_next_report = next_report_time - current_time_ns;
204✔
1585
            if (wait_time_next_report < wait_time_ns) {
204✔
1586
                wait_time_ns = wait_time_next_report;
69✔
1587
                if (wait_time_ns < 0) {
69✔
UNCOV
1588
                    wait_time_ns = 0;
×
1589
                }
1590
            }
23✔
1591

1592
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1593
        }
66✔
1594

1595
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1596
        if (wait_time_ns == -1) {
1,272✔
1597
            break;
2✔
1598
        }
1599

1600
        /* end of loop was requested by interrupt signal handler */
1601
        if (finish_requested) {
1,270✔
1602
            break;
3✔
1603
        }
1604

1605
        /* Receive replies */
1606
        /* (this is what sleeps during each loop iteration) */
1607
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1608
        if (wait_for_reply(wait_time_ns)) {
1,267✔
1609
            while (wait_for_reply(0))
1,182✔
1610
                ; /* process other replies in the queue */
1611
        }
209✔
1612

1613
        update_current_time();
1,267✔
1614

1615
        if (status_snapshot) {
1,267✔
UNCOV
1616
            status_snapshot = 0;
×
UNCOV
1617
            print_per_system_splits();
×
1618
        }
1619

1620
        /* Print report */
1621
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,267✔
1622
            if (netdata_flag)
42✔
1623
                print_netdata();
3✔
1624
            else
1625
                print_per_system_splits();
39✔
1626

1627
            while (current_time_ns >= next_report_time) {
84✔
1628
                next_report_time += report_interval;
42✔
1629
            }
1630
        }
14✔
1631
    }
1632
}
280✔
1633

1634
/************************************************************
1635

1636
  Function: signal_handler
1637

1638
*************************************************************
1639

1640
  Inputs:  int signum
1641

1642
  Description:
1643

1644
  SIGQUIT signal handler - set flag and return
1645
  SIGINT signal handler - set flag and return
1646

1647
************************************************************/
1648

UNCOV
1649
void signal_handler(int signum)
×
1650
{
UNCOV
1651
    switch (signum) {
×
1652
    case SIGINT:
UNCOV
1653
        finish_requested = 1;
×
UNCOV
1654
        break;
×
1655

1656
    case SIGQUIT:
UNCOV
1657
        status_snapshot = 1;
×
UNCOV
1658
        break;
×
1659
    }
1660
}
×
1661

1662
/************************************************************
1663

1664
  Function: update_current_time
1665

1666
*************************************************************/
1667

1668
void update_current_time()
3,911✔
1669
{
1670
    clock_gettime(CLOCKID, &current_time);
3,911✔
1671
    current_time_ns = timespec_ns(&current_time);
3,911✔
1672
}
3,911✔
1673

1674
/************************************************************
1675

1676
  Function: finish
1677

1678
*************************************************************
1679

1680
  Inputs:  void (none)
1681

1682
  Description:
1683

1684
  Main program clean up and exit point
1685

1686
************************************************************/
1687

1688
void finish()
280✔
1689
{
1690
    int i;
1691
    HOST_ENTRY *h;
1692

1693
    update_current_time();
280✔
1694
    end_time = current_time_ns;
280✔
1695

1696
    /* tot up unreachables */
1697
    for (i = 0; i < num_hosts; i++) {
726✔
1698
        h = table[i];
446✔
1699

1700
        if (!h->num_recv) {
446✔
1701
            num_unreachable++;
46✔
1702

1703
            if (verbose_flag || unreachable_flag) {
46✔
1704
                printf("%s", h->host);
10✔
1705

1706
                if (verbose_flag)
10✔
1707
                    printf(" is unreachable");
7✔
1708

1709
                printf("\n");
10✔
1710
            }
2✔
1711
        }
12✔
1712
    }
130✔
1713

1714
    if (count_flag || loop_flag)
280✔
1715
        print_per_system_stats();
100✔
1716
#if defined(DEBUG) || defined(_DEBUG)
1717
    else if (print_per_system_flag)
1718
        print_per_system_stats();
1719
#endif /* DEBUG || _DEBUG */
1720

1721
    if (stats_flag)
280✔
1722
        print_global_stats();
9✔
1723

1724
    if (min_reachable) {
280✔
1725
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
1726
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
1727
            exit(0);
6✔
1728
        }
1729
        else {
1730
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
1731
            exit(1);
3✔
1732
        }
1733
    }
1734

1735
    if (num_noaddress)
271✔
1736
        exit(2);
3✔
1737
    else if (num_alive != num_hosts)
268✔
1738
        exit(1);
25✔
1739

1740
    exit(0);
243✔
1741
}
1742

1743
/************************************************************
1744

1745
  Function: print_per_system_stats
1746

1747
*************************************************************
1748

1749
  Inputs:  void (none)
1750

1751
  Description:
1752

1753

1754
************************************************************/
1755

1756
void print_per_system_stats(void)
100✔
1757
{
1758
    int i, j, avg, outage_ms;
1759
    HOST_ENTRY *h;
1760
    int64_t resp;
1761

1762
    if (verbose_flag || per_recv_flag)
100✔
1763
        fprintf(stderr, "\n");
59✔
1764

1765
    for (i = 0; i < num_hosts; i++) {
234✔
1766
        h = table[i];
134✔
1767
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
1768

1769
        if (report_all_rtts_flag) {
134✔
1770
            for (j = 0; j < h->num_sent; j++) {
248✔
1771
                if ((resp = h->resp_times[j]) >= 0)
204✔
1772
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
1773
                else
1774
                    fprintf(stderr, " -");
8✔
1775
            }
64✔
1776

1777
            fprintf(stderr, "\n");
44✔
1778
        }
12✔
1779
        else {
1780
            if (h->num_recv <= h->num_sent) {
90✔
1781
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
1782
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
1783

1784
                if (outage_flag) {
90✔
1785
                    /* Time outage total */
1786
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
1787
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
1788
                }
3✔
1789
            }
26✔
1790
            else {
UNCOV
1791
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
1792
                    h->num_sent, h->num_recv,
UNCOV
1793
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
1794
            }
1795

1796
            if (h->num_recv) {
90✔
1797
                avg = h->total_time / h->num_recv;
79✔
1798
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
1799
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
1800
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
1801
            }
23✔
1802

1803
            fprintf(stderr, "\n");
90✔
1804
        }
1805
    }
38✔
1806
}
100✔
1807

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

1810
  Function: print_netdata
1811

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

1814
  Inputs:  void (none)
1815

1816
  Description:
1817

1818

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

1821
void print_netdata(void)
3✔
1822
{
1823
    static int sent_charts = 0;
1824

1825
    int i;
1826
    int64_t avg;
1827
    HOST_ENTRY *h;
1828

1829
    for (i = 0; i < num_hosts; i++) {
6✔
1830
        h = table[i];
3✔
1831

1832
        if (!sent_charts) {
3✔
1833
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1834
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
1835
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
1836
        }
1✔
1837

1838
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
1839
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
1840
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
1841
        printf("END\n");
3✔
1842

1843
        if (!sent_charts) {
3✔
1844
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1845
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
1846
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
1847
        }
1✔
1848

1849
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
1850
        /*
1851
        if( h->num_recv_i <= h->num_sent_i )
1852
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
1853
        else
1854
            printf("SET lost = 0\n");
1855
*/
1856

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

1860
        if (!sent_charts) {
3✔
1861
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
1862
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
1863
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
1864
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
1865
        }
1✔
1866

1867
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
1868
        if (h->num_recv_i) {
3✔
1869
            avg = h->total_time_i / h->num_recv_i;
3✔
1870
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
1871
            printf("SET avg = %" PRId64 "\n", avg);
3✔
1872
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
1873
        }
1✔
1874
        printf("END\n");
3✔
1875

1876
        stats_reset_interval(h);
3✔
1877
    }
1✔
1878

1879
    sent_charts = 1;
3✔
1880
}
3✔
1881

1882
/************************************************************
1883

1884
  Function: print_per_system_splits
1885

1886
*************************************************************
1887

1888
  Inputs:  void (none)
1889

1890
  Description:
1891

1892

1893
************************************************************/
1894

1895
void print_per_system_splits(void)
39✔
1896
{
1897
    int i, avg, outage_ms_i;
1898
    HOST_ENTRY *h;
1899
    struct tm *curr_tm;
1900

1901
    if (verbose_flag || per_recv_flag)
39✔
UNCOV
1902
        fprintf(stderr, "\n");
×
1903

1904
    update_current_time();
39✔
1905
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
1906
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
1907
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
1908

1909
    for (i = 0; i < num_hosts; i++) {
78✔
1910
        h = table[i];
39✔
1911
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
1912

1913
        if (h->num_recv_i <= h->num_sent_i) {
39✔
1914
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
1915
                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✔
1916

1917
            if (outage_flag) {
39✔
1918
                /* Time outage  */
1919
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
1920
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
1921
            }
4✔
1922
        }
13✔
1923
        else {
UNCOV
1924
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
UNCOV
1925
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
1926
        }
1927

1928
        if (h->num_recv_i) {
39✔
1929
            avg = h->total_time_i / h->num_recv_i;
27✔
1930
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
1931
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
1932
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
1933
        }
9✔
1934

1935
        fprintf(stderr, "\n");
39✔
1936
        if (!cumulative_stats_flag) {
39✔
1937
            stats_reset_interval(h);
27✔
1938
        }
9✔
1939
    }
13✔
1940
}
39✔
1941

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

1944
  Function: print_global_stats
1945

1946
*************************************************************
1947

1948
  Inputs:  void (none)
1949

1950
  Description:
1951

1952

1953
************************************************************/
1954

1955
void print_global_stats(void)
9✔
1956
{
1957
    fprintf(stderr, "\n");
9✔
1958
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
1959
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
1960
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
1961
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
1962
    fprintf(stderr, "\n");
9✔
1963
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
1964
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
1965
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
1966
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
1967
    fprintf(stderr, "\n");
9✔
1968

1969
    if (total_replies == 0) {
9✔
1970
        min_reply = 0;
3✔
1971
        max_reply = 0;
3✔
1972
        total_replies = 1;
3✔
1973
        sum_replies = 0;
3✔
1974
    }
1✔
1975

1976
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
1977
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
1978
        sprint_tm(sum_replies / total_replies));
3✔
1979
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
1980
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
1981
        (end_time - start_time) / 1e9);
9✔
1982
    fprintf(stderr, "\n");
9✔
1983
}
9✔
1984

1985
/************************************************************
1986

1987
  Function: send_ping
1988

1989
*************************************************************
1990

1991
  Inputs:  int s, HOST_ENTRY *h
1992

1993
  Description:
1994

1995
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
1996
  will be added on by the kernel.  The ID field is our UNIX process ID,
1997
  and the sequence number is an index into an array of outstanding
1998
  ping requests. The sequence number will later be used to quickly
1999
  figure out who the ping reply came from.
2000

2001
************************************************************/
2002

2003
int send_ping(HOST_ENTRY *h, int index)
776✔
2004
{
2005
    int n;
2006
    int myseq;
2007
    int ret = 1;
776✔
2008
    uint8_t proto = ICMP_ECHO;
776✔
2009

2010
    update_current_time();
776✔
2011
    h->last_send_time = current_time_ns;
776✔
2012
    myseq = seqmap_add(h->i, index, current_time_ns);
776✔
2013

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

2016
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
776✔
2017
        if(icmp_request_typ == 13)
695✔
2018
            proto = ICMP_TSTAMP;
14✔
2019
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
695✔
2020
    }
217✔
2021
#ifdef IPV6
2022
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
81✔
2023
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
81✔
2024
    }
15✔
2025
#endif
2026
    else {
UNCOV
2027
        return 0;
×
2028
    }
2029

2030
    /* error sending? */
2031
    if (
544✔
2032
        (n < 0)
232✔
2033
#if defined(EHOSTDOWN)
2034
        && errno != EHOSTDOWN
238✔
2035
#endif
2036
    ) {
2037
        if (verbose_flag) {
6✔
2038
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
×
2039
        }
2040
        else {
2041
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2042
        }
2043

2044
        h->num_sent++;
6✔
2045
        h->num_sent_i++;
6✔
2046
        if (!loop_flag)
6✔
2047
            h->resp_times[index] = RESP_ERROR;
6✔
2048

2049
        ret = 0;
6✔
2050
    }
2051
    else {
2052
        /* schedule timeout */
2053
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
770✔
2054

2055
        /* mark this trial as outstanding */
2056
        if (!loop_flag) {
770✔
2057
            h->resp_times[index] = RESP_WAITING;
770✔
2058
        }
232✔
2059
    }
2060

2061
    num_pingsent++;
776✔
2062
    last_send_time = h->last_send_time;
776✔
2063

2064
    return (ret);
776✔
2065
}
232✔
2066

2067
int socket_can_read(struct timeval *timeout)
2,449✔
2068
{
2069
    int nfound;
2070
    fd_set readset;
2071
    int socketmax;
2072

2073
#ifndef IPV6
2074
    socketmax = socket4;
2075
#else
2076
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,449✔
2077
#endif
2078

2079
select_again:
1,839✔
2080
    FD_ZERO(&readset);
17,169✔
2081
    if (socket4 >= 0)
2,449✔
2082
        FD_SET(socket4, &readset);
2,449✔
2083
#ifdef IPV6
2084
    if (socket6 >= 0)
2,449✔
2085
        FD_SET(socket6, &readset);
2,449✔
2086
#endif
2087

2088
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,449✔
2089
    if (nfound < 0) {
2,449✔
UNCOV
2090
        if (errno == EINTR) {
×
2091
            /* interrupted system call: redo the select */
UNCOV
2092
            goto select_again;
×
2093
        }
2094
        else {
UNCOV
2095
            perror("select");
×
2096
        }
2097
    }
2098

2099
    if (nfound > 0) {
2,449✔
2100
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,182✔
2101
            return socket4;
1,022✔
2102
        }
2103
#ifdef IPV6
2104
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
160✔
2105
            return socket6;
160✔
2106
        }
2107
#endif
2108
    }
2109

2110
    return -1;
1,267✔
2111
}
610✔
2112

2113
int receive_packet(int64_t wait_time,
2,449✔
2114
#if HAVE_SO_TIMESTAMPNS
2115
    int64_t *reply_timestamp,
2116
#else
2117
    int64_t *reply_timestamp __attribute__((unused)),
2118
#endif
2119
    struct sockaddr *reply_src_addr,
2120
    size_t reply_src_addr_len,
2121
    char *reply_buf,
2122
    size_t reply_buf_len)
2123
{
2124
    struct timeval to;
2125
    int s = 0;
2,449✔
2126
    int recv_len;
2127
    static unsigned char msg_control[40];
2128
    struct iovec msg_iov = {
3,669✔
2129
        reply_buf,
610✔
2130
        reply_buf_len
610✔
2131
    };
2132
    struct msghdr recv_msghdr = {0};
2,449✔
2133
    recv_msghdr.msg_name = reply_src_addr;
2,449✔
2134
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,449✔
2135
    recv_msghdr.msg_iov = &msg_iov;
2,449✔
2136
    recv_msghdr.msg_iovlen = 1;
2,449✔
2137
    recv_msghdr.msg_control = &msg_control;
2,449✔
2138
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,449✔
2139
#if HAVE_SO_TIMESTAMPNS
2140
    struct cmsghdr *cmsg;
2141
#endif
2142

2143
    /* Wait for a socket to become ready */
2144
    if (wait_time) {
2,449✔
2145
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,266✔
2146
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,266✔
2147
    }
386✔
2148
    else {
2149
        to.tv_sec = 0;
1,183✔
2150
        to.tv_usec = 0;
1,183✔
2151
    }
2152
    s = socket_can_read(&to);
2,449✔
2153
    if (s == -1) {
2,449✔
2154
        return 0; /* timeout */
1,267✔
2155
    }
2156

2157
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,182✔
2158
    if (recv_len <= 0) {
1,182✔
UNCOV
2159
        return 0;
×
2160
    }
2161

2162
#if HAVE_SO_TIMESTAMPNS
2163
    /* ancilliary data */
2164
    {
2165
        struct timespec reply_timestamp_ts;
2166
        struct timeval reply_timestamp_tv;
2167
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,437✔
2168
             cmsg != NULL;
958✔
2169
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
958✔
2170
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
958✔
2171
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
958✔
2172
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
958✔
2173
            }
2174
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
958✔
2175
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2176
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2177
            }
2178
        }
2179
    }
2180
#endif
2181

2182
#if defined(DEBUG) || defined(_DEBUG)
2183
    if (randomly_lose_flag) {
2184
        if ((random() & 0x07) <= lose_factor)
2185
            return 0;
2186
    }
2187
#endif
2188

2189
    return recv_len;
1,182✔
2190
}
610✔
2191

2192
/* stats_add: update host statistics for a single packet that was received (or timed out)
2193
 * h: host entry to update
2194
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2195
 * success: 1 if response received, 0 otherwise
2196
 * latency: response time, in ns
2197
 */
2198
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
770✔
2199
{
2200
    /* sent count - we update only on receive/timeout, so that we don't get
2201
     * weird loss percentage, just because a packet was note recived yet.
2202
     */
2203
    h->num_sent++;
770✔
2204
    h->num_sent_i++;
770✔
2205

2206
    if (!success) {
770✔
2207
        if (!loop_flag && index >= 0) {
89✔
2208
            h->resp_times[index] = RESP_TIMEOUT;
89✔
2209
        }
23✔
2210
        num_timeout++;
89✔
2211
        return;
89✔
2212
    }
2213

2214
    /* received count */
2215
    h->num_recv++;
681✔
2216
    h->num_recv_i++;
681✔
2217

2218
    /* maximum */
2219
    if (!h->max_reply || latency > h->max_reply) {
681✔
2220
        h->max_reply = latency;
510✔
2221
    }
158✔
2222
    if (!h->max_reply_i || latency > h->max_reply_i) {
681✔
2223
        h->max_reply_i = latency;
523✔
2224
    }
162✔
2225

2226
    /* minimum */
2227
    if (!h->min_reply || latency < h->min_reply) {
681✔
2228
        h->min_reply = latency;
456✔
2229
    }
134✔
2230
    if (!h->min_reply_i || latency < h->min_reply_i) {
681✔
2231
        h->min_reply_i = latency;
483✔
2232
    }
142✔
2233

2234
    /* total time (for average) */
2235
    h->total_time += latency;
681✔
2236
    h->total_time_i += latency;
681✔
2237

2238
    /* response time per-packet (count mode) */
2239
    if (!loop_flag && index >= 0) {
681✔
2240
        h->resp_times[index] = latency;
681✔
2241
    }
209✔
2242
}
232✔
2243

2244
/* stats_reset_interval: reset interval statistics
2245
 * h: host entry to update
2246
 */
2247
void stats_reset_interval(HOST_ENTRY *h)
30✔
2248
{
2249
    h->num_sent_i = 0;
30✔
2250
    h->num_recv_i = 0;
30✔
2251
    h->max_reply_i = 0;
30✔
2252
    h->min_reply_i = 0;
30✔
2253
    h->total_time_i = 0;
30✔
2254
}
30✔
2255

2256
int decode_icmp_ipv4(
1,022✔
2257
    struct sockaddr *response_addr,
2258
    size_t response_addr_len,
2259
    char *reply_buf,
2260
    size_t reply_buf_len,
2261
    unsigned short *id,
2262
    unsigned short *seq,
2263
    int *ip_header_tos,
2264
    int *ip_header_ttl,
2265
    uint32_t *ip_header_otime_ms,
2266
    uint32_t *ip_header_rtime_ms,
2267
    uint32_t *ip_header_ttime_ms)
2268
{
2269
    struct icmp *icp;
2270
    int hlen = 0;
1,022✔
2271

2272
    if (!using_sock_dgram4) {
1,022✔
2273
        struct ip *ip = (struct ip *)reply_buf;
1,014✔
2274
        *ip_header_tos = ip->ip_tos;
1,014✔
2275
        *ip_header_ttl = ip->ip_ttl;
1,014✔
2276

2277
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2278
        /* The alpha headers are decidedly broken.
2279
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2280
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2281
         */
2282
        hlen = (ip->ip_vhl & 0x0F) << 2;
2283
#else
2284
        hlen = ip->ip_hl << 2;
1,014✔
2285
#endif
2286
    }
194✔
2287

2288
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,022✔
2289
        /* too short */
UNCOV
2290
        if (verbose_flag) {
×
2291
            char buf[INET6_ADDRSTRLEN];
UNCOV
2292
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
UNCOV
2293
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2294
        }
UNCOV
2295
        return -1;
×
2296
    }
2297

2298
    icp = (struct icmp *)(reply_buf + hlen);
1,022✔
2299

2300
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,022✔
2301
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
432✔
2302
        /* Handle other ICMP packets */
2303
        struct icmp *sent_icmp;
2304
        SEQMAP_VALUE *seqmap_value;
2305
        char addr_ascii[INET6_ADDRSTRLEN];
2306
        HOST_ENTRY *h;
2307

2308
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2309
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
798✔
2310
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
UNCOV
2311
            return -1;
×
2312
        }
2313

2314
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
410✔
2315

2316
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
410✔
2317
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
14✔
UNCOV
2318
            sent_icmp->icmp_id != ident4) {
×
2319
            /* not caused by us */
2320
            return -1;
410✔
2321
        }
2322

UNCOV
2323
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
UNCOV
2324
        if (seqmap_value == NULL) {
×
UNCOV
2325
            return -1;
×
2326
        }
2327

UNCOV
2328
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2329

UNCOV
2330
        switch (icp->icmp_type) {
×
2331
        case ICMP_UNREACH:
UNCOV
2332
            h = table[seqmap_value->host_nr];
×
UNCOV
2333
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2334
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2335
                    addr_ascii, h->host);
2336
            }
2337
            else {
UNCOV
2338
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2339
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2340
            }
2341

UNCOV
2342
            print_warning("\n");
×
2343
            num_othericmprcvd++;
×
2344
            break;
×
2345

2346
        case ICMP_SOURCEQUENCH:
2347
        case ICMP_REDIRECT:
2348
        case ICMP_TIMXCEED:
2349
        case ICMP_PARAMPROB:
2350
            h = table[seqmap_value->host_nr];
×
UNCOV
2351
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
UNCOV
2352
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2353
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2354
            }
2355
            else {
UNCOV
2356
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
UNCOV
2357
                    icp->icmp_type, addr_ascii, h->host);
×
2358
            }
UNCOV
2359
            print_warning("\n");
×
UNCOV
2360
            num_othericmprcvd++;
×
2361
            break;
×
2362
        }
2363

2364
        return -1;
×
2365
    }
2366

2367
    *id = icp->icmp_id;
612✔
2368
    *seq = ntohs(icp->icmp_seq);
612✔
2369
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
612✔
2370

2371
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2372
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
14✔
UNCOV
2373
            if (verbose_flag) {
×
2374
                char buf[INET6_ADDRSTRLEN];
2375
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
UNCOV
2376
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2377
            }
UNCOV
2378
            return -1;
×
2379
        }
2380

2381
        *ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
14✔
2382
        *ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
14✔
2383
        *ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
14✔
2384
    }
2385

2386
    return hlen;
612✔
2387
}
194✔
2388

2389
#ifdef IPV6
2390
int decode_icmp_ipv6(
160✔
2391
    struct sockaddr *response_addr,
2392
    size_t response_addr_len,
2393
    char *reply_buf,
2394
    size_t reply_buf_len,
2395
    unsigned short *id,
2396
    unsigned short *seq)
2397
{
2398
    struct icmp6_hdr *icp;
2399

2400
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
160✔
UNCOV
2401
        if (verbose_flag) {
×
2402
            char buf[INET6_ADDRSTRLEN];
UNCOV
2403
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
UNCOV
2404
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2405
        }
UNCOV
2406
        return 0; /* too short */
×
2407
    }
2408

2409
    icp = (struct icmp6_hdr *)reply_buf;
160✔
2410

2411
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
160✔
2412
        /* Handle other ICMP packets */
2413
        struct icmp6_hdr *sent_icmp;
2414
        SEQMAP_VALUE *seqmap_value;
2415
        char addr_ascii[INET6_ADDRSTRLEN];
2416
        HOST_ENTRY *h;
2417

2418
        /* reply icmp packet (ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2419
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
79✔
2420
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
UNCOV
2421
            return 0;
×
2422
        }
2423

2424
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip));
79✔
2425

2426
        if (sent_icmp->icmp6_type != ICMP_ECHO || sent_icmp->icmp6_id != ident6) {
79✔
2427
            /* not caused by us */
2428
            return 0;
79✔
2429
        }
2430

UNCOV
2431
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2432
        if (seqmap_value == NULL) {
×
UNCOV
2433
            return 0;
×
2434
        }
2435

UNCOV
2436
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2437

UNCOV
2438
        switch (icp->icmp6_type) {
×
2439
        case ICMP_UNREACH:
UNCOV
2440
            h = table[seqmap_value->host_nr];
×
UNCOV
2441
            if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) {
×
2442
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2443
                    addr_ascii, h->host);
2444
            }
2445
            else {
UNCOV
2446
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2447
                    icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2448
            }
2449

UNCOV
2450
            print_warning("\n");
×
2451
            num_othericmprcvd++;
×
2452
            break;
×
2453

2454
        case ICMP_SOURCEQUENCH:
2455
        case ICMP_REDIRECT:
2456
        case ICMP_TIMXCEED:
2457
        case ICMP_PARAMPROB:
2458
            h = table[seqmap_value->host_nr];
×
UNCOV
2459
            if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) {
×
UNCOV
2460
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2461
                    icmp_type_str[icp->icmp6_type], addr_ascii, h->host);
×
2462
            }
2463
            else {
UNCOV
2464
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
UNCOV
2465
                    icp->icmp6_type, addr_ascii, h->host);
×
2466
            }
UNCOV
2467
            print_warning("\n");
×
UNCOV
2468
            num_othericmprcvd++;
×
2469
            break;
×
2470
        }
2471

2472
        return 0;
×
2473
    }
2474

2475
    *id = icp->icmp6_id;
81✔
2476
    *seq = ntohs(icp->icmp6_seq);
81✔
2477

2478
    return 1;
81✔
2479
}
30✔
2480
#endif
2481

2482
int wait_for_reply(int64_t wait_time)
2,449✔
2483
{
2484
    int result;
2485
    static char buffer[RECV_BUFSIZE];
2486
    struct sockaddr_storage response_addr;
2487
    int n, avg;
2488
    HOST_ENTRY *h;
2489
    int64_t this_reply;
2490
    int this_count;
2491
    int64_t recv_time = 0;
2,449✔
2492
    SEQMAP_VALUE *seqmap_value;
2493
    unsigned short id;
2494
    unsigned short seq;
2495
    int ip_header_tos = -1;
2,449✔
2496
    int ip_header_ttl = -1;
2,449✔
2497
    // ICMP Timestamp
2498
    uint32_t ip_header_otime_ms = 0x80000000U;
2,449✔
2499
    uint32_t ip_header_rtime_ms = 0x80000000U;
2,449✔
2500
    uint32_t ip_header_ttime_ms = 0x80000000U;
2,449✔
2501

2502
    /* Receive packet */
2503
    result = receive_packet(wait_time, /* max. wait time, in ns */
2,449✔
2504
        &recv_time, /* reply_timestamp */
2505
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2506
        sizeof(response_addr), /* reply_src_addr_len */
2507
        buffer, /* reply_buf */
2508
        sizeof(buffer) /* reply_buf_len */
2509
    );
2510

2511
    if (result <= 0) {
2,449✔
2512
        return 0;
1,267✔
2513
    }
2514

2515
    update_current_time();
1,182✔
2516
    if (recv_time == 0)
1,182✔
2517
        recv_time = current_time_ns;
224✔
2518

2519
    /* Process ICMP packet and retrieve id/seq */
2520
    if (response_addr.ss_family == AF_INET) {
1,182✔
2521
        int ip_hlen = decode_icmp_ipv4(
1,022✔
2522
            (struct sockaddr *)&response_addr,
2523
            sizeof(response_addr),
2524
            buffer,
2525
            sizeof(buffer),
2526
            &id,
2527
            &seq,
2528
            &ip_header_tos,
2529
            &ip_header_ttl,
2530
            &ip_header_otime_ms,
2531
            &ip_header_rtime_ms,
2532
            &ip_header_ttime_ms);
2533
        if (ip_hlen < 0) {
1,022✔
2534
            return 1;
410✔
2535
        }
2536
        if (id != ident4) {
612✔
UNCOV
2537
            return 1; /* packet received, but not the one we are looking for! */
×
2538
        }
2539
        if (!using_sock_dgram4) {
612✔
2540
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2541
             * with fping with IPv6 hosts */
2542
            result -= ip_hlen;
604✔
2543
        }
194✔
2544
    }
194✔
2545
#ifdef IPV6
2546
    else if (response_addr.ss_family == AF_INET6) {
160✔
2547
        if (!decode_icmp_ipv6(
160✔
2548
                (struct sockaddr *)&response_addr,
2549
                sizeof(response_addr),
2550
                buffer,
2551
                sizeof(buffer),
2552
                &id,
2553
                &seq)) {
2554
            return 1;
79✔
2555
        }
2556
        if (id != ident6) {
81✔
UNCOV
2557
            return 1; /* packet received, but not the one we are looking for! */
×
2558
        }
2559
    }
15✔
2560
#endif
2561
    else {
UNCOV
2562
        return 1;
×
2563
    }
2564

2565
    seqmap_value = seqmap_fetch(seq, current_time_ns);
693✔
2566
    if (seqmap_value == NULL) {
693✔
UNCOV
2567
        return 1;
×
2568
    }
2569

2570
    /* find corresponding host_entry */
2571
    n = seqmap_value->host_nr;
693✔
2572
    h = table[n];
693✔
2573
    this_count = seqmap_value->ping_count;
693✔
2574
    this_reply = recv_time - seqmap_value->ping_ts;
693✔
2575

2576
    /* update stats that include invalid replies */
2577
    h->num_recv_total++;
693✔
2578
    num_pingreceived++;
693✔
2579

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

2582
    /* optionally require reply source equal to target address */
2583
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
693✔
2584
        dbg_printf("discarding reply from wrong source address\n");
2585
        return 1;
12✔
2586
    }
2587

2588
    /* discard duplicates */
2589
    if (!loop_flag && h->resp_times[this_count] >= 0) {
681✔
UNCOV
2590
        if (!per_recv_flag) {
×
UNCOV
2591
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2592
                h->host, this_count, result, sprint_tm(this_reply));
2593

UNCOV
2594
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2595
                char buf[INET6_ADDRSTRLEN];
UNCOV
2596
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
UNCOV
2597
                fprintf(stderr, " [<- %s]", buf);
×
2598
            }
UNCOV
2599
            fprintf(stderr, "\n");
×
2600
        }
2601
        return 1;
×
2602
    }
2603

2604
    /* discard reply if delay is larger than timeout
2605
     * (see also: github #32) */
2606
    if (this_reply > h->timeout) {
681✔
2607
        return 1;
×
2608
    }
2609

2610
    /* update stats */
2611
    stats_add(h, this_count, 1, this_reply);
681✔
2612
    // TODO: move to stats_add?
2613
    if (!max_reply || this_reply > max_reply)
681✔
2614
        max_reply = this_reply;
410✔
2615
    if (!min_reply || this_reply < min_reply)
681✔
2616
        min_reply = this_reply;
355✔
2617
    sum_replies += this_reply;
681✔
2618
    total_replies++;
681✔
2619

2620
    /* initialize timeout to initial timeout (without backoff) */
2621
    h->timeout = timeout;
681✔
2622

2623
    /* remove timeout event */
2624
    struct event *timeout_event = host_get_timeout_event(h, this_count);
681✔
2625
    if (timeout_event) {
681✔
2626
        ev_remove(&event_queue_timeout, timeout_event);
681✔
2627
    }
209✔
2628

2629
    /* print "is alive" */
2630
    if (h->num_recv == 1) {
681✔
2631
        num_alive++;
400✔
2632
        if (fast_reachable && num_alive >= min_reachable)
400✔
2633
                finish_requested = 1;
3✔
2634

2635
        if (verbose_flag || alive_flag) {
400✔
2636
            printf("%s", h->host);
269✔
2637

2638
            if (verbose_flag)
269✔
2639
                printf(" is alive");
249✔
2640
        }
79✔
2641
    }
118✔
2642

2643
    /* print received ping (unless --quiet) */
2644
    if (per_recv_flag) {
681✔
2645
        if (timestamp_flag) {
151✔
2646
            print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2647
        }
10✔
2648
        avg = h->total_time / h->num_recv;
151✔
2649
        printf("%-*s : [%d], %d bytes, %s ms",
151✔
2650
            max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2651
        printf(" (%s avg, ", sprint_tm(avg));
151✔
2652

2653
        if (h->num_recv <= h->num_sent) {
151✔
2654
            printf("%d%% loss)",
151✔
2655
                ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2656
        }
45✔
2657
        else {
UNCOV
2658
            printf("%d%% return)",
×
UNCOV
2659
                (h->num_recv_total * 100) / h->num_sent);
×
2660
        }
2661
    }
45✔
2662

2663
    if (verbose_flag || alive_flag || per_recv_flag) {
681✔
2664

2665
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
420✔
2666
            char buf[INET6_ADDRSTRLEN];
2667
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2668
            fprintf(stderr, " [<- %s]", buf);
6✔
2669
        }
2670

2671
        if (icmp_request_typ == 13) {
420✔
2672
            printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2673
                   alive_flag ? "" : ",",
14✔
2674
                   ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms,
2675
                   ms_since_midnight_utc(recv_time));
2676
        }
2677

2678
        if(print_tos_flag) {
420✔
2679
            if(ip_header_tos != -1) {
43✔
2680
                printf(" (TOS %d)", ip_header_tos);
39✔
2681
            }
11✔
2682
            else {
2683
                printf(" (TOS unknown)");
4✔
2684
            }
2685
        }
11✔
2686

2687
        if (print_ttl_flag) {
420✔
2688
          if(ip_header_ttl != -1) {
43✔
2689
              printf(" (TTL %d)", ip_header_ttl);
39✔
2690
          }
11✔
2691
          else {
2692
              printf(" (TTL unknown)");
4✔
2693
          }
2694
        }
11✔
2695

2696
        if (elapsed_flag && !per_recv_flag)
420✔
2697
                printf(" (%s ms)", sprint_tm(this_reply));
8✔
2698

2699
        printf("\n");
420✔
2700
    }
124✔
2701

2702
    return 1;
681✔
2703
}
610✔
2704

2705
/************************************************************
2706

2707
  Function: add_name
2708

2709
*************************************************************
2710

2711
  Inputs:  char* name
2712

2713
  Description:
2714

2715
  process input name for addition to target list
2716
  name can turn into multiple targets via multiple interfaces (-m)
2717
  or via NIS groups
2718

2719
************************************************************/
2720

2721
void add_name(char *name)
468✔
2722
{
2723
    struct addrinfo *res0, *res, hints;
2724
    int ret_ga;
2725
    char *printname;
2726
    char namebuf[256];
2727
    char addrbuf[256];
2728

2729
    /* getaddrinfo */
2730
    memset(&hints, 0, sizeof(struct addrinfo));
468✔
2731
    hints.ai_flags = AI_UNUSABLE;
468✔
2732
    hints.ai_socktype = SOCK_RAW;
468✔
2733
    hints.ai_family = hints_ai_family;
468✔
2734
    if (hints_ai_family == AF_INET) {
468✔
2735
        hints.ai_protocol = IPPROTO_ICMP;
47✔
2736
    }
11✔
2737
#ifdef IPV6
2738
    else if (hints_ai_family == AF_INET6) {
421✔
2739
        hints.ai_protocol = IPPROTO_ICMPV6;
7✔
2740
    }
1✔
2741
#endif
2742
    else {
2743
        hints.ai_socktype = SOCK_STREAM;
414✔
2744
        hints.ai_protocol = 0;
414✔
2745
    }
2746
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
468✔
2747
    if (ret_ga) {
468✔
2748
        if (!quiet_flag)
17✔
2749
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
17✔
2750
        num_noaddress++;
17✔
2751
        return;
17✔
2752
    }
2753

2754
    /* NOTE: we could/should loop with res on all addresses like this:
2755
     * for (res = res0; res; res = res->ai_next) {
2756
     * We don't do it yet, however, because is is an incompatible change
2757
     * (need to implement a separate option for this)
2758
     */
2759
    for (res = res0; res; res = res->ai_next) {
451✔
2760
        /* name_flag: addr -> name lookup requested) */
2761
        if (name_flag || rdns_flag) {
453✔
2762
            int do_rdns = rdns_flag ? 1 : 0;
3✔
2763
            if (name_flag) {
3✔
2764
                /* Was it a numerical address? Only then do a rdns-query */
2765
                struct addrinfo *nres;
UNCOV
2766
                hints.ai_flags = AI_NUMERICHOST;
×
UNCOV
2767
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
UNCOV
2768
                    do_rdns = 1;
×
UNCOV
2769
                    freeaddrinfo(nres);
×
2770
                }
2771
            }
2772

2773
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2774
                printname = namebuf;
3✔
2775
            }
1✔
2776
            else {
2777
                printname = name;
×
2778
            }
2779
        }
1✔
2780
        else {
2781
            printname = name;
448✔
2782
        }
2783

2784
        /* addr_flag: name -> addr lookup requested */
2785
        if (addr_flag) {
451✔
2786
            int ret;
2787
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2788
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2789
            if (ret) {
3✔
UNCOV
2790
                if (!quiet_flag) {
×
UNCOV
2791
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2792
                }
UNCOV
2793
                continue;
×
2794
            }
2795

2796
            if (name_flag || rdns_flag) {
3✔
2797
                char nameaddrbuf[512 + 3];
UNCOV
2798
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
UNCOV
2799
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2800
            }
2801
            else {
2802
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2803
            }
2804
        }
1✔
2805
        else {
2806
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
448✔
2807
        }
2808

2809
        if (!multif_flag) {
451✔
2810
            break;
451✔
2811
        }
2812
    }
2813

2814
    freeaddrinfo(res0);
451✔
2815
}
136✔
2816

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

2819
  Function: add_addr
2820

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

2823
  Description:
2824

2825
  add single address to list of hosts to be pinged
2826

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

2829
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
451✔
2830
{
2831
    HOST_ENTRY *p;
2832
    int n;
2833
    int64_t *i;
2834

2835
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
451✔
2836
    if (!p)
451✔
UNCOV
2837
        crash_and_burn("can't allocate HOST_ENTRY");
×
2838

2839
    p->name = strdup(name);
451✔
2840
    p->host = strdup(host);
451✔
2841
    memcpy(&p->saddr, ipaddr, ipaddr_len);
451✔
2842
    p->saddr_len = ipaddr_len;
451✔
2843
    p->timeout = timeout;
451✔
2844
    p->min_reply = 0;
451✔
2845

2846
    if (netdata_flag) {
451✔
2847
        char *s = p->name;
3✔
2848
        while (*s) {
30✔
2849
            if (!isalnum(*s))
27✔
2850
                *s = '_';
9✔
2851
            s++;
27✔
2852
        }
2853
    }
1✔
2854

2855
    if (strlen(p->host) > max_hostname_len)
451✔
2856
        max_hostname_len = strlen(p->host);
294✔
2857

2858
    /* array for response time results */
2859
    if (!loop_flag) {
451✔
2860
        i = (int64_t *)malloc(trials * sizeof(int64_t));
451✔
2861
        if (!i)
451✔
UNCOV
2862
            crash_and_burn("can't allocate resp_times array");
×
2863

2864
        for (n = 1; n < trials; n++)
1,891✔
2865
            i[n] = RESP_UNUSED;
1,440✔
2866

2867
        p->resp_times = i;
451✔
2868
    }
131✔
2869

2870
    /* allocate event storage */
2871
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
451✔
2872
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
451✔
2873

2874
    /* schedule first ping */
2875
    host_add_ping_event(p, 0, current_time_ns);
451✔
2876

2877
    num_hosts++;
451✔
2878
}
451✔
2879

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

2882
  Function: crash_and_burn
2883

2884
*************************************************************
2885

2886
  Inputs:  char* message
2887

2888
  Description:
2889

2890
************************************************************/
2891

UNCOV
2892
void crash_and_burn(char *message)
×
2893
{
UNCOV
2894
    fprintf(stderr, "%s: %s\n", prog, message);
×
UNCOV
2895
    exit(4);
×
2896
}
2897

2898
/************************************************************
2899

2900
  Function: errno_crash_and_burn
2901

2902
*************************************************************
2903

2904
  Inputs:  char* message
2905

2906
  Description:
2907

2908
************************************************************/
2909

2910
void errno_crash_and_burn(char *message)
8✔
2911
{
2912
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
2913
    exit(4);
8✔
2914
}
2915

2916
/************************************************************
2917

2918
  Function: print_warning
2919

2920
  Description: fprintf(stderr, ...), unless running with -q
2921

2922
*************************************************************/
2923

2924
void print_warning(char *format, ...)
17✔
2925
{
2926
    va_list args;
2927
    if (!quiet_flag) {
17✔
2928
        va_start(args, format);
17✔
2929
        vfprintf(stderr, format, args);
17✔
2930
        va_end(args);
17✔
2931
    }
5✔
2932
}
17✔
2933

2934
/************************************************************
2935

2936
  Function: sprint_tm
2937

2938
*************************************************************
2939

2940
  render nanosecond int64_t value into milliseconds string with three digits of
2941
  precision.
2942

2943
************************************************************/
2944

2945
const char *sprint_tm(int64_t ns)
851✔
2946
{
2947
    static char buf[10];
2948
    double t = (double)ns / 1e6;
851✔
2949

2950
    if (t < 0.0) {
851✔
2951
        /* negative (unexpected) */
UNCOV
2952
        sprintf(buf, "%.2g", t);
×
2953
    }
2954
    else if (t < 1.0) {
851✔
2955
        /* <= 0.99 ms */
2956
        sprintf(buf, "%.3f", t);
851✔
2957
    }
261✔
UNCOV
2958
    else if (t < 10.0) {
×
2959
        /* 1.00 - 9.99 ms */
UNCOV
2960
        sprintf(buf, "%.2f", t);
×
2961
    }
UNCOV
2962
    else if (t < 100.0) {
×
2963
        /* 10.0 - 99.9 ms */
UNCOV
2964
        sprintf(buf, "%.1f", t);
×
2965
    }
UNCOV
2966
    else if (t < 1000000.0) {
×
2967
        /* 100 - 1'000'000 ms */
UNCOV
2968
        sprintf(buf, "%.0f", t);
×
2969
    }
2970
    else {
2971
        sprintf(buf, "%.3e", t);
×
2972
    }
2973

2974
    return (buf);
851✔
2975
}
2976

2977
/************************************************************
2978

2979
  Function: addr_cmp
2980

2981
*************************************************************/
2982
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
442✔
2983
{
2984
    if (a->sa_family != b->sa_family) {
442✔
UNCOV
2985
        return a->sa_family - b->sa_family;
×
2986
    }
2987
    else {
2988
        if (a->sa_family == AF_INET) {
442✔
2989
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
388✔
2990
        }
2991
        else if (a->sa_family == AF_INET6) {
54✔
2992
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
62✔
2993
                &((struct sockaddr_in6 *)b)->sin6_addr,
54✔
2994
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
2995
        }
2996
    }
2997

UNCOV
2998
    return 0;
×
2999
}
126✔
3000

3001
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
766✔
3002
{
3003
    struct event *event = &h->event_storage_ping[index % event_storage_count];
766✔
3004
    event->host = h;
766✔
3005
    event->ping_index = index;
766✔
3006
    event->ev_time = ev_time;
766✔
3007
    ev_enqueue(&event_queue_ping, event);
766✔
3008

3009
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3010
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3011
}
766✔
3012

3013
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
770✔
3014
{
3015
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
770✔
3016
    event->host = h;
770✔
3017
    event->ping_index = index;
770✔
3018
    event->ev_time = ev_time;
770✔
3019
    ev_enqueue(&event_queue_timeout, event);
770✔
3020

3021
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3022
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3023
}
770✔
3024

3025
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
681✔
3026
{
3027
    return &h->event_storage_timeout[index % event_storage_count];
681✔
3028
}
3029

3030
/************************************************************
3031

3032
  Function: ev_enqueue
3033

3034
  Enqueue an event
3035

3036
  The queue is sorted by event->ev_time, so that queue->first always points to
3037
  the earliest event.
3038

3039
  We start scanning the queue from the tail, because we assume
3040
  that new events mostly get inserted with a event time higher
3041
  than the others.
3042

3043
*************************************************************/
3044
void ev_enqueue(struct event_queue *queue, struct event *event)
1,536✔
3045
{
3046
    struct event *i;
3047
    struct event *i_prev;
3048

3049
    /* Empty list */
3050
    if (queue->last == NULL) {
1,536✔
3051
        event->ev_next = NULL;
1,179✔
3052
        event->ev_prev = NULL;
1,179✔
3053
        queue->first = event;
1,179✔
3054
        queue->last = event;
1,179✔
3055
        return;
1,179✔
3056
    }
3057

3058
    /* Insert on tail? */
3059
    if (event->ev_time - queue->last->ev_time >= 0) {
357✔
3060
        event->ev_next = NULL;
357✔
3061
        event->ev_prev = queue->last;
357✔
3062
        queue->last->ev_next = event;
357✔
3063
        queue->last = event;
357✔
3064
        return;
357✔
3065
    }
3066

3067
    /* Find insertion point */
UNCOV
3068
    i = queue->last;
×
3069
    while (1) {
UNCOV
3070
        i_prev = i->ev_prev;
×
UNCOV
3071
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
UNCOV
3072
            event->ev_prev = i_prev;
×
UNCOV
3073
            event->ev_next = i;
×
UNCOV
3074
            i->ev_prev = event;
×
UNCOV
3075
            if (i_prev != NULL) {
×
UNCOV
3076
                i_prev->ev_next = event;
×
3077
            }
3078
            else {
3079
                queue->first = event;
×
3080
            }
3081
            return;
×
3082
        }
3083
        i = i_prev;
×
3084
    }
3085
}
464✔
3086

3087
/************************************************************
3088

3089
  Function: ev_dequeue
3090

3091
*************************************************************/
3092
struct event *ev_dequeue(struct event_queue *queue)
835✔
3093
{
3094
    struct event *dequeued;
3095

3096
    if (queue->first == NULL) {
835✔
UNCOV
3097
        return NULL;
×
3098
    }
3099
    dequeued = queue->first;
835✔
3100
    ev_remove(queue, dequeued);
835✔
3101

3102
    return dequeued;
835✔
3103
}
249✔
3104

3105
/************************************************************
3106

3107
  Function: ev_remove
3108

3109
*************************************************************/
3110
void ev_remove(struct event_queue *queue, struct event *event)
1,516✔
3111
{
3112
    if (queue->first == event) {
1,516✔
3113
        queue->first = event->ev_next;
1,507✔
3114
    }
455✔
3115
    if (queue->last == event) {
1,516✔
3116
        queue->last = event->ev_prev;
1,180✔
3117
    }
347✔
3118
    if (event->ev_prev) {
1,516✔
3119
        event->ev_prev->ev_next = event->ev_next;
9✔
3120
    }
3✔
3121
    if (event->ev_next) {
1,516✔
3122
        event->ev_next->ev_prev = event->ev_prev;
336✔
3123
    }
111✔
3124
    event->ev_prev = NULL;
1,516✔
3125
    event->ev_next = NULL;
1,516✔
3126
}
1,516✔
3127

3128
/************************************************************
3129

3130
  Function: print_human_readable_time from current_time_ns
3131

3132
*************************************************************/
3133
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
30✔
3134
{
3135
    char time_buffer[100];
3136
    time_t current_time_s;
3137
    struct tm *local_time;
3138

3139
    current_time_s = current_time_ns / 1000000000;
30✔
3140
    local_time = localtime(&current_time_s);
30✔
3141
    switch(timestamp_format) {
30✔
3142
        case 1:
4✔
3143
            // timestamp-format ctime
3144
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
6✔
3145
            printf("[%s] ", time_buffer);
6✔
3146
            break;
6✔
3147
        case 2:
4✔
3148
            // timestamp-format iso
3149
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
6✔
3150
            printf("[%s] ", time_buffer);
6✔
3151
            break;
6✔
3152
        case 3:
4✔
3153
            // timestamp-format rfc3339
3154
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
6✔
3155
            printf("[%s] ", time_buffer);
6✔
3156
            break;
6✔
3157
        default:
8✔
3158
            printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3159
    }
4✔
3160
}
30✔
3161

3162
/************************************************************
3163

3164
  Function: ms_since_midnight_utc
3165

3166
*************************************************************
3167

3168
  Input: int64_t: current UTC time in ns
3169

3170
  Output: uint32_t: current time in ms since midnight UTC
3171

3172
  Description:
3173

3174
  Return ICMP Timestamp value corresponding to the given time value.
3175
  The given time value must be in UTC.
3176

3177
*************************************************************/
3178
static uint32_t ms_since_midnight_utc(int64_t time_val)
14✔
3179
{
3180
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
14✔
3181
}
3182

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

3185
  Function: usage
3186

3187
*************************************************************
3188

3189
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3190

3191
  Description:
3192

3193
************************************************************/
3194

3195
void usage(int is_error)
115✔
3196
{
3197
    FILE *out = is_error ? stderr : stdout;
115✔
3198
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
115✔
3199
    fprintf(out, "\n");
115✔
3200
    fprintf(out, "Probing options:\n");
115✔
3201
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
115✔
3202
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
115✔
3203
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
115✔
3204
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
115✔
3205
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
115✔
3206
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
115✔
3207
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
115✔
3208
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
115✔
3209
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
115✔
3210
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
115✔
3211
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
115✔
3212
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
115✔
3213
#ifdef SO_BINDTODEVICE
3214
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
78✔
3215
#endif
3216
#ifdef SO_MARK
3217
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
78✔
3218
#endif
3219
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
115✔
3220
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
115✔
3221
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
115✔
3222
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
115✔
3223
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
115✔
3224
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
115✔
3225
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
115✔
3226
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
115✔
3227
    fprintf(out, "   -S, --src=IP       set source address\n");
115✔
3228
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
115✔
3229
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
115✔
3230
    fprintf(out, "       --check-source discard replies not from target address\n");
115✔
3231
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
115✔
3232
    fprintf(out, "\n");
115✔
3233
    fprintf(out, "Output options:\n");
115✔
3234
    fprintf(out, "   -a, --alive        show targets that are alive\n");
115✔
3235
    fprintf(out, "   -A, --addr         show targets by address\n");
115✔
3236
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
115✔
3237
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
115✔
3238
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
115✔
3239
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
115✔
3240
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
115✔
3241
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
115✔
3242
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
115✔
3243
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
115✔
3244
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
115✔
3245
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
115✔
3246
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
115✔
3247
    fprintf(out, "   -s, --stats        print final stats\n");
115✔
3248
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
115✔
3249
    fprintf(out, "   -v, --version      show version\n");
115✔
3250
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
115✔
3251
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
115✔
3252
    fprintf(out, "       --print-tos    show received TOS value\n");
115✔
3253
    fprintf(out, "       --print-ttl    show IP TTL value\n");
115✔
3254
    exit(is_error);
115✔
3255
}
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