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

schweikert / fping / 20690521614

04 Jan 2026 09:01AM UTC coverage: 84.703% (-3.4%) from 88.065%
20690521614

Pull #451

github

web-flow
Merge c0134984f into f689c314d
Pull Request #451: New option --traceroute to send a traceroute with fping

18 of 91 new or added lines in 1 file covered. (19.78%)

2 existing lines in 1 file now uncovered.

1628 of 1922 relevant lines covered (84.7%)

364.21 hits per line

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

84.55
/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
/* Traceroute */
172
#define TRACEROUTE_DEFAULT_MAX_TTL 30
173
#define TRACEROUTE_DONE_TTL 100
174

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

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

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

228
#define ICMP_UNREACH_MAXTYPE 15
229

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

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

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

270
typedef struct ip_header_result {
271
    int tos;
272
    int ttl;
273
    uint32_t otime_ms;
274
    uint32_t rtime_ms;
275
    uint32_t ttime_ms;
276
} IP_HEADER_RESULT;
277

278
IP_HEADER_RESULT default_ip_header_result() {
3,122✔
279
    return (IP_HEADER_RESULT){-1, -1, 0x80000000U, 0x80000000U, 0x80000000U};
3,122✔
280
}
281

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

306
    /* to avoid allocating two struct events each time that we send a ping, we
307
     * preallocate here two struct events for each ping that we might send for
308
     * this host. */
309
    struct event *event_storage_ping;
310
    struct event *event_storage_timeout;
311
} HOST_ENTRY;
312

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

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

330
#define EV_TYPE_PING 1
331
#define EV_TYPE_TIMEOUT 2
332

333
struct event {
334
    struct event *ev_prev;
335
    struct event *ev_next;
336
    int64_t ev_time;
337
    struct host_entry *host;
338
    int ping_index;
339
};
340

341
struct event_queue {
342
    struct event *first;
343
    struct event *last;
344
};
345

346
/*** globals ***/
347

348
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
349

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

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

374
volatile sig_atomic_t status_snapshot = 0;
375
volatile sig_atomic_t finish_requested = 0;
376

377
unsigned int debugging = 0;
378

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

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

413
struct timespec current_time; /* current time (pseudo) */
414
int64_t current_time_ns;
415
int64_t start_time;
416
int64_t end_time;
417
int64_t last_send_time; /* time last ping was sent */
418
int64_t next_report_time; /* time next -Q report is expected */
419

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

442
unsigned int fwmark = 0;
443

444
char *filename = NULL; /* file containing hosts to ping */
445

446
/*** forward declarations ***/
447

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

496
/************************************************************
497

498
  Function: p_setsockopt
499

500
*************************************************************
501

502
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
503

504
  Description:
505

506
  Elevates privileges to p_uid when required, calls
507
  setsockopt, and drops privileges back.
508

509
************************************************************/
510

511
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
22✔
512
    const void *optval, socklen_t optlen)
513
{
514
    const uid_t saved_uid = geteuid();
22✔
515
    int res;
516

517
    if (p_uid != saved_uid && seteuid(p_uid)) {
22✔
518
        perror("cannot elevate privileges for setsockopt");
×
519
    }
520

521
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
522

523
    if (p_uid != saved_uid && seteuid(saved_uid)) {
22✔
524
        perror("fatal error: could not drop privileges after setsockopt");
×
525
        /* continuing would be a security hole */
526
        exit(4);
×
527
    }
528

529
    return res;
22✔
530
}
531

532
unsigned long strtoul_strict(const char *arg, int base)
454✔
533
{
534
    char *endptr;
535
    unsigned long val;
536

537
    while (isspace(*arg))
480✔
538
        arg++;
26✔
539

540
    if (arg[0] == '-')
454✔
541
        usage(1);
52✔
542

543
    errno = 0;
418✔
544
    val = strtoul(arg, &endptr, base);
418✔
545
    if (errno != 0 || arg == endptr || *endptr != '\0')
418✔
546
        usage(1);
100✔
547

548
    return val;
344✔
549
}
550

551
double strtod_strict(const char *arg)
290✔
552
{
553
    char *endptr;
554
    double val;
555

556
    errno = 0;
290✔
557
    val = strtod(arg, &endptr);
290✔
558
    if (errno != 0 || arg == endptr || *endptr != '\0' || val < 0)
290✔
559
        usage(1);
102✔
560

561
    return val;
222✔
562
}
563

564
/************************************************************
565

566
  Function: main
567

568
*************************************************************
569

570
  Inputs:  int argc, char** argv
571

572
  Description:
573

574
  Main program entry point
575

576
************************************************************/
577

578
int main(int argc, char **argv)
968✔
579
{
580
/* Debug: CPU Performance */
581
#if defined(DEBUG) || defined(_DEBUG)
582
    clock_t perf_cpu_start, perf_cpu_end;
583
    double perf_cpu_time_used;
584
    perf_cpu_start = clock();
585
#endif /* DEBUG || _DEBUG */
586

587
    int c;
588
    char *endptr;
589
    const uid_t suid = geteuid();
968✔
590
    int tos = 0;
968✔
591
    struct optparse optparse_state;
592
#ifdef USE_SIGACTION
593
    struct sigaction act;
594
#endif
595

596
    /* pre-parse -h/--help, so that we also can output help information
597
     * without trying to open the socket, which might fail */
598
    prog = argv[0];
968✔
599
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
968✔
600
        usage(0);
3✔
601
    }
1✔
602

603
    socket4 = open_ping_socket_ipv4(&socktype4);
966✔
604
#ifdef __linux__
605
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
606
     * structure is missing in the message.
607
     */
608
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
638✔
609
#endif
610

611
#ifdef IPV6
612
    socket6 = open_ping_socket_ipv6(&socktype6);
966✔
613
    /* if called (sym-linked) via 'fping6', imply '-6'
614
     * for backward compatibility */
615
    if (strstr(prog, "fping6")) {
966✔
616
        hints_ai_family = AF_INET6;
2✔
617
    }
618
#endif
619

620
    memset(&src_addr, 0, sizeof(src_addr));
966✔
621
#ifdef IPV6
622
    memset(&src_addr6, 0, sizeof(src_addr6));
966✔
623
#endif
624

625
    if (!suid && suid != getuid()) {
966✔
626
        /* *temporarily* drop privileges */
627
        if (seteuid(getuid()) == -1)
865✔
628
            perror("cannot setuid");
×
629
    }
239✔
630

631
    optparse_init(&optparse_state, argv);
966✔
632
    ident4 = ident6 = htons(getpid() & 0xFFFF);
966✔
633
    verbose_flag = 1;
966✔
634
    backoff_flag = 1;
966✔
635
    opterr = 1;
966✔
636

637
    /* get command line options */
638

639
    struct optparse_long longopts[] = {
966✔
640
        { "ipv4", '4', OPTPARSE_NONE },
641
        { "ipv6", '6', OPTPARSE_NONE },
642
        { "alive", 'a', OPTPARSE_NONE },
643
        { "addr", 'A', OPTPARSE_NONE },
644
        { "size", 'b', OPTPARSE_REQUIRED },
645
        { "backoff", 'B', OPTPARSE_REQUIRED },
646
        { "count", 'c', OPTPARSE_REQUIRED },
647
        { "vcount", 'C', OPTPARSE_REQUIRED },
648
        { "rdns", 'd', OPTPARSE_NONE },
649
        { "timestamp", 'D', OPTPARSE_NONE },
650
        { "timestamp-format", '0', OPTPARSE_REQUIRED },
651
        { "traceroute", '0', OPTPARSE_NONE },
652
        { "elapsed", 'e', OPTPARSE_NONE },
653
        { "file", 'f', OPTPARSE_REQUIRED },
654
        { "generate", 'g', OPTPARSE_NONE },
655
        { "help", 'h', OPTPARSE_NONE },
656
        { "ttl", 'H', OPTPARSE_REQUIRED },
657
        { "interval", 'i', OPTPARSE_REQUIRED },
658
        { "iface", 'I', OPTPARSE_REQUIRED },
659
        { "json", 'J', OPTPARSE_NONE },
660
        { "icmp-timestamp", '0', OPTPARSE_NONE },
661
#ifdef SO_MARK
662
        { "fwmark", 'k', OPTPARSE_REQUIRED },
663
#endif
664
        { "loop", 'l', OPTPARSE_NONE },
665
        { "all", 'm', OPTPARSE_NONE },
666
        { "dontfrag", 'M', OPTPARSE_NONE },
667
        { "name", 'n', OPTPARSE_NONE },
668
        { "netdata", 'N', OPTPARSE_NONE },
669
        { "outage", 'o', OPTPARSE_NONE },
670
        { "tos", 'O', OPTPARSE_REQUIRED },
671
        { "period", 'p', OPTPARSE_REQUIRED },
672
        { "quiet", 'q', OPTPARSE_NONE },
673
        { "squiet", 'Q', OPTPARSE_REQUIRED },
674
        { "retry", 'r', OPTPARSE_REQUIRED },
675
        { "random", 'R', OPTPARSE_NONE },
676
        { "stats", 's', OPTPARSE_NONE },
677
        { "src", 'S', OPTPARSE_REQUIRED },
678
        { "timeout", 't', OPTPARSE_REQUIRED },
679
        { NULL, 'T', OPTPARSE_REQUIRED },
680
        { "unreach", 'u', OPTPARSE_NONE },
681
        { "version", 'v', OPTPARSE_NONE },
682
        { "reachable", 'x', OPTPARSE_REQUIRED },
683
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
684
        { "check-source", '0', OPTPARSE_NONE },
685
        { "print-tos", '0', OPTPARSE_NONE },
686
        { "print-ttl", '0', OPTPARSE_NONE },
687
        { "seqmap-timeout", '0', OPTPARSE_REQUIRED },
688
#if defined(DEBUG) || defined(_DEBUG)
689
        { NULL, 'z', OPTPARSE_REQUIRED },
690
#endif
691
        { 0, 0, 0 }
692
    };
693

694
    double opt_val_double;
695
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
2,193✔
696
        switch (c) {
1,448✔
697
        case '0':
116✔
698
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
153✔
699
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
24✔
700
                  timestamp_format_flag = 1;
6✔
701
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
20✔
702
                  timestamp_format_flag = 2;
6✔
703
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
14✔
704
                  timestamp_format_flag = 3;
6✔
705
                }else{
2✔
706
                  usage(1);
6✔
707
                }
708
            } else if (strstr(optparse_state.optlongname, "traceroute") != NULL) {
137✔
NEW
709
                traceroute_flag = 1;
×
710
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
129✔
711
                check_source_flag = 1;
9✔
712
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
121✔
713
#ifdef IPV6
714
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
29✔
715
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
716
                    exit(1);
2✔
717
                }
718
                hints_ai_family = AF_INET;
27✔
719
#endif
720
                icmp_request_typ = 13;
27✔
721
                ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
27✔
722
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
94✔
723
                print_tos_flag = 1;
35✔
724
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
35✔
725
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTOS, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
726
                        perror("setsockopt IP_RECVTOS");
×
727
                    }
728
                }
729
#if defined(IPV6) && defined(IPV6_RECVTCLASS)
730
                if (socket6 >= 0) {
35✔
731
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVTCLASS, &sock_opt_on, sizeof(sock_opt_on))) {
35✔
732
                        perror("setsockopt IPV6_RECVTCLASS");
×
733
                    }
734
                }
9✔
735
#endif
736
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
65✔
737
                print_ttl_flag = 1;
29✔
738
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
29✔
739
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTTL, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
740
                        perror("setsockopt IP_RECVTTL");
×
741
                    }
742
                }
743
#if defined(IPV6) && defined(IPV6_RECVHOPLIMIT)
744
                if (socket6 >= 0) {
29✔
745
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &sock_opt_on, sizeof(sock_opt_on))) {
29✔
746
                        perror("setsockopt IPV6_RECVHOPLIMIT");
×
747
                    }
748
                }
7✔
749
#endif
750
            } else if (strstr(optparse_state.optlongname, "seqmap-timeout") != NULL) {
34✔
751
                seqmap_timeout = strtod_strict(optparse_state.optarg) * 1000000;
27✔
752
            } else {
9✔
753
                usage(1);
×
754
            }
755
            break;
133✔
756
        case '4':
32✔
757
#ifdef IPV6
758
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
759
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
760
                exit(1);
3✔
761
            }
762
            hints_ai_family = AF_INET;
40✔
763
#endif
764
            break;
40✔
765
        case '6':
28✔
766
#ifdef IPV6
767
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
31✔
768
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
769
                exit(1);
5✔
770
            }
771
            hints_ai_family = AF_INET6;
26✔
772
#else
773
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
774
            exit(1);
775
#endif
776
            break;
26✔
777
        case 'M':
2✔
778
#ifdef IP_MTU_DISCOVER
779
            if (socket4 >= 0) {
2✔
780
                int val = IP_PMTUDISC_DO;
2✔
781
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
782
                    perror("setsockopt IP_MTU_DISCOVER");
783
                }
784
            }
785
#ifdef IPV6
786
            if (socket6 >= 0) {
2✔
787
                int val = IPV6_PMTUDISC_DO;
2✔
788
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
789
                    perror("setsockopt IPV6_MTU_DISCOVER");
790
                }
791
            }
792
#endif
793
#else
794
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
795
            exit(1);
796
#endif
797
            break;
2✔
798

799
        case 't':
56✔
800
            timeout = strtod_strict(optparse_state.optarg) * 1000000;
66✔
801
            timeout_flag = 1;
52✔
802
            break;
52✔
803

804
        case 'r':
44✔
805
            retry = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
51✔
806
            break;
41✔
807

808
        case 'i':
38✔
809
            interval = strtod_strict(optparse_state.optarg) * 1000000;
48✔
810
            break;
34✔
811

812
        case 'p':
90✔
813
            perhost_interval = strtod_strict(optparse_state.optarg) * 1000000;
123✔
814
            break;
109✔
815

816
        case 'c':
102✔
817
            count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
139✔
818
            if (!count)
129✔
819
                usage(1);
3✔
820

821
            count_flag = 1;
127✔
822
            break;
127✔
823

824
        case 'C':
42✔
825
            count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
60✔
826
            if (!count)
50✔
827
                usage(1);
3✔
828

829
            count_flag = 1;
48✔
830
            report_all_rtts_flag = 1;
48✔
831
            break;
48✔
832

833
        case 'b':
20✔
834
            ping_data_size = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
30✔
835
            size_flag = 1;
20✔
836
            break;
20✔
837

838
        case 'h':
2✔
839
            usage(0);
3✔
840
            break;
1✔
841

842
        case 'q':
26✔
843
            verbose_flag = 0;
38✔
844
            quiet_flag = 1;
38✔
845
            break;
38✔
846

847
        case 'Q':
34✔
848
            verbose_flag = 0;
43✔
849
            quiet_flag = 1;
43✔
850
            errno = 0;
43✔
851
            opt_val_double = strtod(optparse_state.optarg, &endptr);
43✔
852
            if (errno != 0 || optparse_state.optarg == endptr || (*endptr != '\0' && *endptr != ','))
43✔
853
                usage(1);
12✔
854
            if (opt_val_double < 0) {
35✔
855
                usage(1);
9✔
856
            }
