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

schweikert / fping / 17201171370

25 Aug 2025 06:36AM UTC coverage: 86.511% (+0.3%) from 86.235%
17201171370

Pull #400

github

web-flow
Merge 613a5b44b into a54fb46b5
Pull Request #400: Print receive ping moved to new functions

93 of 101 new or added lines in 1 file covered. (92.08%)

2 existing lines in 1 file now uncovered.

1597 of 1846 relevant lines covered (86.51%)

324.23 hits per line

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

86.51
/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,757✔
275
    return (IP_HEADER_RESULT){-1, -1, 0x80000000U, 0x80000000U, 0x80000000U};
2,757✔
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_recv_json(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg);
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)
714✔
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();
714✔
551
    int tos = 0;
714✔
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];
714✔
560
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
714✔
561
        usage(0);
3✔
562
    }
1✔
563

564
    socket4 = open_ping_socket_ipv4(&socktype4);
712✔
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);
712✔
574
    /* if called (sym-linked) via 'fping6', imply '-6'
575
     * for backward compatibility */
576
    if (strstr(prog, "fping6")) {
712✔
577
        hints_ai_family = AF_INET6;
×
578
    }
579
#endif
580

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

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

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

598
    /* get command line options */
599

600
    struct optparse_long longopts[] = {
712✔
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,775✔
656
        switch (c) {
1,172✔
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)))
12✔
879
                usage(1);
6✔
880

881
            break;
8✔
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)))
10✔
928
                usage(1);
6✔
929
            break;
6✔
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) {
534✔
1076
        fprintf(stderr, "%s: option -J, --json requires -c or -C\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
    if (report_interval) {
361✔
1438
        next_report_time = current_time_ns + report_interval;
27✔
1439
    }
9✔
1440

1441
    last_send_time = 0;
361✔
1442

1443
    seqmap_init(seqmap_timeout);
361✔
1444

1445
    /* main loop */
1446
    main_loop();
361✔
1447

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

1455
    finish();
361✔
1456

1457
    return 0;
85✔
1458
}
1459

1460
static inline int64_t timespec_ns(struct timespec *a)
5,660✔
1461
{
1462
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
5,660✔
1463
}
1464

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

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

1487
    /* Split address from mask */
1488
    addr_end = strrchr(addr, '/');
57✔
1489
    if (addr_end == NULL) {
57✔
1490
        usage(1);
3✔
1491
    }
1✔
1492
    mask_str = addr_end + 1;
55✔
1493

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

1503
    *addr_end = '\0';
53✔
1504
    mask = atoi(mask_str);
53✔
1505

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

1534
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1535
{
1536
    unsigned long bitmask;
1537
    unsigned long net_last;
1538

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

1545
    /* convert mask integer from 1 to 32 to a bitmask */
1546
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1547

1548
    /* calculate network range */
1549
    net_addr &= bitmask;
18✔
1550
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1551

1552
    /* exclude network and broadcast address for regular prefixes */
1553
    if (mask < 31) {
18✔
1554
        net_last--;
12✔
1555
        net_addr++;
12✔
1556
    }
4✔
1557

1558
    /* add all hosts in that network (net_addr and net_last inclusive) */
1559
    add_addr_range_ipv4(net_addr, net_last);
18✔
1560
}
16✔
1561

1562
#ifdef IPV6
1563
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1564
{
1565
    uint64_t bitmask_lower;
1566
    uint64_t last_lower;
1567

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

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

1577
    /* calculate network range */
1578
    net_lower &= bitmask_lower;
16✔
1579
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1580

1581
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1582
}
12✔
1583
#endif /* IPV6 */
1584

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

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

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

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

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

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

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

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

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

1718
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1719
                         uint64_t end_upper, uint64_t end_lower,
1720
                         const char *scope_str)
1721
{
1722
    struct in6_addr in6_addr_tmp;
1723
    char buffer[100];
1724

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

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

1756
void main_loop()
373✔
1757
{
1758
    int64_t lt;
1759
    int64_t wait_time_ns;
1760
    struct event *event;
1761
    struct host_entry *h;
1762

1763
    while (event_queue_ping.first || event_queue_timeout.first) {
1,951✔
1764
        dbg_printf("%s", "# main_loop\n");
1765

1766
        /* timeout event ? */
1767
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,595✔
1768
            event = ev_dequeue(&event_queue_timeout);
113✔
1769
            h = event->host;
113✔
1770

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

1773
            stats_add(h, event->ping_index, 0, -1);
113✔
1774

1775
            if (per_recv_flag) {
113✔
1776
                if (json_flag)
25✔
1777
                    printf("{\"timeout\": {");
6✔
1778

1779
                if (timestamp_flag)
25✔
1780
                    print_timestamp_format(current_time_ns, timestamp_format_flag);
×
1781

1782
                if (json_flag)
25✔
1783
                {
1784
                  printf("\"host\": \"%s\", ", h->host);
6✔
1785
                  printf("\"seq\": %d", event->ping_index);
6✔
1786
                }
2✔
1787
                else {
1788
                    printf("%-*s : [%d], timed out",
19✔
1789
                        max_hostname_len, h->host, event->ping_index);
5✔
1790
                }
1791
                if (h->num_recv > 0) {
25✔
1792
                    if (json_flag)
×
1793
                        printf(", \"rttAvg\": %s", sprint_tm(h->total_time / h->num_recv));
×
1794
                    else
1795
                        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
1796
                }
1797
                else {
1798
                    if (json_flag)
25✔
1799
                        printf(", \"rttAvg\": \"NaN\"");
6✔
1800
                    else
1801
                        printf(" (NaN avg, ");
19✔
1802
                }
1803
                if (h->num_recv <= h->num_sent) {
25✔
1804
                    if (json_flag) {
25✔
1805
                        printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
6✔
1806
                    }
2✔
1807
                    else {
1808
                        printf("%d%% loss)",
19✔
1809
                            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
1810
                    }
1811
                }
7✔
1812
                else {
1813
                    if (json_flag) {
×
1814
                        printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
1815
                    }
1816
                    else {
1817
                        printf("%d%% return)",
×
1818
                            (h->num_recv_total * 100) / h->num_sent);
×
1819
                    }
1820
                }
1821

1822
                if (json_flag)
25✔
1823
                    printf("}}");
6✔
1824
                
1825
                printf("\n");
25✔
1826
            }
7✔
1827

1828
            /* do we need to send a retry? */
1829
            if (!loop_flag && !count_flag) {
113✔
1830
                if (h->num_sent < retry + 1) {
64✔
1831
                    if (backoff_flag) {
39✔
1832
                        h->timeout *= backoff;
39✔
1833
                    }
9✔
1834
                    send_ping(h, event->ping_index);
39✔
1835
                }
9✔
1836
            }
14✔
1837

1838
            /* note: we process first timeout events, because we might need to
1839
             * wait to process ping events, while we for sure never need to
1840
             * wait for timeout events.
1841
             */
1842
            continue;
113✔
1843
        }
1844

1845
        /* ping event ? */
1846
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,741✔
1847
            /* Make sure that we don't ping more than once every "interval" */
1848
            lt = current_time_ns - last_send_time;
1,146✔
1849
            if (lt < interval)
1,146✔
1850
                goto wait_for_reply;
253✔
1851

1852
            /* Dequeue the event */
1853
            event = ev_dequeue(&event_queue_ping);
893✔
1854
            h = event->host;
893✔
1855

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

1858
            /* Send the ping */
1859
            send_ping(h, event->ping_index);
893✔
1860

1861
            /* Loop and count mode: schedule next ping */
1862
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
893✔
1863
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
333✔
1864
            }
107✔
1865
        }
259✔
1866

1867
    wait_for_reply:
648✔
1868

1869
        /* When is the next ping next event? */
1870
        wait_time_ns = -1;
1,482✔
1871
        if (event_queue_ping.first) {
1,482✔
1872
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,028✔
1873
            if (wait_time_ns < 0)
1,028✔
1874
                wait_time_ns = 0;
533✔
1875
            /* make sure that we wait enough, so that the inter-ping delay is
1876
             * bigger than 'interval' */
1877
            if (wait_time_ns < interval) {
1,028✔
1878
                lt = current_time_ns - last_send_time;
533✔
1879
                if (lt < interval) {
533✔
1880
                    wait_time_ns = interval - lt;
533✔
1881
                }
177✔
1882
            }
177✔
1883

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

1887
        /* When is the next timeout event? */
1888
        if (event_queue_timeout.first) {
1,482✔
1889
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
959✔
1890
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
959✔
1891
                wait_time_ns = wait_time_timeout;
451✔
1892
                if (wait_time_ns < 0) {
451✔
1893
                    wait_time_ns = 0;
1✔
1894
                }
1895
            }
119✔
1896

1897
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1898
        }
283✔
1899

1900
        /* When is the next report due? */
1901
        if (report_interval && (loop_flag || count_flag)) {
1,482✔
1902
            int64_t wait_time_next_report = next_report_time - current_time_ns;
217✔
1903
            if (wait_time_next_report < wait_time_ns) {
217✔
1904
                wait_time_ns = wait_time_next_report;
72✔
1905
                if (wait_time_ns < 0) {
72✔
1906
                    wait_time_ns = 0;
×
1907
                }
1908
            }
24✔
1909

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

1913
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1914
        if (wait_time_ns == -1) {
1,482✔
1915
            break;
14✔
1916
        }
1917

1918
        /* end of loop was requested by interrupt signal handler */
1919
        if (finish_requested) {
1,468✔
1920
            break;
3✔
1921
        }
1922

1923
        /* Receive replies */
1924
        /* (this is what sleeps during each loop iteration) */
1925
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1926
        if (wait_for_reply(wait_time_ns)) {
1,465✔
1927
            while (wait_for_reply(0))
1,292✔
1928
                ; /* process other replies in the queue */
1929
        }
244✔
1930

1931
        update_current_time();
1,465✔
1932

1933
        if (status_snapshot) {
1,465✔
1934
            status_snapshot = 0;
×
1935
            if (json_flag)
×
1936
                print_per_system_splits_json();
×
1937
            else
1938
                print_per_system_splits();
×
1939
        }
1940

1941
        /* Print report */
1942
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,465✔
1943
            if (netdata_flag) {
45✔
1944
                print_netdata();
3✔
1945
            }
1✔
1946
            else if (json_flag) {
42✔
1947
                print_per_system_splits_json();
3✔
1948
            }
1✔
1949
            else {
1950
                print_per_system_splits();
39✔
1951
            }
1952

1953
            while (current_time_ns >= next_report_time) {
90✔
1954
                next_report_time += report_interval;
45✔
1955
            }
1956
        }
15✔
1957
    }
