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

schweikert / fping / 20482860148

24 Dec 2025 09:15AM UTC coverage: 86.543% (-0.1%) from 86.659%
20482860148

push

github

schweikert
ci: improve autotools build reliability with ftpmirror and retries

1582 of 1828 relevant lines covered (86.54%)

329.27 hits per line

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

86.54
/src/fping.c
1
/*
2
 * fping: fast-ping, file-ping, favorite-ping, funky-ping
3
 *
4
 *   Ping a list of target hosts in a round robin fashion.
5
 *   A better ping overall.
6
 *
7
 * fping website:  http://www.fping.org
8
 *
9
 * Current maintainer of fping: David Schweikert
10
 * Please send suggestions and patches to: david@schweikert.ch
11
 *
12
 *
13
 * Original author:  Roland Schemers  <schemers@stanford.edu>
14
 * IPv6 Support:     Jeroen Massar    <jeroen@unfix.org / jeroen@ipng.nl>
15
 * Improved main loop: David Schweikert <david@schweikert.ch>
16
 * Debian Merge, TOS settings: Tobi Oetiker <tobi@oetiker.ch>
17
 * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de)
18
 *
19
 *
20
 * Redistribution and use in source and binary forms are permitted
21
 * provided that the above copyright notice and this paragraph are
22
 * duplicated in all such forms and that any documentation,
23
 * advertising materials, and other materials related to such
24
 * distribution and use acknowledge that the software was developed
25
 * by Stanford University.  The name of the University may not be used
26
 * to endorse or promote products derived from this software without
27
 * specific prior written permission.
28
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31
 */
32

33
#ifdef __cplusplus
34
extern "C" {
35
#endif /* __cplusplus */
36

37
#include "config.h"
38
#include "fping.h"
39
#include "options.h"
40
#include "optparse.h"
41

42
#include <errno.h>
43
#include <inttypes.h>
44
#include <limits.h>
45
#include <signal.h>
46
#include <stdarg.h>
47
#include <stdint.h>
48
#include <stdio.h>
49
#include <time.h>
50

51
#include "seqmap.h"
52

53
#ifdef HAVE_UNISTD_H
54
#include <unistd.h>
55
#endif /* HAVE_UNISTD_H */
56

57
#ifdef HAVE_STDLIB_H
58
#include <stdlib.h>
59
#endif /* HAVE_STDLIB_H */
60

61
#include <stddef.h>
62
#include <string.h>
63

64
#include <sys/socket.h>
65
#include <sys/time.h>
66
#include <sys/types.h>
67

68
#if HAVE_SYS_FILE_H
69
#include <sys/file.h>
70
#endif /* HAVE_SYS_FILE_H */
71

72
#ifdef IPV6
73
#include <netinet/icmp6.h>
74
#include <netinet/ip6.h>
75
#endif
76
#include <netinet/in_systm.h>
77

78
#include <netinet/ip.h>
79
#include <netinet/ip_icmp.h>
80

81
#include <arpa/inet.h>
82
#include <ctype.h>
83
#include <netdb.h>
84

85
#include <sys/select.h>
86

87
/*** compatibility ***/
88

89
/* Mac OS X's getaddrinfo() does not fail if we use an invalid combination,
90
 * e.g. AF_INET6 with "127.0.0.1". If we pass AI_UNUSABLE to flags, it behaves
91
 * like other platforms. But AI_UNUSABLE isn't available on other platforms,
92
 * and we can safely use 0 for flags instead.
93
 */
94
#ifndef AI_UNUSABLE
95
#define AI_UNUSABLE 0
96
#endif
97

98
/* MSG_TRUNC available on Linux kernel 2.2+, makes recvmsg return the full
99
 * length of the raw packet received, even if the buffer is smaller */
100
#ifndef MSG_TRUNC
101
#define MSG_TRUNC 0
102
#define RECV_BUFSIZE 4096
103
#else
104
#define RECV_BUFSIZE 128
105
#endif
106

107
/*** externals ***/
108

109
extern char *optarg;
110
extern int optind, opterr;
111
#ifndef h_errno
112
extern int h_errno;
113
#endif
114

115
#ifdef __cplusplus
116
}
117
#endif /* __cplusplus */
118

119
/*** Constants ***/
120

121
/* CLOCK_MONTONIC starts under macOS, OpenBSD and FreeBSD with undefined positive point and can not be use
122
 * see github PR #217
123
 * The configure script detect the predefined operating systems an set CLOCK_REALTIME using over ONLY_CLOCK_REALTIME variable
124
 */
125
#if HAVE_SO_TIMESTAMPNS || ONLY_CLOCK_REALTIME
126
#define CLOCKID CLOCK_REALTIME
127
#endif
128

129
#if !defined(CLOCKID)
130
#if defined(CLOCK_MONOTONIC)
131
#define CLOCKID CLOCK_MONOTONIC
132
#else
133
#define CLOCKID CLOCK_REALTIME
134
#endif
135
#endif
136

137
/*** Ping packet defines ***/
138

139
#define MAX_IP_PACKET 65535 /* (theoretical) max IPv4 packet size */
140
#define SIZE_IP_HDR 20 /* min IPv4 header size */
141
#define SIZE_ICMP_HDR 8 /* from ip_icmp.h */
142
#define MAX_PING_DATA (MAX_IP_PACKET - SIZE_IP_HDR - SIZE_ICMP_HDR)
143

144
#define MAX_GENERATE 131072 /* maximum number of hosts that -g can generate */
145
#define MAX_TARGET_NAME_LEN 255 /* maximum target name length read from file */
146

147
/* sized so as to be like traditional ping */
148
#define DEFAULT_PING_DATA_SIZE 56
149

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

153
#ifdef FPING_SAFE_LIMITS
154
#define MIN_INTERVAL_MS 1 /* in millisec */
155
#define MIN_PERHOST_INTERVAL_MS 10 /* in millisec */
156
#else
157
#define MIN_INTERVAL_MS 0
158
/* Set a very low limit for the per-host interval, even if safe limits are
159
 * disabled, so that the memory allocation of the event storage is not
160
 * unreasonably high. 0.001 ms would mean in theory at least 592 mbps of data
161
 * sent to a single host, which probably doesn't make sense in any scenario. */
162
#define MIN_PERHOST_INTERVAL_MS 0.001
163
#endif
164

165
/* response time array flags */
166
#define RESP_WAITING -1
167
#define RESP_UNUSED -2
168
#define RESP_ERROR -3
169
#define RESP_TIMEOUT -4
170

171
/* debugging flags */
172
#if defined(DEBUG) || defined(_DEBUG)
173
#define DBG_TRACE 1
174
#define DBG_SENT_TIMES 2
175
#define DBG_RANDOM_LOSE_FEW 4
176
#define DBG_RANDOM_LOSE_MANY 8
177
#define DBG_PRINT_PER_SYSTEM 16
178
#define DBG_REPORT_ALL_RTTS 32
179
#endif /* DEBUG || _DEBUG */
180

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

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

224
#define ICMP_UNREACH_MAXTYPE 15
225

226
#ifdef IPV6
227
/* Long names for ICMPv6 unreachable codes */
228
#define ICMP6_UNREACH_MAXCODE 9
229
char *icmp6_unreach_str[ICMP6_UNREACH_MAXCODE + 1] = {
230
    "No route to destination", /* 0 */
231
    "Communication with destination administratively prohibited", /* 1 */
232
    "Beyond scope of source address", /* 2 */
233
    "Address unreachable", /* 3 */
234
    "Port unreachable", /* 4 */
235
    "Source address failed ingress/egress policy", /* 5 */
236
    "Reject route to destination", /* 6 */
237
    "Error in Source Routing Header", /* 7 */
238
    "Headers too long", /* 8 */
239
    "Error in P-Route", /* 9 */
240
};
241

242
/* Long names for ICMPv6 time exceeded codes */
243
#define ICMP6_TIME_EXCEEDED_MAXCODE 1
244
char *icmp6_time_exceeded_str[ICMP6_TIME_EXCEEDED_MAXCODE + 1] = {
245
    "Hop limit exceeded in transit", /* 0 */
246
    "Fragment reassembly time exceeded", /* 1 */
247
};
248

249
/* Long names for ICMPv6 parameter problem codes */
250
#define ICMP6_PARAM_PROB_MAXCODE 10
251
char *icmp6_param_prob_str[ICMP6_PARAM_PROB_MAXCODE + 1] = {
252
    "Erroneous header field encountered", /* 0 */
253
    "Unrecognized Next Header type encountered", /* 1 */
254
    "Unrecognized IPv6 option encountered", /* 2 */
255
    "IPv6 First Fragment has incomplete IPv6 Header Chain", /* 3 */
256
    "SR Upper-layer Header Error", /* 4 */
257
    "Unrecognized Next Header type encountered by intermediate node", /* 5 */
258
    "Extension header too big", /* 6 */
259
    "Extension header chain too long", /* 7 */
260
    "Too many extension headers", /* 8 */
261
    "Too many options in extension header", /* 9 */
262
    "Option too big", /* 10 */
263
};
264
#endif
265

266
typedef struct ip_header_result {
267
    int tos;
268
    int ttl;
269
    uint32_t otime_ms;
270
    uint32_t rtime_ms;
271
    uint32_t ttime_ms;
272
} IP_HEADER_RESULT;
273

274
IP_HEADER_RESULT default_ip_header_result() {
2,756✔
275
    return (IP_HEADER_RESULT){-1, -1, 0x80000000U, 0x80000000U, 0x80000000U};
2,756✔
276
}
277

278
struct event;
279
typedef struct host_entry {
280
    int i; /* index into array */
281
    char *name; /* name as given by user */
282
    char *host; /* text description of host */
283
    struct sockaddr_storage saddr; /* internet address */
284
    socklen_t saddr_len;
285
    int64_t timeout; /* time to wait for response */
286
    int64_t last_send_time; /* time of last packet sent */
287
    int num_sent; /* number of ping packets sent (for statistics) */
288
    int num_recv; /* number of pings received (duplicates ignored) */
289
    int num_recv_total; /* number of pings received, including duplicates */
290
    int64_t max_reply; /* longest response time */
291
    int64_t min_reply; /* shortest response time */
292
    int64_t total_time; /* sum of response times */
293
    /* _i -> splits (reset on every report interval) */
294
    int num_sent_i; /* number of ping packets sent */
295
    int num_recv_i; /* number of pings received */
296
    int64_t max_reply_i; /* longest response time */
297
    int64_t min_reply_i; /* shortest response time */
298
    int64_t total_time_i; /* sum of response times */
299
    int64_t *resp_times; /* individual response times */
300

301
    /* to avoid allocating two struct events each time that we send a ping, we
302
     * preallocate here two struct events for each ping that we might send for
303
     * this host. */
304
    struct event *event_storage_ping;
305
    struct event *event_storage_timeout;
306
} HOST_ENTRY;
307

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

310
/* basic algorithm to ensure that we have correct data at all times:
311
 *
312
 * 1. when a ping is sent:
313
 *    - two events get added into event_queue:
314
 *      - t+PERIOD: ping event
315
 *      - t+TIMEOUT: timeout event
316
 *
317
 * 2. when a ping is received:
318
 *    - record statistics (increase num_sent and num_received)
319
 *    - remove timeout event (we store the event in seqmap, so that we can retrieve it when the response is received)
320
 *
321
 * 3. when a timeout happens:
322
 *    - record statistics (increase num_sent only)
323
 */
324

325
#define EV_TYPE_PING 1
326
#define EV_TYPE_TIMEOUT 2
327

328
struct event {
329
    struct event *ev_prev;
330
    struct event *ev_next;
331
    int64_t ev_time;
332
    struct host_entry *host;
333
    int ping_index;
334
};
335

336
struct event_queue {
337
    struct event *first;
338
    struct event *last;
339
};
340

341
/*** globals ***/
342

343
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
344

345
/* we keep two separate queues: a ping queue, for when the next ping should be
346
 * sent, and a timeout queue. the reason for having two separate queues is that
347
 * the ping period and the timeout value are different, so if we put them in
348
 * the same event queue, we would need to scan many more entries when inserting
349
 * into the sorted list.
350
 */
351
struct event_queue event_queue_ping;
352
struct event_queue event_queue_timeout;
353

354
char *prog;
355
int ident4 = 0; /* our icmp identity field */
356
int ident6 = 0;
357
const int sock_opt_on = 1; /* to activate a socket option */
358
int socket4 = -1;
359
int socktype4 = -1;
360
int using_sock_dgram4 = 0;
361
#ifndef IPV6
362
int hints_ai_family = AF_INET;
363
#else
364
int socket6 = -1;
365
int socktype6 = -1;
366
int hints_ai_family = AF_UNSPEC;
367
#endif
368

369
volatile sig_atomic_t status_snapshot = 0;
370
volatile sig_atomic_t finish_requested = 0;
371

372
unsigned int debugging = 0;
373

374
/* all time-related values are int64_t nanoseconds */
375
unsigned int retry = DEFAULT_RETRY;
376
int64_t timeout = (int64_t)DEFAULT_TIMEOUT * 1000000;
377
int64_t seqmap_timeout = (int64_t)DEFAULT_SEQMAP_TIMEOUT * 1000000;
378
int64_t interval = (int64_t)DEFAULT_INTERVAL * 1000000;
379
int64_t perhost_interval = (int64_t)DEFAULT_PERHOST_INTERVAL * 1000000;
380
float backoff = DEFAULT_BACKOFF_FACTOR;
381
unsigned int ping_data_size = DEFAULT_PING_DATA_SIZE;
382
unsigned int count = 1, min_reachable = 0;
383
unsigned int trials;
384
int64_t report_interval = 0;
385
unsigned int ttl = 0;
386
int src_addr_set = 0;
387
struct in_addr src_addr;
388
#ifdef IPV6
389
int src_addr6_set = 0;
390
struct in6_addr src_addr6;
391
#endif
392

393
/* global stats */
394
int64_t max_reply = 0;
395
int64_t min_reply = 0;
396
int64_t total_replies = 0;
397
int64_t sum_replies = 0;
398
int max_hostname_len = 0;
399
int num_hosts = 0; /* total number of hosts */
400
int num_alive = 0, /* total number alive */
401
    num_unreachable = 0, /* total number unreachable */
402
    num_noaddress = 0; /* total number of addresses not found */
403
int num_timeout = 0, /* number of times select timed out */
404
    num_pingsent = 0, /* total pings sent */
405
    num_pingreceived = 0, /* total pings received */
406
    num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
407

408
struct timespec current_time; /* current time (pseudo) */
409
int64_t current_time_ns;
410
int64_t start_time;
411
int64_t end_time;
412
int64_t last_send_time; /* time last ping was sent */
413
int64_t next_report_time; /* time next -Q report is expected */
414

415
/* switches */
416
int generate_flag = 0; /* flag for IP list generation */
417
int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
418
int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag, json_flag;
419
int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
420
int multif_flag, timeout_flag, fast_reachable;
421
int outage_flag = 0;
422
int timestamp_flag = 0;
423
int timestamp_format_flag = 0;
424
int random_data_flag = 0;
425
int cumulative_stats_flag = 0;
426
int check_source_flag = 0;
427
int icmp_request_typ = 0;
428
int print_tos_flag = 0;
429
int print_ttl_flag = 0;
430
int size_flag = 0;
431
#if defined(DEBUG) || defined(_DEBUG)
432
int randomly_lose_flag, trace_flag, print_per_system_flag;
433
int lose_factor;
434
#endif /* DEBUG || _DEBUG */
435