3✔
857
            report_interval = opt_val_double * 1e9;
29✔
858

859
            /* recognize keyword(s) after number, ignore everything else */
860
            {
861
                char *comma = strchr(optparse_state.optarg, ',');
29✔
862
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
29✔
863
                    cumulative_stats_flag = 1;
4✔
864
                }
865
            }
866

867
            break;
29✔
868

869
        case 'e':
6✔
870
            elapsed_flag = 1;
8✔
871
            break;
8✔
872

873
        case 'm':
874
            multif_flag = 1;
×
875
            break;
×
876

877
        case 'N':
2✔
878
            netdata_flag = 1;
3✔
879
            break;
3✔
880

881
        case 'n':
4✔
882
            name_flag = 1;
6✔
883
            if (rdns_flag) {
6✔
884
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
885
                exit(1);
3✔
886
            }
887
            break;
3✔
888

889
        case 'd':
6✔
890
            rdns_flag = 1;
9✔
891
            if (name_flag) {
9✔
892
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
893
                exit(1);
3✔
894
            }
895
            break;
6✔
896

897
        case 'A':
2✔
898
            addr_flag = 1;
3✔
899
            break;
3✔
900

901
        case 'B':
18✔
902
            backoff = strtod_strict(optparse_state.optarg);
26✔
903
            break;
14✔
904

905
        case 's':
8✔
906
            stats_flag = 1;
12✔
907
            break;
12✔
908

909
        case 'D':
22✔
910
            timestamp_flag = 1;
33✔
911
            break;
33✔
912

913
        case 'R':
4✔
914
            random_data_flag = 1;
5✔
915
            break;
5✔
916

917
        case 'l':
12✔
918
            loop_flag = 1;
17✔
919
            backoff_flag = 0;
17✔
920
            break;
17✔
921

922
        case 'u':
8✔
923
            unreachable_flag = 1;
12✔
924
            break;
12✔
925

926
        case 'a':
18✔
927
            alive_flag = 1;
26✔
928
            break;
26✔
929

930
        case 'H':
16✔
931
            ttl = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
932
            if (!ttl)
14✔
933
                usage(1);
3✔
934
            break;
12✔
935

936
#if defined(DEBUG) || defined(_DEBUG)
937
        case 'z':
938
            debugging = (unsigned int)strtoul_strict(optparse_state.optarg, 0);
939
            break;
940
#endif /* DEBUG || _DEBUG */
941

942
        case 'v':
4✔
943
            printf("%s: Version %s\n", prog, VERSION);
6✔
944
            exit(0);
6✔
945

946
        case 'x':
16✔
947
            min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
948
            if (!min_reachable)
14✔
949
                usage(1);
3✔
950
            break;
12✔
951

952
        case 'X':
14✔
953
            min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
21✔
954
            if (!min_reachable)
11✔
955
                usage(1);
3✔
956
            fast_reachable = 1;
9✔
957
            break;
9✔
958

959
        case 'f':
18✔
960
            filename = optparse_state.optarg;
27✔
961
            break;
27✔
962
#ifdef SO_MARK
963
        case 'k':
18✔
964
            fwmark = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
18✔
965
            if (!fwmark)
8✔
966
                usage(1);
2✔
967

968
            if (socket4 >= 0)
6✔
969
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
970
                    perror("fwmark ipv4");
4✔
971

972
#ifdef IPV6
973
            if (socket6 >= 0)
6✔
974
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
975
                    perror("fwmark ipv6");
4✔
976
#endif
977

978
            break;
6✔
979
#endif
980

981
        case 'g':
106✔
982
            /* use IP list generation */
983
            /* mutually exclusive with using file input or command line targets */
984
            generate_flag = 1;
129✔
985
            break;
129✔
986

987
        case 'S':
10✔
988
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
989
                src_addr_set = 1;
6✔
990
                break;
6✔
991
            }
992
#ifdef IPV6
993
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
994
                src_addr6_set = 1;
4✔
995
                break;
4✔
996
            }
997
#endif
998
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
999
            exit(1);
3✔
1000

1001
        case 'I':
6✔
1002
#ifdef SO_BINDTODEVICE
1003
            if (socket4 >= 0) {
6✔
1004
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
1005
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
1006
                    exit(1);
2✔
1007
                }
1008
            }
1009
#ifdef IPV6
1010
            if (socket6 >= 0) {
4✔
1011
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
1012
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
×
1013
                    exit(1);
×
1014
                }
1015
            }
1016
#endif
1017
#else
1018
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
1019
            exit(3);
1020
            ;
1021
#endif
1022
            break;
4✔
1023

1024
        case 'J':
42✔
1025
            json_flag = 1;
62✔
1026
            break;
62✔
1027

1028
        case 'T':
2✔
1029
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
1030
            break;
3✔
1031

1032
        case 'O':
16✔
1033
            {
1034
                unsigned long val = strtoul_strict(optparse_state.optarg, 0);
24✔
1035
                if (val > 255)
16✔
1036
                    usage(1);
×
1037
                tos = (int)val;
16✔
1038
            }
1039
            if (socket4 >= 0) {
16✔
1040
                if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
12✔
1041
                    perror("setting type of service octet IP_TOS");
×
1042
                }
1043
            }
4✔
1044
#if defined(IPV6) && defined(IPV6_TCLASS)
1045
            if (socket6 >= 0) {
16✔
1046
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
12✔
1047
                    perror("setting type of service octet IPV6_TCLASS");
×
1048
                }
1049
            }
4✔
1050
#endif
1051
            break;
16✔
1052

1053
        case 'o':
6✔
1054
            outage_flag = 1;
7✔
1055
            break;
7✔
1056

1057
        case '?':
4✔
1058
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
1059
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
1060
            exit(1);
6✔
1061
            break;
1062
        }
1063
    }
1064

1065
    /* permanently drop privileges */
1066
    if (suid != getuid() && setuid(getuid())) {
569✔
1067
        perror("fatal: failed to permanently drop privileges");
×
1068
        /* continuing would be a security hole */
1069
        exit(4);
×
1070
    }
1071

1072
    /* validate various option settings */
1073

1074
#ifndef IPV6
1075
    if (socket4 < 0) {
1076
        crash_and_burn("can't create socket (must run as root?)");
1077
    }
1078
#else
1079
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
569✔
1080
        crash_and_burn("can't create socket (must run as root?)");
×
1081
    }
1082
#endif
1083

1084
    if (ttl > 255) {
567✔
1085
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
1086
        exit(1);
3✔
1087
    }
1088

1089
    if (unreachable_flag && alive_flag) {
564✔
1090
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
1091
        exit(1);
3✔
1092
    }
1093

1094
    if (count_flag && loop_flag) {
561✔
1095
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
1096
        exit(1);
3✔
1097
    }
1098

1099
    if (json_flag && !count_flag && !loop_flag) {
558✔
1100
        fprintf(stderr, "%s: option -J, --json requires -c, -C, or -l\n", prog);
3✔
1101
        exit(1);
3✔
1102
    }
1103

1104
    if (traceroute_flag && (count_flag || loop_flag || netdata_flag || quiet_flag || stats_flag || timestamp_flag || json_flag)) {
555✔
NEW
1105
        fprintf(stderr, "%s: can't combine --traceroute with -c, -C, -l, -N, -q, -Q, -s, -D or -J\n", prog);
×
NEW
1106
        exit(1);
×
1107
    }
1108

1109
    if (traceroute_flag) {
555✔
1110
        /* Preventing race conditions: Only one host allowed */
NEW
1111
        if (num_hosts > 1) {
×
NEW
1112
            fprintf(stderr, "%s: traceroute mode only supports one target at a time\n", prog);
×
NEW
1113
            exit(1);
×
1114
        }
1115
#ifdef __linux__
1116
        if (using_sock_dgram4) {
1117
            fprintf(stderr, "%s: traceroute mode requires raw sockets (run as root)\n", prog);
1118
            exit(1);
1119
        }
1120
#endif
NEW
1121
        if (ttl == 0) {
×
NEW
1122
            ttl = TRACEROUTE_DEFAULT_MAX_TTL; /* Default traceroute limit */
×
NEW
1123
        } else if (ttl > TRACEROUTE_DEFAULT_MAX_TTL) {
×
NEW
1124
            fprintf(stderr, "%s: traceroute ttl max is 30, clamping.\n", prog);
×
NEW
1125
            ttl = TRACEROUTE_DEFAULT_MAX_TTL;
×
1126
        }
1127
    }
1128

1129
    if (interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
555✔
1130
        fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
3✔
1131
        exit(1);
3✔
1132
    }
1133

1134
    if (perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
552✔
1135
        fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
3✔
1136
        exit(1);
3✔
1137
    }
1138

1139
    if (ping_data_size > MAX_PING_DATA) {
549✔
1140
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1141
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1142
        exit(1);
3✔
1143
    }
1144

1145
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
546✔
1146
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1147
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1148
        exit(1);
6✔
1149
    }
1150

1151
    if (icmp_request_typ == 13 && size_flag != 0) {
540✔
1152
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1153
        exit(1);
9✔
1154
    }
1155

1156
    if (count_flag) {
531✔
1157
        if (verbose_flag)
157✔
1158
            per_recv_flag = 1;
99✔
1159

1160
        alive_flag = unreachable_flag = verbose_flag = 0;
157✔
1161
    }
41✔
1162

1163
    if (loop_flag) {
531✔
1164
        if (!report_interval)
14✔
1165
            per_recv_flag = 1;
12✔
1166

1167
        alive_flag = unreachable_flag = verbose_flag = 0;
14✔
1168
    }
4✔
1169

1170
    if (alive_flag || unreachable_flag || min_reachable)
531✔
1171
        verbose_flag = 0;
37✔
1172

1173
    trials = (count > retry + 1) ? count : retry + 1;
547✔
1174

1175
    if (traceroute_flag)
547✔
NEW
1176
        trials = ttl; /* Ensure enough space for up to 'ttl' hops */
×
1177

1178
    /* auto-tune default timeout for count/loop modes
1179
     * see also github #32 */
1180
    if (loop_flag || count_flag) {
547✔
1181
        if (!timeout_flag) {
171✔
1182
            timeout = perhost_interval;
159✔
1183
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
159✔
1184
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1185
            }
1186
        }
43✔
1187
    }
45✔
1188

1189
#if defined(DEBUG) || defined(_DEBUG)
1190
    if (debugging & DBG_TRACE)
1191
        trace_flag = 1;
1192

1193
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1194
        randomly_lose_flag = 1;
1195
        lose_factor = 1; /* ie, 1/4 */
1196
    }
1197

1198
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1199
        randomly_lose_flag = 1;
1200
        lose_factor = 5; /* ie, 3/4 */
1201
    }
1202

1203
    if (debugging & DBG_PRINT_PER_SYSTEM)
1204
        print_per_system_flag = 1;
1205

1206
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1207
        report_all_rtts_flag = 1;
1208

1209
    if (trace_flag) {
1210
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1211
            prog, count, retry, interval / 1e6);
1212
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1213
            perhost_interval / 1e6, timeout / 1e6);
1214
        fprintf(stderr, "  seqmap_timeout: %.0f\n", seqmap_timeout / 1e6);
1215
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1216
            ping_data_size, trials);
1217

1218
        if (verbose_flag)
1219
            fprintf(stderr, "  verbose_flag set\n");
1220
        if (multif_flag)
1221
            fprintf(stderr, "  multif_flag set\n");
1222
        if (name_flag)
1223
            fprintf(stderr, "  name_flag set\n");
1224
        if (addr_flag)
1225
            fprintf(stderr, "  addr_flag set\n");
1226
        if (stats_flag)
1227
            fprintf(stderr, "  stats_flag set\n");
1228
        if (unreachable_flag)
1229
            fprintf(stderr, "  unreachable_flag set\n");
1230
        if (alive_flag)
1231
            fprintf(stderr, "  alive_flag set\n");
1232
        if (elapsed_flag)
1233
            fprintf(stderr, "  elapsed_flag set\n");
1234
        if (version_flag)
1235
            fprintf(stderr, "  version_flag set\n");
1236
        if (count_flag)
1237
            fprintf(stderr, "  count_flag set\n");
1238
        if (loop_flag)
1239
            fprintf(stderr, "  loop_flag set\n");
1240
        if (backoff_flag)
1241
            fprintf(stderr, "  backoff_flag set\n");
1242
        if (per_recv_flag)
1243
            fprintf(stderr, "  per_recv_flag set\n");
1244
        if (report_all_rtts_flag)
1245
            fprintf(stderr, "  report_all_rtts_flag set\n");
1246
        if (randomly_lose_flag)
1247
            fprintf(stderr, "  randomly_lose_flag set\n");
1248
        if (print_per_system_flag)
1249
            fprintf(stderr, "  print_per_system_flag set\n");
1250
        if (outage_flag)
1251
            fprintf(stderr, "  outage_flag set\n");
1252
        if (netdata_flag)
1253
            fprintf(stderr, "  netdata_flag set\n");
1254
        if (json_flag)
1255
            fprintf(stderr, "  json_flag set\n");
1256
    }
1257
#endif /* DEBUG || _DEBUG */
1258

1259
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1260
    if (ttl > 0) {
547✔
1261
        if (socket4 >= 0) {
3✔
1262
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1263
                perror("setting time to live");
×
1264
            }
1265
        }
1✔
1266
#ifdef IPV6
1267
        if (socket6 >= 0) {
3✔
1268
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1269
                perror("setting time to live");
×
1270
            }
1271
        }
1✔
1272
#endif
1273
    }
1✔
1274

1275
#if HAVE_SO_TIMESTAMPNS
1276
    {
1277
        int opt = 1;
402✔
1278
        if (socket4 >= 0) {
402✔
1279
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
402✔
1280
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1281
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1282
                }
1283
            }
1284
        }
1285
#ifdef IPV6
1286
        if (socket6 >= 0) {
402✔
1287
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
402✔
1288
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1289
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1290
                }
1291
            }
1292
        }
1293
#endif
1294
    }
1295
#endif
1296

1297
    update_current_time();
547✔
1298
    start_time = current_time_ns;
547✔
1299

1300
    /* handle host names supplied on command line or in a file */
1301
    /* if the generate_flag is on, then generate the IP list */
1302

1303
    argv = &argv[optparse_state.optind];
547✔
1304
    argc -= optparse_state.optind;
547✔
1305

1306
    /* calculate how many ping can be in-flight per host */
1307
    if (count_flag) {
547✔
1308
        event_storage_count = count;
172✔
1309
    }
56✔
1310
    else if (loop_flag || traceroute_flag) {
375✔
1311
        if (perhost_interval > timeout) {
14✔
1312
            event_storage_count = 1;
×
1313
        }
1314
        else {
1315
            event_storage_count = 1 + timeout / perhost_interval;
14✔
1316
        }
1317
    }
4✔
1318
    else {
1319
        event_storage_count = 1;
361✔
1320
    }
1321

1322
    /* file and generate are mutually exclusive */
1323
    /* file and command line are mutually exclusive */
1324
    /* generate requires command line parameters beyond the switches */
1325
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
547✔
1326
        usage(1);
19✔
1327