1958
}
373✔
1959

1960
/************************************************************
1961

1962
  Function: signal_handler
1963

1964
*************************************************************
1965

1966
  Inputs:  int signum
1967

1968
  Description:
1969

1970
  SIGQUIT signal handler - set flag and return
1971
  SIGINT signal handler - set flag and return
1972

1973
************************************************************/
1974

1975
void signal_handler(int signum)
×
1976
{
1977
    switch (signum) {
×
1978
    case SIGINT:
1979
        finish_requested = 1;
×
1980
        break;
×
1981

1982
    case SIGQUIT:
1983
        status_snapshot = 1;
×
1984
        break;
×
1985
    }
1986
}
×
1987

1988
/************************************************************
1989

1990
  Function: update_current_time
1991

1992
*************************************************************/
1993

1994
void update_current_time()
4,612✔
1995
{
1996
    clock_gettime(CLOCKID, &current_time);
4,612✔
1997
    current_time_ns = timespec_ns(&current_time);
4,612✔
1998
}
4,612✔
1999

2000
/************************************************************
2001

2002
  Function: finish
2003

2004
*************************************************************
2005

2006
  Inputs:  void (none)
2007

2008
  Description:
2009

2010
  Main program clean up and exit point
2011

2012
************************************************************/
2013

2014
void finish()
373✔
2015
{
2016
    int i;
2017
    HOST_ENTRY *h;
2018

2019
    update_current_time();
373✔
2020
    end_time = current_time_ns;
373✔
2021

2022
    /* tot up unreachables */
2023
    for (i = 0; i < num_hosts; i++) {
948✔
2024
        h = table[i];
575✔
2025

2026
        if (!h->num_recv) {
575✔
2027
            num_unreachable++;
89✔
2028

2029
            if (verbose_flag || unreachable_flag) {
89✔
2030
                printf("%s", h->host);
47✔
2031

2032
                if (verbose_flag)
47✔
2033
                    printf(" is unreachable");
44✔
2034

2035
                printf("\n");
47✔
2036
            }
3✔
2037
        }
15✔
2038
    }
157✔
2039

2040
    if (count_flag || loop_flag) {
373✔
2041
        if (json_flag)
156✔
2042
            print_per_system_stats_json();
56✔
2043
        else
2044
            print_per_system_stats();
100✔
2045
    }
46✔
2046
#if defined(DEBUG) || defined(_DEBUG)
2047
    else if (print_per_system_flag) {
2048
        if (json_flag)
2049
            print_per_system_stats_json();
2050
        else
2051
            print_per_system_stats();
2052
    }
2053
#endif /* DEBUG || _DEBUG */
2054

2055
    if (stats_flag) {
373✔
2056
        if (json_flag)
12✔
2057
            print_global_stats_json();
3✔
2058
        else
2059
            print_global_stats();
9✔
2060
    }
4✔
2061

2062
    if (min_reachable) {
373✔
2063
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
2064
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
2065
            exit(0);
6✔
2066
        }
2067
        else {
2068
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
2069
            exit(1);
3✔
2070
        }
2071
    }
2072

2073
    if (num_noaddress)
364✔
2074
        exit(2);
3✔
2075
    else if (num_alive != num_hosts)
361✔
2076
        exit(1);
50✔
2077

2078
    exit(0);
311✔
2079
}
2080

2081
/************************************************************
2082

2083
  Function: print_recv
2084

2085
*************************************************************
2086

2087
  Inputs: HOST_ENTRY *h, int64_t recv_time, int result,
2088
          int this_count, int64_t this_reply, int avg
2089

2090
  Description:
2091

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

2094
void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) {
151✔
2095
    if (timestamp_flag)
151✔
2096
        print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2097

2098
    printf("%-*s : [%d], %d bytes, %s ms",
151✔
2099
        max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
45✔
2100
    
2101
    printf(" (%s avg, ", sprint_tm(avg));
151✔
2102

2103
    if (h->num_recv <= h->num_sent) {
151✔
2104
        printf("%d%% loss)",
151✔
2105
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
151✔
2106
    }
45✔
2107
    else {
NEW
2108
        printf("%d%% return)",
×
NEW
2109
            (h->num_recv_total * 100) / h->num_sent);
×
2110
    }
2111
}
151✔
2112

2113
/************************************************************
2114

2115
  Function: print_recv_json
2116

2117
*************************************************************
2118

2119
  Inputs: HOST_ENTRY *h, int64_t recv_time, int result,
2120
          int this_count, int64_t this_reply, int avg
2121

2122
  Description:
2123

2124
************************************************************/
2125

2126
void print_recv_json(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) {
35✔
2127
    printf("{\"resp\": {");
35✔
2128

2129
    if (timestamp_flag)
35✔
2130
        print_timestamp_format(recv_time, timestamp_format_flag);
15✔
2131
    
2132
    printf("\"host\": \"%s\", ", h->host);
35✔
2133
    printf("\"seq\": %d, ", this_count);
35✔
2134
    printf("\"size\": %d, ", result);
35✔
2135
    printf("\"rtt\": %s, ", sprint_tm(this_reply));
35✔
2136
    printf("\"rttAvg\": %s", sprint_tm(avg));
35✔
2137

2138
    if (h->num_recv <= h->num_sent) {
35✔
2139
        printf(", \"loss\": %d", ((h->num_sent - h->num_recv) * 100) / h->num_sent);
35✔
2140
    }
11✔
2141
    else {
NEW
2142
        printf(", \"return\": %d", (h->num_recv_total * 100) / h->num_sent);
×
2143
    }
2144
}
35✔
2145

2146
/************************************************************
2147

2148
  Function: print_recv_ext
2149

2150
*************************************************************
2151

2152
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2153
           int64_t recv_time, int64_t this_reply
2154

2155
  Description:
2156

2157
************************************************************/
2158

2159
void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { 
450✔
2160
    if (icmp_request_typ == 13) {
450✔
2161
        printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2162
            alive_flag ? "" : ",",
14✔
2163
            ip_header_res->otime_ms, ip_header_res->rtime_ms, ip_header_res->ttime_ms,
2164
            ms_since_midnight_utc(recv_time));
2165
    }
2166

2167
    if(print_tos_flag) {
450✔
2168
        if(ip_header_res->tos != -1) {
43✔
2169
            printf(" (TOS %d)", ip_header_res->tos);
43✔
2170
        }
11✔
2171
        else {
NEW
2172
            printf(" (TOS unknown)");
×
2173
        }
2174
    }
11✔
2175

2176
    if (print_ttl_flag) {
450✔
2177
        if(ip_header_res->ttl != -1) {
43✔
2178
            printf(" (TTL %d)", ip_header_res->ttl);
43✔
2179
        }
11✔
2180
        else {
NEW
2181
            printf(" (TTL unknown)");
×
2182
        }
2183
    }