436
unsigned int fwmark = 0;
437

438
char *filename = NULL; /* file containing hosts to ping */
439

440
/*** forward declarations ***/
441

442
void add_name(char *name);
443
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len);
444
char *na_cat(char *name, struct in_addr ipaddr);
445
void crash_and_burn(char *message);
446
void errno_crash_and_burn(char *message);
447
char *get_host_by_address(struct in_addr in);
448
int send_ping(HOST_ENTRY *h, int index);
449
void usage(int);
450
int wait_for_reply(int64_t);
451
void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg);
452
void print_timeout(HOST_ENTRY *h, int ping_index);
453
void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply);
454
void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply);
455
void print_per_system_stats(void);
456
void print_per_system_stats_json(void);
457
void print_per_system_splits(void);
458
void print_per_system_splits_json(void);
459
void stats_reset_interval(HOST_ENTRY *h);
460
void print_netdata(void);
461
void print_global_stats(void);
462
void print_global_stats_json(void);
463
void main_loop();
464
void signal_handler(int);
465
void finish();
466
const char *sprint_tm(int64_t t);
467
void ev_enqueue(struct event_queue *queue, struct event *event);
468
struct event *ev_dequeue(struct event_queue *queue);
469
void ev_remove(struct event_queue *queue, struct event *event);
470
void add_cidr(char *);
471
void add_cidr_ipv4(unsigned long, unsigned long);
472
void add_range(char *, char *);
473
void add_addr_range_ipv4(unsigned long, unsigned long);
474
#ifdef IPV6
475
uint64_t be_octets_to_uint64(uint8_t*);
476
void uint64_to_be_octets(uint64_t, uint8_t*);
477
void add_cidr_ipv6(uint64_t, uint64_t, unsigned long, const char *);
478
void add_addr_range_ipv6(uint64_t, uint64_t, uint64_t, uint64_t, const char *);
479
#endif
480
void print_warning(char *fmt, ...);
481
int addr_cmp(struct sockaddr *a, struct sockaddr *b);
482
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
483
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
484
struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
485
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
486
void update_current_time();
487
void print_timestamp_format(int64_t current_time_ns, int timestamp_format);
488
static uint32_t ms_since_midnight_utc(int64_t time_val);
489

490
/************************************************************
491

492
  Function: p_setsockopt
493

494
*************************************************************
495

496
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
497

498
  Description:
499

500
  Elevates privileges to p_uid when required, calls
501
  setsockopt, and drops privileges back.
502

503
************************************************************/
504

505
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
22✔
506
    const void *optval, socklen_t optlen)
507
{
508
    const uid_t saved_uid = geteuid();
22✔
509
    int res;
510

511
    if (p_uid != saved_uid && seteuid(p_uid)) {
22✔
512
        perror("cannot elevate privileges for setsockopt");
×
513
    }
514

515
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
516

517
    if (p_uid != saved_uid && seteuid(saved_uid)) {
22✔
518
        perror("fatal error: could not drop privileges after setsockopt");
×
519
        /* continuing would be a security hole */
520
        exit(4);
×
521
    }
522

523
    return res;
22✔
524
}
525

526
/************************************************************
527

528
  Function: main
529

530
*************************************************************
531

532
  Inputs:  int argc, char** argv
533

534
  Description:
535

536
  Main program entry point
537

538
************************************************************/
539

540
int main(int argc, char **argv)
722✔
541
{
542
/* Debug: CPU Performance */
543
#if defined(DEBUG) || defined(_DEBUG)
544
    clock_t perf_cpu_start, perf_cpu_end;
545
    double perf_cpu_time_used;
546
    perf_cpu_start = clock();
547
#endif /* DEBUG || _DEBUG */
548

549
    int c;
550
    const uid_t suid = geteuid();
722✔
551
    int tos = 0;
722✔
552
    struct optparse optparse_state;
553
#ifdef USE_SIGACTION
554
    struct sigaction act;
555
#endif
556

557
    /* pre-parse -h/--help, so that we also can output help information
558
     * without trying to open the socket, which might fail */
559
    prog = argv[0];
722✔
560
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
722✔
561
        usage(0);
3✔
562
    }
1✔
563

564
    socket4 = open_ping_socket_ipv4(&socktype4);
720✔
565
#ifdef __linux__
566
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
567
     * structure is missing in the message.
568
     */
569
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
504✔
570
#endif
571

572
#ifdef IPV6
573
    socket6 = open_ping_socket_ipv6(&socktype6);
720✔
574
    /* if called (sym-linked) via 'fping6', imply '-6'
575
     * for backward compatibility */
576
    if (strstr(prog, "fping6")) {
720✔
577
        hints_ai_family = AF_INET6;
×
578
    }
579
#endif
580

581
    memset(&src_addr, 0, sizeof(src_addr));
720✔
582
#ifdef IPV6
583
    memset(&src_addr6, 0, sizeof(src_addr6));
720✔
584
#endif
585

586
    if (!suid && suid != getuid()) {
720✔
587
        /* *temporarily* drop privileges */
588
        if (seteuid(getuid()) == -1)
677✔
589
            perror("cannot setuid");
×
590
    }
185✔
591

592
    optparse_init(&optparse_state, argv);
720✔
593
    ident4 = ident6 = htons(getpid() & 0xFFFF);
720✔
594
    verbose_flag = 1;
720✔
595
    backoff_flag = 1;
720✔
596
    opterr = 1;
720✔
597

598
    /* get command line options */
599

600
    struct optparse_long longopts[] = {
720✔
601
        { "ipv4", '4', OPTPARSE_NONE },
602
        { "ipv6", '6', OPTPARSE_NONE },
603
        { "alive", 'a', OPTPARSE_NONE },
604
        { "addr", 'A', OPTPARSE_NONE },
605
        { "size", 'b', OPTPARSE_REQUIRED },
606
        { "backoff", 'B', OPTPARSE_REQUIRED },
607
        { "count", 'c', OPTPARSE_REQUIRED },
608
        { "vcount", 'C', OPTPARSE_REQUIRED },
609
        { "rdns", 'd', OPTPARSE_NONE },
610
        { "timestamp", 'D', OPTPARSE_NONE },
611
        { "timestamp-format", '0', OPTPARSE_REQUIRED },
612
        { "elapsed", 'e', OPTPARSE_NONE },
613
        { "file", 'f', OPTPARSE_REQUIRED },
614
        { "generate", 'g', OPTPARSE_NONE },
615
        { "help", 'h', OPTPARSE_NONE },
616
        { "ttl", 'H', OPTPARSE_REQUIRED },
617
        { "interval", 'i', OPTPARSE_REQUIRED },
618
        { "iface", 'I', OPTPARSE_REQUIRED },
619
        { "json", 'J', OPTPARSE_NONE },
620
        { "icmp-timestamp", '0', OPTPARSE_NONE },
621
#ifdef SO_MARK
622
        { "fwmark", 'k', OPTPARSE_REQUIRED },
623
#endif
624
        { "loop", 'l', OPTPARSE_NONE },
625
        { "all", 'm', OPTPARSE_NONE },
626
        { "dontfrag", 'M', OPTPARSE_NONE },
627
        { "name", 'n', OPTPARSE_NONE },
628
        { "netdata", 'N', OPTPARSE_NONE },
629
        { "outage", 'o', OPTPARSE_NONE },
630
        { "tos", 'O', OPTPARSE_REQUIRED },
631
        { "period", 'p', OPTPARSE_REQUIRED },
632
        { "quiet", 'q', OPTPARSE_NONE },
633
        { "squiet", 'Q', OPTPARSE_REQUIRED },
634
        { "retry", 'r', OPTPARSE_REQUIRED },
635
        { "random", 'R', OPTPARSE_NONE },
636
        { "stats", 's', OPTPARSE_NONE },
637
        { "src", 'S', OPTPARSE_REQUIRED },
638
        { "timeout", 't', OPTPARSE_REQUIRED },
639
        { NULL, 'T', OPTPARSE_REQUIRED },
640
        { "unreach", 'u', OPTPARSE_NONE },
641
        { "version", 'v', OPTPARSE_NONE },
642
        { "reachable", 'x', OPTPARSE_REQUIRED },
643
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
644
        { "check-source", '0', OPTPARSE_NONE },
645
        { "print-tos", '0', OPTPARSE_NONE },
646
        { "print-ttl", '0', OPTPARSE_NONE },
647
        { "seqmap-timeout", '0', OPTPARSE_REQUIRED },
648
#if defined(DEBUG) || defined(_DEBUG)
649
        { NULL, 'z', OPTPARSE_REQUIRED },
650
#endif
651
        { 0, 0, 0 }
652
    };
653

654
    float opt_value_float;
655
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
1,791✔
656
        switch (c) {
1,180✔
657
        case '0':
102✔
658
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
129✔
659
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
24✔
660
                  timestamp_format_flag = 1;
6✔
661
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
20✔
662
                  timestamp_format_flag = 2;
6✔
663
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
14✔
664
                  timestamp_format_flag = 3;
6✔
665
                }else{
2✔
666
                  usage(1);
6✔
667
                }
668
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
113✔
669
                check_source_flag = 1;
9✔
670
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
97✔
671
#ifdef IPV6
672
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
29✔
673
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
674
                    exit(1);
2✔
675
                }
676
                hints_ai_family = AF_INET;
27✔
677
#endif
678
                icmp_request_typ = 13;
27✔
679
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
27✔
680
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
70✔
681
                print_tos_flag = 1;
29✔
682
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
29✔
683
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTOS, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
684
                        perror("setsockopt IP_RECVTOS");
×
685
                    }
686
                }
687
#if defined(IPV6) && defined(IPV6_RECVTCLASS)
688
                if (socket6 >= 0) {
29✔
689
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVTCLASS, &sock_opt_on, sizeof(sock_opt_on))) {
29✔
690
                        perror("setsockopt IPV6_RECVTCLASS");
×
691
                    }
692
                }
7✔
693
#endif
694
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
45✔
695
                print_ttl_flag = 1;
26✔
696
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
26✔
697
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTTL, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
698
                        perror("setsockopt IP_RECVTTL");
×
699
                    }
700
                }
701
#if defined(IPV6) && defined(IPV6_RECVHOPLIMIT)
702
                if (socket6 >= 0) {
26✔
703
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &sock_opt_on, sizeof(sock_opt_on))) {
26✔
704
                        perror("setsockopt IPV6_RECVHOPLIMIT");
×
705
                    }
706
                }
6✔
707
#endif
708
            } else if (strstr(optparse_state.optlongname, "seqmap-timeout") != NULL) {
18✔
709
                if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
12✔
710
                    usage(1);
6✔
711
                if (opt_value_float < 0)
8✔
712
                    usage(1);
3✔
713
                seqmap_timeout = opt_value_float * 1000000;
6✔
714
            } else {
2✔
715
                usage(1);
×
716
            }
717
            break;
117✔
718
        case '4':
32✔
719
#ifdef IPV6
720
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
721
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
722
                exit(1);
3✔
723
            }
724
            hints_ai_family = AF_INET;
40✔
725
#endif
726
            break;
40✔
727
        case '6':
28✔
728
#ifdef IPV6
729
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
31✔
730
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
731
                exit(1);
5✔
732
            }
733
            hints_ai_family = AF_INET6;
26✔
734
#else
735
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
736
            exit(1);
737
#endif
738
            break;
26✔
739
        case 'M':
2✔
740
#ifdef IP_MTU_DISCOVER
741
            if (socket4 >= 0) {
2✔
742
                int val = IP_PMTUDISC_DO;
2✔
743
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
744
                    perror("setsockopt IP_MTU_DISCOVER");
745
                }
746
            }
747
#ifdef IPV6
748
            if (socket6 >= 0) {
2✔
749
                int val = IPV6_PMTUDISC_DO;
2✔
750
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
751
                    perror("setsockopt IPV6_MTU_DISCOVER");
752
                }
753
            }
754
#endif
755
#else
756
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
757
            exit(1);
758
#endif
759
            break;
2✔
760

761
        case 't':
40✔
762
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
45✔
763
                usage(1);
6✔
764
            if (opt_value_float < 0) {
41✔
765
                usage(1);
3✔
766
            }
1✔
767
            timeout = opt_value_float * 1000000;
39✔
768
            timeout_flag = 1;
39✔
769
            break;
39✔
770

771
        case 'r':
36✔
772
            if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
40✔
773
                usage(1);
6✔
774
            break;
36✔
775

776
        case 'i':
30✔
777
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
36✔
778
                usage(1);
6✔
779
            if (opt_value_float < 0) {
32✔
780
                usage(1);
3✔
781
            }
1✔
782
            interval = opt_value_float * 1000000;
30✔
783
            break;
30✔
784

785
        case 'p':
70✔
786
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
102✔
787
                usage(1);
6✔
788
            if (opt_value_float < 0) {
98✔
789
                usage(1);
3✔
790
            }
1✔
791
            perhost_interval = opt_value_float * 1000000;
96✔
792

793
            break;
96✔
794

795
        case 'c':
90✔
796
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
129✔
797
                usage(1);
6✔
798

799
            count_flag = 1;
125✔
800
            break;
125✔
801

802
        case 'C':
34✔
803
            if (!(count = (unsigned int)atoi(optparse_state.optarg)))
48✔
804
                usage(1);
6✔
805

806
            count_flag = 1;
44✔
807
            report_all_rtts_flag = 1;
44✔
808
            break;
44✔
809

810
        case 'b':
14✔
811
            if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
21✔
812
                usage(1);
6✔
813
            size_flag = 1;
17✔
814
            break;
17✔
815

816
        case 'h':
2✔
817
            usage(0);
3✔
818
            break;
1✔
819

820
        case 'q':
26✔
821
            verbose_flag = 0;
38✔
822
            quiet_flag = 1;
38✔
823
            break;
38✔
824

825
        case 'Q':
24✔
826
            verbose_flag = 0;
36✔
827
            quiet_flag = 1;
36✔
828
            if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
36✔
829
                usage(1);
6✔
830
            if (opt_value_float < 0) {
32✔
831
                usage(1);
3✔
832
            }
1✔
833
            report_interval = opt_value_float * 1e9;
30✔
834

835
            /* recognize keyword(s) after number, ignore everything else */
836
            {
837
                char *comma = strchr(optparse_state.optarg, ',');
30✔
838
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
30✔
839
                    cumulative_stats_flag = 1;
6✔
840
                }
2✔
841
            }
842

843
            break;
30✔
844

845
        case 'e':
6✔
846
            elapsed_flag = 1;
8✔
847
            break;
8✔
848

849
        case 'm':
850
            multif_flag = 1;
×
851
            break;
×
852

853
        case 'N':
2✔
854
            netdata_flag = 1;
3✔
855
            break;
3✔
856

857
        case 'n':
4✔
858
            name_flag = 1;
6✔
859
            if (rdns_flag) {
6✔
860
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
861
                exit(1);
3✔
862
            }
863
            break;
3✔
864

865
        case 'd':
6✔
866
            rdns_flag = 1;
9✔
867
            if (name_flag) {
9✔
868
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
869
                exit(1);
3✔
870
            }
871
            break;
6✔
872

873
        case 'A':
2✔
874
            addr_flag = 1;
3✔
875
            break;
3✔
876

877
        case 'B':
10✔
878
            if (!(backoff = atof(optparse_state.optarg)))
14✔
879
                usage(1);
6✔
880

881
            break;
10✔
882

883
        case 's':
8✔
884
            stats_flag = 1;
12✔
885
            break;
12✔
886