1328
    /* if no conditions are specified, then assume input from stdin */
1329
    if (!*argv && !filename && !generate_flag)
529✔
1330
        filename = "-";
69✔
1331

1332
    if (*argv && !generate_flag) {
529✔
1333
        while (*argv) {
681✔
1334
            add_name(*argv);
371✔
1335
            ++argv;
371✔
1336
        }
1337
    }
76✔
1338
    else if (filename) {
219✔
1339
        FILE *ping_file;
1340
        char line[MAX_TARGET_NAME_LEN + 1];
1341
        char host[MAX_TARGET_NAME_LEN + 1];
1342
        char scratch[MAX_TARGET_NAME_LEN + 1];
1343
        int skip, non_empty;
1344

1345
        if (strcmp(filename, "-") == 0)
96✔
1346
            ping_file = fdopen(0, "r");
72✔
1347
        else
1348
            ping_file = fopen(filename, "r");
24✔
1349

1350
        if (!ping_file)
96✔
1351
            errno_crash_and_burn("fopen");
3✔
1352

1353
        /*
1354
         * Read the first word of every non-comment line, skip everything else.
1355
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1356
         * character is a '#' are interpreted as comments and ignored.)
1357
        */
1358
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1359
            skip = non_empty = 0;
309✔
1360

1361
            /* skip over a prefix of the line where sscanf finds nothing */
1362
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1363
                continue;
99✔
1364
            }
1365

1366
            /* the first word of the line can indicate a comment line */
1367
            if (host[0] == '#') {
210✔
1368
                skip = 1; /* skip remainder of line */
42✔
1369
            } else {
14✔
1370
                non_empty = 1; /* we have something to add as a target name */
168✔
1371
                /*
1372
                 * We have found the start of a word.
1373
                 * This part of the line may contain all of the first word.
1374
                 */
1375
                if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
168✔
1376
                    char discard1[MAX_TARGET_NAME_LEN + 1];
1377
                    char discard2[MAX_TARGET_NAME_LEN + 1];
1378
                    if (sscanf(line, "%s%s", discard1, discard2) == 2) {
102✔
1379
                        skip = 1; /* a second word starts in this part */
6✔
1380
                    }
2✔
1381
                    if (isspace(line[sizeof(line) - 2])) {
102✔
1382
                        skip = 1; /* the first word ends in this part */
42✔
1383
                    }
14✔
1384
                }
34✔
1385
            }
1386
            /* read remainder of this input line */
1387
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
336✔
1388
                if (skip) {
150✔
1389
                    continue; /* skip rest of data in this input line */
102✔
1390
                }
1391
                if (isspace(line[0])) {
48✔
1392
                    skip = 1; /* first word ended in previous part */
6✔
1393
                    continue;
6✔
1394
                }
1395
                if ((sscanf(line, "%s", scratch) != 1) || (!*scratch)) {
42✔
1396
                    skip = 1; /* empty or blank part of line, skip the rest */
×
1397
                    continue;
×
1398
                }
1399
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
42✔
1400
                    fprintf(stderr, "%s: target name too long\n", prog);
24✔
1401
                    exit(1);
24✔
1402
                }
1403
                /* append remainder of word started in previous line part */
1404
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
18✔
1405
                /*
1406
                 * Since the "host" buffer is the same size as the "line"
1407
                 * buffer, a target name that fits into the "host" buffer
1408
                 * cannot use more than two consecutive line parts.
1409
                 * A target name that uses two consecutive line parts
1410
                 * and fits into the "host" buffer must end before the
1411
                 * end of the second "line" buffer.  Thus the rest of
1412
                 * the line can be skipped.
1413
                 */
1414
                skip = 1;
18✔
1415
            }
1416

1417
            if (non_empty)
186✔
1418
                add_name(host);
144✔
1419
        }
1420

1421
        fclose(ping_file);
70✔
1422
    }
24✔
1423
    else if (*argv && generate_flag) {
123✔
1424
        if (argc == 1) {
123✔
1425
            /* one target: we expect a cidr range (n.n.n.n/m) */
1426
            add_cidr(argv[0]);
68✔
1427
        }
14✔
1428
        else if (argc == 2) {
55✔
1429
            add_range(argv[0], argv[1]);
52✔
1430
        }
6✔
1431
        else {
1432
            usage(1);
3✔
1433
        }
1434
    }
21✔
1435
    else {
1436
        usage(1);
×
1437
    }
1438

1439
    if (!num_hosts) {
443✔
1440
        exit(num_noaddress ? 2 : 1);
36✔
1441
    }
1442

1443
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
407✔
1444
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1445
    }
2✔
1446
#ifdef IPV6
1447
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
405✔
1448
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1449
    }
1450
#endif
1451

1452
    /* allocate and initialize array to map host nr to host_entry */
1453
    {
1454
        struct event *cursor = event_queue_ping.first;
403✔
1455
        int i = 0;
403✔
1456
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
403✔
1457
        if (!table)
403✔
1458
            crash_and_burn("Can't malloc array of hosts");
×
1459
        /* initialize table of hosts. we know that we have ping events scheduled
1460
         * for each of them */
1461
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
993✔
1462
            table[i] = cursor->host;
590✔
1463
            cursor->host->i = i;
590✔
1464
            i++;
590✔
1465
        }
156✔
1466
    }
1467

1468
    init_ping_buffer_ipv4(ping_data_size);
403✔
1469
#ifdef IPV6
1470
    init_ping_buffer_ipv6(ping_data_size);
403✔
1471
#endif
1472

1473
#ifdef USE_SIGACTION
1474
    memset(&act, 0, sizeof(act));
403✔
1475
    act.sa_handler = signal_handler;
403✔
1476
    sigemptyset(&act.sa_mask);
403✔
1477
    sigaddset(&act.sa_mask, SIGINT);
403✔
1478
    sigaddset(&act.sa_mask, SIGQUIT);
403✔
1479
    act.sa_flags = SA_RESTART;
403✔
1480
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
403✔
1481
        crash_and_burn("failure to set signal handler");
30✔
1482
    }
30✔
1483
#else
1484
    signal(SIGINT, signal_handler);
1485
    signal(SIGQUIT, signal_handler);
1486
#endif
1487
    setlinebuf(stdout);
373✔
1488

1489
    // Last time we updated current_time_ns was before adding the hosts and
1490
    // possibly doing DNS resolution, which means that it isn't accurate
1491
    // anymore.
1492
    update_current_time();
373✔
1493
    if (report_interval) {
373✔
1494
        next_report_time = current_time_ns + report_interval;
22✔
1495
    }
2✔
1496

1497
    last_send_time = 0;
373✔
1498

1499
    seqmap_init(seqmap_timeout);
373✔
1500

1501
    /* Traceroute header output */
1502
    if (traceroute_flag) {
373✔
1503
        int i;
NEW
1504
        for (i = 0; i < num_hosts; i++) {
×
NEW
1505
            HOST_ENTRY *h = table[i];
×
1506
            char ip_str[INET6_ADDRSTRLEN];
NEW
1507
            int total_len = ping_data_size + SIZE_ICMP_HDR;
×
1508

1509
            /* Resolve IP string and calculate header length */
NEW
1510
            getnameinfo((struct sockaddr *)&h->saddr, h->saddr_len, ip_str, sizeof(ip_str), NULL, 0, NI_NUMERICHOST);
×
1511

NEW
1512
            if (h->saddr.ss_family == AF_INET6)
×
NEW
1513
                total_len += 40; /* IPv6 Header fix 40 bytes */
×
1514
            else
NEW
1515
                total_len += SIZE_IP_HDR; /* IPv4 Header min 20 bytes */
×
1516
            
NEW
1517
            printf("fping traceroute to %s (%s), %d hops max, %d byte packets\n", h->name, ip_str, (int)ttl, total_len);
×
1518
        }
1519
    }
1520

1521
    /* main loop */
1522
    main_loop();
373✔
1523

1524
/* Debug: CPU Performance */
1525
#if defined(DEBUG) || defined(_DEBUG)
1526
    perf_cpu_end = clock();
1527
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1528
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1529
#endif /* DEBUG || _DEBUG */
1530

1531
    finish();
373✔
1532

1533
    return 0;
81✔
1534
}
1535

1536
static inline int64_t timespec_ns(struct timespec *a)
6,738✔
1537
{
1538
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
6,738✔
1539
}
1540

1541
#if HAVE_SO_TIMESTAMPNS
1542
/* convert a struct timeval to nanoseconds */
1543
static inline int64_t timeval_ns(struct timeval *a)
1544
{
1545
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1546
}
1547
#endif /* HAVE_SO_TIMESTAMPNS */
1548

1549
void add_cidr(char *addr)
63✔
1550
{
1551
    char *addr_end;
1552
    char *mask_str;
1553
    unsigned long mask;
1554
    int ret;
1555
    struct addrinfo addr_hints;
1556
    struct addrinfo *addr_res;
1557
    unsigned long net_addr;
1558
#ifdef IPV6
1559
    uint64_t net_upper, net_lower;
1560
    char *scope_str;
1561
#endif /* IPV6 */
1562

1563
    /* Split address from mask */
1564
    addr_end = strrchr(addr, '/');
63✔
1565
    if (addr_end == NULL) {
63✔
1566
        usage(1);
3✔
1567
    }
1✔
1568
    mask_str = addr_end + 1;
61✔
1569

1570
#ifdef IPV6
1571
    /* IPv6 addresses can have a scope */
1572
    scope_str = strchr(addr, '%');
61✔
1573
    if (scope_str && mask_str < scope_str) {
61✔
1574
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1575
        exit(1);
2✔
1576
    }
1577
#endif /*IPV6 */
1578

1579
    *addr_end = '\0';
59✔
1580
    mask = strtoul_strict(mask_str, 10);
59✔
1581

1582
    /* parse address */
1583
    memset(&addr_hints, 0, sizeof(struct addrinfo));
53✔
1584
    addr_hints.ai_family = hints_ai_family;
53✔
1585
    addr_hints.ai_flags = AI_NUMERICHOST;
53✔
1586
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
53✔
1587
    if (ret) {
53✔
1588
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1589
        exit(1);
7✔
1590
    }
1591
    if (addr_res->ai_family == AF_INET) {
46✔
1592
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
24✔
1593
        freeaddrinfo(addr_res);
24✔
1594
        add_cidr_ipv4(net_addr, mask);
24✔
1595
#ifdef IPV6
1596
    } else if (addr_res->ai_family == AF_INET6) {
30✔
1597
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1598
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1599
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1600
        freeaddrinfo(addr_res);
22✔
1601
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1602
#endif /* IPV6 */
1603
    } else {
1604
        freeaddrinfo(addr_res);
×
1605
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1606
        exit(1);
×
1607
    }
1608
}
30✔
1609

1610
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1611
{
1612
    unsigned long bitmask;
1613
    unsigned long net_last;
1614

1615
    /* check mask */
1616
    if (mask < 1 || mask > 32) {
24✔
1617
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1618
        exit(1);
6✔
1619
    }
1620

1621
    /* convert mask integer from 1 to 32 to a bitmask */
1622
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1623

1624
    /* calculate network range */
1625
    net_addr &= bitmask;
18✔
1626
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1627

1628
    /* exclude network and broadcast address for regular prefixes */
1629
    if (mask < 31) {
18✔
1630
        net_last--;
12✔
1631
        net_addr++;
12✔
1632
    }
4✔
1633

1634
    /* add all hosts in that network (net_addr and net_last inclusive) */
1635
    add_addr_range_ipv4(net_addr, net_last);
18✔
1636
}
16✔
1637

1638
#ifdef IPV6
1639
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1640
{
1641
    uint64_t bitmask_lower;
1642
    uint64_t last_lower;
1643

1644
    /* check mask -- 2^63 addresses should suffice for now */
1645
    if (mask < 65 || mask > 128) {
23✔
1646
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1647
        exit(1);
7✔
1648
    }
1649

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

1653
    /* calculate network range */
1654
    net_lower &= bitmask_lower;
16✔
1655
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1656

1657
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1658
}
12✔
1659
#endif /* IPV6 */
1660

1661
void add_range(char *start, char *end)
52✔
1662
{
1663
    struct addrinfo addr_hints;
1664
    struct addrinfo *addr_res;
1665
    unsigned long start_long;
1666
    unsigned long end_long;
1667
    int ret;
1668
#ifdef IPV6
1669
    uint64_t start_upper, start_lower;
1670
    uint64_t end_upper, end_lower;
1671
    char *start_scope_str, *end_scope_str;
1672

1673
    /*
1674
     * The compiler does not know that setting the address family hint to
1675
     * ensure that start and end are from the same address family also
1676
     * ensures that either start_long and end_long are initialized and used,
1677
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1678
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1679
     * supported to suppress compiler warnings.
1680
     */
1681
    start_long = -1;
52✔
1682
    end_long = 0;
52✔
1683
    start_upper = start_lower = -1;
52✔
1684
    end_upper = end_lower = 0;
52✔
1685
    start_scope_str = end_scope_str = NULL;
52✔
1686
#endif /* IPV6 */
1687

1688
    /* parse start address */
1689
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1690
    addr_hints.ai_family = hints_ai_family;
52✔
1691
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1692
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1693
    if (ret) {
52✔
1694
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1695
        exit(1);
9✔
1696
    }
1697
    /* start and end must be from the same address family */
1698
    hints_ai_family = addr_res->ai_family;
43✔
1699
    if (addr_res->ai_family == AF_INET) {
43✔
1700
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1701
        freeaddrinfo(addr_res);
17✔
1702
#ifdef IPV6
1703
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1704
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1705
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1706
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1707
        freeaddrinfo(addr_res);
26✔
1708
#endif /* IPV6 */
1709
    } else {
1710
        freeaddrinfo(addr_res);
×
1711
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1712
        exit(1);
×
1713
    }
1714

1715
#ifdef IPV6
1716
    /* IPv6 addresses can have a scope */
1717
    if (hints_ai_family == AF_INET6) {
43✔
1718
        start_scope_str = strchr(start, '%');
26✔
1719
        end_scope_str = strchr(end, '%');
26✔
1720
        if ((!start_scope_str && end_scope_str) ||
26✔
1721
            (start_scope_str && !end_scope_str) ||
24✔
1722
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1723
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1724
                exit(1);
6✔
1725
        }
1726
    }
1727
#endif
1728

1729
    /* parse end address */
1730
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1731
    addr_hints.ai_family = hints_ai_family;
37✔
1732
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1733
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1734
    if (ret) {
37✔
1735
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1736
        exit(1);
7✔
1737
    }
1738
    if (addr_res->ai_family == AF_INET) {
30✔
1739
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1740
        freeaddrinfo(addr_res);
12✔
1741
        add_addr_range_ipv4(start_long, end_long);
12✔
1742
#ifdef IPV6
1743
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1744
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1745
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1746
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1747
        freeaddrinfo(addr_res);
18✔
1748
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1749
#endif /* IPV6 */
1750
    } else {
1751
        freeaddrinfo(addr_res);
×
1752
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1753
        exit(1);
×
1754
    }
1755
}
24✔
1756

1757
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1758
{
1759
    /* check if generator limit is exceeded */
1760
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1761
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1762
        exit(1);
6✔
1763
    }