11✔
2184

2185
    if (elapsed_flag && !per_recv_flag)
450✔
2186
        printf(" (%s ms)", sprint_tm(this_reply));
8✔
2187
    
2188
    printf("\n");
450✔
2189
}
450✔
2190

2191
/************************************************************
2192

2193
  Function: print_recv_ext_json
2194

2195
*************************************************************
2196

2197
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2198
           int64_t recv_time, int64_t this_reply
2199

2200
  Description:
2201

2202
************************************************************/
2203

2204
void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) {
35✔
2205
    if (icmp_request_typ == 13) {
35✔
2206
        printf(", \"timestamps\": {");
2✔
2207
        printf("\"originate\": %u, ", ip_header_res->otime_ms);
2✔
2208
        printf("\"receive\": %u, ", ip_header_res->rtime_ms);
2✔
2209
        printf("\"transmit\": %u, ", ip_header_res->ttime_ms);
2✔
2210
        printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
2211
    }
2212

2213
    if(print_tos_flag) {
35✔
2214
        if(ip_header_res->tos != -1) {
6✔
2215
            printf(", \"tos\": %d", ip_header_res->tos);
6✔
2216
        }
2✔
2217
        else {
NEW
2218
            printf(", \"tos\": -1");
×
2219
        }
2220
    }
2✔
2221

2222
    if (print_ttl_flag) {
35✔
2223
        if(ip_header_res->ttl != -1) {
6✔
2224
            printf(", \"ttl\": %d", ip_header_res->ttl);
6✔
2225
        }
2✔
2226
        else {
NEW
2227
            printf(", \"ttl\": -1");
×
2228
        }
2229
    }
2✔
2230

2231
    if (elapsed_flag && !per_recv_flag)
35✔
NEW
2232
        printf(" (%s ms)", sprint_tm(this_reply));
×
2233

2234
    printf("}}");
35✔
2235
    printf("\n");
35✔
2236
}
35✔
2237

2238
/************************************************************
2239

2240
  Function: print_per_system_stats
2241

2242
*************************************************************
2243

2244
  Inputs:  void (none)
2245

2246
  Description:
2247

2248

2249
************************************************************/
2250

2251
void print_per_system_stats(void)
100✔
2252
{
2253
    int i, j, avg, outage_ms;
2254
    HOST_ENTRY *h;
2255
    int64_t resp;
2256

2257
    if (verbose_flag || per_recv_flag)
100✔
2258
        fprintf(stderr, "\n");
59✔
2259

2260
    for (i = 0; i < num_hosts; i++) {
234✔
2261
        h = table[i];
134✔
2262
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
134✔
2263

2264
        if (report_all_rtts_flag) {
134✔
2265
            for (j = 0; j < h->num_sent; j++) {
248✔
2266
                if ((resp = h->resp_times[j]) >= 0)
204✔
2267
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2268
                else
2269
                    fprintf(stderr, " -");
8✔
2270
            }
64✔
2271

2272
            fprintf(stderr, "\n");
44✔
2273
        }
12✔
2274
        else {
2275
            if (h->num_recv <= h->num_sent) {
90✔
2276
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
116✔
2277
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
90✔
2278

2279
                if (outage_flag) {
90✔
2280
                    /* Time outage total */
2281
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
9✔
2282
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
9✔
2283
                }
3✔
2284
            }
26✔
2285
            else {
2286
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2287
                    h->num_sent, h->num_recv,
2288
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2289
            }
2290

2291
            if (h->num_recv) {
90✔
2292
                avg = h->total_time / h->num_recv;
79✔
2293
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
79✔
2294
                fprintf(stderr, "/%s", sprint_tm(avg));
79✔
2295
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
79✔
2296
            }
23✔
2297

2298
            fprintf(stderr, "\n");
90✔
2299
        }
2300
    }
38✔
2301
}
100✔
2302

2303
/************************************************************
2304

2305
  Function: print_per_system_stats_json
2306

2307
*************************************************************
2308

2309
  Inputs:  void (none)
2310

2311
  Description:
2312

2313

2314
************************************************************/
2315

2316
void print_per_system_stats_json(void)
56✔
2317
{
2318
    int i, j, avg, outage_ms;
2319
    HOST_ENTRY *h;
2320
    int64_t resp;
2321

2322
    for (i = 0; i < num_hosts; i++) {
118✔
2323
        h = table[i];
62✔
2324

2325
        if (report_all_rtts_flag)
62✔
2326
            fprintf(stdout, "{\"vSum\": {");
18✔
2327
        else
2328
            fprintf(stdout, "{\"summary\": {");
44✔
2329

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

2332
        if (report_all_rtts_flag) {
62✔
2333
            fprintf(stdout, "\"values\": [");
18✔
2334
            for (j = 0; j < h->num_sent; j++) {
42✔
2335
                if (j > 0)
24✔
2336
                  fprintf(stdout, ", ");
6✔
2337
                
2338
                if ((resp = h->resp_times[j]) >= 0)
24✔
2339
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2340
                else
2341
                    fprintf(stdout, "null");
3✔
2342
            }
8✔
2343

2344
            fprintf(stdout, "]}");
18✔
2345
        }
6✔
2346
        else {
2347
            if (h->num_recv <= h->num_sent) {
44✔
2348
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
44✔
2349
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
44✔
2350
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
44✔
2351

2352
                if (outage_flag) {
44✔
2353
                    /* Time outage total */
2354
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
2355
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2356
                }
2357
            }
14✔
2358
            else {
2359
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
2360
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
2361
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2362
            }
2363

2364
            if (h->num_recv) {
44✔
2365
                avg = h->total_time / h->num_recv;
41✔
2366
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
41✔
2367
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
41✔
2368
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
41✔
2369
            }
13✔
2370

2371
            fprintf(stdout, "}");
44✔
2372
        }
2373
        fprintf(stdout, "}\n");
62✔
2374
    }
20✔
2375
}
56✔
2376

2377
/************************************************************
2378

2379
  Function: print_netdata
2380

2381
*************************************************************
2382

2383
  Inputs:  void (none)
2384

2385
  Description:
2386

2387

2388
************************************************************/
2389

2390
void print_netdata(void)
3✔
2391
{
2392
    static int sent_charts = 0;
2393

2394
    int i;
2395
    int64_t avg;
2396
    HOST_ENTRY *h;
2397

2398
    for (i = 0; i < num_hosts; i++) {
6✔
2399
        h = table[i];
3✔
2400

2401
        if (!sent_charts) {
3✔
2402
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2403
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2404
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2405
        }
1✔
2406

2407
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2408
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2409
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2410
        printf("END\n");
3✔
2411

2412
        if (!sent_charts) {
3✔
2413
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2414
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2415
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2416
        }
1✔
2417

2418
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2419
        /*
2420
        if( h->num_recv_i <= h->num_sent_i )
2421
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2422
        else
2423
            printf("SET lost = 0\n");
2424
*/
2425

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

2429
        if (!sent_charts) {
3✔
2430
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2431
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2432
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2433
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2434
        }
1✔
2435

2436
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2437
        if (h->num_recv_i) {
3✔
2438
            avg = h->total_time_i / h->num_recv_i;
3✔
2439
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2440
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2441
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2442
        }
1✔
2443
        printf("END\n");
3✔
2444

2445
        stats_reset_interval(h);
3✔
2446
    }
1✔
2447

2448
    sent_charts = 1;
3✔
2449
}
3✔
2450

2451
/************************************************************
2452

2453
  Function: print_per_system_splits
2454

2455
*************************************************************
2456

2457
  Inputs:  void (none)
2458

2459
  Description:
2460

2461

2462
************************************************************/
2463

2464
void print_per_system_splits(void)
39✔
2465
{
2466
    int i, avg, outage_ms_i;
2467
    HOST_ENTRY *h;
2468
    struct tm *curr_tm;
2469

2470
    if (verbose_flag || per_recv_flag)
39✔
2471
        fprintf(stderr, "\n");
×
2472

2473
    update_current_time();
39✔
2474
    curr_tm = localtime((time_t *)&current_time.tv_sec);
39✔
2475
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
52✔
2476
        curr_tm->tm_min, curr_tm->tm_sec);
13✔
2477