887
        case 'D':
22✔
888
            timestamp_flag = 1;
33✔
889
            break;
33✔
890

891
        case 'R':
4✔
892
            random_data_flag = 1;
5✔
893
            break;
5✔
894

895
        case 'l':
2✔
896
            loop_flag = 1;
3✔
897
            backoff_flag = 0;
3✔
898
            break;
3✔
899

900
        case 'u':
8✔
901
            unreachable_flag = 1;
12✔
902
            break;
12✔
903

904
        case 'a':
18✔
905
            alive_flag = 1;
26✔
906
            break;
26✔
907

908
        case 'H':
8✔
909
            if (!(ttl = (unsigned int)atoi(optparse_state.optarg)))
12✔
910
                usage(1);
6✔
911
            break;
8✔
912

913
#if defined(DEBUG) || defined(_DEBUG)
914
        case 'z':
915
            if (sscanf(optparse_state.optarg, "0x%x", &debugging) != 1)
916
                if (sscanf(optparse_state.optarg, "%u", &debugging) != 1)
917
                    usage(1);
918

919
            break;
920
#endif /* DEBUG || _DEBUG */
921

922
        case 'v':
4✔
923
            printf("%s: Version %s\n", prog, VERSION);
6✔
924
            exit(0);
6✔
925

926
        case 'x':
8✔
927
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
12✔
928
                usage(1);
6✔
929
            break;
8✔
930

931
        case 'X':
6✔
932
            if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
9✔
933
                usage(1);
6✔
934
            fast_reachable = 1;
5✔
935
            break;
5✔
936

937
        case 'f':
18✔
938
            filename = optparse_state.optarg;
27✔
939
            break;
27✔
940
#ifdef SO_MARK
941
        case 'k':
10✔
942
            if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
10✔
943
                usage(1);
4✔
944

945
            if (socket4 >= 0)
6✔
946
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
947
                    perror("fwmark ipv4");
4✔
948

949
#ifdef IPV6
950
            if (socket6 >= 0)
6✔
951
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
952
                    perror("fwmark ipv6");
4✔
953
#endif
954

955
            break;
6✔
956
#endif
957

958
        case 'g':
100✔
959
            /* use IP list generation */
960
            /* mutually exclusive with using file input or command line targets */
961
            generate_flag = 1;
120✔
962
            break;
120✔
963

964
        case 'S':
10✔
965
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
966
                src_addr_set = 1;
6✔
967
                break;
6✔
968
            }
969
#ifdef IPV6
970
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
971
                src_addr6_set = 1;
4✔
972
                break;
4✔
973
            }
974
#endif
975
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
976
            exit(1);
3✔
977

978
        case 'I':
6✔
979
#ifdef SO_BINDTODEVICE
980
            if (socket4 >= 0) {
6✔
981
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
982
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
983
                    exit(1);
2✔
984
                }
985
            }
986
#ifdef IPV6
987
            if (socket6 >= 0) {
4✔
988
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
989
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
×
990
                    exit(1);
×
991
                }
992
            }
993
#endif
994
#else
995
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
996
            exit(3);
997
            ;
998
#endif
999
            break;
4✔
1000

1001
        case 'J':
42✔
1002
            json_flag = 1;
62✔
1003
            break;
62✔
1004

1005
        case 'T':
2✔
1006
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
1007
            break;
3✔
1008

1009
        case 'O':
10✔
1010
            if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
15✔
1011
                if (socket4 >= 0) {
9✔
1012
                    if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
1013
                        perror("setting type of service octet IP_TOS");
×
1014
                    }
1015
                }
3✔
1016
#if defined(IPV6) && defined(IPV6_TCLASS)
1017
                if (socket6 >= 0) {
9✔
1018
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
1019
                        perror("setting type of service octet IPV6_TCLASS");
×
1020
                    }
1021
                }
3✔
1022
#endif
1023
            }
3✔
1024
            else {
1025
                usage(1);
6✔
1026
            }
1027
            break;
11✔
1028

1029
        case 'o':
6✔
1030
            outage_flag = 1;
9✔
1031
            break;
9✔
1032

1033
        case '?':
4✔
1034
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
1035
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
1036
            exit(1);
6✔
1037
            break;
1038
        }
1039
    }
1040

1041
    /* permanently drop privileges */
1042
    if (suid != getuid() && setuid(getuid())) {
545✔
1043
        perror("fatal: failed to permanently drop privileges");
×
1044
        /* continuing would be a security hole */
1045
        exit(4);
×
1046
    }
1047

1048
    /* validate various option settings */
1049

1050
#ifndef IPV6
1051
    if (socket4 < 0) {
1052
        crash_and_burn("can't create socket (must run as root?)");
1053
    }
1054
#else
1055
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
545✔
1056
        crash_and_burn("can't create socket (must run as root?)");
×
1057
    }
1058
#endif
1059

1060
    if (ttl > 255) {
543✔
1061
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
1062
        exit(1);
3✔
1063
    }
1064

1065
    if (unreachable_flag && alive_flag) {
540✔
1066
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
1067
        exit(1);
3✔
1068
    }
1069

1070
    if (count_flag && loop_flag) {
537✔
1071
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
1072
        exit(1);
3✔
1073
    }
1074

1075
    if (json_flag && !count_flag && !loop_flag) {
534✔
1076
        fprintf(stderr, "%s: option -J, --json requires -c, -C, or -l\n", prog);
3✔
1077
        exit(1);
3✔
1078
    }
1079

1080
    if (interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
531✔
1081
        fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
3✔
1082
        exit(1);
3✔
1083
    }
1084

1085
    if (perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
528✔
1086
        fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
3✔
1087
        exit(1);
3✔
1088
    }
1089

1090
    if (ping_data_size > MAX_PING_DATA) {
525✔
1091
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1092
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1093
        exit(1);
3✔
1094
    }
1095

1096
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
522✔
1097
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1098
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1099
        exit(1);
6✔
1100
    }
1101

1102
    if (icmp_request_typ == 13 && size_flag != 0) {
516✔
1103
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1104
        exit(1);
9✔
1105
    }
1106

1107
    if (count_flag) {
507✔
1108
        if (verbose_flag)
159✔
1109
            per_recv_flag = 1;
94✔
1110

1111
        alive_flag = unreachable_flag = verbose_flag = 0;
159✔
1112
    }
47✔
1113

1114
    if (loop_flag) {
507✔
1115
        if (!report_interval)
×
1116
            per_recv_flag = 1;
×
1117

1118
        alive_flag = unreachable_flag = verbose_flag = 0;
×
1119
    }
1120

1121
    if (alive_flag || unreachable_flag || min_reachable)
507✔
1122
        verbose_flag = 0;
37✔
1123

1124
    trials = (count > retry + 1) ? count : retry + 1;
523✔
1125

1126
    /* auto-tune default timeout for count/loop modes
1127
     * see also github #32 */
1128
    if (loop_flag || count_flag) {
523✔
1129
        if (!timeout_flag) {
159✔
1130
            timeout = perhost_interval;
154✔
1131
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
154✔
1132
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1133
            }
1134
        }
46✔
1135
    }
47✔
1136

1137
#if defined(DEBUG) || defined(_DEBUG)
1138
    if (debugging & DBG_TRACE)
1139
        trace_flag = 1;
1140

1141
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1142
        randomly_lose_flag = 1;
1143
        lose_factor = 1; /* ie, 1/4 */
1144
    }
1145

1146
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1147
        randomly_lose_flag = 1;
1148
        lose_factor = 5; /* ie, 3/4 */
1149
    }
1150

1151
    if (debugging & DBG_PRINT_PER_SYSTEM)
1152
        print_per_system_flag = 1;
1153

1154
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1155
        report_all_rtts_flag = 1;
1156

1157
    if (trace_flag) {
1158
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1159
            prog, count, retry, interval / 1e6);
1160
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1161
            perhost_interval / 1e6, timeout / 1e6);
1162
        fprintf(stderr, "  seqmap_timeout: %.0f\n", seqmap_timeout / 1e6);
1163
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1164
            ping_data_size, trials);
1165

1166
        if (verbose_flag)
1167
            fprintf(stderr, "  verbose_flag set\n");
1168
        if (multif_flag)
1169
            fprintf(stderr, "  multif_flag set\n");
1170
        if (name_flag)
1171
            fprintf(stderr, "  name_flag set\n");
1172
        if (addr_flag)
1173
            fprintf(stderr, "  addr_flag set\n");
1174
        if (stats_flag)
1175
            fprintf(stderr, "  stats_flag set\n");
1176
        if (unreachable_flag)
1177
            fprintf(stderr, "  unreachable_flag set\n");
1178
        if (alive_flag)
1179
            fprintf(stderr, "  alive_flag set\n");
1180
        if (elapsed_flag)
1181
            fprintf(stderr, "  elapsed_flag set\n");
1182
        if (version_flag)
1183
            fprintf(stderr, "  version_flag set\n");
1184
        if (count_flag)
1185
            fprintf(stderr, "  count_flag set\n");
1186
        if (loop_flag)
1187
            fprintf(stderr, "  loop_flag set\n");
1188
        if (backoff_flag)
1189
            fprintf(stderr, "  backoff_flag set\n");
1190
        if (per_recv_flag)
1191
            fprintf(stderr, "  per_recv_flag set\n");
1192
        if (report_all_rtts_flag)
1193
            fprintf(stderr, "  report_all_rtts_flag set\n");
1194
        if (randomly_lose_flag)
1195
            fprintf(stderr, "  randomly_lose_flag set\n");
1196
        if (print_per_system_flag)
1197
            fprintf(stderr, "  print_per_system_flag set\n");
1198
        if (outage_flag)
1199
            fprintf(stderr, "  outage_flag set\n");
1200
        if (netdata_flag)
1201
            fprintf(stderr, "  netdata_flag set\n");
1202
        if (json_flag)
1203
            fprintf(stderr, "  json_flag set\n");
1204
    }
1205
#endif /* DEBUG || _DEBUG */
1206

1207
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1208
    if (ttl > 0) {
523✔
1209
        if (socket4 >= 0) {
3✔
1210
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1211
                perror("setting time to live");
×
1212
            }
1213
        }
1✔
1214
#ifdef IPV6
1215
        if (socket6 >= 0) {
3✔
1216
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1217
                perror("setting time to live");
×
1218
            }
1219
        }
1✔
1220
#endif
1221
    }
1✔
1222

1223
#if HAVE_SO_TIMESTAMPNS
1224
    {
1225
        int opt = 1;
380✔
1226
        if (socket4 >= 0) {
380✔
1227
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
380✔
1228
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1229
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1230
                }
1231
            }
1232
        }
1233
#ifdef IPV6
1234
        if (socket6 >= 0) {
380✔
1235
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
380✔
1236
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1237
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1238
                }
1239
            }
1240
        }
1241
#endif
1242
    }
1243
#endif
1244

1245
    update_current_time();
523✔
1246
    start_time = current_time_ns;
523✔
1247

1248
    /* handle host names supplied on command line or in a file */
1249
    /* if the generate_flag is on, then generate the IP list */
1250

1251
    argv = &argv[optparse_state.optind];
523✔
1252
    argc -= optparse_state.optind;
523✔
1253

1254
    /* calculate how many ping can be in-flight per host */
1255
    if (count_flag) {
523✔
1256
        event_storage_count = count;
174✔
1257
    }
62✔
1258
    else if (loop_flag) {
349✔
1259
        if (perhost_interval > timeout) {
×
1260
            event_storage_count = 1;
×
1261
        }
1262
        else {
1263
            event_storage_count = 1 + timeout / perhost_interval;
×
1264
        }
1265
    }
1266
    else {
1267
        event_storage_count = 1;
349✔
1268
    }
1269

1270
    /* file and generate are mutually exclusive */
1271
    /* file and command line are mutually exclusive */
1272
    /* generate requires command line parameters beyond the switches */
1273
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
523✔
1274
        usage(1);
19✔
1275

1276
    /* if no conditions are specified, then assume input from stdin */
1277
    if (!*argv && !filename && !generate_flag)
505✔
1278
        filename = "-";
69✔
1279

1280
    if (*argv && !generate_flag) {
505✔
1281
        while (*argv) {
651✔
1282
            add_name(*argv);
356✔
1283
            ++argv;
356✔
1284
        }
1285
    }
77✔
1286
    else if (filename) {
210✔
1287
        FILE *ping_file;
1288
        char line[MAX_TARGET_NAME_LEN + 1];
1289
        char host[MAX_TARGET_NAME_LEN + 1];
1290
        char scratch[MAX_TARGET_NAME_LEN + 1];
1291
        int skip, non_empty;
1292

1293
        if (strcmp(filename, "-") == 0)
96✔
1294
            ping_file = fdopen(0, "r");
72✔
1295
        else
1296
            ping_file = fopen(filename, "r");
24✔
1297

1298
        if (!ping_file)
96✔
1299
            errno_crash_and_burn("fopen");
3✔
1300

1301
        /*
1302
         * Read the first word of every non-comment line, skip everything else.
1303
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1304
         * character is a '#' are interpreted as comments and ignored.)
1305
        */
1306
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1307
            skip = non_empty = 0;
309✔
1308

1309
            /* skip over a prefix of the line where sscanf finds nothing */
1310
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1311
                continue;
99✔
1312
            }
1313

1314
            /* the first word of the line can indicate a comment line */
1315
            if (host[0] == '#') {
210✔
1316
                skip = 1; /* skip remainder of line */
42✔
1317
            } else {
14✔
1318
                non_empty = 1; /* we have something to add as a target name */
168✔
1319
                /*
1320
                 * We have found the start of a word.
1321
                 * This part of the line may contain all of the first word.
1322
                 */
1323
                if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
168✔
1324
                    char discard1[MAX_TARGET_NAME_LEN + 1];
1325
                    char discard2[MAX_TARGET_NAME_LEN + 1];
1326
                    if (sscanf(line, "%s%s", discard1, discard2) == 2) {
102✔
1327
                        skip = 1; /* a second word starts in this part */
6✔
1328
                    }
2✔
1329
                    if (isspace(line[sizeof(line) - 2])) {
102✔
1330
                        skip = 1; /* the first word ends in this part */
42✔
1331
                    }
14✔
1332
                }
34✔
1333
            }
1334
            /* read remainder of this input line */
1335
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
336✔
1336
                if (skip) {
150✔
1337
                    continue; /* skip rest of data in this input line */
102✔
1338
                }
1339
                if (isspace(line[0])) {
48✔
1340
                    skip = 1; /* first word ended in previous part */
6✔
1341
                    continue;
6✔
1342
                }
1343
                if ((sscanf(line, "%s", scratch) != 1) || (!*scratch)) {
42✔
1344
                    skip = 1; /* empty or blank part of line, skip the rest */
×
1345
                    continue;
×
1346
                }
1347
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
42✔
1348
                    fprintf(stderr, "%s: target name too long\n", prog);
24✔
1349
                    exit(1);
24✔
1350
                }
1351
                /* append remainder of word started in previous line part */
1352
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
18✔
1353
                /*
1354
                 * Since the "host" buffer is the same size as the "line"
1355
                 * buffer, a target name that fits into the "host" buffer
1356
                 * cannot use more than two consecutive line parts.
1357
                 * A target name that uses two consecutive line parts
1358
                 * and fits into the "host" buffer must end before the
1359
                 * end of the second "line" buffer.  Thus the rest of
1360
                 * the line can be skipped.
1361
                 */
1362
                skip = 1;
18✔
1363
            }
1364

1365
            if (non_empty)