1764

1765
    /* generate */
1766
    for (; start_long <= end_long; start_long++) {
93✔
1767
        struct in_addr in_addr_tmp;
1768
        char buffer[20];
1769
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1770
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1771
        add_name(buffer);
69✔
1772
    }
23✔
1773
}
24✔
1774

1775
#ifdef IPV6
1776
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1777
{
1778
    int i;
1779
    uint64_t ret = 0;
134✔
1780
    for (i = 0; i < 8; i++) {
1,206✔
1781
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1782
    }
16✔
1783
    return ret;
134✔
1784
}
1785

1786
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1787
{
1788
    int i;
1789
    for (i = 0; i < 8; i++) {
720✔
1790
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1791
    }
1792
}
80✔
1793

1794
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1795
                         uint64_t end_upper, uint64_t end_lower,
1796
                         const char *scope_str)
1797
{
1798
    struct in6_addr in6_addr_tmp;
1799
    char buffer[100];
1800

1801
    /* prevent generating too many addresses */
1802
    if ((start_upper + 1 < end_upper) ||
34✔
1803
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1804
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1805
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1806
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1807
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1808
        exit(1);
8✔
1809
    }
1810

1811
    while ((start_upper < end_upper) ||
66✔
1812
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1813
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1814
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1815
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1816
        if (scope_str) {
40✔
1817
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1818
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1819
                exit(1);
×
1820
            }
1821
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1822
        }
1823
        add_name(buffer);
40✔
1824
        start_lower++;
40✔
1825
        if (start_lower == 0) {
40✔
1826
            start_upper++;
2✔
1827
        }
1828
    }
1829
}
26✔
1830
#endif /* IPv6 */
1831

1832
void main_loop()
388✔
1833
{
1834
    int64_t lt;
1835
    int64_t wait_time_ns;
1836
    struct event *event;
1837
    struct host_entry *h;
1838

1839
    while (event_queue_ping.first || event_queue_timeout.first) {
2,124✔
1840
        dbg_printf("%s", "# main_loop\n");
1841

1842
        /* timeout event ? */
1843
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,767✔
1844
            event = ev_dequeue(&event_queue_timeout);
101✔
1845
            h = event->host;
101✔
1846

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

1849
            stats_add(h, event->ping_index, 0, -1);
101✔
1850

1851
            if (traceroute_flag) {
101✔
NEW
1852
                printf("%s: hop %d no reply\n", h->host, h->trace_ttl);
×
NEW
1853
                h->trace_ttl++;
×
NEW
1854
                if (h->trace_ttl > (int)ttl) h->trace_ttl = (int)ttl;
×
1855
                /* Continue to the next hop, no retry for this hop */
NEW
1856
                continue;
×
1857
            }
1858

1859
            if (per_recv_flag) {
101✔
1860
                print_timeout(h, event->ping_index);
25✔
1861
            }
7✔
1862

1863
            /* do we need to send a retry? */
1864
            if (!loop_flag && !count_flag) {
101✔
1865
                if (h->num_sent < retry + 1) {
60✔
1866
                    if (backoff_flag) {
35✔
1867
                        h->timeout *= backoff;
35✔
1868
                    }
9✔
1869
                    send_ping(h, event->ping_index);
35✔
1870
                }
9✔
1871
            }
14✔
1872

1873
            /* note: we process first timeout events, because we might need to
1874
             * wait to process ping events, while we for sure never need to
1875
             * wait for timeout events.
1876
             */
1877
            continue;
101✔
1878
        }
1879

1880
        /* ping event ? */
1881
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,929✔
1882
            /* Make sure that we don't ping more than once every "interval" */
1883
            lt = current_time_ns - last_send_time;
1,268✔
1884
            if (lt < interval)
1,268✔
1885
                goto wait_for_reply;
267✔
1886

1887
            /* Dequeue the event */
1888
            event = ev_dequeue(&event_queue_ping);
1,001✔
1889
            h = event->host;
1,001✔
1890

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

1893
            /* Traceroute */
1894
            if (traceroute_flag) {
1,001✔
NEW
1895
                if (h->trace_ttl == TRACEROUTE_DONE_TTL) {
×
NEW
1896
                    continue;
×
1897
                }
1898

NEW
1899
                int ttl_set = h->trace_ttl;
×
NEW
1900
                if (ttl_set > (int)ttl) ttl_set = (int)ttl;
×
NEW
1901
                if (socket4 >= 0) {
×
NEW
1902
                    if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl_set, sizeof(ttl_set)))
×
NEW
1903
                        perror("setsockopt IP_TTL");
×
1904
                }
1905
#ifdef IPV6
NEW
1906
                if (socket6 >= 0) {
×
1907
                    /* Set hop limit for IPv6 */
NEW
1908
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl_set, sizeof(ttl_set))) {
×
NEW
1909
                        perror("setsockopt IPV6_UNICAST_HOPS");
×
1910
                    }
1911
                }
1912
#endif
1913
            }
1914

1915
            /* Send the ping */
1916
            send_ping(h, event->ping_index);
1,001✔
1917

1918
            /* Loop, count and traceroute mode: schedule next ping */
1919
            if (loop_flag || (count_flag && event->ping_index + 1 < count) || (traceroute_flag && h->trace_ttl < (int)ttl)) {
1,001✔
1920
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
440✔
1921
            }
116✔
1922
        }
263✔
1923

1924
    wait_for_reply:
735✔
1925

1926
        /* When is the next ping next event? */
1927
        wait_time_ns = -1;
1,666✔
1928
        if (event_queue_ping.first) {
1,666✔
1929
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,224✔
1930
            if (wait_time_ns < 0)
1,224✔
1931
                wait_time_ns = 0;
563✔
1932
            /* make sure that we wait enough, so that the inter-ping delay is
1933
             * bigger than 'interval' */
1934
            if (wait_time_ns < interval) {
1,224✔
1935
                lt = current_time_ns - last_send_time;
568✔
1936
                if (lt < interval) {
568✔
1937
                    wait_time_ns = interval - lt;
567✔
1938
                }
211✔
1939
            }
211✔
1940

1941
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1942
        }
334✔
1943

1944
        /* When is the next timeout event? */
1945
        if (event_queue_timeout.first) {
1,666✔
1946
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
1,051✔
1947
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
1,051✔
1948
                wait_time_ns = wait_time_timeout;
437✔
1949
                if (wait_time_ns < 0) {
437✔
1950
                    wait_time_ns = 0;
×
1951
                }
1952
            }
108✔
1953

1954
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1955
        }
280✔
1956

1957
        /* When is the next report due? */
1958
        if (report_interval && (loop_flag || count_flag)) {
1,666✔
1959
            int64_t wait_time_next_report = next_report_time - current_time_ns;
214✔
1960
            if (wait_time_next_report < wait_time_ns) {
214✔
1961
                wait_time_ns = wait_time_next_report;
50✔
1962
                if (wait_time_ns < 0) {
50✔
1963
                    wait_time_ns = 0;
×
1964
                }
1965
            }
2✔
1966

1967
            dbg_printf("next report  event in %0.f ms\n", wait_time_next_report / 1e6);
1968
        }
6✔
1969

1970
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1971
        if (wait_time_ns == -1) {
1,666✔
1972
            break;
14✔
1973
        }
1974

1975
        /* end of loop was requested by interrupt signal handler */
1976
        if (finish_requested) {
1,652✔
1977
            break;
17✔
1978
        }
1979

1980
        /* Receive replies */
1981
        /* (this is what sleeps during each loop iteration) */
1982
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1983
        if (wait_for_reply(wait_time_ns)) {
1,635✔
1984
            while (wait_for_reply(0))
1,487✔
1985
                ; /* process other replies in the queue */
1986
        }
251✔
1987

1988
        update_current_time();
1,635✔
1989

1990
        if (status_snapshot) {
1,635✔
1991
            status_snapshot = 0;
3✔
1992
            if (json_flag)
3✔
1993
                print_per_system_splits_json();
×
1994
            else
1995
                print_per_system_splits();
3✔
1996
        }
1✔
1997

1998
        /* Print report */
1999
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,635✔
2000
            if (netdata_flag) {
36✔
2001
                print_netdata();
3✔
2002
            }
1✔
2003
            else if (json_flag) {
33✔
2004
                print_per_system_splits_json();
3✔
2005
            }
1✔
2006
            else {
2007
                print_per_system_splits();
30✔
2008
            }
2009

2010
            while (current_time_ns >= next_report_time) {
72✔
2011
                next_report_time += report_interval;
36✔
2012
            }
2013
        }
2✔
2014
    }
2015
}
388✔
2016

2017
/************************************************************
2018

2019
  Function: signal_handler
2020

2021
*************************************************************
2022

2023
  Inputs:  int signum
2024

2025
  Description:
2026

2027
  SIGQUIT signal handler - set flag and return
2028
  SIGINT signal handler - set flag and return
2029

2030
************************************************************/
2031

2032
void signal_handler(int signum)
17✔
2033
{
2034
    switch (signum) {
17✔
2035
    case SIGINT:
10✔
2036
        finish_requested = 1;
14✔
2037
        break;
14✔
2038

2039
    case SIGQUIT:
2✔
2040
        status_snapshot = 1;
3✔
2041
        break;
3✔
2042
    }
2043
}
17✔
2044

2045
/************************************************************
2046

2047
  Function: update_current_time
2048

2049
*************************************************************/
2050

2051
void update_current_time()
5,502✔
2052
{
2053
    clock_gettime(CLOCKID, &current_time);
5,502✔
2054
    current_time_ns = timespec_ns(&current_time);
5,502✔
2055
}
5,502✔
2056

2057
/************************************************************
2058

2059
  Function: finish
2060

2061
*************************************************************
2062

2063
  Inputs:  void (none)
2064

2065
  Description:
2066

2067
  Main program clean up and exit point
2068

2069
************************************************************/
2070

2071
void finish()
388✔
2072
{
2073
    int i;
2074
    HOST_ENTRY *h;
2075

2076
    update_current_time();
388✔
2077
    end_time = current_time_ns;
388✔
2078

2079
    /* tot up unreachables */
2080
    for (i = 0; i < num_hosts; i++) {
978✔
2081
        h = table[i];
590✔
2082

2083
        if (!h->num_recv) {
590✔
2084
            num_unreachable++;
87✔
2085

2086
            if (verbose_flag || unreachable_flag) {
87✔
2087
                printf("%s", h->host);
47✔
2088

2089
                if (verbose_flag)
47✔
2090
                    printf(" is unreachable");
44✔
2091

2092
                printf("\n");
47✔
2093
            }
3✔
2094
        }
13✔
2095
    }
156✔
2096

2097
    if (count_flag || loop_flag) {
388✔
2098
        if (json_flag)
168✔
2099
            print_per_system_stats_json();
56✔
2100
        else
2101
            print_per_system_stats();
112✔
2102
    }
44✔
2103
#if defined(DEBUG) || defined(_DEBUG)
2104
    else if (print_per_system_flag) {
2105
        if (json_flag)
2106
            print_per_system_stats_json();
2107
        else
2108
            print_per_system_stats();
2109
    }
2110
#endif /* DEBUG || _DEBUG */
2111

2112
    if (stats_flag) {
388✔
2113
        if (json_flag)
12✔
2114
            print_global_stats_json();
3✔
2115
        else
2116
            print_global_stats();
9✔
2117
    }
4✔
2118

2119
    if (min_reachable) {
388✔
2120
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
2121
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
2122
            exit(0);
6✔
2123
        }
2124
        else {
2125
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
2126
            exit(1);
3✔
2127
        }
2128
    }
2129

2130
    if (num_noaddress)
379✔
2131
        exit(2);
3✔
2132
    else if (num_alive != num_hosts)
376✔
2133
        exit(1);
48✔
2134

2135
    exit(0);
328✔
2136
}
2137

2138
/************************************************************
2139

2140
  Function: print_recv
2141

2142
*************************************************************
2143

2144
  Inputs: HOST_ENTRY *h, int64_t recv_time, int result,
2145
          int this_count, int64_t this_reply, int avg
2146

2147
  Description:
2148

2149
************************************************************/
2150