2478
    for (i = 0; i < num_hosts; i++) {
78✔
2479
        h = table[i];
39✔
2480
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
39✔
2481

2482
        if (h->num_recv_i <= h->num_sent_i) {
39✔
2483
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
52✔
2484
                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✔
2485

2486
            if (outage_flag) {
39✔
2487
                /* Time outage  */
2488
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
12✔
2489
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
12✔
2490
            }
4✔
2491
        }
13✔
2492
        else {
2493
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2494
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2495
        }
2496

2497
        if (h->num_recv_i) {
39✔
2498
            avg = h->total_time_i / h->num_recv_i;
27✔
2499
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
27✔
2500
            fprintf(stderr, "/%s", sprint_tm(avg));
27✔
2501
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
27✔
2502
        }
9✔
2503

2504
        fprintf(stderr, "\n");
39✔
2505
        if (!cumulative_stats_flag) {
39✔
2506
            stats_reset_interval(h);
27✔
2507
        }
9✔
2508
    }
13✔
2509
}
39✔
2510

2511
/************************************************************
2512

2513
  Function: print_per_system_splits_json
2514

2515
*************************************************************
2516

2517
  Inputs:  void (none)
2518

2519
  Description:
2520

2521

2522
************************************************************/
2523

2524
void print_per_system_splits_json(void)
3✔
2525
{
2526
    int i, avg, outage_ms_i;
2527
    HOST_ENTRY *h;
2528

2529
    update_current_time();
3✔
2530

2531
    for (i = 0; i < num_hosts; i++) {
6✔
2532
        h = table[i];
3✔
2533
        fprintf(stdout, "{\"intSum\": {");
3✔
2534
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2535
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2536

2537
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2538
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2539
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2540
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2541

2542
            if (outage_flag) {
3✔
2543
                /* Time outage  */
2544
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
2545
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2546
            }
2547
        }
1✔
2548
        else {
2549
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
2550
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
2551
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2552
        }
2553

2554
        if (h->num_recv_i) {
3✔
2555
            avg = h->total_time_i / h->num_recv_i;
3✔
2556
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2557
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2558
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2559
        }
1✔
2560

2561
        fprintf(stdout, "}}\n");
3✔
2562
        if (!cumulative_stats_flag) {
3✔
2563
            stats_reset_interval(h);
3✔
2564
        }
1✔
2565
    }
1✔
2566
}
3✔
2567

2568
/************************************************************
2569

2570
  Function: print_global_stats
2571

2572
*************************************************************
2573

2574
  Inputs:  void (none)
2575

2576
  Description:
2577

2578

2579
************************************************************/
2580

2581
void print_global_stats(void)
9✔
2582
{
2583
    fprintf(stderr, "\n");
9✔
2584
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2585
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2586
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2587
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2588
    fprintf(stderr, "\n");
9✔
2589
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2590
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2591
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2592
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2593
    fprintf(stderr, "\n");
9✔
2594

2595
    if (total_replies == 0) {
9✔
2596
        min_reply = 0;
3✔
2597
        max_reply = 0;
3✔
2598
        total_replies = 1;
3✔
2599
        sum_replies = 0;
3✔
2600
    }
1✔
2601

2602
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2603
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2604
        sprint_tm(sum_replies / total_replies));
3✔
2605
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2606
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2607
        (end_time - start_time) / 1e9);
9✔
2608
    fprintf(stderr, "\n");
9✔
2609
}
9✔
2610

2611
/************************************************************
2612

2613
  Function: print_global_stats_json
2614

2615
*************************************************************
2616

2617
  Inputs:  void (none)
2618

2619
  Description:
2620

2621

2622
************************************************************/
2623

2624
void print_global_stats_json(void)
3✔
2625
{
2626
    fprintf(stdout, "{\"globalSum\": {");
3✔
2627
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2628
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2629
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2630
    fprintf(stdout, "\"unknown addresses\": %d, ", num_noaddress);
3✔
2631
    fprintf(stdout, "\"timeouts (waiting for response)\": %d, ", num_timeout);
3✔
2632
    fprintf(stdout, "\"ICMP Echos sent\": %d, ", num_pingsent);
3✔
2633
    fprintf(stdout, "\"ICMP Echo Replies received\": %d, ", num_pingreceived);
3✔
2634
    fprintf(stdout, "\"other ICMP received\": %d, ", num_othericmprcvd);
3✔
2635

2636
    if (total_replies == 0) {
3✔
2637
        min_reply = 0;
×
2638
        max_reply = 0;
×
2639
        total_replies = 1;
×
2640
        sum_replies = 0;
×
2641
    }
2642

2643
    fprintf(stdout, "\"ms (min round trip time)\": %s, ", sprint_tm(min_reply));
3✔
2644
    fprintf(stdout, "\"ms (avg round trip time)\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2645
    fprintf(stdout, "\"ms (max round trip time)\": %s, ", sprint_tm(max_reply));
3✔
2646
    fprintf(stdout, "\"sec (elapsed real time)\": %.3f", (end_time - start_time) / 1e9);
3✔
2647
    fprintf(stdout, "}}\n");
3✔
2648
}
3✔
2649

2650
/************************************************************
2651

2652
  Function: send_ping
2653

2654
*************************************************************
2655

2656
  Inputs:  int s, HOST_ENTRY *h
2657

2658
  Description:
2659

2660
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2661
  will be added on by the kernel.  The ID field is our UNIX process ID,
2662
  and the sequence number is an index into an array of outstanding
2663
  ping requests. The sequence number will later be used to quickly
2664
  figure out who the ping reply came from.
2665

2666
************************************************************/
2667

2668
int send_ping(HOST_ENTRY *h, int index)
932✔
2669
{
2670
    int n;
2671
    int myseq;
2672
    int ret = 1;
932✔
2673
    uint8_t proto = ICMP_ECHO;
932✔
2674

2675
    update_current_time();
932✔
2676
    h->last_send_time = current_time_ns;
932✔
2677
    myseq = seqmap_add(h->i, index, current_time_ns);
932✔
2678

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

2681
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
932✔
2682
        if(icmp_request_typ == 13)
809✔
2683
            proto = ICMP_TSTAMP;
18✔
2684
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
809✔
2685
    }
253✔
2686
#ifdef IPV6
2687
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
2688
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
2689
    }
15✔
2690
#endif
2691
    else {
2692
        return 0;
×
2693
    }
2694

2695
    /* error sending? */
2696
    if (
664✔
2697
        (n < 0)
268✔
2698
#if defined(EHOSTDOWN)
2699
        && errno != EHOSTDOWN
302✔
2700
#endif
2701
    ) {
2702
        if (verbose_flag) {
34✔
2703
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2704
        }
2705
        else {
2706
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2707
        }
2708

2709
        h->num_sent++;
34✔
2710
        h->num_sent_i++;
34✔
2711
        if (!loop_flag)
34✔
2712
            h->resp_times[index] = RESP_ERROR;
34✔
2713

2714
        ret = 0;
34✔
2715
    }
2716
    else {
2717
        /* schedule timeout */
2718
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
898✔
2719

2720
        /* mark this trial as outstanding */
2721
        if (!loop_flag) {
898✔
2722
            h->resp_times[index] = RESP_WAITING;
898✔
2723
        }
268✔
2724
    }
2725

2726
    num_pingsent++;
932✔
2727
    last_send_time = h->last_send_time;
932✔
2728

2729
    return (ret);
932✔
2730
}
268✔
2731

2732
int socket_can_read(struct timeval *timeout)
2,757✔
2733
{
2734
    int nfound;
2735
    fd_set readset;
2736
    int socketmax;
2737

2738
#ifndef IPV6
2739
    socketmax = socket4;
2740
#else
2741
    socketmax = socket4 > socket6 ? socket4 : socket6;
2,757✔
2742
#endif
2743

2744
select_again:
2,072✔
2745
    FD_ZERO(&readset);
35,909✔
2746
    if (socket4 >= 0)
2,757✔
2747
        FD_SET(socket4, &readset);
2,757✔
2748
#ifdef IPV6
2749
    if (socket6 >= 0)
2,757✔
2750
        FD_SET(socket6, &readset);
2,757✔
2751
#endif
2752

2753
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
2,757✔
2754
    if (nfound < 0) {
2,757✔
2755
        if (errno == EINTR) {
×
2756
            /* interrupted system call: redo the select */
2757
            goto select_again;
×
2758
        }
2759
        else {
2760
            perror("select");
×
2761
        }
2762
    }
2763

2764
    if (nfound > 0) {
2,757✔
2765
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,292✔
2766
            return socket4;
1,201✔
2767
        }
2768
#ifdef IPV6
2769
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
91✔
2770
            return socket6;
91✔
2771
        }
2772
#endif
2773
    }
2774

2775
    return -1;
1,465✔
2776
}
685✔
2777