186✔
1366
                add_name(host);
144✔
1367
        }
1368

1369
        fclose(ping_file);
70✔
1370
    }
24✔
1371
    else if (*argv && generate_flag) {
114✔
1372
        if (argc == 1) {
114✔
1373
            /* one target: we expect a cidr range (n.n.n.n/m) */
1374
            add_cidr(argv[0]);
59✔
1375
        }
11✔
1376
        else if (argc == 2) {
55✔
1377
            add_range(argv[0], argv[1]);
52✔
1378
        }
6✔
1379
        else {
1380
            usage(1);
3✔
1381
        }
1382
    }
18✔
1383
    else {
1384
        usage(1);
×
1385
    }
1386

1387
    if (!num_hosts) {
425✔
1388
        exit(num_noaddress ? 2 : 1);
36✔
1389
    }
1390

1391
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
389✔
1392
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1393
    }
2✔
1394
#ifdef IPV6
1395
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
387✔
1396
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1397
    }
1398
#endif
1399

1400
    /* allocate and initialize array to map host nr to host_entry */
1401
    {
1402
        struct event *cursor = event_queue_ping.first;
385✔
1403
        int i = 0;
385✔
1404
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
385✔
1405
        if (!table)
385✔
1406
            crash_and_burn("Can't malloc array of hosts");
×
1407
        /* initialize table of hosts. we know that we have ping events scheduled
1408
         * for each of them */
1409
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
960✔
1410
            table[i] = cursor->host;
575✔
1411
            cursor->host->i = i;
575✔
1412
            i++;
575✔
1413
        }
157✔
1414
    }
1415

1416
    init_ping_buffer_ipv4(ping_data_size);
385✔
1417
#ifdef IPV6
1418
    init_ping_buffer_ipv6(ping_data_size);
385✔
1419
#endif
1420

1421
#ifdef USE_SIGACTION
1422
    memset(&act, 0, sizeof(act));
385✔
1423
    act.sa_handler = signal_handler;
385✔
1424
    sigemptyset(&act.sa_mask);
385✔
1425
    sigaddset(&act.sa_mask, SIGINT);
385✔
1426
    sigaddset(&act.sa_mask, SIGQUIT);
385✔
1427
    act.sa_flags = SA_RESTART;
385✔
1428
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
385✔
1429
        crash_and_burn("failure to set signal handler");
24✔
1430
    }
24✔
1431
#else
1432
    signal(SIGINT, signal_handler);
1433
    signal(SIGQUIT, signal_handler);
1434
#endif
1435
    setlinebuf(stdout);
361✔
1436

1437
    // Last time we updated current_time_ns was before adding the hosts and
1438
    // possibly doing DNS resolution, which means that it isn't accurate
1439
    // anymore.
1440
    update_current_time();
361✔
1441
    if (report_interval) {
361✔
1442
        next_report_time = current_time_ns + report_interval;
27✔
1443
    }
9✔
1444

1445
    last_send_time = 0;
361✔
1446

1447
    seqmap_init(seqmap_timeout);
361✔
1448

1449
    /* main loop */
1450
    main_loop();
361✔
1451

1452
/* Debug: CPU Performance */
1453
#if defined(DEBUG) || defined(_DEBUG)
1454
    perf_cpu_end = clock();
1455
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1456
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1457
#endif /* DEBUG || _DEBUG */
1458

1459
    finish();
361✔
1460

1461
    return 0;
85✔
1462
}
1463

1464
static inline int64_t timespec_ns(struct timespec *a)
6,032✔
1465
{
1466
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
6,032✔
1467
}
1468

1469
#if HAVE_SO_TIMESTAMPNS
1470
/* convert a struct timeval to nanoseconds */
1471
static inline int64_t timeval_ns(struct timeval *a)
1472
{
1473
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1474
}
1475
#endif /* HAVE_SO_TIMESTAMPNS */
1476

1477
void add_cidr(char *addr)
57✔
1478
{
1479
    char *addr_end;
1480
    char *mask_str;
1481
    unsigned long mask;
1482
    int ret;
1483
    struct addrinfo addr_hints;
1484
    struct addrinfo *addr_res;
1485
    unsigned long net_addr;
1486
#ifdef IPV6
1487
    uint64_t net_upper, net_lower;
1488
    char *scope_str;
1489
#endif /* IPV6 */
1490

1491
    /* Split address from mask */
1492
    addr_end = strrchr(addr, '/');
57✔
1493
    if (addr_end == NULL) {
57✔
1494
        usage(1);
3✔
1495
    }
1✔
1496
    mask_str = addr_end + 1;
55✔
1497

1498
#ifdef IPV6
1499
    /* IPv6 addresses can have a scope */
1500
    scope_str = strchr(addr, '%');
55✔
1501
    if (scope_str && mask_str < scope_str) {
55✔
1502
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1503
        exit(1);
2✔
1504
    }
1505
#endif /*IPV6 */
1506

1507
    *addr_end = '\0';
53✔
1508
    mask = atoi(mask_str);
53✔
1509

1510
    /* parse address */
1511
    memset(&addr_hints, 0, sizeof(struct addrinfo));
53✔
1512
    addr_hints.ai_family = hints_ai_family;
53✔
1513
    addr_hints.ai_flags = AI_NUMERICHOST;
53✔
1514
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
53✔
1515
    if (ret) {
53✔
1516
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1517
        exit(1);
7✔
1518
    }
1519
    if (addr_res->ai_family == AF_INET) {
46✔
1520
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
24✔
1521
        freeaddrinfo(addr_res);
24✔
1522
        add_cidr_ipv4(net_addr, mask);
24✔
1523
#ifdef IPV6
1524
    } else if (addr_res->ai_family == AF_INET6) {
30✔
1525
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1526
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1527
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1528
        freeaddrinfo(addr_res);
22✔
1529
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1530
#endif /* IPV6 */
1531
    } else {
1532
        freeaddrinfo(addr_res);
×
1533
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1534
        exit(1);
×
1535
    }
1536
}
30✔
1537

1538
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1539
{
1540
    unsigned long bitmask;
1541
    unsigned long net_last;
1542

1543
    /* check mask */
1544
    if (mask < 1 || mask > 32) {
24✔
1545
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1546
        exit(1);
6✔
1547
    }
1548

1549
    /* convert mask integer from 1 to 32 to a bitmask */
1550
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1551

1552
    /* calculate network range */
1553
    net_addr &= bitmask;
18✔
1554
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1555

1556
    /* exclude network and broadcast address for regular prefixes */
1557
    if (mask < 31) {
18✔
1558
        net_last--;
12✔
1559
        net_addr++;
12✔
1560
    }
4✔
1561

1562
    /* add all hosts in that network (net_addr and net_last inclusive) */
1563
    add_addr_range_ipv4(net_addr, net_last);
18✔
1564
}
16✔
1565

1566
#ifdef IPV6
1567
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1568
{
1569
    uint64_t bitmask_lower;
1570
    uint64_t last_lower;
1571

1572
    /* check mask -- 2^63 addresses should suffice for now */
1573
    if (mask < 65 || mask > 128) {
23✔
1574
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1575
        exit(1);
7✔
1576
    }
1577

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

1581
    /* calculate network range */
1582
    net_lower &= bitmask_lower;
16✔
1583
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1584

1585
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1586
}
12✔
1587
#endif /* IPV6 */
1588

1589
void add_range(char *start, char *end)
52✔
1590
{
1591
    struct addrinfo addr_hints;
1592
    struct addrinfo *addr_res;
1593
    unsigned long start_long;
1594
    unsigned long end_long;
1595
    int ret;
1596
#ifdef IPV6
1597
    uint64_t start_upper, start_lower;
1598
    uint64_t end_upper, end_lower;
1599
    char *start_scope_str, *end_scope_str;
1600

1601
    /*
1602
     * The compiler does not know that setting the address family hint to
1603
     * ensure that start and end are from the same address family also
1604
     * ensures that either start_long and end_long are initialized and used,
1605
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1606
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1607
     * supported to suppress compiler warnings.
1608
     */
1609
    start_long = -1;
52✔
1610
    end_long = 0;
52✔
1611
    start_upper = start_lower = -1;
52✔
1612
    end_upper = end_lower = 0;
52✔
1613
    start_scope_str = end_scope_str = NULL;
52✔
1614
#endif /* IPV6 */
1615

1616
    /* parse start address */
1617
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1618
    addr_hints.ai_family = hints_ai_family;
52✔
1619
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1620
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1621
    if (ret) {
52✔
1622
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1623
        exit(1);
9✔
1624
    }
1625
    /* start and end must be from the same address family */
1626
    hints_ai_family = addr_res->ai_family;
43✔
1627
    if (addr_res->ai_family == AF_INET) {
43✔
1628
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1629
        freeaddrinfo(addr_res);
17✔
1630
#ifdef IPV6
1631
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1632
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1633
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1634
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1635
        freeaddrinfo(addr_res);
26✔
1636
#endif /* IPV6 */
1637
    } else {
1638
        freeaddrinfo(addr_res);
×
1639
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1640
        exit(1);
×
1641
    }
1642

1643
#ifdef IPV6
1644
    /* IPv6 addresses can have a scope */
1645
    if (hints_ai_family == AF_INET6) {
43✔
1646
        start_scope_str = strchr(start, '%');
26✔
1647
        end_scope_str = strchr(end, '%');
26✔
1648
        if ((!start_scope_str && end_scope_str) ||
26✔
1649
            (start_scope_str && !end_scope_str) ||
24✔
1650
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1651
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1652
                exit(1);
6✔
1653
        }
1654
    }
1655
#endif
1656

1657
    /* parse end address */
1658
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1659
    addr_hints.ai_family = hints_ai_family;
37✔
1660
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1661
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1662
    if (ret) {
37✔
1663
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1664
        exit(1);
7✔
1665
    }
1666
    if (addr_res->ai_family == AF_INET) {
30✔
1667
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1668
        freeaddrinfo(addr_res);
12✔
1669
        add_addr_range_ipv4(start_long, end_long);
12✔
1670
#ifdef IPV6
1671
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1672
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1673
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1674
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1675
        freeaddrinfo(addr_res);
18✔
1676
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1677
#endif /* IPV6 */
1678
    } else {
1679
        freeaddrinfo(addr_res);
×
1680
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1681
        exit(1);
×
1682
    }
1683
}
24✔
1684

1685
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1686
{
1687
    /* check if generator limit is exceeded */
1688
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1689
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1690
        exit(1);
6✔
1691
    }
1692

1693
    /* generate */
1694
    for (; start_long <= end_long; start_long++) {
93✔
1695
        struct in_addr in_addr_tmp;
1696
        char buffer[20];
1697
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1698
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1699
        add_name(buffer);
69✔
1700
    }
23✔
1701
}
24✔
1702

1703
#ifdef IPV6
1704
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1705
{
1706
    int i;
1707
    uint64_t ret = 0;
134✔
1708
    for (i = 0; i < 8; i++) {
1,206✔
1709
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1710
    }
16✔
1711
    return ret;
134✔
1712
}
1713

1714
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1715
{
1716
    int i;
1717
    for (i = 0; i < 8; i++) {
720✔
1718
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1719
    }
1720
}
80✔
1721

1722
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1723
                         uint64_t end_upper, uint64_t end_lower,
1724
                         const char *scope_str)
1725
{
1726
    struct in6_addr in6_addr_tmp;
1727
    char buffer[100];
1728

1729
    /* prevent generating too many addresses */
1730
    if ((start_upper + 1 < end_upper) ||
34✔
1731
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1732
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1733
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1734
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1735
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1736
        exit(1);
8✔
1737
    }
1738

1739
    while ((start_upper < end_upper) ||
66✔
1740
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1741
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1742
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1743
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1744
        if (scope_str) {
40✔
1745
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1746
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1747
                exit(1);
×
1748
            }
1749
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1750
        }
1751
        add_name(buffer);
40✔
1752
        start_lower++;
40✔
1753
        if (start_lower == 0) {
40✔
1754
            start_upper++;
2✔
1755
        }
1756
    }
1757
}
26✔
1758
#endif /* IPv6 */
1759

1760
void main_loop()
373✔
1761
{
1762
    int64_t lt;
1763
    int64_t wait_time_ns;
1764
    struct event *event;
1765
    struct host_entry *h;
1766

1767
    while (event_queue_ping.first || event_queue_timeout.first) {
1,950✔
1768
        dbg_printf("%s", "# main_loop\n");
1769

1770
        /* timeout event ? */
1771
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,594✔
1772
            event = ev_dequeue(&event_queue_timeout);
113✔
1773
            h = event->host;
113✔
1774

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

1777
            stats_add(h, event->ping_index, 0, -1);
113✔
1778

1779
            if (per_recv_flag) {
113✔
1780
                print_timeout(h, event->ping_index);
25✔
1781
            }
7✔
1782

1783
            /* do we need to send a retry? */
1784
            if (!loop_flag && !count_flag) {
113✔
1785
                if (h->num_sent < retry + 1) {
64✔
1786
                    if (backoff_flag) {
39✔
1787
                        h->timeout *= backoff;
39✔
1788
                    }
9✔
1789
                    send_ping(h, event->ping_index);
39✔
1790
                }
9✔
1791
            }
14✔
1792

1793
            /* note: we process first timeout events, because we might need to
1794
             * wait to process ping events, while we for sure never need to
1795
             * wait for timeout events.
1796
             */
1797
            continue;
113✔
1798
        }
1799

1800
        /* ping event ? */
1801
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,740✔
1802
            /* Make sure that we don't ping more than once every "interval" */
1803
            lt = current_time_ns - last_send_time;
1,147✔
1804
            if (lt < interval)
1,147✔
1805
                goto wait_for_reply;
254✔
1806

1807
            /* Dequeue the event */
1808
            event = ev_dequeue(&event_queue_ping);
893✔
1809
            h = event->host;
893✔
1810

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

1813
            /* Send the ping */
1814
            send_ping(h, event->ping_index);
893✔
1815

1816
            /* Loop and count mode: schedule next ping */
1817
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
893✔
1818
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
333✔
1819
            }
107✔
1820
        }
259✔
1821

1822
    wait_for_reply:
646✔
1823

1824
        /* When is the next ping next event? */
1825
        wait_time_ns = -1;
1,481✔
1826
        if (event_queue_ping.first) {
1,481✔
1827
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,028✔
1828
            if (wait_time_ns < 0)
1,028✔
1829
                wait_time_ns = 0;
535✔
1830
            /* make sure that we wait enough, so that the inter-ping delay is
1831
             * bigger than 'interval' */
1832
            if (wait_time_ns < interval) {
1,028✔
1833
                lt = current_time_ns - last_send_time;
535✔
1834
                if (lt < interval) {
535✔
1835
                    wait_time_ns = interval - lt;
535✔
1836
                }
179✔
1837
            }
179✔
1838

1839
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1840
        }
326✔
1841

1842
        /* When is the next timeout event? */
1843
        if (event_queue_timeout.first) {
1,481✔
1844
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
958✔
1845
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
958✔
1846
                wait_time_ns = wait_time_timeout;
449✔
1847
                if (wait_time_ns < 0) {
449✔
1848
                    wait_time_ns = 0;
×
1849
                }
1850
            }
119✔
1851

1852
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1853
        }
284✔
1854

1855
        /* When is the next report due? */