2151
void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) {
279✔
2152
    if (json_flag) {
279✔
2153
        printf("{\"resp\": {");
35✔
2154

2155
        if (timestamp_flag)
35✔
2156
            print_timestamp_format(recv_time, timestamp_format_flag);
15✔
2157

2158
        printf("\"host\": \"%s\", ", h->host);
35✔
2159
        printf("\"seq\": %d, ", this_count);
35✔
2160
        printf("\"size\": %d, ", result);
35✔
2161
        printf("\"rtt\": %s", sprint_tm(this_reply));
35✔
2162
        return;
35✔
2163
    }
2164

2165
    /* Normal Output */
2166
    if (timestamp_flag)
244✔
2167
        print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2168

2169
    printf("%-*s : [%d], %d bytes, %s ms",
244✔
2170
        max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
76✔
2171

2172
    printf(" (%s avg, ", sprint_tm(avg));
244✔
2173

2174
    if (h->num_recv <= h->num_sent) {
244✔
2175
        printf("%d%% loss)",
244✔
2176
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
244✔
2177
    }
76✔
2178
    else {
2179
        printf("%d%% return)",
×
2180
            (h->num_recv_total * 100) / h->num_sent);
×
2181
    }
2182
}
87✔
2183

2184
/************************************************************
2185

2186
  Function: print_timeout
2187

2188
*************************************************************
2189

2190
  Inputs: HOST_ENTRY *h, int ping_index
2191

2192
  Description:
2193

2194
************************************************************/
2195

2196
void print_timeout(HOST_ENTRY *h, int ping_index) {
25✔
2197
    if (json_flag) {
25✔
2198
        printf("{\"timeout\": {");
6✔
2199
        if (timestamp_flag)
6✔
2200
            print_timestamp_format(current_time_ns, timestamp_format_flag);
×
2201

2202
        printf("\"host\": \"%s\", ", h->host);
6✔
2203
        printf("\"seq\": %d", ping_index);
6✔
2204
        printf("}}\n");
6✔
2205
        return;
6✔
2206
    }
2207

2208
    /* Normal Output */
2209
    if (timestamp_flag)
19✔
2210
        print_timestamp_format(current_time_ns, timestamp_format_flag);
×
2211

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

2215
    if (h->num_recv > 0) {
19✔
2216
        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
2217
    }
2218
    else {
2219
        printf(" (NaN avg, ");
19✔
2220
    }
2221

2222
    if (h->num_recv <= h->num_sent) {
19✔
2223
        printf("%d%% loss)",
19✔
2224
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
2225
    }
5✔
2226
    else {
2227
        printf("%d%% return)",
×
2228
            (h->num_recv_total * 100) / h->num_sent);
×
2229
    }
2230
    printf("\n");
19✔
2231
}
7✔
2232

2233
/************************************************************
2234

2235
  Function: print_recv_ext
2236

2237
*************************************************************
2238

2239
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2240
           int64_t recv_time, int64_t this_reply
2241

2242
  Description:
2243

2244
************************************************************/
2245

2246
void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { 
546✔
2247
    if (icmp_request_typ == 13) {
546✔
2248
        printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2249
            alive_flag ? "" : ",",
14✔
2250
            ip_header_res->otime_ms, ip_header_res->rtime_ms, ip_header_res->ttime_ms,
2251
            ms_since_midnight_utc(recv_time));
2252
    }
2253

2254
    if(print_tos_flag) {
546✔
2255
        if(ip_header_res->tos != -1) {
63✔
2256
            printf(" (TOS %d)", ip_header_res->tos);
63✔
2257
        }
17✔
2258
        else {
2259
            printf(" (TOS unknown)");
×
2260
        }
2261
    }
17✔
2262

2263
    if (print_ttl_flag) {
546✔
2264
        if(ip_header_res->ttl != -1) {
60✔
2265
            printf(" (TTL %d)", ip_header_res->ttl);
60✔
2266
        }
16✔
2267
        else {
2268
            printf(" (TTL unknown)");
×
2269
        }
2270
    }
16✔
2271

2272
    if (elapsed_flag && !per_recv_flag)
546✔
2273
        printf(" (%s ms)", sprint_tm(this_reply));
8✔
2274
    
2275
    printf("\n");
546✔
2276
}
546✔
2277

2278
/************************************************************
2279

2280
  Function: print_recv_ext_json
2281

2282
*************************************************************
2283

2284
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2285
           int64_t recv_time, int64_t this_reply
2286

2287
  Description:
2288

2289
************************************************************/
2290

2291
void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) {
35✔
2292
    if (icmp_request_typ == 13) {
35✔
2293
        printf(", \"timestamps\": {");
2✔
2294
        printf("\"originate\": %u, ", ip_header_res->otime_ms);
2✔
2295
        printf("\"receive\": %u, ", ip_header_res->rtime_ms);
2✔
2296
        printf("\"transmit\": %u, ", ip_header_res->ttime_ms);
2✔
2297
        printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
2298
    }
2299

2300
    if(print_tos_flag) {
35✔
2301
        if(ip_header_res->tos != -1) {
6✔
2302
            printf(", \"tos\": %d", ip_header_res->tos);
6✔
2303
        }
2✔
2304
        else {
2305
            printf(", \"tos\": -1");
×
2306
        }
2307
    }
2✔
2308

2309
    if (print_ttl_flag) {
35✔
2310
        if(ip_header_res->ttl != -1) {
6✔
2311
            printf(", \"ttl\": %d", ip_header_res->ttl);
6✔
2312
        }
2✔
2313
        else {
2314
            printf(", \"ttl\": -1");
×
2315
        }
2316
    }
2✔
2317

2318
    if (elapsed_flag && !per_recv_flag)
35✔
2319
        printf(" (%s ms)", sprint_tm(this_reply));
×
2320

2321
    printf("}}");
35✔
2322
    printf("\n");
35✔
2323
}
35✔
2324

2325
/************************************************************
2326

2327
  Function: print_per_system_stats
2328

2329
*************************************************************
2330

2331
  Inputs:  void (none)
2332

2333
  Description:
2334

2335

2336
************************************************************/
2337

2338
void print_per_system_stats(void)
112✔
2339
{
2340
    int i, j, avg, outage_ms;
2341
    HOST_ENTRY *h;
2342
    int64_t resp;
2343

2344
    if (verbose_flag || per_recv_flag)
112✔
2345
        fprintf(stderr, "\n");
76✔
2346

2347
    for (i = 0; i < num_hosts; i++) {
258✔
2348
        h = table[i];
146✔
2349
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
146✔
2350

2351
        if (report_all_rtts_flag) {
146✔
2352
            for (j = 0; j < h->num_sent; j++) {
248✔
2353
                if ((resp = h->resp_times[j]) >= 0)
204✔
2354
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2355
                else
2356
                    fprintf(stderr, " -");
8✔
2357
            }
64✔
2358

2359
            fprintf(stderr, "\n");
44✔
2360
        }
12✔
2361
        else {
2362
            if (h->num_recv <= h->num_sent) {
102✔
2363
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
126✔
2364
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
102✔
2365

2366
                if (outage_flag) {
102✔
2367
                    /* Time outage total */
2368
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
7✔
2369
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
7✔
2370
                }
1✔
2371
            }
24✔
2372
            else {
2373
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2374
                    h->num_sent, h->num_recv,
2375
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2376
            }
2377

2378
            if (h->num_recv) {
102✔
2379
                avg = h->total_time / h->num_recv;
93✔
2380
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
93✔
2381
                fprintf(stderr, "/%s", sprint_tm(avg));
93✔
2382
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
93✔
2383
            }
23✔
2384

2385
            fprintf(stderr, "\n");
102✔
2386
        }
2387
    }
36✔
2388
}
112✔
2389

2390
/************************************************************
2391

2392
  Function: print_per_system_stats_json
2393

2394
*************************************************************
2395

2396
  Inputs:  void (none)
2397

2398
  Description:
2399

2400

2401
************************************************************/
2402

2403
void print_per_system_stats_json(void)
56✔
2404
{
2405
    int i, j, avg, outage_ms;
2406
    HOST_ENTRY *h;
2407
    int64_t resp;
2408

2409
    for (i = 0; i < num_hosts; i++) {
118✔
2410
        h = table[i];
62✔
2411

2412
        if (report_all_rtts_flag)
62✔
2413
            fprintf(stdout, "{\"vSum\": {");
18✔
2414
        else
2415
            fprintf(stdout, "{\"summary\": {");
44✔
2416

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

2419
        if (report_all_rtts_flag) {
62✔
2420
            fprintf(stdout, "\"values\": [");
18✔
2421
            for (j = 0; j < h->num_sent; j++) {
42✔
2422
                if (j > 0)
24✔
2423
                  fprintf(stdout, ", ");
6✔
2424
                
2425
                if ((resp = h->resp_times[j]) >= 0)
24✔
2426
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2427
                else
2428
                    fprintf(stdout, "null");
3✔
2429
            }
8✔
2430

2431
            fprintf(stdout, "]}");
18✔
2432
        }
6✔
2433
        else {
2434
            if (h->num_recv <= h->num_sent) {
44✔
2435
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
44✔
2436
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
44✔
2437
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
44✔
2438

2439
                if (outage_flag) {
44✔
2440
                    /* Time outage total */
2441
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
2442
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2443
                }
2444
            }
14✔
2445
            else {
2446
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
2447
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
2448
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2449
            }
2450

2451
            if (h->num_recv) {
44✔
2452
                avg = h->total_time / h->num_recv;
41✔
2453
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
41✔
2454
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
41✔
2455
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
41✔
2456
            }
13✔
2457

2458
            fprintf(stdout, "}");
44✔
2459
        }
2460
        fprintf(stdout, "}\n");
62✔
2461
    }
20✔
2462
}
56✔
2463

2464
/************************************************************
2465

2466
  Function: print_netdata
2467

2468
*************************************************************
2469

2470
  Inputs:  void (none)
2471

2472
  Description:
2473

2474

2475
************************************************************/
2476

2477
void print_netdata(void)
3✔
2478
{
2479
    static int sent_charts = 0;
2480

2481
    int i;
2482
    int64_t avg;
2483
    HOST_ENTRY *h;
2484

2485
    for (i = 0; i < num_hosts; i++) {
6✔
2486
        h = table[i];
3✔
2487

2488
        if (!sent_charts) {
3✔
2489
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2490
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2491
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2492
        }
1✔
2493

2494
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2495
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2496
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2497
        printf("END\n");
3✔
2498

2499
        if (!sent_charts) {
3✔
2500
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2501
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2502
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2503
        }
1✔
2504

2505
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2506
        /*
2507
        if( h->num_recv_i <= h->num_sent_i )
2508
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2509
        else
2510
            printf("SET lost = 0\n");
2511
*/
2512

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

2516
        if (!sent_charts) {
3✔
2517
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2518
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2519
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2520
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2521
        }
1✔
2522

2523
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2524
        if (h->num_recv_i) {
3✔
2525
            avg = h->total_time_i / h->num_recv_i;
3✔
2526
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2527
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2528
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2529
        }
1✔
2530
        printf("END\n");
3✔
2531

2532
        stats_reset_interval(h);
3✔
2533
    }
1✔
2534

2535
    sent_charts = 1;
3✔
2536
}
3✔
2537

2538
/************************************************************
2539

2540
  Function: print_per_system_splits
2541

2542
*************************************************************
2543

2544
  Inputs:  void (none)
2545

2546
  Description:
2547

2548

2549
************************************************************/
2550

2551
void print_per_system_splits(void)
33✔
2552
{
2553
    int i, avg, outage_ms_i;
2554
    HOST_ENTRY *h;
2555
    struct tm *curr_tm;
2556

2557
    if (verbose_flag || per_recv_flag)
33✔
2558
        fprintf(stderr, "\n");
3✔
2559

2560
    update_current_time();
33✔
2561
    curr_tm = localtime((time_t *)&current_time.tv_sec);
33✔
2562
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
34✔
2563
        curr_tm->tm_min, curr_tm->tm_sec);
1✔
2564

2565
    for (i = 0; i < num_hosts; i++) {
66✔
2566
        h = table[i];
33✔
2567
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
33✔
2568

2569
        if (h->num_recv_i <= h->num_sent_i) {
33✔
2570
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
34✔
2571
                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);
33✔
2572

2573
            if (outage_flag) {
33✔
2574
                /* Time outage  */
2575
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
8✔
2576
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
8✔
2577
            }
2578
        }
1✔
2579
        else {
2580
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2581
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2582
        }
2583

2584
        if (h->num_recv_i) {
33✔
2585
            avg = h->total_time_i / h->num_recv_i;
25✔
2586
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
25✔
2587
            fprintf(stderr, "/%s", sprint_tm(avg));
25✔
2588
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
25✔
2589
        }
1✔
2590

2591
        fprintf(stderr, "\n");
33✔
2592
        if (!cumulative_stats_flag) {
33✔
2593
            stats_reset_interval(h);
25✔
2594
        }
1✔
2595
    }
1✔
2596
}
33✔
2597

2598
/************************************************************
2599

2600
  Function: print_per_system_splits_json
2601

2602
*************************************************************
2603

2604
  Inputs:  void (none)
2605

2606
  Description:
2607

2608

2609
************************************************************/
2610

2611
void print_per_system_splits_json(void)
3✔
2612
{
2613
    int i, avg, outage_ms_i;
2614
    HOST_ENTRY *h;
2615

2616
    update_current_time();
3✔
2617

2618
    for (i = 0; i < num_hosts; i++) {
6✔
2619
        h = table[i];
3✔
2620
        fprintf(stdout, "{\"intSum\": {");
3✔
2621
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2622
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2623

2624
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2625
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2626
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2627
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2628

2629
            if (outage_flag) {
3✔
2630
                /* Time outage  */
2631
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
2632
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2633
            }
2634
        }
1✔
2635
        else {
2636
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
2637
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
2638
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2639
        }
2640

2641
        if (h->num_recv_i) {
3✔
2642
            avg = h->total_time_i / h->num_recv_i;
3✔
2643
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2644
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2645
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2646
        }
1✔
2647

2648
        fprintf(stdout, "}}\n");
3✔
2649
        if (!cumulative_stats_flag) {
3✔
2650
            stats_reset_interval(h);
3✔
2651
        }
1✔
2652
    }
1✔
2653
}
3✔
2654

2655
/************************************************************
2656

2657
  Function: print_global_stats
2658

2659
*************************************************************
2660

2661
  Inputs:  void (none)
2662

2663
  Description:
2664

2665

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

2668
void print_global_stats(void)
9✔
2669
{
2670
    fprintf(stderr, "\n");
9✔
2671
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2672
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2673
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2674
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2675
    fprintf(stderr, "\n");
9✔
2676
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2677
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2678
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2679
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2680
    fprintf(stderr, "\n");
9✔
2681

2682
    if (total_replies == 0) {
9✔
2683
        min_reply = 0;
3✔
2684
        max_reply = 0;
3✔
2685
        total_replies = 1;
3✔
2686
        sum_replies = 0;
3✔
2687
    }
1✔
2688

2689
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2690
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2691
        sprint_tm(sum_replies / total_replies));
3✔
2692
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2693
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2694
        (end_time - start_time) / 1e9);
9✔
2695
    fprintf(stderr, "\n");
9✔
2696
}
9✔
2697

2698
/************************************************************
2699

2700
  Function: print_global_stats_json
2701

2702
*************************************************************
2703

2704
  Inputs:  void (none)
2705

2706
  Description:
2707

2708

2709
************************************************************/
2710

2711
void print_global_stats_json(void)
3✔
2712
{
2713
    fprintf(stdout, "{\"stats\": {");
3✔
2714
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2715
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2716
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2717
    fprintf(stdout, "\"unknownAddresses\": %d, ", num_noaddress);
3✔
2718
    fprintf(stdout, "\"timeouts\": %d, ", num_timeout);
3✔
2719
    fprintf(stdout, "\"icmpEchosSent\": %d, ", num_pingsent);
3✔
2720
    fprintf(stdout, "\"icmpEchoRepliesReceived\": %d, ", num_pingreceived);
3✔
2721
    fprintf(stdout, "\"otherIcmpReceived\": %d, ", num_othericmprcvd);
3✔
2722

2723
    if (total_replies == 0) {
3✔
2724
        min_reply = 0;
×
2725
        max_reply = 0;
×
2726
        total_replies = 1;
×
2727
        sum_replies = 0;
×
2728
    }
2729

2730
    fprintf(stdout, "\"rttMin\": %s, ", sprint_tm(min_reply));
3✔
2731
    fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2732
    fprintf(stdout, "\"rttMax\": %s, ", sprint_tm(max_reply));
3✔
2733
    fprintf(stdout, "\"elapsed\": %.3f", (end_time - start_time) / 1e9);
3✔
2734
    fprintf(stdout, "}}\n");
3✔
2735
}
3✔
2736

2737
/************************************************************
2738

2739
  Function: send_ping
2740

2741
*************************************************************
2742

2743
  Inputs:  int s, HOST_ENTRY *h
2744

2745
  Description:
2746

2747
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2748
  will be added on by the kernel.  The ID field is our UNIX process ID,
2749
  and the sequence number is an index into an array of outstanding
2750
  ping requests. The sequence number will later be used to quickly
2751
  figure out who the ping reply came from.
2752

2753
************************************************************/
2754

2755
int send_ping(HOST_ENTRY *h, int index)
1,036✔
2756
{
2757
    int n;
2758
    int myseq;
2759
    int ret = 1;
1,036✔
2760
    uint8_t proto = ICMP_ECHO;
1,036✔
2761

2762
    update_current_time();
1,036✔
2763
    h->last_send_time = current_time_ns;
1,036✔
2764
    myseq = seqmap_add(h->i, index, current_time_ns);
1,036✔
2765

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

2768
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
1,036✔
2769
        if(icmp_request_typ == 13)
915✔
2770
            proto = ICMP_TSTAMP;
18✔
2771
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
915✔
2772
    }
257✔
2773
#ifdef IPV6
2774
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
121✔
2775
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
121✔
2776
    }
15✔
2777
#endif
2778
    else {
2779
        return 0;
×
2780
    }
2781

2782
    /* error sending? */