2778
int receive_packet(int64_t wait_time,
2,757✔
2779
#if HAVE_SO_TIMESTAMPNS
2780
    int64_t *reply_timestamp,
2781
#else
2782
    int64_t *reply_timestamp __attribute__((unused)),
2783
#endif
2784
    struct sockaddr *reply_src_addr,
2785
    size_t reply_src_addr_len,
2786
    char *reply_buf,
2787
    size_t reply_buf_len,
2788
    int *ip_header_tos,
2789
    int *ip_header_ttl)
2790
{
2791
    struct timeval to;
2792
    int s = 0;
2,757✔
2793
    int recv_len;
2794
    static unsigned char msg_control[128];
2795
    struct iovec msg_iov = {
4,127✔
2796
        reply_buf,
685✔
2797
        reply_buf_len
685✔
2798
    };
2799
    struct msghdr recv_msghdr = {0};
2,757✔
2800
    recv_msghdr.msg_name = reply_src_addr;
2,757✔
2801
    recv_msghdr.msg_namelen = reply_src_addr_len;
2,757✔
2802
    recv_msghdr.msg_iov = &msg_iov;
2,757✔
2803
    recv_msghdr.msg_iovlen = 1;
2,757✔
2804
    recv_msghdr.msg_control = &msg_control;
2,757✔
2805
    recv_msghdr.msg_controllen = sizeof(msg_control);
2,757✔
2806
    struct cmsghdr *cmsg;
2807

2808
    /* Wait for a socket to become ready */
2809
    if (wait_time) {
2,757✔
2810
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,464✔
2811
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,464✔
2812
    }
441✔
2813
    else {
2814
        to.tv_sec = 0;
1,293✔
2815
        to.tv_usec = 0;
1,293✔
2816
    }
2817
    s = socket_can_read(&to);
2,757✔
2818
    if (s == -1) {
2,757✔
2819
        return 0; /* timeout */
1,465✔
2820
    }
2821

2822
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,292✔
2823
    if (recv_len <= 0) {
1,292✔
2824
        return 0;
×
2825
    }
2826

2827
    /* ancilliary data */
2828
    {
2829
#if HAVE_SO_TIMESTAMPNS
2830
        struct timespec reply_timestamp_ts;
2831
        struct timeval reply_timestamp_tv;
2832
#endif
2833
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,292✔
2834
             cmsg != NULL;
2,348✔
2835
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,056✔
2836
#if HAVE_SO_TIMESTAMPNS
2837
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,056✔
2838
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,048✔
2839
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,048✔
2840
            }
2841
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,056✔
2842
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2843
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2844
            }
2845
#endif
2846
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,056✔
2847
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2848
            }
2849
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,056✔
2850
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2851
            }
2852
#ifdef IPV6
2853
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,056✔
2854
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2855
            }
2856
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,056✔
2857
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2858
            }
2859
#endif
2860
        }
2861
    }
2862

2863
#if defined(DEBUG) || defined(_DEBUG)
2864
    if (randomly_lose_flag) {
2865
        if ((random() & 0x07) <= lose_factor)
2866
            return 0;
2867
    }
2868
#endif
2869

2870
    return recv_len;
1,292✔
2871
}
685✔
2872

2873
/* stats_add: update host statistics for a single packet that was received (or timed out)
2874
 * h: host entry to update
2875
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2876
 * success: 1 if response received, 0 otherwise
2877
 * latency: response time, in ns
2878
 */
2879
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
898✔
2880
{
2881
    /* sent count - we update only on receive/timeout, so that we don't get
2882
     * weird loss percentage, just because a packet was note recived yet.
2883
     */
2884
    h->num_sent++;
898✔
2885
    h->num_sent_i++;
898✔
2886

2887
    if (!success) {
898✔
2888
        if (!loop_flag && index >= 0) {
113✔
2889
            h->resp_times[index] = RESP_TIMEOUT;
113✔
2890
        }
29✔
2891
        num_timeout++;
113✔
2892
        return;
113✔
2893
    }
2894

2895
    /* received count */
2896
    h->num_recv++;
785✔
2897
    h->num_recv_i++;
785✔
2898

2899
    /* maximum */
2900
    if (!h->max_reply || latency > h->max_reply) {
785✔
2901
        h->max_reply = latency;
617✔
2902
    }
187✔
2903
    if (!h->max_reply_i || latency > h->max_reply_i) {
785✔
2904
        h->max_reply_i = latency;
632✔
2905
    }
191✔
2906

2907
    /* minimum */
2908
    if (!h->min_reply || latency < h->min_reply) {
785✔
2909
        h->min_reply = latency;
534✔
2910
    }
158✔
2911
    if (!h->min_reply_i || latency < h->min_reply_i) {
785✔
2912
        h->min_reply_i = latency;
564✔
2913
    }
168✔
2914

2915
    /* total time (for average) */
2916
    h->total_time += latency;
785✔
2917
    h->total_time_i += latency;
785✔
2918

2919
    /* response time per-packet (count mode) */
2920
    if (!loop_flag && index >= 0) {
785✔
2921
        h->resp_times[index] = latency;
785✔
2922
    }
239✔
2923
}
268✔
2924

2925
/* stats_reset_interval: reset interval statistics
2926
 * h: host entry to update
2927
 */
2928
void stats_reset_interval(HOST_ENTRY *h)
33✔
2929
{
2930
    h->num_sent_i = 0;
33✔
2931
    h->num_recv_i = 0;
33✔
2932
    h->max_reply_i = 0;
33✔
2933
    h->min_reply_i = 0;
33✔
2934
    h->total_time_i = 0;
33✔
2935
}
33✔
2936

2937
int decode_icmp_ipv4(
1,201✔
2938
    struct sockaddr *response_addr,
2939
    size_t response_addr_len,
2940
    char *reply_buf,
2941
    size_t reply_buf_len,
2942
    unsigned short *id,
2943
    unsigned short *seq,
2944
    IP_HEADER_RESULT *ip_header_res)
2945
{
2946
    struct icmp *icp;
2947
    int hlen = 0;
1,201✔
2948

2949
    if (!using_sock_dgram4) {
1,201✔
2950
        struct ip *ip = (struct ip *)reply_buf;
1,193✔
2951
        ip_header_res->tos = ip->ip_tos;
1,193✔
2952
        ip_header_res->ttl = ip->ip_ttl;
1,193✔
2953

2954
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2955
        /* The alpha headers are decidedly broken.
2956
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2957
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2958
         */
2959
        hlen = (ip->ip_vhl & 0x0F) << 2;
2960
#else
2961
        hlen = ip->ip_hl << 2;
1,193✔
2962
#endif
2963
    }
229✔
2964

2965
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,201✔
2966
        /* too short */
2967
        if (verbose_flag) {
×
2968
            char buf[INET6_ADDRSTRLEN];
2969
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2970
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2971
        }
2972
        return -1;
×
2973
    }
2974

2975
    icp = (struct icmp *)(reply_buf + hlen);
1,201✔
2976

2977
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,201✔
2978
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
506✔
2979
        /* Handle other ICMP packets */
2980
        struct icmp *sent_icmp;
2981
        SEQMAP_VALUE *seqmap_value;
2982
        char addr_ascii[INET6_ADDRSTRLEN];
2983
        HOST_ENTRY *h;
2984

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

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

2993
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
483✔
2994
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
2995
            sent_icmp->icmp_id != ident4) {
×
2996
            /* not caused by us */
2997
            return -1;
483✔
2998
        }
2999

3000
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
3001
        if (seqmap_value == NULL) {
×
3002
            return -1;
×
3003
        }
3004

3005
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3006

3007
        switch (icp->icmp_type) {
×
3008
        case ICMP_UNREACH:
3009
            h = table[seqmap_value->host_nr];
×
3010
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
3011
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
3012
                    addr_ascii, h->host);
3013
            }
3014
            else {
3015
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3016
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
3017
            }
3018

3019
            print_warning("\n");
×
3020
            num_othericmprcvd++;
×
3021
            break;
×
3022

3023
        case ICMP_SOURCEQUENCH:
3024
        case ICMP_REDIRECT:
3025
        case ICMP_TIMXCEED:
3026
        case ICMP_PARAMPROB:
3027
            h = table[seqmap_value->host_nr];
×
3028
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
3029
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3030
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
3031
            }
3032
            else {
3033
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
3034
                    icp->icmp_type, addr_ascii, h->host);
×
3035
            }
3036
            print_warning("\n");
×
3037
            num_othericmprcvd++;
×
3038
            break;
×
3039
        }
3040

3041
        return -1;
×
3042
    }
3043

3044
    *id = icp->icmp_id;
718✔
3045
    *seq = ntohs(icp->icmp_seq);
718✔
3046
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
718✔
3047

3048
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
3049
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
3050
            if (verbose_flag) {
×
3051
                char buf[INET6_ADDRSTRLEN];
3052
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3053
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3054
            }