1856
        if (report_interval && (loop_flag || count_flag)) {
1,481✔
1857
            int64_t wait_time_next_report = next_report_time - current_time_ns;
217✔
1858
            if (wait_time_next_report < wait_time_ns) {
217✔
1859
                wait_time_ns = wait_time_next_report;
72✔
1860
                if (wait_time_ns < 0) {
72✔
1861
                    wait_time_ns = 0;
×
1862
                }
1863
            }
24✔
1864

1865
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1866
        }
73✔
1867

1868
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1869
        if (wait_time_ns == -1) {
1,481✔
1870
            break;
14✔
1871
        }
1872

1873
        /* end of loop was requested by interrupt signal handler */
1874
        if (finish_requested) {
1,467✔
1875
            break;
3✔
1876
        }
1877

1878
        /* Receive replies */
1879
        /* (this is what sleeps during each loop iteration) */
1880
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1881
        if (wait_for_reply(wait_time_ns)) {
1,464✔
1882
            while (wait_for_reply(0))
1,292✔
1883
                ; /* process other replies in the queue */
1884
        }
244✔
1885

1886
        update_current_time();
1,464✔
1887

1888
        if (status_snapshot) {
1,464✔
1889
            status_snapshot = 0;
×
1890
            if (json_flag)
×
1891
                print_per_system_splits_json();
×
1892
            else
1893
                print_per_system_splits();
×
1894
        }
1895

1896
        /* Print report */
1897
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,464✔
1898
            if (netdata_flag) {
45✔
1899
                print_netdata();
3✔
1900
            }
1✔
1901
            else if (json_flag) {
42✔
1902
                print_per_system_splits_json();
3✔
1903
            }
1✔
1904
            else {
1905
                print_per_system_splits();
39✔
1906
            }
1907

1908
            while (current_time_ns >= next_report_time) {
90✔
1909
                next_report_time += report_interval;
45✔
1910
            }
1911
        }
15✔
1912
    }
1913
}
373✔
1914

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

1917
  Function: signal_handler
1918

1919
*************************************************************
1920

1921
  Inputs:  int signum
1922

1923
  Description:
1924

1925
  SIGQUIT signal handler - set flag and return
1926
  SIGINT signal handler - set flag and return
1927

1928
************************************************************/
1929

1930
void signal_handler(int signum)
×
1931
{
1932
    switch (signum) {
×
1933
    case SIGINT:
1934
        finish_requested = 1;
×
1935
        break;
×
1936

1937
    case SIGQUIT:
1938
        status_snapshot = 1;
×
1939
        break;
×
1940
    }
1941
}
×
1942

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

1945
  Function: update_current_time
1946

1947
*************************************************************/
1948

1949
void update_current_time()
4,984✔
1950
{
1951
    clock_gettime(CLOCKID, &current_time);
4,984✔
1952
    current_time_ns = timespec_ns(&current_time);
4,984✔
1953
}
4,984✔
1954

1955
/************************************************************
1956

1957
  Function: finish
1958

1959
*************************************************************
1960

1961
  Inputs:  void (none)
1962

1963
  Description:
1964

1965
  Main program clean up and exit point
1966

1967
************************************************************/
1968

1969
void finish()
373✔
1970
{
1971
    int i;
1972
    HOST_ENTRY *h;
1973

1974
    update_current_time();
373✔
1975
    end_time = current_time_ns;
373✔
1976

1977
    /* tot up unreachables */
1978
    for (i = 0; i < num_hosts; i++) {
948✔
1979
        h = table[i];
575✔
1980

1981
        if (!h->num_recv) {
575✔
1982
            num_unreachable++;
89✔
1983

1984
            if (verbose_flag || unreachable_flag) {
89✔
1985
                printf("%s", h->host);
47✔
1986

1987
                if (verbose_flag)
47✔
1988
                    printf(" is unreachable");
44✔
1989

1990
                printf("\n");
47✔
1991
            }
3✔
1992
        }
15✔
1993
    }
157✔
1994

1995
    if (count_flag || loop_flag) {
373✔
1996
        if (json_flag)
156✔
1997
            print_per_system_stats_json();
56✔
1998
        else
1999
            print_per_system_stats();
100✔
2000
    }
46✔
2001
#if defined(DEBUG) || defined(_DEBUG)
2002
    else if (print_per_system_flag) {
2003
        if (json_flag)
2004
            print_per_system_stats_json();
2005
        else
2006
            print_per_system_stats();
2007
    }
2008
#endif /* DEBUG || _DEBUG */
2009

2010
    if (stats_flag) {
373✔
2011
        if (json_flag)
12✔
2012
            print_global_stats_json();
3✔
2013
        else
2014
            print_global_stats();
9✔
2015
    }
4✔
2016

2017
    if (min_reachable) {
373✔
2018
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
2019
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
2020
            exit(0);
6✔
2021
        }
2022
        else {
2023
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
2024
            exit(1);
3✔
2025
        }
2026
    }
2027

2028
    if (num_noaddress)
364✔
2029
        exit(2);
3✔
2030
    else if (num_alive != num_hosts)
361✔
2031
        exit(1);
50✔
2032

2033
    exit(0);
311✔
2034
}
2035

2036
/************************************************************
2037

2038
  Function: print_recv
2039

2040
*************************************************************
2041

2042
  Inputs: HOST_ENTRY *h, int64_t recv_time, int result,
2043
          int this_count, int64_t this_reply, int avg
2044

2045
  Description:
2046

2047
************************************************************/
2048

2049
void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) {
186✔
2050
    if (json_flag) {
186✔
2051
        printf("{\"resp\": {");
35✔
2052

2053
        if (timestamp_flag)
35✔
2054
            print_timestamp_format(recv_time, timestamp_format_flag);
15✔
2055

2056
        printf("\"host\": \"%s\", ", h->host);
35✔
2057
        printf("\"seq\": %d, ", this_count);
35✔
2058
        printf("\"size\": %d, ", result);
35✔
2059
        printf("\"rtt\": %s", sprint_tm(this_reply));
35✔
2060
        return;
35✔
2061
    }
2062

2063
    /* Normal Output */
2064
    if (timestamp_flag)
151✔
2065
        print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2066

2067
    printf("%-*s : [%d], %d bytes, %s ms",
151✔
2068
        max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2069

2070
    printf(" (%s avg, ", sprint_tm(avg));
151✔
2071

2072
    if (h->num_recv <= h->num_sent) {
151✔
2073
        printf("%d%% loss)",
151✔
2074
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2075
    }
45✔
2076
    else {
2077
        printf("%d%% return)",
×
2078
            (h->num_recv_total * 100) / h->num_sent);
×
2079
    }
2080
}
56✔
2081

2082
/************************************************************
2083

2084
  Function: print_timeout
2085

2086
*************************************************************
2087

2088
  Inputs: HOST_ENTRY *h, int ping_index
2089

2090
  Description:
2091

2092
************************************************************/
2093

2094
void print_timeout(HOST_ENTRY *h, int ping_index) {
25✔
2095
    if (json_flag) {
25✔
2096
        printf("{\"timeout\": {");
6✔
2097
        if (timestamp_flag)
6✔
2098
            print_timestamp_format(current_time_ns, timestamp_format_flag);
×
2099

2100
        printf("\"host\": \"%s\", ", h->host);
6✔
2101
        printf("\"seq\": %d", ping_index);
6✔
2102
        printf("}}\n");
6✔
2103
        return;
6✔
2104
    }
2105

2106
    /* Normal Output */
2107
    if (timestamp_flag)
19✔
2108
        print_timestamp_format(current_time_ns, timestamp_format_flag);
×
2109

2110
    printf("%-*s : [%d], timed out",
19✔
2111
        max_hostname_len, h->host, ping_index);
5✔
2112

2113
    if (h->num_recv > 0) {
19✔
2114
        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
2115
    }
2116
    else {
2117
        printf(" (NaN avg, ");
19✔
2118
    }
2119

2120
    if (h->num_recv <= h->num_sent) {
19✔
2121
        printf("%d%% loss)",
19✔
2122
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
2123
    }
5✔
2124
    else {
2125
        printf("%d%% return)",
×
2126
            (h->num_recv_total * 100) / h->num_sent);
×
2127
    }
2128
    printf("\n");
19✔
2129
}
7✔
2130

2131
/************************************************************
2132

2133
  Function: print_recv_ext
2134

2135
*************************************************************
2136

2137
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2138
           int64_t recv_time, int64_t this_reply
2139

2140
  Description:
2141

2142
************************************************************/
2143

2144
void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { 
450✔
2145
    if (icmp_request_typ == 13) {
450✔
2146
        printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2147
            alive_flag ? "" : ",",
14✔
2148
            ip_header_res->otime_ms, ip_header_res->rtime_ms, ip_header_res->ttime_ms,
2149
            ms_since_midnight_utc(recv_time));
2150
    }
2151

2152
    if(print_tos_flag) {
450✔
2153
        if(ip_header_res->tos != -1) {
43✔
2154
            printf(" (TOS %d)", ip_header_res->tos);
43✔
2155
        }
11✔
2156
        else {
2157
            printf(" (TOS unknown)");
×
2158
        }
2159
    }
11✔
2160

2161
    if (print_ttl_flag) {
450✔
2162
        if(ip_header_res->ttl != -1) {
43✔
2163
            printf(" (TTL %d)", ip_header_res->ttl);
43✔
2164
        }
11✔
2165
        else {
2166
            printf(" (TTL unknown)");
×
2167
        }
2168
    }
11✔
2169

2170
    if (elapsed_flag && !per_recv_flag)
450✔
2171
        printf(" (%s ms)", sprint_tm(this_reply));
8✔
2172
    
2173
    printf("\n");
450✔
2174
}
450✔
2175

2176
/************************************************************
2177

2178
  Function: print_recv_ext_json
2179

2180
*************************************************************
2181

2182
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2183
           int64_t recv_time, int64_t this_reply
2184

2185
  Description:
2186

2187
************************************************************/
2188

2189
void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) {
35✔
2190
    if (icmp_request_typ == 13) {
35✔
2191
        printf(", \"timestamps\": {");
2✔
2192
        printf("\"originate\": %u, ", ip_header_res->otime_ms);
2✔
2193
        printf("\"receive\": %u, ", ip_header_res->rtime_ms);
2✔
2194
        printf("\"transmit\": %u, ", ip_header_res->ttime_ms);
2✔
2195
        printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
2196
    }
2197

2198
    if(print_tos_flag) {
35✔
2199
        if(ip_header_res->tos != -1) {
6✔
2200
            printf(", \"tos\": %d", ip_header_res->tos);
6✔
2201
        }
2✔
2202
        else {
2203
            printf(", \"tos\": -1");
×
2204
        }
2205
    }
2✔
2206

2207
    if (print_ttl_flag) {
35✔
2208
        if(ip_header_res->ttl != -1) {
6✔
2209
            printf(", \"ttl\": %d", ip_header_res->ttl);
6✔
2210
        }
2✔
2211
        else {
2212
            printf(", \"ttl\": -1");
×
2213
        }
2214
    }
2✔
2215

2216
    if (elapsed_flag && !per_recv_flag)
35✔
2217
        printf(" (%s ms)", sprint_tm(this_reply));
×
2218

2219
    printf("}}");
35✔
2220
    printf("\n");
35✔
2221
}
35✔
2222

2223
/************************************************************
2224

2225
  Function: print_per_system_stats
2226

2227
*************************************************************
2228

2229
  Inputs:  void (none)
2230

2231
  Description:
2232

2233

2234
************************************************************/
2235

2236
void print_per_system_stats(void)
100✔
2237
{
2238
    int i, j, avg, outage_ms;
2239
    HOST_ENTRY *h;
2240
    int64_t resp;
2241

2242
    if (verbose_flag || per_recv_flag)
100✔
2243
        fprintf(stderr, "\n");
59✔
2244

2245
    for (i = 0; i < num_hosts; i++) {
234✔
2246
        h = table[i];
134✔
2247
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
2248

2249
        if (report_all_rtts_flag) {
134✔
2250
            for (j = 0; j < h->num_sent; j++) {
248✔
2251
                if ((resp = h->resp_times[j]) >= 0)
204✔
2252
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2253
                else
2254
                    fprintf(stderr, " -");
8✔
2255
            }
64✔
2256

2257
            fprintf(stderr, "\n");
44✔
2258
        }
12✔
2259
        else {
2260
            if (h->num_recv <= h->num_sent) {
90✔
2261
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
2262
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
2263

2264
                if (outage_flag) {
90✔
2265
                    /* Time outage total */
2266
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
2267
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
2268
                }
3✔
2269
            }
26✔
2270
            else {
2271
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2272
                    h->num_sent, h->num_recv,
2273
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2274
            }
2275

2276
            if (h->num_recv) {
90✔
2277
                avg = h->total_time / h->num_recv;
79✔
2278
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
2279
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
2280
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
2281
            }
23✔
2282

2283
            fprintf(stderr, "\n");
90✔
2284
        }
2285
    }
38✔
2286
}
100✔
2287

2288
/************************************************************
2289

2290
  Function: print_per_system_stats_json
2291

2292
*************************************************************
2293

2294
  Inputs:  void (none)
2295

2296
  Description:
2297

2298

2299
************************************************************/
2300

2301
void print_per_system_stats_json(void)
56✔
2302
{
2303
    int i, j, avg, outage_ms;
2304
    HOST_ENTRY *h;
2305
    int64_t resp;
2306

2307
    for (i = 0; i < num_hosts; i++) {
118✔
2308
        h = table[i];
62✔
2309

2310
        if (report_all_rtts_flag)
62✔
2311
            fprintf(stdout, "{\"vSum\": {");
18✔
2312
        else
2313
            fprintf(stdout, "{\"summary\": {");
44✔
2314

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

2317
        if (report_all_rtts_flag) {
62✔
2318
            fprintf(stdout, "\"values\": [");
18✔
2319
            for (j = 0; j < h->num_sent; j++) {
42✔
2320
                if (j > 0)
24✔
2321
                  fprintf(stdout, ", ");
6✔
2322
                
2323
                if ((resp = h->resp_times[j]) >= 0)
24✔
2324
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2325
                else
2326
                    fprintf(stdout, "null");
3✔
2327
            }
8✔
2328

2329
            fprintf(stdout, "]}");
18✔
2330
        }
6✔
2331
        else {
2332
            if (h->num_recv <= h->num_sent) {
44✔
2333
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
44✔
2334
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
44✔
2335
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
44✔
2336

2337
                if (outage_flag) {
44✔
2338
                    /* Time outage total */
2339
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
2340
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2341
                }
2342
            }
14✔
2343
            else {
2344
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
2345
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
2346
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2347
            }
2348

2349
            if (h->num_recv) {
44✔
2350
                avg = h->total_time / h->num_recv;
41✔
2351
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
41✔
2352
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
41✔
2353
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
41✔
2354
            }
13✔
2355

2356
            fprintf(stdout, "}");
44✔
2357
        }
2358
        fprintf(stdout, "}\n");
62✔
2359
    }
20✔
2360
}
56✔
2361

2362
/************************************************************
2363

2364
  Function: print_netdata
2365

2366
*************************************************************
2367

2368
  Inputs:  void (none)
2369

2370
  Description:
2371

2372

2373
************************************************************/
2374

2375
void print_netdata(void)
3✔
2376
{
2377
    static int sent_charts = 0;
2378

2379
    int i;
2380
    int64_t avg;
2381
    HOST_ENTRY *h;
2382

2383
    for (i = 0; i < num_hosts; i++) {
6✔
2384
        h = table[i];
3✔
2385

2386
        if (!sent_charts) {
3✔
2387
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2388
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2389
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2390
        }
1✔
2391

2392
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2393
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2394
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2395
        printf("END\n");
3✔
2396

2397
        if (!sent_charts) {
3✔
2398
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2399
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2400
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2401
        }
1✔
2402

2403
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2404
        /*
2405
        if( h->num_recv_i <= h->num_sent_i )
2406
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2407
        else
2408
            printf("SET lost = 0\n");
2409
*/
2410

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

2414
        if (!sent_charts) {
3✔
2415
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2416
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2417
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2418
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2419
        }
1✔
2420

2421
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2422
        if (h->num_recv_i) {
3✔
2423
            avg = h->total_time_i / h->num_recv_i;
3✔
2424
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2425
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2426
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2427
        }
1✔
2428
        printf("END\n");
3✔
2429

2430
        stats_reset_interval(h);
3✔
2431
    }
1✔
2432

2433
    sent_charts = 1;
3✔
2434
}
3✔
2435

2436
/************************************************************
2437

2438
  Function: print_per_system_splits
2439

2440
*************************************************************
2441

2442
  Inputs:  void (none)
2443

2444
  Description:
2445

2446

2447
************************************************************/
2448

2449
void print_per_system_splits(void)
39✔
2450
{
2451
    int i, avg, outage_ms_i;
2452
    HOST_ENTRY *h;
2453
    struct tm *curr_tm;
2454

2455
    if (verbose_flag || per_recv_flag)
39✔
2456
        fprintf(stderr, "\n");
×
2457

2458
    update_current_time();
39✔
2459
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2460
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2461
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2462

2463
    for (i = 0; i < num_hosts; i++) {
78✔
2464
        h = table[i];
39✔
2465
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2466

2467
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2468
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2469
                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✔
2470

2471
            if (outage_flag) {
39✔
2472
                /* Time outage  */
2473
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2474
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2475
            }
4✔
2476
        }
13✔
2477
        else {
2478
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2479
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2480
        }
2481

2482
        if (h->num_recv_i) {
39✔
2483
            avg = h->total_time_i / h->num_recv_i;
27✔
2484
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2485
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2486
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2487
        }
9✔
2488

2489
        fprintf(stderr, "\n");
39✔
2490
        if (!cumulative_stats_flag) {
39✔
2491
            stats_reset_interval(h);
27✔
2492
        }
9✔
2493
    }
13✔
2494
}
39✔
2495