2783
    if (
764✔
2784
        (n < 0)
272✔
2785
#if defined(EHOSTDOWN)
2786
        && errno != EHOSTDOWN
306✔
2787
#endif
2788
    ) {
2789
        if (verbose_flag) {
34✔
2790
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2791
        }
2792
        else {
2793
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2794
        }
2795

2796
        h->num_sent++;
34✔
2797
        h->num_sent_i++;
34✔
2798
        if (!loop_flag)
34✔
2799
            h->resp_times[index] = RESP_ERROR;
34✔
2800

2801
        ret = 0;
34✔
2802
    }
2803
    else {
2804
        /* schedule timeout */
2805
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
1,002✔
2806

2807
        /* mark this trial as outstanding */
2808
        if (!loop_flag) {
1,002✔
2809
            h->resp_times[index] = RESP_WAITING;
868✔
2810
        }
238✔
2811
    }
2812

2813
    num_pingsent++;
1,036✔
2814
    last_send_time = h->last_send_time;
1,036✔
2815

2816
    return (ret);
1,036✔
2817
}
272✔
2818

2819
int socket_can_read(struct timeval *timeout)
3,122✔
2820
{
2821
    int nfound;
2822
    fd_set readset;
2823
    int socketmax;
2824

2825
#ifndef IPV6
2826
    socketmax = socket4;
2827
#else
2828
    socketmax = socket4 > socket6 ? socket4 : socket6;
3,122✔
2829
#endif
2830

2831
select_again:
2,447✔
2832
    FD_ZERO(&readset);
42,291✔
2833
    if (socket4 >= 0)
3,139✔
2834
        FD_SET(socket4, &readset);
3,139✔
2835
#ifdef IPV6
2836
    if (socket6 >= 0)
3,139✔
2837
        FD_SET(socket6, &readset);
3,139✔
2838
#endif
2839

2840
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
3,139✔
2841
    if (nfound < 0) {
3,139✔
2842
        if (errno == EINTR) {
17✔
2843
            /* interrupted system call: redo the select */
2844
            goto select_again;
17✔
2845
        }
2846
        else {
2847
            perror("select");
×
2848
        }
2849
    }
2850

2851
    if (nfound > 0) {
3,122✔
2852
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,487✔
2853
            return socket4;
1,392✔
2854
        }
2855
#ifdef IPV6
2856
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
95✔
2857
            return socket6;
95✔
2858
        }
2859
#endif
2860
    }
2861

2862
    return -1;
1,635✔
2863
}
687✔
2864

2865
int receive_packet(int64_t wait_time,
3,122✔
2866
#if HAVE_SO_TIMESTAMPNS
2867
    int64_t *reply_timestamp,
2868
#else
2869
    int64_t *reply_timestamp __attribute__((unused)),
2870
#endif
2871
    struct sockaddr *reply_src_addr,
2872
    size_t reply_src_addr_len,
2873
    char *reply_buf,
2874
    size_t reply_buf_len,
2875
    int *ip_header_tos,
2876
    int *ip_header_ttl)
2877
{
2878
    struct timeval to;
2879
    int s = 0;
3,122✔
2880
    int recv_len;
2881
    static unsigned char msg_control[128];
2882
    struct iovec msg_iov = {
4,496✔
2883
        reply_buf,
687✔
2884
        reply_buf_len
687✔
2885
    };
2886
    struct msghdr recv_msghdr = {0};
3,122✔
2887
    recv_msghdr.msg_name = reply_src_addr;
3,122✔
2888
    recv_msghdr.msg_namelen = reply_src_addr_len;
3,122✔
2889
    recv_msghdr.msg_iov = &msg_iov;
3,122✔
2890
    recv_msghdr.msg_iovlen = 1;
3,122✔
2891
    recv_msghdr.msg_control = &msg_control;
3,122✔
2892
    recv_msghdr.msg_controllen = sizeof(msg_control);
3,122✔
2893
    struct cmsghdr *cmsg;
2894

2895
    /* Wait for a socket to become ready */
2896
    if (wait_time) {
3,122✔
2897
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,635✔
2898
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,635✔
2899
    }
436✔
2900
    else {
2901
        to.tv_sec = 0;
1,487✔
2902
        to.tv_usec = 0;
1,487✔
2903
    }
2904
    s = socket_can_read(&to);
3,122✔
2905
    if (s == -1) {
3,122✔
2906
        return 0; /* timeout */
1,635✔
2907
    }
2908

2909
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,487✔
2910
    if (recv_len <= 0) {
1,487✔
2911
        return 0;
×
2912
    }
2913

2914
    /* ancilliary data */
2915
    {
2916
#if HAVE_SO_TIMESTAMPNS
2917
        struct timespec reply_timestamp_ts;
2918
        struct timeval reply_timestamp_tv;
2919
#endif
2920
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,487✔
2921
             cmsg != NULL;
2,731✔
2922
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,244✔
2923
#if HAVE_SO_TIMESTAMPNS
2924
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,244✔
2925
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,236✔
2926
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,236✔
2927
            }
2928
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,244✔
2929
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2930
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2931
            }
2932
#endif
2933
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,244✔
2934
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2935
            }
2936
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,244✔
2937
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2938
            }
2939
#ifdef IPV6
2940
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,244✔
2941
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2942
            }
2943
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,244✔
2944
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2945
            }
2946
#endif
2947
        }
2948
    }
2949

2950
#if defined(DEBUG) || defined(_DEBUG)
2951
    if (randomly_lose_flag) {
2952
        if ((random() & 0x07) <= lose_factor)
2953
            return 0;
2954
    }
2955
#endif
2956

2957
    return recv_len;
1,487✔
2958
}
687✔
2959

2960
/* stats_add: update host statistics for a single packet that was received (or timed out)
2961
 * h: host entry to update
2962
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2963
 * success: 1 if response received, 0 otherwise
2964
 * latency: response time, in ns
2965
 */
2966
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
988✔
2967
{
2968
    /* sent count - we update only on receive/timeout, so that we don't get
2969
     * weird loss percentage, just because a packet was note recived yet.
2970
     */
2971
    h->num_sent++;
988✔
2972
    h->num_sent_i++;
988✔
2973

2974
    if (!success) {
988✔
2975
        if (!loop_flag && index >= 0) {
101✔
2976
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2977
        }
21✔
2978
        num_timeout++;
101✔
2979
        return;
101✔
2980
    }
2981

2982
    /* received count */
2983
    h->num_recv++;
887✔
2984
    h->num_recv_i++;
887✔
2985

2986
    /* maximum */
2987
    if (!h->max_reply || latency > h->max_reply) {
887✔
2988
        h->max_reply = latency;
656✔
2989
    }
183✔
2990
    if (!h->max_reply_i || latency > h->max_reply_i) {
887✔
2991
        h->max_reply_i = latency;
684✔
2992
    }
185✔
2993

2994
    /* minimum */
2995
    if (!h->min_reply || latency < h->min_reply) {
887✔
2996
        h->min_reply = latency;
548✔
2997
    }
160✔
2998
    if (!h->min_reply_i || latency < h->min_reply_i) {
887✔
2999
        h->min_reply_i = latency;
579✔
3000
    }
163✔
3001

3002
    /* total time (for average) */
3003
    h->total_time += latency;
887✔
3004
    h->total_time_i += latency;
887✔
3005

3006
    /* response time per-packet (count mode) */
3007
    if (!loop_flag && index >= 0) {
887✔
3008
        h->resp_times[index] = latency;
767✔
3009
    }
217✔
3010
}
268✔
3011

3012
/* stats_reset_interval: reset interval statistics
3013
 * h: host entry to update
3014
 */
3015
void stats_reset_interval(HOST_ENTRY *h)
31✔
3016
{
3017
    h->num_sent_i = 0;
31✔
3018
    h->num_recv_i = 0;
31✔
3019
    h->max_reply_i = 0;
31✔
3020
    h->min_reply_i = 0;
31✔
3021
    h->total_time_i = 0;
31✔
3022
}
31✔
3023

3024
int decode_icmp_ipv4(
1,392✔
3025
    struct sockaddr *response_addr,
3026
    size_t response_addr_len,
3027
    char *reply_buf,
3028
    size_t reply_buf_len,
3029
    unsigned short *id,
3030
    unsigned short *seq,
3031
    IP_HEADER_RESULT *ip_header_res)
3032
{
3033
    struct icmp *icp;
3034
    int hlen = 0;
1,392✔
3035

3036
    if (!using_sock_dgram4) {
1,392✔
3037
        struct ip *ip = (struct ip *)reply_buf;
1,384✔
3038
        ip_header_res->tos = ip->ip_tos;
1,384✔
3039
        ip_header_res->ttl = ip->ip_ttl;
1,384✔
3040

3041
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
3042
        /* The alpha headers are decidedly broken.
3043
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
3044
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
3045
         */
3046
        hlen = (ip->ip_vhl & 0x0F) << 2;
3047
#else
3048
        hlen = ip->ip_hl << 2;
1,384✔
3049
#endif
3050
    }
236✔
3051

3052
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,392✔
3053
        /* too short */
3054
        if (verbose_flag) {
×
3055
            char buf[INET6_ADDRSTRLEN];
3056
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3057
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3058
        }
3059
        return -1;
×
3060
    }
3061

3062
    icp = (struct icmp *)(reply_buf + hlen);
1,392✔
3063

3064
    if (traceroute_flag && icp->icmp_type == ICMP_TIMXCEED) {
1,392✔
3065
        struct ip *inner_ip;
3066
        int inner_hlen;
3067
        struct icmp *inner_icmp;
3068

NEW
3069
        if (reply_buf_len >= hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
×
NEW
3070
            inner_ip = (struct ip *) (reply_buf + hlen + ICMP_MINLEN);
×
NEW
3071
            inner_hlen = inner_ip->ip_hl << 2;
×
3072

NEW
3073
            if (inner_hlen < sizeof(struct ip)) {
×
3074
                /* Invalid inner IP header length */
NEW
3075
                return -1;
×
3076
            }
3077

NEW
3078
            if (reply_buf_len >= hlen + ICMP_MINLEN + inner_hlen + ICMP_MINLEN) {
×
NEW
3079
                inner_icmp = (struct icmp *) ((char *)inner_ip + inner_hlen);
×
NEW
3080
                if (inner_icmp->icmp_id == ident4) {
×
NEW
3081
                    *id = inner_icmp->icmp_id;
×
NEW
3082
                    *seq = ntohs(inner_icmp->icmp_seq);
×
NEW
3083
                    return hlen;
×
3084
                }
3085
            }
3086
        }
3087
    }
3088

3089
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,392✔
3090
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
598✔
3091
        /* Handle other ICMP packets */
3092
        struct icmp *sent_icmp;
3093
        SEQMAP_VALUE *seqmap_value;
3094
        char addr_ascii[INET6_ADDRSTRLEN];
3095
        HOST_ENTRY *h;
3096

3097
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
3098
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
1,046✔
3099
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
3100
            return -1;
×
3101
        }
3102

3103
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
574✔
3104

3105
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
574✔
3106
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
3107
            sent_icmp->icmp_id != ident4) {
×
3108
            /* not caused by us */
3109
            return -1;
574✔
3110
        }
3111

3112
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
3113
        if (seqmap_value == NULL) {
×
3114
            return -1;
×
3115
        }
3116

3117
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3118

3119
        switch (icp->icmp_type) {
×
3120
        case ICMP_UNREACH:
3121
            h = table[seqmap_value->host_nr];
×
3122
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
3123
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
3124
                    addr_ascii, h->host);
3125
            }
3126
            else {
3127
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3128
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
3129
            }
3130

3131
            print_warning("\n");
×
3132
            num_othericmprcvd++;
×
3133
            break;
×
3134

3135
        case ICMP_SOURCEQUENCH:
3136
        case ICMP_REDIRECT:
3137
        case ICMP_TIMXCEED:
3138
        case ICMP_PARAMPROB:
3139
            h = table[seqmap_value->host_nr];
×
3140
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
3141
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3142
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
3143
            }
3144
            else {
3145
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
3146
                    icp->icmp_type, addr_ascii, h->host);
×
3147
            }
3148
            print_warning("\n");
×
3149
            num_othericmprcvd++;
×
3150
            break;
×
3151
        }
3152

3153
        return -1;
×
3154
    }
3155

3156
    *id = icp->icmp_id;
818✔
3157
    *seq = ntohs(icp->icmp_seq);
818✔
3158
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
818✔
3159

3160
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
3161
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
3162
            if (verbose_flag) {
×
3163
                char buf[INET6_ADDRSTRLEN];
3164
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3165
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3166
            }
3167
            return -1;
×
3168
        }
3169

3170
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
3171
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
3172
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
3173
    }
3174

3175
    return hlen;
818✔
3176
}
236✔
3177

3178
#ifdef IPV6
3179
int decode_icmp_ipv6(
95✔
3180
    struct sockaddr *response_addr,
3181
    size_t response_addr_len,
3182
    char *reply_buf,
3183
    size_t reply_buf_len,
3184
    unsigned short *id,
3185
    unsigned short *seq)
3186
{
3187
    struct icmp6_hdr *icp;
3188

3189
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
95✔
3190
        if (verbose_flag) {
×
3191
            char buf[INET6_ADDRSTRLEN];
3192
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3193
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3194
        }
3195
        return 0; /* too short */
×
3196
    }
3197

3198
    icp = (struct icmp6_hdr *)reply_buf;
95✔
3199

3200
    /* Traceroute Logic for IPv6 Time Exceeded */
3201
    if (traceroute_flag && icp->icmp6_type == ICMP6_TIME_EXCEEDED) {
95✔
3202
        struct ip6_hdr *inner_ip6;
3203
        struct icmp6_hdr *inner_icmp6;
3204

NEW
3205
        if (reply_buf_len >= sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
×
NEW
3206
            inner_ip6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
×
3207

3208
            /* Check whether the inner packet is ICMPv6 */
NEW
3209
            if (inner_ip6->ip6_nxt == IPPROTO_ICMPV6) {
×
NEW
3210
                inner_icmp6 = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
×
3211

NEW
3212
                if (inner_icmp6->icmp6_id == ident6) {
×
NEW
3213
                    *id = inner_icmp6->icmp6_id;
×
NEW
3214
                    *seq = ntohs(inner_icmp6->icmp6_seq);
×
NEW
3215
                    return 1;
×
3216
                }
3217
            }
3218
        }
3219
    }
3220

3221
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
95✔
3222
        /* Handle other ICMPv6 packets */
3223
        struct ip6_hdr *sent_ipv6;
3224
        struct icmp6_hdr *sent_icmp;
3225
        SEQMAP_VALUE *seqmap_value;
3226
        char addr_ascii[INET6_ADDRSTRLEN];
3227
        HOST_ENTRY *h;
3228

3229
        /* reply icmp packet (ICMPv6 header) followed by "sent packet" (IPv6 + ICMPv6 header) */
3230
        if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
6✔
3231
            /* discard ICMPv6 message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
3232
            return 0;
×
3233
        }
3234

3235
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
6✔
3236
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
6✔
3237
            /* discard ICMPv6 message if we can't tell that it was caused by
3238
             * us, because the IPv6 header is not directly followed by an
3239
             * ICMPv6 header
3240
             */