3055
            return -1;
×
3056
        }
3057

3058
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
3059
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
3060
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
3061
    }
3062

3063
    return hlen;
718✔
3064
}
229✔
3065

3066
#ifdef IPV6
3067
int decode_icmp_ipv6(
91✔
3068
    struct sockaddr *response_addr,
3069
    size_t response_addr_len,
3070
    char *reply_buf,
3071
    size_t reply_buf_len,
3072
    unsigned short *id,
3073
    unsigned short *seq)
3074
{
3075
    struct icmp6_hdr *icp;
3076

3077
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
91✔
3078
        if (verbose_flag) {
×
3079
            char buf[INET6_ADDRSTRLEN];
3080
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3081
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3082
        }
3083
        return 0; /* too short */
×
3084
    }
3085

3086
    icp = (struct icmp6_hdr *)reply_buf;
91✔
3087

3088
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
91✔
3089
        /* Handle other ICMPv6 packets */
3090
        struct ip6_hdr *sent_ipv6;
3091
        struct icmp6_hdr *sent_icmp;
3092
        SEQMAP_VALUE *seqmap_value;
3093
        char addr_ascii[INET6_ADDRSTRLEN];
3094
        HOST_ENTRY *h;
3095

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

3102
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
×
3103
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
×
3104
            /* discard ICMPv6 message if we can't tell that it was caused by
3105
             * us, because the IPv6 header is not directly followed by an
3106
             * ICMPv6 header
3107
             */
3108
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
3109
            return 0;
×
3110
        }
3111
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
×
3112

3113
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
×
3114
            /* not caused by us */
3115
            return 0;
×
3116
        }
3117

3118
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
3119
        if (seqmap_value == NULL) {
×
3120
            return 0;
×
3121
        }
3122

3123
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3124
        h = table[seqmap_value->host_nr];
×
3125

3126
        switch (icp->icmp6_type) {
×
3127
        case ICMP6_DST_UNREACH:
3128
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
3129
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3130
                    icp->icmp6_code, addr_ascii, h->host);
×
3131
            } else {
3132
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3133
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
3134
            }
3135
            print_warning("\n");
×
3136
            num_othericmprcvd++;
×
3137
            break;
×
3138

3139
        case ICMP6_PACKET_TOO_BIG:
3140
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
3141
                addr_ascii, h->host);
3142
            num_othericmprcvd++;
×
3143
            break;
×
3144

3145
        case ICMP6_TIME_EXCEEDED:
3146
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
3147
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3148
                    icp->icmp6_code, addr_ascii, h->host);
×
3149
            } else {
3150
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3151
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
3152
            }
3153
            print_warning("\n");
×
3154
            num_othericmprcvd++;
×
3155
            break;
×
3156

3157
        case ICMP6_PARAM_PROB:
3158
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
3159
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3160
                    icp->icmp6_code, addr_ascii, h->host);
×
3161
            } else {
3162
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3163
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
3164
            }
3165
            print_warning("\n");
×
3166
            num_othericmprcvd++;
×
3167
            break;
×
3168

3169
        default:
3170
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
3171
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
3172
            num_othericmprcvd++;
×
3173
            break;
×
3174
        }
3175

3176
        return 0;
×
3177
    }
3178

3179
    *id = icp->icmp6_id;
91✔
3180
    *seq = ntohs(icp->icmp6_seq);
91✔
3181

3182
    return 1;
91✔
3183
}
15✔
3184
#endif
3185

3186
int wait_for_reply(int64_t wait_time)
2,757✔
3187
{
3188
    int result;
3189
    static char buffer[RECV_BUFSIZE];
3190
    struct sockaddr_storage response_addr;
3191
    int n, avg;
3192
    HOST_ENTRY *h;
3193
    int64_t this_reply;
3194
    int this_count;
3195
    int64_t recv_time = 0;
2,757✔
3196
    SEQMAP_VALUE *seqmap_value;
3197
    unsigned short id;
3198
    unsigned short seq;
3199
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
2,757✔
3200

3201
    /* Receive packet */
3202
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,442✔
3203
        &recv_time, /* reply_timestamp */
3204
        (struct sockaddr *)&response_addr, /* reply_src_addr */
3205
        sizeof(response_addr), /* reply_src_addr_len */
3206
        buffer, /* reply_buf */
3207
        sizeof(buffer), /* reply_buf_len */
3208
        &ip_header_res.tos, /* TOS resp. TC byte */
685✔
3209
        &ip_header_res.ttl /* TTL resp. hop limit */
685✔
3210
    );
3211

3212
    if (result <= 0) {
2,757✔
3213
        return 0;
1,465✔
3214
    }
3215

3216
    update_current_time();
1,292✔
3217
    if (recv_time == 0)
1,292✔
3218
        recv_time = current_time_ns;
244✔
3219

3220
    /* Process ICMP packet and retrieve id/seq */
3221
    if (response_addr.ss_family == AF_INET) {
1,292✔
3222
        int ip_hlen = decode_icmp_ipv4(
1,201✔
3223
            (struct sockaddr *)&response_addr,
3224
            sizeof(response_addr),
3225
            buffer,
3226
            sizeof(buffer),
3227
            &id,
3228
            &seq,
3229
            &ip_header_res);
3230
        if (ip_hlen < 0) {
1,201✔
3231
            return 1;
483✔
3232
        }
3233
        if (id != ident4) {
718✔
3234
            return 1; /* packet received, but not the one we are looking for! */
×
3235
        }
3236
        if (!using_sock_dgram4) {
718✔
3237
            /* do not include IP header in returned size, to be consistent with ping(8) and also
3238
             * with fping with IPv6 hosts */
3239
            result -= ip_hlen;
710✔
3240
        }
228✔
3241
    }
228✔
3242
#ifdef IPV6
3243
    else if (response_addr.ss_family == AF_INET6) {
91✔
3244
        if (!decode_icmp_ipv6(
91✔
3245
                (struct sockaddr *)&response_addr,
3246
                sizeof(response_addr),
3247
                buffer,
3248
                sizeof(buffer),
3249
                &id,
3250
                &seq)) {
3251
            return 1;
×
3252
        }
3253
        if (id != ident6) {
91✔
3254
            return 1; /* packet received, but not the one we are looking for! */
×
3255
        }
3256
    }
15✔
3257
#endif
3258
    else {
3259
        return 1;
×
3260
    }
3261

3262
    seqmap_value = seqmap_fetch(seq, current_time_ns);
809✔
3263
    if (seqmap_value == NULL) {
809✔
3264
        return 1;
12✔
3265
    }
3266

3267
    /* find corresponding host_entry */
3268
    n = seqmap_value->host_nr;
797✔
3269
    h = table[n];
797✔
3270
    this_count = seqmap_value->ping_count;
797✔
3271
    this_reply = recv_time - seqmap_value->ping_ts;
797✔
3272

3273
    /* update stats that include invalid replies */
3274
    h->num_recv_total++;
797✔
3275
    num_pingreceived++;
797✔
3276

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

3279
    /* optionally require reply source equal to target address */
3280
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
797✔
3281
        dbg_printf("%s\n", "discarding reply from wrong source address");
3282
        return 1;
12✔
3283
    }
3284

3285
    /* discard duplicates */
3286
    if (!loop_flag && !(count_flag && quiet_flag) && h->resp_times[this_count] >= 0) {
785✔
3287
        if (!per_recv_flag) {
×
3288
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3289
                h->host, this_count, result, sprint_tm(this_reply));
3290

3291
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3292
                char buf[INET6_ADDRSTRLEN];
3293
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3294
                fprintf(stderr, " [<- %s]", buf);
×
3295
            }
3296
            fprintf(stderr, "\n");
×
3297
        }
3298
        return 1;
×
3299
    }
3300

3301
    /* discard reply if delay is larger than timeout
3302
     * (see also: github #32) */
3303
    if (this_reply > h->timeout) {
785✔
3304
        return 1;
×
3305
    }
3306

3307
    /* update stats */
3308
    stats_add(h, this_count, 1, this_reply);
785✔
3309
    // TODO: move to stats_add?
3310
    if (!max_reply || this_reply > max_reply)
785✔
3311
        max_reply = this_reply;
485✔
3312
    if (!min_reply || this_reply < min_reply)
785✔
3313
        min_reply = this_reply;
438✔
3314
    sum_replies += this_reply;
785✔
3315
    total_replies++;
785✔
3316

3317
    /* initialize timeout to initial timeout (without backoff) */
3318
    h->timeout = timeout;
785✔
3319

3320
    /* remove timeout event */
3321
    struct event *timeout_event = host_get_timeout_event(h, this_count);