2496
/************************************************************
2497

2498
  Function: print_per_system_splits_json
2499

2500
*************************************************************
2501

2502
  Inputs:  void (none)
2503

2504
  Description:
2505

2506

2507
************************************************************/
2508

2509
void print_per_system_splits_json(void)
3✔
2510
{
2511
    int i, avg, outage_ms_i;
2512
    HOST_ENTRY *h;
2513

2514
    update_current_time();
3✔
2515

2516
    for (i = 0; i < num_hosts; i++) {
6✔
2517
        h = table[i];
3✔
2518
        fprintf(stdout, "{\"intSum\": {");
3✔
2519
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2520
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2521

2522
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2523
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2524
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2525
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2526

2527
            if (outage_flag) {
3✔
2528
                /* Time outage  */
2529
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
2530
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2531
            }
2532
        }
1✔
2533
        else {
2534
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
2535
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
2536
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2537
        }
2538

2539
        if (h->num_recv_i) {
3✔
2540
            avg = h->total_time_i / h->num_recv_i;
3✔
2541
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2542
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2543
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2544
        }
1✔
2545

2546
        fprintf(stdout, "}}\n");
3✔
2547
        if (!cumulative_stats_flag) {
3✔
2548
            stats_reset_interval(h);
3✔
2549
        }
1✔
2550
    }
1✔
2551
}
3✔
2552

2553
/************************************************************
2554

2555
  Function: print_global_stats
2556

2557
*************************************************************
2558

2559
  Inputs:  void (none)
2560

2561
  Description:
2562

2563

2564
************************************************************/
2565

2566
void print_global_stats(void)
9✔
2567
{
2568
    fprintf(stderr, "\n");
9✔
2569
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2570
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2571
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2572
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2573
    fprintf(stderr, "\n");
9✔
2574
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2575
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2576
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2577
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2578
    fprintf(stderr, "\n");
9✔
2579

2580
    if (total_replies == 0) {
9✔
2581
        min_reply = 0;
3✔
2582
        max_reply = 0;
3✔
2583
        total_replies = 1;
3✔
2584
        sum_replies = 0;
3✔
2585
    }
1✔
2586

2587
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2588
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2589
        sprint_tm(sum_replies / total_replies));
3✔
2590
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2591
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2592
        (end_time - start_time) / 1e9);
9✔
2593
    fprintf(stderr, "\n");
9✔
2594
}
9✔
2595

2596
/************************************************************
2597

2598
  Function: print_global_stats_json
2599

2600
*************************************************************
2601

2602
  Inputs:  void (none)
2603

2604
  Description:
2605

2606

2607
************************************************************/
2608

2609
void print_global_stats_json(void)
3✔
2610
{
2611
    fprintf(stdout, "{\"globalSum\": {");
3✔
2612
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2613
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2614
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2615
    fprintf(stdout, "\"unknown addresses\": %d, ", num_noaddress);
3✔
2616
    fprintf(stdout, "\"timeouts (waiting for response)\": %d, ", num_timeout);
3✔
2617
    fprintf(stdout, "\"ICMP Echos sent\": %d, ", num_pingsent);
3✔
2618
    fprintf(stdout, "\"ICMP Echo Replies received\": %d, ", num_pingreceived);
3✔
2619
    fprintf(stdout, "\"other ICMP received\": %d, ", num_othericmprcvd);
3✔
2620

2621
    if (total_replies == 0) {
3✔
2622
        min_reply = 0;
×
2623
        max_reply = 0;
×
2624
        total_replies = 1;
×
2625
        sum_replies = 0;
×
2626
    }
2627

2628
    fprintf(stdout, "\"ms (min round trip time)\": %s, ", sprint_tm(min_reply));
3✔
2629
    fprintf(stdout, "\"ms (avg round trip time)\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2630
    fprintf(stdout, "\"ms (max round trip time)\": %s, ", sprint_tm(max_reply));
3✔
2631
    fprintf(stdout, "\"sec (elapsed real time)\": %.3f", (end_time - start_time) / 1e9);
3✔
2632
    fprintf(stdout, "}}\n");
3✔
2633
}
3✔
2634

2635
/************************************************************
2636

2637
  Function: send_ping
2638

2639
*************************************************************
2640

2641
  Inputs:  int s, HOST_ENTRY *h
2642

2643
  Description:
2644

2645
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2646
  will be added on by the kernel.  The ID field is our UNIX process ID,
2647
  and the sequence number is an index into an array of outstanding
2648
  ping requests. The sequence number will later be used to quickly
2649
  figure out who the ping reply came from.
2650

2651
************************************************************/
2652

2653
int send_ping(HOST_ENTRY *h, int index)
932✔
2654
{
2655
    int n;
2656
    int myseq;
2657
    int ret = 1;
932✔
2658
    uint8_t proto = ICMP_ECHO;
932✔
2659

2660
    update_current_time();
932✔
2661
    h->last_send_time = current_time_ns;
932✔
2662
    myseq = seqmap_add(h->i, index, current_time_ns);
932✔
2663

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

2666
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
932✔
2667
        if(icmp_request_typ == 13)
809✔
2668
            proto = ICMP_TSTAMP;
18✔
2669
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
809✔
2670
    }
253✔
2671
#ifdef IPV6
2672
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2673
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2674
    }
15✔
2675
#endif
2676
    else {
2677
        return 0;
×
2678
    }
2679

2680
    /* error sending? */
2681
    if (
664✔
2682
        (n < 0)
268✔
2683
#if defined(EHOSTDOWN)
2684
        && errno != EHOSTDOWN
302✔
2685
#endif
2686
    ) {
2687
        if (verbose_flag) {
34✔
2688
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2689
        }
2690
        else {
2691
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2692
        }
2693

2694
        h->num_sent++;
34✔
2695
        h->num_sent_i++;
34✔
2696
        if (!loop_flag)
34✔
2697
            h->resp_times[index] = RESP_ERROR;
34✔
2698

2699
        ret = 0;
34✔
2700
    }
2701
    else {
2702
        /* schedule timeout */
2703
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
898✔
2704

2705
        /* mark this trial as outstanding */
2706
        if (!loop_flag) {
898✔
2707
            h->resp_times[index] = RESP_WAITING;
898✔
2708
        }
268✔
2709
    }
2710

2711
    num_pingsent++;
932✔
2712
    last_send_time = h->last_send_time;
932✔
2713

2714
    return (ret);
932✔
2715
}
268✔
2716

2717
int socket_can_read(struct timeval *timeout)
2,756✔
2718
{
2719
    int nfound;
2720
    fd_set readset;
2721
    int socketmax;
2722

2723
#ifndef IPV6
2724
    socketmax = socket4;
2725
#else
2726
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,756✔
2727
#endif
2728

2729
select_again:
2,070✔
2730
    FD_ZERO(&readset);
35,876✔
2731
    if (socket4 >= 0)
2,756✔
2732
        FD_SET(socket4, &readset);
2,756✔
2733
#ifdef IPV6
2734
    if (socket6 >= 0)
2,756✔
2735
        FD_SET(socket6, &readset);
2,756✔
2736
#endif
2737

2738
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,756✔
2739
    if (nfound < 0) {
2,756✔
2740
        if (errno == EINTR) {
×
2741
            /* interrupted system call: redo the select */
2742
            goto select_again;
×
2743
        }
2744
        else {
2745
            perror("select");
×
2746
        }
2747
    }
2748

2749
    if (nfound > 0) {
2,756✔
2750
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,292✔
2751
            return socket4;
1,201✔
2752
        }
2753
#ifdef IPV6
2754
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
91✔
2755
            return socket6;
91✔
2756
        }
2757
#endif
2758
    }
2759

2760
    return -1;
1,464✔
2761
}
686✔
2762

2763
int receive_packet(int64_t wait_time,
2,756✔
2764
#if HAVE_SO_TIMESTAMPNS
2765
    int64_t *reply_timestamp,
2766
#else
2767
    int64_t *reply_timestamp __attribute__((unused)),
2768
#endif
2769
    struct sockaddr *reply_src_addr,
2770
    size_t reply_src_addr_len,
2771
    char *reply_buf,
2772
    size_t reply_buf_len,
2773
    int *ip_header_tos,
2774
    int *ip_header_ttl)
2775
{
2776
    struct timeval to;
2777
    int s = 0;
2,756✔
2778
    int recv_len;
2779
    static unsigned char msg_control[128];
2780
    struct iovec msg_iov = {
4,128✔
2781
        reply_buf,
686✔
2782
        reply_buf_len
686✔
2783
    };
2784
    struct msghdr recv_msghdr = {0};
2,756✔
2785
    recv_msghdr.msg_name = reply_src_addr;
2,756✔
2786
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,756✔
2787
    recv_msghdr.msg_iov = &msg_iov;
2,756✔
2788
    recv_msghdr.msg_iovlen = 1;
2,756✔
2789
    recv_msghdr.msg_control = &msg_control;
2,756✔
2790
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,756✔
2791
    struct cmsghdr *cmsg;
2792

2793
    /* Wait for a socket to become ready */
2794
    if (wait_time) {
2,756✔
2795
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,464✔
2796
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,464✔
2797
    }
442✔
2798
    else {
2799
        to.tv_sec = 0;
1,292✔
2800
        to.tv_usec = 0;
1,292✔
2801
    }
2802
    s = socket_can_read(&to);
2,756✔
2803
    if (s == -1) {
2,756✔
2804
        return 0; /* timeout */
1,464✔
2805
    }
2806

2807
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,292✔
2808
    if (recv_len <= 0) {
1,292✔
2809
        return 0;
×
2810
    }
2811

2812
    /* ancilliary data */
2813
    {
2814
#if HAVE_SO_TIMESTAMPNS
2815
        struct timespec reply_timestamp_ts;
2816
        struct timeval reply_timestamp_tv;
2817
#endif
2818
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,292✔
2819
             cmsg != NULL;
2,348✔
2820
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,056✔
2821
#if HAVE_SO_TIMESTAMPNS
2822
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,056✔
2823
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,048✔
2824
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,048✔
2825
            }
2826
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,056✔
2827
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2828
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2829
            }
2830
#endif
2831
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,056✔
2832
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2833
            }
2834
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,056✔
2835
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2836
            }
2837
#ifdef IPV6
2838
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,056✔
2839
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2840
            }
2841
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,056✔
2842
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2843
            }
2844
#endif
2845
        }
2846
    }
2847

2848
#if defined(DEBUG) || defined(_DEBUG)
2849
    if (randomly_lose_flag) {
2850
        if ((random() & 0x07) <= lose_factor)
2851
            return 0;
2852
    }
2853
#endif
2854

2855
    return recv_len;
1,292✔
2856
}
686✔
2857

2858
/* stats_add: update host statistics for a single packet that was received (or timed out)
2859
 * h: host entry to update
2860
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2861
 * success: 1 if response received, 0 otherwise
2862
 * latency: response time, in ns
2863
 */
2864
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
898✔
2865
{
2866
    /* sent count - we update only on receive/timeout, so that we don't get
2867
     * weird loss percentage, just because a packet was note recived yet.
2868
     */
2869
    h->num_sent++;
898✔
2870
    h->num_sent_i++;
898✔
2871

2872
    if (!success) {
898✔
2873
        if (!loop_flag && index >= 0) {
113✔
2874
            h->resp_times[index] = RESP_TIMEOUT;
113✔
2875
        }
29✔
2876
        num_timeout++;
113✔
2877
        return;
113✔
2878
    }
2879

2880
    /* received count */
2881
    h->num_recv++;
785✔
2882
    h->num_recv_i++;
785✔
2883

2884
    /* maximum */
2885
    if (!h->max_reply || latency > h->max_reply) {
785✔
2886
        h->max_reply = latency;
614✔
2887
    }
178✔
2888
    if (!h->max_reply_i || latency > h->max_reply_i) {
785✔
2889
        h->max_reply_i = latency;
628✔
2890
    }
182✔
2891

2892
    /* minimum */
2893
    if (!h->min_reply || latency < h->min_reply) {
785✔
2894
        h->min_reply = latency;
543✔
2895
    }
166✔
2896
    if (!h->min_reply_i || latency < h->min_reply_i) {
785✔
2897
        h->min_reply_i = latency;
578✔
2898
    }
178✔
2899

2900
    /* total time (for average) */
2901
    h->total_time += latency;
785✔
2902
    h->total_time_i += latency;
785✔
2903

2904
    /* response time per-packet (count mode) */
2905
    if (!loop_flag && index >= 0) {
785✔
2906
        h->resp_times[index] = latency;
785✔
2907
    }
239✔
2908
}
268✔
2909

2910
/* stats_reset_interval: reset interval statistics
2911
 * h: host entry to update
2912
 */
2913
void stats_reset_interval(HOST_ENTRY *h)
33✔
2914
{
2915
    h->num_sent_i = 0;
33✔
2916
    h->num_recv_i = 0;
33✔
2917
    h->max_reply_i = 0;
33✔
2918
    h->min_reply_i = 0;
33✔
2919
    h->total_time_i = 0;
33✔
2920
}
33✔
2921

2922
int decode_icmp_ipv4(
1,201✔
2923
    struct sockaddr *response_addr,
2924
    size_t response_addr_len,
2925
    char *reply_buf,
2926
    size_t reply_buf_len,
2927
    unsigned short *id,
2928
    unsigned short *seq,
2929
    IP_HEADER_RESULT *ip_header_res)