3241
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
3242
            return 0;
×
3243
        }
3244
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
6✔
3245

3246
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
6✔
3247
            /* not caused by us */
3248
            return 0;
6✔
3249
        }
3250

3251
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
3252
        if (seqmap_value == NULL) {
×
3253
            return 0;
×
3254
        }
3255

3256
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3257
        h = table[seqmap_value->host_nr];
×
3258

3259
        switch (icp->icmp6_type) {
×
3260
        case ICMP6_DST_UNREACH:
3261
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
3262
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3263
                    icp->icmp6_code, addr_ascii, h->host);
×
3264
            } else {
3265
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3266
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
3267
            }
3268
            print_warning("\n");
×
3269
            num_othericmprcvd++;
×
3270
            break;
×
3271

3272
        case ICMP6_PACKET_TOO_BIG:
3273
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
3274
                addr_ascii, h->host);
3275
            num_othericmprcvd++;
×
3276
            break;
×
3277

3278
        case ICMP6_TIME_EXCEEDED:
3279
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
3280
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3281
                    icp->icmp6_code, addr_ascii, h->host);
×
3282
            } else {
3283
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3284
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
3285
            }
3286
            print_warning("\n");
×
3287
            num_othericmprcvd++;
×
3288
            break;
×
3289

3290
        case ICMP6_PARAM_PROB:
3291
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
3292
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3293
                    icp->icmp6_code, addr_ascii, h->host);
×
3294
            } else {
3295
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3296
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
3297
            }
3298
            print_warning("\n");
×
3299
            num_othericmprcvd++;
×
3300
            break;
×
3301

3302
        default:
3303
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
3304
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
3305
            num_othericmprcvd++;
×
3306
            break;
×
3307
        }
3308

3309
        return 0;
×
3310
    }
3311

3312
    *id = icp->icmp6_id;
89✔
3313
    *seq = ntohs(icp->icmp6_seq);
89✔
3314

3315
    return 1;
89✔
3316
}
15✔
3317
#endif
3318

3319
int wait_for_reply(int64_t wait_time)
3,122✔
3320
{
3321
    int result;
3322
    static char buffer[RECV_BUFSIZE];
3323
    struct sockaddr_storage response_addr;
3324
    int n, avg;
3325
    int ip_hlen = 0;
3,122✔
3326
    HOST_ENTRY *h;
3327
    int64_t this_reply;
3328
    int this_count;
3329
    int64_t recv_time = 0;
3,122✔
3330
    SEQMAP_VALUE *seqmap_value;
3331
    unsigned short id;
3332
    unsigned short seq;
3333
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
3,122✔
3334

3335
    /* Receive packet */
3336
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,809✔
3337
        &recv_time, /* reply_timestamp */
3338
        (struct sockaddr *)&response_addr, /* reply_src_addr */
3339
        sizeof(response_addr), /* reply_src_addr_len */
3340
        buffer, /* reply_buf */
3341
        sizeof(buffer), /* reply_buf_len */
3342
        &ip_header_res.tos, /* TOS resp. TC byte */
687✔
3343
        &ip_header_res.ttl /* TTL resp. hop limit */
687✔
3344
    );
3345

3346
    if (result <= 0) {
3,122✔
3347
        return 0;
1,635✔
3348
    }
3349

3350
    update_current_time();
1,487✔
3351
    if (recv_time == 0)
1,487✔
3352
        recv_time = current_time_ns;
251✔
3353

3354
    /* Process ICMP packet and retrieve id/seq */
3355
    if (response_addr.ss_family == AF_INET) {
1,487✔
3356
        ip_hlen = decode_icmp_ipv4(
1,392✔
3357
            (struct sockaddr *)&response_addr,
3358
            sizeof(response_addr),
3359
            buffer,
3360
            sizeof(buffer),
3361
            &id,
3362
            &seq,
3363
            &ip_header_res);
3364
        if (ip_hlen < 0) {
1,392✔
3365
            return 1;
574✔
3366
        }
3367
        if (id != ident4) {
818✔
3368
            return 1; /* packet received, but not the one we are looking for! */
×
3369
        }
3370
        if (!using_sock_dgram4) {
818✔
3371
            /* do not include IP header in returned size, to be consistent with ping(8) and also
3372
             * with fping with IPv6 hosts */
3373
            result -= ip_hlen;
810✔
3374
        }
236✔
3375
    }
236✔
3376
#ifdef IPV6
3377
    else if (response_addr.ss_family == AF_INET6) {
95✔
3378
        if (!decode_icmp_ipv6(
95✔
3379
                (struct sockaddr *)&response_addr,
3380
                sizeof(response_addr),
3381
                buffer,
3382
                sizeof(buffer),
3383
                &id,
3384
                &seq)) {
3385
            return 1;
6✔
3386
        }
3387
        if (id != ident6) {
89✔
3388
            return 1; /* packet received, but not the one we are looking for! */
×
3389
        }
3390
    }
15✔
3391
#endif
3392
    else {
3393
        return 1;
×
3394
    }
3395

3396
    seqmap_value = seqmap_fetch(seq, current_time_ns);
907✔
3397
    if (seqmap_value == NULL) {
907✔
3398
        return 1;
12✔
3399
    }
3400

3401
    /* find corresponding host_entry */
3402
    n = seqmap_value->host_nr;
895✔
3403
    h = table[n];
895✔
3404
    this_count = seqmap_value->ping_count;
895✔
3405
    this_reply = recv_time - seqmap_value->ping_ts;
895✔
3406

3407
    /* update stats that include invalid replies */
3408
    h->num_recv_total++;
895✔
3409
    num_pingreceived++;
895✔
3410

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

3413
    /* optionally require reply source equal to target address */
3414
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
895✔
3415
        dbg_printf("%s\n", "discarding reply from wrong source address");
3416
        return 1;
8✔
3417
    }
3418

3419
    /* discard duplicates */
3420
    if (!loop_flag && !(count_flag && quiet_flag) && h->resp_times[this_count] >= 0) {
887✔
3421
        if (!per_recv_flag) {
×
3422
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3423
                h->host, this_count, result, sprint_tm(this_reply));
3424

3425
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3426
                char buf[INET6_ADDRSTRLEN];
3427
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3428
                fprintf(stderr, " [<- %s]", buf);
×
3429
            }
3430
            fprintf(stderr, "\n");
×
3431
        }
3432
        return 1;
×
3433
    }
3434

3435
    /* discard reply if delay is larger than timeout
3436
     * (see also: github #32) */
3437
    if (this_reply > h->timeout) {
887✔
3438
        return 1;
×
3439
    }
3440

3441
    if (traceroute_flag && response_addr.ss_family == AF_INET) {
887✔
NEW
3442
        struct icmp *icp = (struct icmp *)(buffer + ip_hlen);
×
3443
        char ip_str[INET_ADDRSTRLEN];
NEW
3444
        getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), ip_str, sizeof(ip_str), NULL, 0, NI_NUMERICHOST);
×
3445

NEW
3446
        if (icp->icmp_type == ICMP_TIMXCEED) {
×
NEW
3447
            printf("%s: hop %d reached %s (%s ms)\n", h->host, h->trace_ttl, ip_str, sprint_tm(this_reply));
×
NEW
3448
            h->trace_ttl++;
×
NEW
3449
            if (h->trace_ttl > (int)ttl) {
×
NEW
3450
                h->trace_ttl = (int)ttl;
×
3451
            }
3452
        } else { /* ICMP_ECHOREPLY */
NEW
3453
            printf("%s: hop %d reached DESTINATION %s (%s ms)\n", h->host, h->trace_ttl, ip_str, sprint_tm(this_reply));
×
NEW
3454
            h->trace_ttl = TRACEROUTE_DONE_TTL; /* Goal achieved: artificially increase TTL to stop loop in main_loop */
×
3455
        }
3456
    }
3457
#ifdef IPV6
3458
    else if (traceroute_flag && response_addr.ss_family == AF_INET6) {
887✔
NEW
3459
        struct icmp6_hdr *icp = (struct icmp6_hdr *)buffer;
×
3460

NEW
3461
        if (icp->icmp6_type == ICMP6_TIME_EXCEEDED || icp->icmp6_type == ICMP6_ECHO_REPLY) {
×
3462
            /* With IPv6, buffer is directly the ICMP header payload, since receive_packet uses recvmsg */
3463
            char ip_str[INET6_ADDRSTRLEN];
NEW
3464
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), ip_str, sizeof(ip_str), NULL, 0, NI_NUMERICHOST);
×
3465

NEW
3466
            if (icp->icmp6_type == ICMP6_TIME_EXCEEDED) {
×
NEW
3467
                printf("%s: hop %d reached %s (%s ms)\n", h->host, h->trace_ttl, ip_str, sprint_tm(this_reply));
×
NEW
3468
                h->trace_ttl++;
×
NEW
3469
                if (h->trace_ttl > (int)ttl) {
×
NEW
3470
                    h->trace_ttl = (int)ttl;
×
3471
                }
3472
            } else { /* ICMP6_ECHO_REPLY */
NEW
3473
                printf("%s: hop %d reached DESTINATION %s (%s ms)\n", h->host, h->trace_ttl, ip_str, sprint_tm(this_reply));
×
NEW
3474
                h->trace_ttl = TRACEROUTE_DONE_TTL; /* Goal achieved: artificially increase TTL to stop loop in main_loop */
×
3475
            }
3476
        }
3477
    }
3478
#endif
3479

3480
    /* update stats */
3481
    stats_add(h, this_count, 1, this_reply);
887✔
3482
    // TODO: move to stats_add?
3483
    if (!max_reply || this_reply > max_reply)
887✔
3484
        max_reply = this_reply;
525✔
3485
    if (!min_reply || this_reply < min_reply)
887✔
3486
        min_reply = this_reply;
449✔
3487
    sum_replies += this_reply;
887✔
3488
    total_replies++;
887✔
3489

3490
    /* initialize timeout to initial timeout (without backoff) */
3491
    h->timeout = timeout;
887✔
3492

3493
    /* remove timeout event */
3494
    struct event *timeout_event = host_get_timeout_event(h, this_count);
887✔
3495
    if (timeout_event) {
887✔
3496
        ev_remove(&event_queue_timeout, timeout_event);
887✔
3497
    }
247✔
3498

3499
    if (traceroute_flag) {
887✔
NEW
3500
      return 1;
×
3501
    }
3502

3503
    /* print "is alive" */
3504
    if (h->num_recv == 1) {
887✔
3505
        num_alive++;
503✔
3506
        if (fast_reachable && num_alive >= min_reachable)
503✔
3507
            finish_requested = 1;
3✔
3508

3509
        if (verbose_flag || alive_flag) {
503✔
3510
            printf("%s", h->host);
302✔
3511

3512
            if (verbose_flag)
302✔
3513
                printf(" is alive");
282✔
3514
        }
86✔
3515
    }
143✔
3516

3517
    /* print received ping (unless --quiet) */
3518
    if (per_recv_flag) {
887✔
3519
        avg = h->total_time / h->num_recv;
279✔
3520
        print_recv(h,
366✔
3521
            recv_time,
87✔
3522
            result,
87✔
3523
            this_count,
87✔
3524
            this_reply,
87✔
3525
            avg);
87✔
3526
    }
87✔
3527

3528
    if (verbose_flag || alive_flag || per_recv_flag) {
887✔
3529
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
581✔
3530
            char buf[INET6_ADDRSTRLEN];
3531
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
3532
            fprintf(stderr, " [<- %s]", buf);
6✔
3533
        }
3534
        if (json_flag) {
581✔
3535
            print_recv_ext_json(&ip_header_res,
35✔
3536
                recv_time,
11✔
3537
                this_reply);
11✔
3538
        }
11✔
3539
        else {
3540
            print_recv_ext(&ip_header_res,
546✔
3541
                recv_time,
162✔
3542
                this_reply);
162✔
3543
        }
3544
    }
173✔
3545
    return 1;
887✔
3546
}
687✔
3547

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

3550
  Function: add_name
3551

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

3554
  Inputs:  char* name
3555

3556
  Description:
3557

3558
  process input name for addition to target list
3559
  name can turn into multiple targets via multiple interfaces (-m)
3560
  or via NIS groups
3561

3562
************************************************************/
3563

3564
void add_name(char *name)
624✔
3565
{
3566
    struct addrinfo *res0, *res, hints;
3567
    int ret_ga;
3568
    char *printname;
3569
    char namebuf[256];
3570
    char addrbuf[256];
3571

3572
    /* getaddrinfo */
3573
    memset(&hints, 0, sizeof(struct addrinfo));
624✔
3574
    hints.ai_flags = AI_UNUSABLE;
624✔
3575
    hints.ai_socktype = SOCK_RAW;
624✔
3576
    hints.ai_family = hints_ai_family;
624✔
3577
    if (hints_ai_family == AF_INET) {
624✔
3578
        hints.ai_protocol = IPPROTO_ICMP;
81✔
3579
    }
21✔
3580
#ifdef IPV6
3581
    else if (hints_ai_family == AF_INET6) {
543✔
3582
        hints.ai_protocol = IPPROTO_ICMPV6;
33✔
3583
    }
1✔
3584
#endif
3585
    else {
3586
        hints.ai_socktype = SOCK_STREAM;
510✔
3587
        hints.ai_protocol = 0;
510✔
3588
    }
3589
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
624✔
3590
    if (ret_ga) {
624✔
3591
        if (!quiet_flag)
29✔
3592
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
3593
        num_noaddress++;
29✔
3594

3595
        // Handle JSON output for invalid hosts
3596
        if (json_flag) {
29✔
3597
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
3598
            return;
21✔
3599
        }
3600

3601
        return;
26✔
3602
    }
3603

3604
    /* NOTE: we could/should loop with res on all addresses like this:
3605
     * for (res = res0; res; res = res->ai_next) {
3606
     * We don't do it yet, however, because is is an incompatible change
3607
     * (need to implement a separate option for this)
3608
     */
3609
    for (res = res0; res; res = res->ai_next) {
595✔
3610
        /* name_flag: addr -> name lookup requested) */
3611
        if (name_flag || rdns_flag) {
597✔
3612
            int do_rdns = rdns_flag ? 1 : 0;
3✔
3613
            if (name_flag) {
3✔
3614
                /* Was it a numerical address? Only then do a rdns-query */
3615
                struct addrinfo *nres;
3616
                hints.ai_flags = AI_NUMERICHOST;
×
3617
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
3618
                    do_rdns = 1;
×
3619
                    freeaddrinfo(nres);
×
3620
                }
3621
            }
3622

3623
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
3624
                printname = namebuf;
3✔
3625
            }
1✔
3626
            else {
3627
                printname = name;
×
3628
            }
3629
        }
1✔
3630
        else {
3631
            printname = name;
592✔
3632
        }
3633

3634
        /* addr_flag: name -> addr lookup requested */
3635
        if (addr_flag) {
595✔
3636
            int ret;
3637
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
3638
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
3639
            if (ret) {
3✔
3640
                if (!quiet_flag) {
×
3641
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
3642
                }
3643
                continue;
×
3644
            }
3645

3646
            if (name_flag || rdns_flag) {
3✔
3647
                char nameaddrbuf[512 + 3];
3648
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
3649
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
3650
            }
3651
            else {
3652
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
3653
            }
3654
        }