785✔
3322
    if (timeout_event) {
785✔
3323
        ev_remove(&event_queue_timeout, timeout_event);
785✔
3324
    }
239✔
3325

3326
    /* print "is alive" */
3327
    if (h->num_recv == 1) {
785✔
3328
        num_alive++;
486✔
3329
        if (fast_reachable && num_alive >= min_reachable)
486✔
3330
            finish_requested = 1;
3✔
3331

3332
        if (verbose_flag || alive_flag) {
486✔
3333
            printf("%s", h->host);
299✔
3334

3335
            if (verbose_flag)
299✔
3336
                printf(" is alive");
279✔
3337
        }
85✔
3338
    }
142✔
3339

3340
    /* print received ping (unless --quiet) */
3341
    if (per_recv_flag) {
785✔
3342
        avg = h->total_time / h->num_recv;
186✔
3343
        if (json_flag) {
186✔
3344
            print_recv_json(h,
46✔
3345
                recv_time,
11✔
3346
                result,
11✔
3347
                this_count,
11✔
3348
                this_reply,
11✔
3349
                avg);
11✔
3350
        }
11✔
3351
        else {
3352
            print_recv(h,
196✔
3353
                recv_time,
45✔
3354
                result,
45✔
3355
                this_count,
45✔
3356
                this_reply,
45✔
3357
                avg);
45✔
3358
        }
3359
    }
56✔
3360

3361
    if (verbose_flag || alive_flag || per_recv_flag) {
785✔
3362
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
485✔
3363
            char buf[INET6_ADDRSTRLEN];
3364
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3365
            fprintf(stderr, " [<- %s]", buf);
6✔
3366
        }
3367
        if (json_flag) {
485✔
3368
            print_recv_ext_json(&ip_header_res,
35✔
3369
                recv_time,
11✔
3370
                this_reply);
11✔
3371
        }
11✔
3372
        else {
3373
            print_recv_ext(&ip_header_res,
450✔
3374
                recv_time,
130✔
3375
                this_reply);
130✔
3376
        }
3377
    }
141✔
3378
    return 1;
785✔
3379
}
685✔
3380

3381
/************************************************************
3382

3383
  Function: add_name
3384

3385
*************************************************************
3386

3387
  Inputs:  char* name
3388

3389
  Description:
3390

3391
  process input name for addition to target list
3392
  name can turn into multiple targets via multiple interfaces (-m)
3393
  or via NIS groups
3394

3395
************************************************************/
3396

3397
void add_name(char *name)
609✔
3398
{
3399
    struct addrinfo *res0, *res, hints;
3400
    int ret_ga;
3401
    char *printname;
3402
    char namebuf[256];
3403
    char addrbuf[256];
3404

3405
    /* getaddrinfo */
3406
    memset(&hints, 0, sizeof(struct addrinfo));
609✔
3407
    hints.ai_flags = AI_UNUSABLE;
609✔
3408
    hints.ai_socktype = SOCK_RAW;
609✔
3409
    hints.ai_family = hints_ai_family;
609✔
3410
    if (hints_ai_family == AF_INET) {
609✔
3411
        hints.ai_protocol = IPPROTO_ICMP;
81✔
3412
    }
21✔
3413
#ifdef IPV6
3414
    else if (hints_ai_family == AF_INET6) {
528✔
3415
        hints.ai_protocol = IPPROTO_ICMPV6;
31✔
3416
    }
1✔
3417
#endif
3418
    else {
3419
        hints.ai_socktype = SOCK_STREAM;
497✔
3420
        hints.ai_protocol = 0;
497✔
3421
    }
3422
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
609✔
3423
    if (ret_ga) {
609✔
3424
        if (!quiet_flag)
29✔
3425
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3426
        num_noaddress++;
29✔
3427

3428
        // Handle JSON output for invalid hosts
3429
        if (json_flag) {
29✔
3430
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
3431
            return;
21✔
3432
        }
3433

3434
        return;
26✔
3435
    }
3436

3437
    /* NOTE: we could/should loop with res on all addresses like this:
3438
     * for (res = res0; res; res = res->ai_next) {
3439
     * We don't do it yet, however, because is is an incompatible change
3440
     * (need to implement a separate option for this)
3441
     */
3442
    for (res = res0; res; res = res->ai_next) {
580✔
3443
        /* name_flag: addr -> name lookup requested) */
3444
        if (name_flag || rdns_flag) {
582✔
3445
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3446
            if (name_flag) {
3✔
3447
                /* Was it a numerical address? Only then do a rdns-query */
3448
                struct addrinfo *nres;
3449
                hints.ai_flags = AI_NUMERICHOST;
×
3450
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3451
                    do_rdns = 1;
×
3452
                    freeaddrinfo(nres);
×
3453
                }
3454
            }
3455

3456
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3457
                printname = namebuf;
3✔
3458
            }
1✔
3459
            else {
3460
                printname = name;
×
3461
            }
3462
        }
1✔
3463
        else {
3464
            printname = name;
577✔
3465
        }
3466

3467
        /* addr_flag: name -> addr lookup requested */
3468
        if (addr_flag) {
580✔
3469
            int ret;
3470
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3471
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3472
            if (ret) {
3✔
3473
                if (!quiet_flag) {
×
3474
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3475
                }
3476
                continue;
×
3477
            }
3478

3479
            if (name_flag || rdns_flag) {
3✔
3480
                char nameaddrbuf[512 + 3];
3481
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3482
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3483
            }
3484
            else {
3485
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3486
            }
3487
        }
1✔
3488
        else {
3489
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
577✔
3490
        }
3491

3492
        if (!multif_flag) {
580✔
3493
            break;
580✔
3494
        }
3495
    }
3496

3497
    freeaddrinfo(res0);
580✔
3498
}
167✔
3499

3500
/************************************************************
3501

3502
  Function: add_addr
3503

3504
*************************************************************
3505

3506
  Description:
3507

3508
  add single address to list of hosts to be pinged
3509

3510
************************************************************/
3511

3512
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
580✔
3513
{
3514
    HOST_ENTRY *p;
3515
    int n;
3516
    int64_t *i;
3517

3518
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
580✔
3519
    if (!p)
580✔
3520
        crash_and_burn("can't allocate HOST_ENTRY");
×
3521

3522
    p->name = strdup(name);
580✔
3523
    p->host = strdup(host);
580✔
3524
    memcpy(&p->saddr, ipaddr, ipaddr_len);
580✔
3525
    p->saddr_len = ipaddr_len;
580✔
3526
    p->timeout = timeout;
580✔
3527
    p->min_reply = 0;
580✔
3528

3529
    if (netdata_flag) {
580✔
3530
        char *s = p->name;
3✔
3531
        while (*s) {
30✔
3532
            if (!isalnum(*s))
27✔
3533
                *s = '_';
9✔
3534
            s++;
27✔
3535
        }
3536
    }
1✔
3537

3538
    if (strlen(p->host) > max_hostname_len)
580✔
3539
        max_hostname_len = strlen(p->host);
387✔
3540

3541
    /* array for response time results */
3542
    if (!loop_flag) {
580✔
3543
#if SIZE_MAX <= UINT_MAX
3544
        if (trials > (SIZE_MAX / sizeof(int64_t)))
3545
            crash_and_burn("resp_times array too large for memory");
3546
#endif
3547
        i = (int64_t *)malloc(trials * sizeof(int64_t));
580✔
3548
        if (!i)
580✔
3549
            crash_and_burn("can't allocate resp_times array");
×
3550

3551
        for (n = 1; n < trials; n++)
2,311✔
3552
            i[n] = RESP_UNUSED;
1,731✔
3553

3554
        p->resp_times = i;
580✔
3555
    }
158✔
3556

3557
    /* allocate event storage */
3558
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
580✔
3559
    if (!p->event_storage_ping) {
580✔
3560
        errno_crash_and_burn("can't allocate event_storage_ping");
×
3561
    }
3562
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
580✔
3563
    if (!p->event_storage_timeout) {
580✔
3564
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
3565
    }
3566

3567
    /* schedule first ping */
3568
    host_add_ping_event(p, 0, current_time_ns);
580✔
3569

3570
    num_hosts++;
580✔
3571
}
580✔
3572

3573
/************************************************************
3574

3575
  Function: crash_and_burn
3576

3577
*************************************************************
3578

3579
  Inputs:  char* message
3580

3581
  Description:
3582

3583
************************************************************/
3584

3585
void crash_and_burn(char *message)
×
3586
{
3587
    fprintf(stderr, "%s: %s\n", prog, message);
×
3588
    exit(4);
×
3589
}
3590

3591
/************************************************************
3592

3593
  Function: errno_crash_and_burn
3594

3595
*************************************************************
3596

3597
  Inputs:  char* message
3598

3599
  Description:
3600

3601
************************************************************/
3602

3603
void errno_crash_and_burn(char *message)
8✔
3604
{
3605
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3606
    exit(4);
8✔
3607
}
3608

3609
/************************************************************
3610

3611
  Function: print_warning
3612

3613
  Description: fprintf(stderr, ...), unless running with -q
3614

3615
*************************************************************/
3616

3617
void print_warning(char *format, ...)
54✔
3618
{
3619
    va_list args;
3620
    if (!quiet_flag) {
54✔
3621
        va_start(args, format);
54✔
3622
        vfprintf(stderr, format, args);
54✔
3623
        va_end(args);
54✔
3624
    }
8✔
3625
}
54✔
3626

3627
/************************************************************
3628

3629
  Function: sprint_tm
3630

3631
*************************************************************
3632

3633
  render nanosecond int64_t value into milliseconds string with three digits of
3634
  precision.
3635

3636
************************************************************/
3637

3638
const char *sprint_tm(int64_t ns)
1,083✔
3639
{
3640
    static char buf[10];
3641
    double t = (double)ns / 1e6;
1,083✔
3642

3643
    if (t < 0.0) {
1,083✔
3644
        /* negative (unexpected) */
3645
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3646
    }
3647
    else if (t < 1.0) {
1,083✔
3648
        /* <= 0.99 ms */
3649
        snprintf(buf, sizeof(buf), "%.3f", t);
1,083✔
3650
    }
335✔
UNCOV
3651
    else if (t < 10.0) {
×
3652
        /* 1.00 - 9.99 ms */
UNCOV
3653
        snprintf(buf, sizeof(buf), "%.2f", t);
×
3654
    }
3655
    else if (t < 100.0) {
×
3656
        /* 10.0 - 99.9 ms */
3657
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3658
    }
3659
    else if (t < 1000000.0) {
×
3660
        /* 100 - 1'000'000 ms */
3661
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3662
    }
3663
    else {
3664
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3665
    }
3666

3667
    return (buf);
1,083✔
3668
}
3669

3670
/************************************************************
3671

3672
  Function: addr_cmp
3673

3674
*************************************************************/
3675
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
507✔
3676
{
3677
    if (a->sa_family != b->sa_family) {
507✔
3678
        return a->sa_family - b->sa_family;
×
3679
    }
3680
    else {
3681
        if (a->sa_family == AF_INET) {
507✔
3682
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
443✔
3683
        }
3684
        else if (a->sa_family == AF_INET6) {
64✔
3685
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
3686
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
3687
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3688
        }
3689
    }
3690

3691
    return 0;
×
3692
}
143✔
3693

3694
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
913✔
3695
{
3696
    struct event *event = &h->event_storage_ping[index % event_storage_count];
913✔
3697
    event->host = h;
913✔
3698
    event->ping_index = index;
913✔
3699
    event->ev_time = ev_time;
913✔
3700
    ev_enqueue(&event_queue_ping, event);
913✔
3701

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

3706
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
898✔
3707
{
3708
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
898✔
3709
    event->host = h;
898✔
3710
    event->ping_index = index;
898✔
3711
    event->ev_time = ev_time;
898✔
3712
    ev_enqueue(&event_queue_timeout, event);
898✔
3713

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

3718
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
785✔
3719
{
3720
    return &h->event_storage_timeout[index % event_storage_count];
785✔
3721
}
3722

3723
/************************************************************
3724

3725
  Function: ev_enqueue
3726

3727
  Enqueue an event
3728

3729
  The queue is sorted by event->ev_time, so that queue->first always points to
3730
  the earliest event.
3731

3732
  We start scanning the queue from the tail, because we assume
3733
  that new events mostly get inserted with a event time higher
3734
  than the others.
3735

3736
*************************************************************/
3737
void ev_enqueue(struct event_queue *queue, struct event *event)
1,811✔
3738
{
3739
    struct event *i;
3740
    struct event *i_prev;
3741

3742
    /* Empty list */
3743
    if (queue->last == NULL) {
1,811✔
3744
        event->ev_next = NULL;
1,415✔
3745
        event->ev_prev = NULL;
1,415✔
3746
        queue->first = event;
1,415✔
3747
        queue->last = event;
1,415✔
3748
        return;
1,415✔
3749
    }
3750

3751
    /* Insert on tail? */
3752
    if (event->ev_time - queue->last->ev_time >= 0) {
396✔
3753
        event->ev_next = NULL;
396✔
3754
        event->ev_prev = queue->last;
396✔
3755
        queue->last->ev_next = event;
396✔
3756
        queue->last = event;
396✔
3757
        return;
396✔
3758
    }
3759

3760
    /* Find insertion point */
3761
    i = queue->last;
×
3762
    while (1) {
3763
        i_prev = i->ev_prev;
×
3764
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3765
            event->ev_prev = i_prev;
×
3766
            event->ev_next = i;
×
3767
            i->ev_prev = event;
×
3768
            if (i_prev != NULL) {
×
3769
                i_prev->ev_next = event;
×
3770
            }
3771
            else {
3772
                queue->first = event;
×
3773
            }
3774
            return;
×
3775
        }
3776
        i = i_prev;
×
3777
    }
3778
}
533✔
3779

3780
/************************************************************
3781

3782
  Function: ev_dequeue
3783

3784
*************************************************************/
3785
struct event *ev_dequeue(struct event_queue *queue)
1,006✔
3786
{
3787
    struct event *dequeued;
3788

3789
    if (queue->first == NULL) {
1,006✔
3790
        return NULL;
×
3791
    }
3792
    dequeued = queue->first;
1,006✔
3793
    ev_remove(queue, dequeued);
1,006✔
3794

3795
    return dequeued;
1,006✔
3796
}
288✔
3797

3798
/************************************************************
3799

3800
  Function: ev_remove
3801

3802
*************************************************************/
3803
void ev_remove(struct event_queue *queue, struct event *event)
1,791✔
3804
{
3805
    if (queue->first == event) {
1,791✔
3806
        queue->first = event->ev_next;
1,782✔
3807
    }
524✔
3808
    if (queue->last == event) {
1,791✔
3809
        queue->last = event->ev_prev;
1,416✔
3810
    }
410✔
3811
    if (event->ev_prev) {
1,791✔
3812
        event->ev_prev->ev_next = event->ev_next;
9✔
3813
    }
3✔
3814
    if (event->ev_next) {
1,791✔
3815
        event->ev_next->ev_prev = event->ev_prev;
375✔
3816
    }
117✔
3817
    event->ev_prev = NULL;
1,791✔
3818
    event->ev_next = NULL;
1,791✔
3819
}
1,791✔
3820

3821
/************************************************************
3822

3823
  Function: print_human_readable_time from current_time_ns
3824

3825
*************************************************************/
3826
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
45✔
3827
{
3828
    char time_buffer[100];
3829
    time_t current_time_s;
3830
    struct tm *local_time;
3831

3832
    current_time_s = current_time_ns / 1000000000;
45✔
3833
    local_time = localtime(&current_time_s);
45✔
3834
    switch(timestamp_format) {
45✔
3835
        case 1:
6✔
3836
            // timestamp-format ctime
3837
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
9✔
3838
            if (json_flag)
9✔
3839
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3840
            else
3841
                printf("[%s] ", time_buffer);
6✔
3842
            break;
9✔
3843
        case 2:
6✔
3844
            // timestamp-format iso
3845
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
9✔
3846
            if (json_flag)
9✔
3847
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3848
            else
3849
                printf("[%s] ", time_buffer);
6✔
3850
            break;
9✔
3851
        case 3:
6✔
3852
            // timestamp-format rfc3339
3853
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
9✔
3854
            if (json_flag)
9✔
3855
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
3856
            else
3857
                printf("[%s] ", time_buffer);
6✔
3858
            break;
9✔
3859
        default:
12✔
3860
            if (json_flag)
18✔
3861
                printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9);
6✔
3862
            else
3863
                printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
3864
    }
6✔
3865
}
45✔
3866

3867
/************************************************************
3868

3869
  Function: ms_since_midnight_utc
3870

3871
*************************************************************
3872

3873
  Input: int64_t: current UTC time in ns
3874

3875
  Output: uint32_t: current time in ms since midnight UTC
3876

3877
  Description:
3878

3879
  Return ICMP Timestamp value corresponding to the given time value.
3880
  The given time value must be in UTC.
3881

3882
*************************************************************/
3883
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
3884
{
3885
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
3886
}
3887

3888
/************************************************************
3889

3890
  Function: usage
3891

3892
*************************************************************
3893

3894
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3895

3896
  Description:
3897

3898
************************************************************/
3899

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

© 2025 Coveralls, Inc