2930
{
2931
    struct icmp *icp;
2932
    int hlen = 0;
1,201✔
2933

2934
    if (!using_sock_dgram4) {
1,201✔
2935
        struct ip *ip = (struct ip *)reply_buf;
1,193✔
2936
        ip_header_res->tos = ip->ip_tos;
1,193✔
2937
        ip_header_res->ttl = ip->ip_ttl;
1,193✔
2938

2939
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2940
        /* The alpha headers are decidedly broken.
2941
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2942
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2943
         */
2944
        hlen = (ip->ip_vhl & 0x0F) << 2;
2945
#else
2946
        hlen = ip->ip_hl << 2;
1,193✔
2947
#endif
2948
    }
229✔
2949

2950
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,201✔
2951
        /* too short */
2952
        if (verbose_flag) {
×
2953
            char buf[INET6_ADDRSTRLEN];
2954
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2955
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2956
        }
2957
        return -1;
×
2958
    }
2959

2960
    icp = (struct icmp *)(reply_buf + hlen);
1,201✔
2961

2962
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,201✔
2963
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
506✔
2964
        /* Handle other ICMP packets */
2965
        struct icmp *sent_icmp;
2966
        SEQMAP_VALUE *seqmap_value;
2967
        char addr_ascii[INET6_ADDRSTRLEN];
2968
        HOST_ENTRY *h;
2969

2970
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2971
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
939✔
2972
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2973
            return -1;
×
2974
        }
2975

2976
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
483✔
2977

2978
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
483✔
2979
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
2980
            sent_icmp->icmp_id != ident4) {
×
2981
            /* not caused by us */
2982
            return -1;
483✔
2983
        }
2984

2985
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2986
        if (seqmap_value == NULL) {
×
2987
            return -1;
×
2988
        }
2989

2990
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2991

2992
        switch (icp->icmp_type) {
×
2993
        case ICMP_UNREACH:
2994
            h = table[seqmap_value->host_nr];
×
2995
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2996
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2997
                    addr_ascii, h->host);
2998
            }
2999
            else {
3000
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3001
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
3002
            }
3003

3004
            print_warning("\n");
×
3005
            num_othericmprcvd++;
×
3006
            break;
×
3007

3008
        case ICMP_SOURCEQUENCH:
3009
        case ICMP_REDIRECT:
3010
        case ICMP_TIMXCEED:
3011
        case ICMP_PARAMPROB:
3012
            h = table[seqmap_value->host_nr];
×
3013
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
3014
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3015
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
3016
            }
3017
            else {
3018
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
3019
                    icp->icmp_type, addr_ascii, h->host);
×
3020
            }
3021
            print_warning("\n");
×
3022
            num_othericmprcvd++;
×
3023
            break;
×
3024
        }
3025

3026
        return -1;
×
3027
    }
3028

3029
    *id = icp->icmp_id;
718✔
3030
    *seq = ntohs(icp->icmp_seq);
718✔
3031
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
718✔
3032

3033
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
3034
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
3035
            if (verbose_flag) {
×
3036
                char buf[INET6_ADDRSTRLEN];
3037
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3038
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3039
            }
3040
            return -1;
×
3041
        }
3042

3043
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
3044
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
3045
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
3046
    }
3047

3048
    return hlen;
718✔
3049
}
229✔
3050

3051
#ifdef IPV6
3052
int decode_icmp_ipv6(
91✔
3053
    struct sockaddr *response_addr,
3054
    size_t response_addr_len,
3055
    char *reply_buf,
3056
    size_t reply_buf_len,
3057
    unsigned short *id,
3058
    unsigned short *seq)
3059
{
3060
    struct icmp6_hdr *icp;
3061

3062
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
91✔
3063
        if (verbose_flag) {
×
3064
            char buf[INET6_ADDRSTRLEN];
3065
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3066
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3067
        }
3068
        return 0; /* too short */
×
3069
    }
3070

3071
    icp = (struct icmp6_hdr *)reply_buf;
91✔
3072

3073
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
91✔
3074
        /* Handle other ICMPv6 packets */
3075
        struct ip6_hdr *sent_ipv6;
3076
        struct icmp6_hdr *sent_icmp;
3077
        SEQMAP_VALUE *seqmap_value;
3078
        char addr_ascii[INET6_ADDRSTRLEN];
3079
        HOST_ENTRY *h;
3080

3081
        /* reply icmp packet (ICMPv6 header) followed by "sent packet" (IPv6 + ICMPv6 header) */
3082
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
×
3083
            /* discard ICMPv6 message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
3084
            return 0;
×
3085
        }
3086

3087
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
×
3088
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
×
3089
            /* discard ICMPv6 message if we can't tell that it was caused by
3090
             * us, because the IPv6 header is not directly followed by an
3091
             * ICMPv6 header
3092
             */
3093
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
3094
            return 0;
×
3095
        }
3096
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
×
3097

3098
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
×
3099
            /* not caused by us */
3100
            return 0;
×
3101
        }
3102

3103
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
3104
        if (seqmap_value == NULL) {
×
3105
            return 0;
×
3106
        }
3107

3108
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3109
        h = table[seqmap_value->host_nr];
×
3110

3111
        switch (icp->icmp6_type) {
×
3112
        case ICMP6_DST_UNREACH:
3113
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
3114
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3115
                    icp->icmp6_code, addr_ascii, h->host);
×
3116
            } else {
3117
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3118
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
3119
            }
3120
            print_warning("\n");
×
3121
            num_othericmprcvd++;
×
3122
            break;
×
3123

3124
        case ICMP6_PACKET_TOO_BIG:
3125
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
3126
                addr_ascii, h->host);
3127
            num_othericmprcvd++;
×
3128
            break;
×
3129

3130
        case ICMP6_TIME_EXCEEDED:
3131
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
3132
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3133
                    icp->icmp6_code, addr_ascii, h->host);
×
3134
            } else {
3135
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3136
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
3137
            }
3138
            print_warning("\n");
×
3139
            num_othericmprcvd++;
×
3140
            break;
×
3141

3142
        case ICMP6_PARAM_PROB:
3143
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
3144
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3145
                    icp->icmp6_code, addr_ascii, h->host);
×
3146
            } else {
3147
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3148
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
3149
            }
3150
            print_warning("\n");
×
3151
            num_othericmprcvd++;
×
3152
            break;
×
3153

3154
        default:
3155
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
3156
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
3157
            num_othericmprcvd++;
×
3158
            break;
×
3159
        }
3160

3161
        return 0;
×
3162
    }
3163

3164
    *id = icp->icmp6_id;
91✔
3165
    *seq = ntohs(icp->icmp6_seq);
91✔
3166

3167
    return 1;
91✔
3168
}
15✔
3169
#endif
3170

3171
int wait_for_reply(int64_t wait_time)
2,756✔
3172
{
3173
    int result;
3174
    static char buffer[RECV_BUFSIZE];
3175
    struct sockaddr_storage response_addr;
3176
    int n, avg;
3177
    HOST_ENTRY *h;
3178
    int64_t this_reply;
3179
    int this_count;
3180
    int64_t recv_time = 0;
2,756✔
3181
    SEQMAP_VALUE *seqmap_value;
3182
    unsigned short id;
3183
    unsigned short seq;
3184
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
2,756✔
3185

3186
    /* Receive packet */
3187
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,442✔
3188
        &recv_time, /* reply_timestamp */
3189
        (struct sockaddr *)&response_addr, /* reply_src_addr */
3190
        sizeof(response_addr), /* reply_src_addr_len */
3191
        buffer, /* reply_buf */
3192
        sizeof(buffer), /* reply_buf_len */
3193
        &ip_header_res.tos, /* TOS resp. TC byte */
686✔
3194
        &ip_header_res.ttl /* TTL resp. hop limit */
686✔
3195
    );
3196

3197
    if (result <= 0) {
2,756✔
3198
        return 0;
1,464✔
3199
    }
3200

3201
    update_current_time();
1,292✔
3202
    if (recv_time == 0)
1,292✔
3203
        recv_time = current_time_ns;
244✔
3204

3205
    /* Process ICMP packet and retrieve id/seq */
3206
    if (response_addr.ss_family == AF_INET) {
1,292✔
3207
        int ip_hlen = decode_icmp_ipv4(
1,201✔
3208
            (struct sockaddr *)&response_addr,
3209
            sizeof(response_addr),
3210
            buffer,
3211
            sizeof(buffer),
3212
            &id,
3213
            &seq,
3214
            &ip_header_res);
3215
        if (ip_hlen < 0) {
1,201✔
3216
            return 1;
483✔
3217
        }
3218
        if (id != ident4) {
718✔
3219
            return 1; /* packet received, but not the one we are looking for! */
×
3220
        }
3221
        if (!using_sock_dgram4) {
718✔
3222
            /* do not include IP header in returned size, to be consistent with ping(8) and also
3223
             * with fping with IPv6 hosts */
3224
            result -= ip_hlen;
710✔
3225
        }
228✔
3226
    }
228✔
3227
#ifdef IPV6
3228
    else if (response_addr.ss_family == AF_INET6) {
91✔
3229
        if (!decode_icmp_ipv6(
91✔
3230
                (struct sockaddr *)&response_addr,
3231
                sizeof(response_addr),
3232
                buffer,
3233
                sizeof(buffer),
3234
                &id,
3235
                &seq)) {
3236
            return 1;
×
3237
        }
3238
        if (id != ident6) {
91✔
3239
            return 1; /* packet received, but not the one we are looking for! */
×
3240
        }
3241
    }
15✔
3242
#endif
3243
    else {
3244
        return 1;
×
3245
    }
3246

3247
    seqmap_value = seqmap_fetch(seq, current_time_ns);
809✔
3248
    if (seqmap_value == NULL) {
809✔
3249
        return 1;
12✔
3250
    }
3251

3252
    /* find corresponding host_entry */
3253
    n = seqmap_value->host_nr;
797✔
3254
    h = table[n];
797✔
3255
    this_count = seqmap_value->ping_count;
797✔
3256
    this_reply = recv_time - seqmap_value->ping_ts;
797✔
3257

3258
    /* update stats that include invalid replies */
3259
    h->num_recv_total++;
797✔
3260
    num_pingreceived++;
797✔
3261

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

3264
    /* optionally require reply source equal to target address */
3265
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
797✔
3266
        dbg_printf("%s\n", "discarding reply from wrong source address");
3267
        return 1;
12✔
3268
    }
3269

3270
    /* discard duplicates */
3271
    if (!loop_flag && !(count_flag && quiet_flag) && h->resp_times[this_count] >= 0) {
785✔
3272
        if (!per_recv_flag) {
×
3273
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3274
                h->host, this_count, result, sprint_tm(this_reply));
3275

3276
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3277
                char buf[INET6_ADDRSTRLEN];
3278
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3279
                fprintf(stderr, " [<- %s]", buf);
×
3280
            }
3281
            fprintf(stderr, "\n");
×
3282
        }
3283
        return 1;
×
3284
    }
3285

3286
    /* discard reply if delay is larger than timeout
3287
     * (see also: github #32) */
3288
    if (this_reply > h->timeout) {
785✔
3289
        return 1;
×
3290
    }
3291

3292
    /* update stats */
3293
    stats_add(h, this_count, 1, this_reply);
785✔
3294
    // TODO: move to stats_add?
3295
    if (!max_reply || this_reply > max_reply)
785✔
3296
        max_reply = this_reply;
482✔
3297
    if (!min_reply || this_reply < min_reply)
785✔
3298
        min_reply = this_reply;
450✔
3299
    sum_replies += this_reply;
785✔
3300
    total_replies++;
785✔
3301

3302
    /* initialize timeout to initial timeout (without backoff) */
3303
    h->timeout = timeout;
785✔
3304

3305
    /* remove timeout event */
3306
    struct event *timeout_event = host_get_timeout_event(h, this_count);
785✔
3307
    if (timeout_event) {
785✔
3308
        ev_remove(&event_queue_timeout, timeout_event);
785✔
3309
    }
239✔
3310

3311
    /* print "is alive" */
3312
    if (h->num_recv == 1) {
785✔
3313
        num_alive++;
486✔
3314
        if (fast_reachable && num_alive >= min_reachable)
486✔
3315
            finish_requested = 1;
3✔
3316

3317
        if (verbose_flag || alive_flag) {
486✔
3318
            printf("%s", h->host);
299✔
3319

3320
            if (verbose_flag)
299✔
3321
                printf(" is alive");
279✔
3322
        }
85✔
3323
    }
142✔
3324

3325
    /* print received ping (unless --quiet) */
3326
    if (per_recv_flag) {
785✔
3327
        avg = h->total_time / h->num_recv;
186✔
3328
        print_recv(h,
242✔
3329
            recv_time,
56✔
3330
            result,
56✔
3331
            this_count,
56✔
3332
            this_reply,
56✔
3333
            avg);
56✔
3334
    }
56✔
3335

3336
    if (verbose_flag || alive_flag || per_recv_flag) {
785✔
3337
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
485✔
3338
            char buf[INET6_ADDRSTRLEN];
3339
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3340
            fprintf(stderr, " [<- %s]", buf);
6✔
3341
        }
3342
        if (json_flag) {
485✔
3343
            print_recv_ext_json(&ip_header_res,
35✔
3344
                recv_time,
11✔
3345
                this_reply);
11✔
3346
        }
11✔
3347
        else {
3348
            print_recv_ext(&ip_header_res,
450✔
3349
                recv_time,
130✔
3350
                this_reply);
130✔
3351
        }
3352
    }
141✔
3353
    return 1;
785✔
3354
}
686✔
3355

3356
/************************************************************
3357

3358
  Function: add_name
3359

3360
*************************************************************
3361

3362
  Inputs:  char* name
3363

3364
  Description:
3365

3366
  process input name for addition to target list
3367
  name can turn into multiple targets via multiple interfaces (-m)
3368
  or via NIS groups
3369

3370
************************************************************/
3371

3372
void add_name(char *name)
609✔
3373
{
3374
    struct addrinfo *res0, *res, hints;
3375
    int ret_ga;
3376
    char *printname;
3377
    char namebuf[256];
3378
    char addrbuf[256];
3379

3380
    /* getaddrinfo */
3381
    memset(&hints, 0, sizeof(struct addrinfo));
609✔
3382
    hints.ai_flags = AI_UNUSABLE;
609✔
3383
    hints.ai_socktype = SOCK_RAW;
609✔
3384
    hints.ai_family = hints_ai_family;
609✔
3385
    if (hints_ai_family == AF_INET) {
609✔
3386
        hints.ai_protocol = IPPROTO_ICMP;
81✔
3387
    }
21✔
3388
#ifdef IPV6
3389
    else if (hints_ai_family == AF_INET6) {
528✔
3390
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
3391
    }
1✔
3392
#endif
3393
    else {
3394
        hints.ai_socktype = SOCK_STREAM;
497✔
3395
        hints.ai_protocol = 0;
497✔
3396
    }
3397
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
609✔
3398
    if (ret_ga) {
609✔
3399
        if (!quiet_flag)
29✔
3400
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3401
        num_noaddress++;
29✔
3402

3403
        // Handle JSON output for invalid hosts
3404
        if (json_flag) {
29✔
3405
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
3406
            return;
21✔
3407
        }
3408

3409
        return;
26✔
3410
    }
3411

3412
    /* NOTE: we could/should loop with res on all addresses like this:
3413
     * for (res = res0; res; res = res->ai_next) {
3414
     * We don't do it yet, however, because is is an incompatible change
3415
     * (need to implement a separate option for this)
3416
     */
3417
    for (res = res0; res; res = res->ai_next) {
580✔
3418
        /* name_flag: addr -> name lookup requested) */
3419
        if (name_flag || rdns_flag) {
582✔
3420
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3421
            if (name_flag) {
3✔
3422
                /* Was it a numerical address? Only then do a rdns-query */
3423
                struct addrinfo *nres;
3424
                hints.ai_flags = AI_NUMERICHOST;
×
3425
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3426
                    do_rdns = 1;
×
3427
                    freeaddrinfo(nres);
×
3428
                }
3429
            }
3430

3431
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3432
                printname = namebuf;
3✔
3433
            }
1✔
3434
            else {
3435
                printname = name;
×
3436
            }
3437
        }
1✔
3438
        else {
3439
            printname = name;
577✔
3440
        }
3441

3442
        /* addr_flag: name -> addr lookup requested */
3443
        if (addr_flag) {
580✔
3444
            int ret;
3445
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3446
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3447
            if (ret) {
3✔
3448
                if (!quiet_flag) {
×
3449
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3450
                }
3451
                continue;
×
3452
            }
3453

3454
            if (name_flag || rdns_flag) {
3✔
3455
                char nameaddrbuf[512 + 3];
3456
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3457
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3458
            }
3459
            else {
3460
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3461
            }
3462
        }
1✔
3463
        else {
3464
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
577✔
3465
        }
3466

3467
        if (!multif_flag) {
580✔
3468
            break;
580✔
3469
        }
3470
    }
3471

3472
    freeaddrinfo(res0);
580✔
3473
}
167✔
3474

3475
/************************************************************
3476

3477
  Function: add_addr
3478

3479
*************************************************************
3480

3481
  Description:
3482

3483
  add single address to list of hosts to be pinged
3484

3485
************************************************************/
3486

3487
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
580✔
3488
{
3489
    HOST_ENTRY *p;
3490
    int n;
3491
    int64_t *i;
3492

3493
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
580✔
3494
    if (!p)
580✔
3495
        crash_and_burn("can't allocate HOST_ENTRY");
×
3496

3497
    p->name = strdup(name);
580✔
3498
    p->host = strdup(host);
580✔
3499
    memcpy(&p->saddr, ipaddr, ipaddr_len);
580✔
3500
    p->saddr_len = ipaddr_len;
580✔
3501
    p->timeout = timeout;
580✔
3502
    p->min_reply = 0;
580✔
3503

3504
    if (netdata_flag) {
580✔
3505
        char *s = p->name;
3✔
3506
        while (*s) {
30✔
3507
            if (!isalnum(*s))
27✔
3508
                *s = '_';
9✔
3509
            s++;
27✔
3510
        }
3511
    }
1✔
3512

3513
    if (strlen(p->host) > max_hostname_len)
580✔
3514
        max_hostname_len = strlen(p->host);
387✔
3515

3516
    /* array for response time results */
3517
    if (!loop_flag) {
580✔
3518
#if SIZE_MAX <= UINT_MAX
3519
        if (trials > (SIZE_MAX / sizeof(int64_t)))
3520
            crash_and_burn("resp_times array too large for memory");
3521
#endif
3522
        i = (int64_t *)malloc(trials * sizeof(int64_t));
580✔
3523
        if (!i)
580✔
3524
            crash_and_burn("can't allocate resp_times array");
×
3525

3526
        for (n = 0; n < trials; n++)
2,891✔
3527
            i[n] = RESP_UNUSED;
2,311✔
3528

3529
        p->resp_times = i;
580✔
3530
    }
158✔
3531

3532
    /* allocate event storage */
3533
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
580✔
3534
    if (!p->event_storage_ping) {
580✔
3535
        errno_crash_and_burn("can't allocate event_storage_ping");
×
3536
    }
3537
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
580✔
3538
    if (!p->event_storage_timeout) {
580✔
3539
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
3540
    }
3541

3542
    /* schedule first ping */
3543
    host_add_ping_event(p, 0, current_time_ns);
580✔
3544

3545
    num_hosts++;
580✔
3546
}
580✔
3547

3548
/************************************************************
3549

3550
  Function: crash_and_burn
3551

3552
*************************************************************
3553

3554
  Inputs:  char* message
3555

3556
  Description:
3557

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

3560
void crash_and_burn(char *message)
×
3561
{
3562
    fprintf(stderr, "%s: %s\n", prog, message);
×
3563
    exit(4);
×
3564
}
3565

3566
/************************************************************
3567

3568
  Function: errno_crash_and_burn
3569

3570
*************************************************************
3571

3572
  Inputs:  char* message
3573

3574
  Description:
3575

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

3578
void errno_crash_and_burn(char *message)
8✔
3579
{
3580
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3581
    exit(4);
8✔
3582
}
3583

3584
/************************************************************
3585

3586
  Function: print_warning
3587

3588
  Description: fprintf(stderr, ...), unless running with -q
3589

3590
*************************************************************/
3591

3592
void print_warning(char *format, ...)
54✔
3593
{
3594
    va_list args;
3595
    if (!quiet_flag) {
54✔
3596
        va_start(args, format);
54✔
3597
        vfprintf(stderr, format, args);
54✔
3598
        va_end(args);
54✔
3599
    }
8✔
3600
}
54✔
3601

3602
/************************************************************
3603

3604
  Function: sprint_tm
3605

3606
*************************************************************
3607

3608
  render nanosecond int64_t value into milliseconds string with three digits of
3609
  precision.
3610

3611
************************************************************/
3612

3613
const char *sprint_tm(int64_t ns)
1,048✔
3614
{
3615
    static char buf[10];
3616
    double t = (double)ns / 1e6;
1,048✔
3617

3618
    if (t < 0.0) {
1,048✔
3619
        /* negative (unexpected) */
3620
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3621
    }
3622
    else if (t < 1.0) {
1,048✔
3623
        /* <= 0.99 ms */
3624
        snprintf(buf, sizeof(buf), "%.3f", t);
1,048✔
3625
    }
324✔
3626
    else if (t < 10.0) {
×
3627
        /* 1.00 - 9.99 ms */
3628
        snprintf(buf, sizeof(buf), "%.2f", t);
×
3629
    }
3630
    else if (t < 100.0) {
×
3631
        /* 10.0 - 99.9 ms */
3632
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3633
    }
3634
    else if (t < 1000000.0) {
×
3635
        /* 100 - 1'000'000 ms */
3636
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3637
    }
3638
    else {
3639
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3640
    }
3641

3642
    return (buf);
1,048✔
3643
}
3644

3645
/************************************************************
3646

3647
  Function: addr_cmp
3648

3649
*************************************************************/
3650
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
507✔
3651
{
3652
    if (a->sa_family != b->sa_family) {
507✔
3653
        return a->sa_family - b->sa_family;
×
3654
    }
3655
    else {
3656
        if (a->sa_family == AF_INET) {
507✔
3657
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
443✔
3658
        }
3659
        else if (a->sa_family == AF_INET6) {
64✔
3660
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3661
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3662
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3663
        }
3664
    }
3665

3666
    return 0;
×
3667
}
143✔
3668

3669
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
913✔
3670
{
3671
    struct event *event = &h->event_storage_ping[index % event_storage_count];
913✔
3672
    event->host = h;
913✔
3673
    event->ping_index = index;
913✔
3674
    event->ev_time = ev_time;
913✔
3675
    ev_enqueue(&event_queue_ping, event);
913✔
3676

3677
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3678
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3679
}
913✔
3680

3681
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
898✔
3682
{
3683
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
898✔
3684
    event->host = h;
898✔
3685
    event->ping_index = index;
898✔
3686
    event->ev_time = ev_time;
898✔
3687
    ev_enqueue(&event_queue_timeout, event);
898✔
3688

3689
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3690
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3691
}
898✔
3692

3693
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
785✔
3694
{
3695
    return &h->event_storage_timeout[index % event_storage_count];
785✔
3696
}
3697

3698
/************************************************************
3699

3700
  Function: ev_enqueue
3701

3702
  Enqueue an event
3703

3704
  The queue is sorted by event->ev_time, so that queue->first always points to
3705
  the earliest event.
3706

3707
  We start scanning the queue from the tail, because we assume
3708
  that new events mostly get inserted with a event time higher
3709
  than the others.
3710

3711
*************************************************************/
3712
void ev_enqueue(struct event_queue *queue, struct event *event)
1,811✔
3713
{
3714
    struct event *i;
3715
    struct event *i_prev;
3716

3717
    /* Empty list */
3718
    if (queue->last == NULL) {
1,811✔
3719
        event->ev_next = NULL;
1,417✔
3720
        event->ev_prev = NULL;
1,417✔
3721
        queue->first = event;
1,417✔
3722
        queue->last = event;
1,417✔
3723
        return;
1,417✔
3724
    }
3725

3726
    /* Insert on tail? */
3727
    if (event->ev_time - queue->last->ev_time >= 0) {
394✔
3728
        event->ev_next = NULL;
394✔
3729
        event->ev_prev = queue->last;
394✔
3730
        queue->last->ev_next = event;
394✔
3731
        queue->last = event;
394✔
3732
        return;
394✔
3733
    }
3734

3735
    /* Find insertion point */
3736
    i = queue->last;
×
3737
    while (1) {
3738
        i_prev = i->ev_prev;
×
3739
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3740
            event->ev_prev = i_prev;
×
3741
            event->ev_next = i;
×
3742
            i->ev_prev = event;
×
3743
            if (i_prev != NULL) {
×
3744
                i_prev->ev_next = event;
×
3745
            }
3746
            else {
3747
                queue->first = event;
×
3748
            }
3749
            return;
×
3750
        }
3751
        i = i_prev;
×
3752
    }
3753
}
533✔
3754

3755
/************************************************************
3756

3757
  Function: ev_dequeue
3758

3759
*************************************************************/
3760
struct event *ev_dequeue(struct event_queue *queue)
1,006✔
3761
{
3762
    struct event *dequeued;
3763

3764
    if (queue->first == NULL) {
1,006✔
3765
        return NULL;
×
3766
    }
3767
    dequeued = queue->first;
1,006✔
3768
    ev_remove(queue, dequeued);
1,006✔
3769

3770
    return dequeued;
1,006✔
3771
}
288✔
3772

3773
/************************************************************
3774

3775
  Function: ev_remove
3776

3777
*************************************************************/
3778
void ev_remove(struct event_queue *queue, struct event *event)
1,791✔
3779
{
3780
    if (queue->first == event) {
1,791✔
3781
        queue->first = event->ev_next;
1,782✔
3782
    }
524✔
3783
    if (queue->last == event) {
1,791✔
3784
        queue->last = event->ev_prev;
1,418✔
3785
    }
410✔
3786
    if (event->ev_prev) {
1,791✔
3787
        event->ev_prev->ev_next = event->ev_next;
9✔
3788
    }
3✔
3789
    if (event->ev_next) {
1,791✔
3790
        event->ev_next->ev_prev = event->ev_prev;
373✔
3791
    }
117✔
3792
    event->ev_prev = NULL;
1,791✔
3793
    event->ev_next = NULL;
1,791✔
3794
}
1,791✔
3795

3796
/************************************************************
3797

3798
  Function: print_human_readable_time from current_time_ns
3799

3800
*************************************************************/
3801
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
45✔
3802
{
3803
    char time_buffer[100];
3804
    time_t current_time_s;
3805
    struct tm *local_time;
3806

3807
    current_time_s = current_time_ns / 1000000000;
45✔
3808
    local_time = localtime(&current_time_s);
45✔
3809
    switch(timestamp_format) {
45✔
3810
        case 1:
6✔
3811
            // timestamp-format ctime
3812
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
9✔
3813
            if (json_flag)
9✔
3814
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3815
            else
3816
                printf("[%s] ", time_buffer);
6✔
3817
            break;
9✔
3818
        case 2:
6✔
3819
            // timestamp-format iso
3820
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
9✔
3821
            if (json_flag)
9✔
3822
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3823
            else
3824
                printf("[%s] ", time_buffer);
6✔
3825
            break;
9✔
3826
        case 3:
6✔
3827
            // timestamp-format rfc3339
3828
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
9✔
3829
            if (json_flag)
9✔
3830
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3831
            else
3832
                printf("[%s] ", time_buffer);
6✔
3833
            break;
9✔
3834
        default:
12✔
3835
            if (json_flag)
18✔
3836
                printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9);
6✔
3837
            else
3838
                printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3839
    }
6✔
3840
}
45✔
3841

3842
/************************************************************
3843

3844
  Function: ms_since_midnight_utc
3845

3846
*************************************************************
3847

3848
  Input: int64_t: current UTC time in ns
3849

3850
  Output: uint32_t: current time in ms since midnight UTC
3851

3852
  Description:
3853

3854
  Return ICMP Timestamp value corresponding to the given time value.
3855
  The given time value must be in UTC.
3856

3857
*************************************************************/
3858
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
3859
{
3860
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
3861
}
3862

3863
/************************************************************
3864

3865
  Function: usage
3866

3867
*************************************************************
3868

3869
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3870

3871
  Description:
3872

3873
************************************************************/
3874

3875
void usage(int is_error)
124✔
3876
{
3877
    FILE *out = is_error ? stderr : stdout;
124✔
3878
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
124✔
3879
    fprintf(out, "\n");
124✔
3880
    fprintf(out, "Probing options:\n");
124✔
3881
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
124✔
3882
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
124✔
3883
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
124✔
3884
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
124✔
3885
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
124✔
3886
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
124✔
3887
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
124✔
3888
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
124✔
3889
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
124✔
3890
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
124✔
3891
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
124✔
3892
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
124✔
3893
#ifdef SO_BINDTODEVICE
3894
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
124✔
3895
#endif
3896
#ifdef SO_MARK
3897
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
84✔
3898
#endif
3899
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
124✔
3900
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
124✔
3901
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
124✔
3902
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
124✔
3903
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
124✔
3904
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
124✔
3905
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
124✔
3906
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
124✔
3907
    fprintf(out, "   -S, --src=IP       set source address\n");
124✔
3908
    fprintf(out, "       --seqmap-timeout=MSEC sequence number mapping timeout (default: %.0f ms)\n", seqmap_timeout / 1e6);
124✔
3909
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
124✔
3910
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
124✔
3911
    fprintf(out, "       --check-source discard replies not from target address\n");
124✔
3912
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
124✔
3913
    fprintf(out, "\n");
124✔
3914
    fprintf(out, "Output options:\n");
124✔
3915
    fprintf(out, "   -a, --alive        show targets that are alive\n");
124✔
3916
    fprintf(out, "   -A, --addr         show targets by address\n");
124✔
3917
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
124✔
3918
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
124✔
3919
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
124✔
3920
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
124✔
3921
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
124✔
3922
    fprintf(out, "   -J, --json         output in JSON format (-c, -C, or -l required)\n");
124✔
3923
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
124✔
3924
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
124✔
3925
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
124✔
3926
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
124✔
3927
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
124✔
3928
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
124✔
3929
    fprintf(out, "   -s, --stats        print final stats\n");
124✔
3930
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
124✔
3931
    fprintf(out, "   -v, --version      show version\n");
124✔
3932
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
124✔
3933
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
124✔
3934
    fprintf(out, "       --print-tos    show received TOS value\n");
124✔
3935
    fprintf(out, "       --print-ttl    show IP TTL value\n");
124✔
3936
    exit(is_error);
124✔
3937
}
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