1✔
3655
        else {
3656
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
592✔
3657
        }
3658

3659
        if (!multif_flag) {
595✔
3660
            break;
595✔
3661
        }
3662
    }
3663

3664
    freeaddrinfo(res0);
595✔
3665
}
166✔
3666

3667
/************************************************************
3668

3669
  Function: add_addr
3670

3671
*************************************************************
3672

3673
  Description:
3674

3675
  add single address to list of hosts to be pinged
3676

3677
************************************************************/
3678

3679
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
595✔
3680
{
3681
    HOST_ENTRY *p;
3682
    int n;
3683
    int64_t *i;
3684

3685
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
595✔
3686
    if (!p)
595✔
3687
        crash_and_burn("can't allocate HOST_ENTRY");
×
3688

3689
    p->name = strdup(name);
595✔
3690
    p->host = strdup(host);
595✔
3691
    memcpy(&p->saddr, ipaddr, ipaddr_len);
595✔
3692
    p->saddr_len = ipaddr_len;
595✔
3693
    p->timeout = timeout;
595✔
3694
    p->min_reply = 0;
595✔
3695
    p->trace_ttl = 1;
595✔
3696

3697
    if (netdata_flag) {
595✔
3698
        char *s = p->name;
3✔
3699
        while (*s) {
30✔
3700
            if (!isalnum(*s))
27✔
3701
                *s = '_';
9✔
3702
            s++;
27✔
3703
        }
3704
    }
1✔
3705

3706
    if (strlen(p->host) > max_hostname_len)
595✔
3707
        max_hostname_len = strlen(p->host);
402✔
3708

3709
    /* array for response time results */
3710
    if (!loop_flag) {
595✔
3711
#if SIZE_MAX <= UINT_MAX
3712
        if (trials > (SIZE_MAX / sizeof(int64_t)))
3713
            crash_and_burn("resp_times array too large for memory");
3714
#endif
3715
        i = (int64_t *)malloc(trials * sizeof(int64_t));
581✔
3716
        if (!i)
581✔
3717
            crash_and_burn("can't allocate resp_times array");
×
3718

3719
        for (n = 0; n < trials; n++)
2,886✔
3720
            i[n] = RESP_UNUSED;
2,305✔
3721

3722
        p->resp_times = i;
581✔
3723
    }
153✔
3724

3725
    /* allocate event storage */
3726
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
595✔
3727
    if (!p->event_storage_ping) {
595✔
3728
        errno_crash_and_burn("can't allocate event_storage_ping");
×
3729
    }
3730
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
595✔
3731
    if (!p->event_storage_timeout) {
595✔
3732
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
3733
    }
3734

3735
    /* schedule first ping */
3736
    host_add_ping_event(p, 0, current_time_ns);
595✔
3737

3738
    num_hosts++;
595✔
3739
}
595✔
3740

3741
/************************************************************
3742

3743
  Function: crash_and_burn
3744

3745
*************************************************************
3746

3747
  Inputs:  char* message
3748

3749
  Description:
3750

3751
************************************************************/
3752

3753
void crash_and_burn(char *message)
×
3754
{
3755
    fprintf(stderr, "%s: %s\n", prog, message);
×
3756
    exit(4);
×
3757
}
3758

3759
/************************************************************
3760

3761
  Function: errno_crash_and_burn
3762

3763
*************************************************************
3764

3765
  Inputs:  char* message
3766

3767
  Description:
3768

3769
************************************************************/
3770

3771
void errno_crash_and_burn(char *message)
8✔
3772
{
3773
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
3774
    exit(4);
8✔
3775
}
3776

3777
/************************************************************
3778

3779
  Function: print_warning
3780

3781
  Description: fprintf(stderr, ...), unless running with -q
3782

3783
*************************************************************/
3784

3785
void print_warning(char *format, ...)
54✔
3786
{
3787
    va_list args;
3788
    if (!quiet_flag) {
54✔
3789
        va_start(args, format);
54✔
3790
        vfprintf(stderr, format, args);
54✔
3791
        va_end(args);
54✔
3792
    }
8✔
3793
}
54✔
3794

3795
/************************************************************
3796

3797
  Function: sprint_tm
3798

3799
*************************************************************
3800

3801
  render nanosecond int64_t value into milliseconds string with three digits of
3802
  precision.
3803

3804
************************************************************/
3805

3806
const char *sprint_tm(int64_t ns)
1,270✔
3807
{
3808
    static char buf[10];
3809
    double t = (double)ns / 1e6;
1,270✔
3810

3811
    if (t < 0.0) {
1,270✔
3812
        /* negative (unexpected) */
3813
        snprintf(buf, sizeof(buf), "%.2g", t);
×
3814
    }
3815
    else if (t < 1.0) {
1,270✔
3816
        /* <= 0.99 ms */
3817
        snprintf(buf, sizeof(buf), "%.3f", t);
1,270✔
3818
    }
362✔
UNCOV
3819
    else if (t < 10.0) {
×
3820
        /* 1.00 - 9.99 ms */
UNCOV
3821
        snprintf(buf, sizeof(buf), "%.2f", t);
×
3822
    }
3823
    else if (t < 100.0) {
×
3824
        /* 10.0 - 99.9 ms */
3825
        snprintf(buf, sizeof(buf), "%.1f", t);
×
3826
    }
3827
    else if (t < 1000000.0) {
×
3828
        /* 100 - 1'000'000 ms */
3829
        snprintf(buf, sizeof(buf), "%.0f", t);
×
3830
    }
3831
    else {
3832
        snprintf(buf, sizeof(buf), "%.3e", t);
×
3833
    }
3834

3835
    return (buf);
1,270✔
3836
}
3837

3838
/************************************************************
3839

3840
  Function: addr_cmp
3841

3842
*************************************************************/
3843
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
599✔
3844
{
3845
    if (a->sa_family != b->sa_family) {
599✔
3846
        return a->sa_family - b->sa_family;
×
3847
    }
3848
    else {
3849
        if (a->sa_family == AF_INET) {
599✔
3850
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
537✔
3851
        }
3852
        else if (a->sa_family == AF_INET6) {
62✔
3853
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
70✔
3854
                &((struct sockaddr_in6 *)b)->sin6_addr,
62✔
3855
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
3856
        }
3857
    }
3858

3859
    return 0;
×
3860
}
175✔
3861

3862
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,035✔
3863
{
3864
    struct event *event = &h->event_storage_ping[index % event_storage_count];
1,035✔
3865
    event->host = h;
1,035✔
3866
    event->ping_index = index;
1,035✔
3867
    event->ev_time = ev_time;
1,035✔
3868
    ev_enqueue(&event_queue_ping, event);
1,035✔
3869

3870
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
3871
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3872
}
1,035✔
3873

3874
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,002✔
3875
{
3876
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
1,002✔
3877
    event->host = h;
1,002✔
3878
    event->ping_index = index;
1,002✔
3879
    event->ev_time = ev_time;
1,002✔
3880
    ev_enqueue(&event_queue_timeout, event);
1,002✔
3881

3882
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
3883
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
3884
}
1,002✔
3885

3886
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
887✔
3887
{
3888
    return &h->event_storage_timeout[index % event_storage_count];
887✔
3889
}
3890

3891
/************************************************************
3892

3893
  Function: ev_enqueue
3894

3895
  Enqueue an event
3896

3897
  The queue is sorted by event->ev_time, so that queue->first always points to
3898
  the earliest event.
3899

3900
  We start scanning the queue from the tail, because we assume
3901
  that new events mostly get inserted with a event time higher
3902
  than the others.
3903

3904
*************************************************************/
3905
void ev_enqueue(struct event_queue *queue, struct event *event)
2,037✔
3906
{
3907
    struct event *i;
3908
    struct event *i_prev;
3909

3910
    /* Empty list */
3911
    if (queue->last == NULL) {
2,037✔
3912
        event->ev_next = NULL;
1,645✔
3913
        event->ev_prev = NULL;
1,645✔
3914
        queue->first = event;
1,645✔
3915
        queue->last = event;
1,645✔
3916
        return;
1,645✔
3917
    }
3918

3919
    /* Insert on tail? */
3920
    if (event->ev_time - queue->last->ev_time >= 0) {
392✔
3921
        event->ev_next = NULL;
392✔
3922
        event->ev_prev = queue->last;
392✔
3923
        queue->last->ev_next = event;
392✔
3924
        queue->last = event;
392✔
3925
        return;
392✔
3926
    }
3927

3928
    /* Find insertion point */
3929
    i = queue->last;
×
3930
    while (1) {
3931
        i_prev = i->ev_prev;
×
3932
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3933
            event->ev_prev = i_prev;
×
3934
            event->ev_next = i;
×
3935
            i->ev_prev = event;
×
3936
            if (i_prev != NULL) {
×
3937
                i_prev->ev_next = event;
×
3938
            }
3939
            else {
3940
                queue->first = event;
×
3941
            }
3942
            return;
×
3943
        }
3944
        i = i_prev;
×
3945
    }
3946
}
545✔
3947

3948
/************************************************************
3949

3950
  Function: ev_dequeue
3951

3952
*************************************************************/
3953
struct event *ev_dequeue(struct event_queue *queue)
1,102✔
3954
{
3955
    struct event *dequeued;
3956

3957
    if (queue->first == NULL) {
1,102✔
3958
        return NULL;
×
3959
    }
3960
    dequeued = queue->first;
1,102✔
3961
    ev_remove(queue, dequeued);
1,102✔
3962

3963
    return dequeued;
1,102✔
3964
}
284✔
3965

3966
/************************************************************
3967

3968
  Function: ev_remove
3969

3970
*************************************************************/
3971
void ev_remove(struct event_queue *queue, struct event *event)
1,989✔
3972
{
3973
    if (queue->first == event) {
1,989✔
3974
        queue->first = event->ev_next;
1,980✔
3975
    }
528✔
3976
    if (queue->last == event) {
1,989✔
3977
        queue->last = event->ev_prev;
1,618✔
3978
    }
417✔
3979
    if (event->ev_prev) {
1,989✔
3980
        event->ev_prev->ev_next = event->ev_next;
9✔
3981
    }
3✔
3982
    if (event->ev_next) {
1,989✔
3983
        event->ev_next->ev_prev = event->ev_prev;
371✔
3984
    }
114✔
3985
    event->ev_prev = NULL;
1,989✔
3986
    event->ev_next = NULL;
1,989✔
3987
}
1,989✔
3988

3989
/************************************************************
3990

3991
  Function: print_human_readable_time from current_time_ns
3992

3993
*************************************************************/
3994
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
45✔
3995
{
3996
    char time_buffer[100];
3997
    time_t current_time_s;
3998
    struct tm *local_time;
3999

4000
    current_time_s = current_time_ns / 1000000000;
45✔
4001
    local_time = localtime(&current_time_s);
45✔
4002
    switch(timestamp_format) {
45✔
4003
        case 1:
6✔
4004
            // timestamp-format ctime
4005
            strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
9✔
4006
            if (json_flag)
9✔
4007
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
4008
            else
4009
                printf("[%s] ", time_buffer);
6✔
4010
            break;
9✔
4011
        case 2:
6✔
4012
            // timestamp-format iso
4013
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
9✔
4014
            if (json_flag)
9✔
4015
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
4016
            else
4017
                printf("[%s] ", time_buffer);
6✔
4018
            break;
9✔
4019
        case 3:
6✔
4020
            // timestamp-format rfc3339
4021
            strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
9✔
4022
            if (json_flag)
9✔
4023
                printf("\"timestamp\": \"%s\", ", time_buffer);
3✔
4024
            else
4025
                printf("[%s] ", time_buffer);
6✔
4026
            break;
9✔
4027
        default:
12✔
4028
            if (json_flag)
18✔
4029
                printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9);
6✔
4030
            else
4031
                printf("[%.5f] ", (double)current_time_ns / 1e9);
12✔
4032
    }
6✔
4033
}
45✔
4034

4035
/************************************************************
4036

4037
  Function: ms_since_midnight_utc
4038

4039
*************************************************************
4040

4041
  Input: int64_t: current UTC time in ns
4042

4043
  Output: uint32_t: current time in ms since midnight UTC
4044

4045
  Description:
4046

4047
  Return ICMP Timestamp value corresponding to the given time value.
4048
  The given time value must be in UTC.
4049

4050
*************************************************************/
4051
static uint32_t ms_since_midnight_utc(int64_t time_val)
16✔
4052
{
4053
    return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000));
16✔
4054
}
4055

4056
/************************************************************
4057

4058
  Function: usage
4059

4060
*************************************************************
4061

4062
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
4063

4064
  Description:
4065

4066
************************************************************/
4067

4068
void usage(int is_error)
297✔
4069
{
4070
    FILE *out = is_error ? stderr : stdout;
297✔
4071
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
297✔
4072
    fprintf(out, "\n");
297✔
4073
    fprintf(out, "Probing options:\n");
297✔
4074
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
297✔
4075
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
297✔
4076
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
297✔
4077
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
297✔
4078
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
297✔
4079
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
297✔
4080
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
297✔
4081
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
297✔
4082
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
297✔
4083
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
297✔
4084
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
297✔
4085
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
297✔
4086
#ifdef SO_BINDTODEVICE
4087
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
297✔
4088
#endif
4089
#ifdef SO_MARK
4090
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
202✔
4091
#endif
4092
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
297✔
4093
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
297✔
4094
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
297✔
4095
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
297✔
4096
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
297✔
4097
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
297✔
4098
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
297✔
4099
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
297✔
4100
    fprintf(out, "   -S, --src=IP       set source address\n");
297✔
4101
    fprintf(out, "       --seqmap-timeout=MSEC sequence number mapping timeout (default: %.0f ms)\n", seqmap_timeout / 1e6);
297✔
4102
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
297✔
4103
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
297✔
4104
    fprintf(out, "       --check-source discard replies not from target address\n");
297✔
4105
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
297✔
4106
    fprintf(out, "       --traceroute   Sends a traceroute based on ICMP echo request (Only one host is allowed)\n");
297✔
4107
    fprintf(out, "\n");
297✔
4108
    fprintf(out, "Output options:\n");
297✔
4109
    fprintf(out, "   -a, --alive        show targets that are alive\n");
297✔
4110
    fprintf(out, "   -A, --addr         show targets by address\n");
297✔
4111
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
297✔
4112
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
297✔
4113
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
297✔
4114
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
297✔
4115
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
297✔
4116
    fprintf(out, "   -J, --json         output in JSON format (-c, -C, or -l required)\n");
297✔
4117
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
297✔
4118
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
297✔
4119
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
297✔
4120
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
297✔
4121
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
297✔
4122
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
297✔
4123
    fprintf(out, "   -s, --stats        print final stats\n");
297✔
4124
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
297✔
4125
    fprintf(out, "   -v, --version      show version\n");
297✔
4126
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
297✔
4127
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
297✔
4128
    fprintf(out, "       --print-tos    show received TOS value\n");
297✔
4129
    fprintf(out, "       --print-ttl    show IP TTL value\n");
297✔
4130
    exit(is_error);
297✔
4131
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc