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

schweikert / fping / 20544511541

27 Dec 2025 09:19PM UTC coverage: 87.915% (-0.2%) from 88.1%
20544511541

Pull #443

github

web-flow
Merge c125082e2 into 24866e92a
Pull Request #443: Replace atoi/atof/sscanf with strtoXX for stricter option parsing

51 of 54 new or added lines in 1 file covered. (94.44%)

3 existing lines in 1 file now uncovered.

1615 of 1837 relevant lines covered (87.92%)

370.13 hits per line

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

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

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

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

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

51
#include "seqmap.h"
52

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

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

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

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

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

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

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

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

85
#include <sys/select.h>
86

87
/*** compatibility ***/
88

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

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

107
/*** externals ***/
108

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

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

119
/*** Constants ***/
120

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

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

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

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

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

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

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

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

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

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

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

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

224
#define ICMP_UNREACH_MAXTYPE 15
225

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

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

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

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

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

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

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

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

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

325
#define EV_TYPE_PING 1
326
#define EV_TYPE_TIMEOUT 2
327

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

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

341
/*** globals ***/
342

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

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

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

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

372
unsigned int debugging = 0;
373

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

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

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

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

436
unsigned int fwmark = 0;
437

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

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

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

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

492
  Function: p_setsockopt
493

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

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

498
  Description:
499

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

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

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

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

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

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

523
    return res;
22✔
524
}
525

526
unsigned long strtoul_strict(const char *arg)
362✔
527
{
528
    char *endptr;
529
    unsigned long val;
530

531
    if (arg[0] == '-')
362✔
532
        usage(1);
26✔
533

534
    errno = 0;
344✔
535
    val = strtoul(arg, &endptr, 10);
344✔
536
    if (errno != 0 || arg == endptr || *endptr != '\0')
344✔
537
        usage(1);
77✔
538

539
    return val;
290✔
540
}
541

542
double strtod_strict(const char *arg)
275✔
543
{
544
    char *endptr;
545
    double val;
546

547
    errno = 0;
275✔
548
    val = strtod(arg, &endptr);
275✔
549
    if (errno != 0 || arg == endptr || *endptr != '\0' || val < 0)
275✔
550
        usage(1);
87✔
551

552
    return val;
217✔
553
}
554

555
/************************************************************
556

557
  Function: main
558

559
*************************************************************
560

561
  Inputs:  int argc, char** argv
562

563
  Description:
564

565
  Main program entry point
566

567
************************************************************/
568

569
int main(int argc, char **argv)
907✔
570
{
571
/* Debug: CPU Performance */
572
#if defined(DEBUG) || defined(_DEBUG)
573
    clock_t perf_cpu_start, perf_cpu_end;
574
    double perf_cpu_time_used;
575
    perf_cpu_start = clock();
576
#endif /* DEBUG || _DEBUG */
577

578
    int c;
579
    char *endptr;
580
    const uid_t suid = geteuid();
907✔
581
    int tos = 0;
907✔
582
    struct optparse optparse_state;
583
#ifdef USE_SIGACTION
584
    struct sigaction act;
585
#endif
586

587
    /* pre-parse -h/--help, so that we also can output help information
588
     * without trying to open the socket, which might fail */
589
    prog = argv[0];
907✔
590
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
907✔
591
        usage(0);
3✔
592
    }
1✔
593

594
    socket4 = open_ping_socket_ipv4(&socktype4);
905✔
595
#ifdef __linux__
596
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
597
     * structure is missing in the message.
598
     */
599
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
606✔
600
#endif
601

602
#ifdef IPV6
603
    socket6 = open_ping_socket_ipv6(&socktype6);
905✔
604
    /* if called (sym-linked) via 'fping6', imply '-6'
605
     * for backward compatibility */
606
    if (strstr(prog, "fping6")) {
905✔
607
        hints_ai_family = AF_INET6;
2✔
608
    }
609
#endif
610

611
    memset(&src_addr, 0, sizeof(src_addr));
905✔
612
#ifdef IPV6
613
    memset(&src_addr6, 0, sizeof(src_addr6));
905✔
614
#endif
615

616
    if (!suid && suid != getuid()) {
905✔
617
        /* *temporarily* drop privileges */
618
        if (seteuid(getuid()) == -1)
818✔
619
            perror("cannot setuid");
×
620
    }
224✔
621

622
    optparse_init(&optparse_state, argv);
905✔
623
    ident4 = ident6 = htons(getpid() & 0xFFFF);
905✔
624
    verbose_flag = 1;
905✔
625
    backoff_flag = 1;
905✔
626
    opterr = 1;
905✔
627

628
    /* get command line options */
629

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

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

787
        case 't':
54✔
788
            timeout = strtod_strict(optparse_state.optarg) * 1000000;
63✔
789
            timeout_flag = 1;
51✔
790
            break;
51✔
791

792
        case 'r':
42✔
793
            retry = (unsigned int)strtoul_strict(optparse_state.optarg);
48✔
794
            break;
40✔
795

796
        case 'i':
36✔
797
            interval = strtod_strict(optparse_state.optarg) * 1000000;
45✔
798
            break;
33✔
799

800
        case 'p':
88✔
801
            perhost_interval = strtod_strict(optparse_state.optarg) * 1000000;
120✔
802
            break;
108✔
803

804
        case 'c':
100✔
805
            count = (unsigned int)strtoul_strict(optparse_state.optarg);
136✔
806
            if (!count)
128✔
807
                usage(1);
3✔
808

809
            count_flag = 1;
126✔
810
            break;
126✔
811

812
        case 'C':
40✔
813
            count = (unsigned int)strtoul_strict(optparse_state.optarg);
57✔
814
            if (!count)
49✔
815
                usage(1);
3✔
816

817
            count_flag = 1;
47✔
818
            report_all_rtts_flag = 1;
47✔
819
            break;
47✔
820

821
        case 'b':
18✔
822
            ping_data_size = (unsigned int)strtoul_strict(optparse_state.optarg);
27✔
823
            size_flag = 1;
19✔
824
            break;
19✔
825

826
        case 'h':
2✔
827
            usage(0);
3✔
828
            break;
1✔
829

830
        case 'q':
26✔
831
            verbose_flag = 0;
38✔
832
            quiet_flag = 1;
38✔
833
            break;
38✔
834

835
        case 'Q':
32✔
836
            verbose_flag = 0;
40✔
837
            quiet_flag = 1;
40✔
838
            errno = 0;
40✔
839
            opt_val_double = strtod(optparse_state.optarg, &endptr);
40✔
840
            if (errno != 0 || optparse_state.optarg == endptr || (*endptr != '\0' && *endptr != ','))
40✔
841
                usage(1);
12✔
842
            if (opt_val_double < 0) {
32✔
843
                usage(1);
6✔
844
            }
2✔
845
            report_interval = opt_val_double * 1e9;
28✔
846

847
            /* recognize keyword(s) after number, ignore everything else */
848
            {
849
                char *comma = strchr(optparse_state.optarg, ',');
28✔
850
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
28✔
851
                    cumulative_stats_flag = 1;
4✔
852
                }
853
            }
854

855
            break;
28✔
856

857
        case 'e':
6✔
858
            elapsed_flag = 1;
8✔
859
            break;
8✔
860

861
        case 'm':
862
            multif_flag = 1;
×
863
            break;
×
864

865
        case 'N':
2✔
866
            netdata_flag = 1;
3✔
867
            break;
3✔
868

869
        case 'n':
4✔
870
            name_flag = 1;
6✔
871
            if (rdns_flag) {
6✔
872
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
873
                exit(1);
3✔
874
            }
875
            break;
3✔
876

877
        case 'd':
6✔
878
            rdns_flag = 1;
9✔
879
            if (name_flag) {
9✔
880
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
881
                exit(1);
3✔
882
            }
883
            break;
6✔
884

885
        case 'A':
2✔
886
            addr_flag = 1;
3✔
887
            break;
3✔
888

889
        case 'B':
16✔
890
            backoff = strtod_strict(optparse_state.optarg);
23✔
891
            if (backoff == 0.0)
13✔
UNCOV
892
                usage(1);
×
893

894
            break;
13✔
895

896
        case 's':
8✔
897
            stats_flag = 1;
12✔
898
            break;
12✔
899

900
        case 'D':
22✔
901
            timestamp_flag = 1;
33✔
902
            break;
33✔
903

904
        case 'R':
4✔
905
            random_data_flag = 1;
5✔
906
            break;
5✔
907

908
        case 'l':
12✔
909
            loop_flag = 1;
17✔
910
            backoff_flag = 0;
17✔
911
            break;
17✔
912

913
        case 'u':
8✔
914
            unreachable_flag = 1;
12✔
915
            break;
12✔
916

917
        case 'a':
18✔
918
            alive_flag = 1;
26✔
919
            break;
26✔
920

921
        case 'H':
14✔
922
            ttl = (unsigned int)strtoul_strict(optparse_state.optarg);
21✔
923
            if (!ttl)
13✔
924
                usage(1);
3✔
925
            break;
11✔
926

927
#if defined(DEBUG) || defined(_DEBUG)
928
        case 'z':
929
            errno = 0;
930
            debugging = (unsigned int)strtoul(optparse_state.optarg, &endptr, 0);
931
            if (errno != 0 || optparse_state.optarg == endptr || *endptr != '\0')
932
                usage(1);
933

934
            break;
935
#endif /* DEBUG || _DEBUG */
936

937
        case 'v':
4✔
938
            printf("%s: Version %s\n", prog, VERSION);
6✔
939
            exit(0);
6✔
940

941
        case 'x':
14✔
942
            min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg);
21✔
943
            if (!min_reachable)
13✔
944
                usage(1);
3✔
945
            break;
11✔
946

947
        case 'X':
12✔
948
            min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg);
18✔
949
            if (!min_reachable)
10✔
950
                usage(1);
3✔
951
            fast_reachable = 1;
8✔
952
            break;
8✔
953

954
        case 'f':
18✔
955
            filename = optparse_state.optarg;
27✔
956
            break;
27✔
957
#ifdef SO_MARK
958
        case 'k':
16✔
959
            fwmark = (unsigned int)strtoul_strict(optparse_state.optarg);
16✔
960
            if (!fwmark)
8✔
961
                usage(1);
2✔
962

963
            if (socket4 >= 0)
6✔
964
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
965
                    perror("fwmark ipv4");
4✔
966

967
#ifdef IPV6
968
            if (socket6 >= 0)
6✔
969
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
970
                    perror("fwmark ipv6");
4✔
971
#endif
972

973
            break;
6✔
974
#endif
975

976
        case 'g':
106✔
977
            /* use IP list generation */
978
            /* mutually exclusive with using file input or command line targets */
979
            generate_flag = 1;
129✔
980
            break;
129✔
981

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

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

1019
        case 'J':
42✔
1020
            json_flag = 1;
62✔
1021
            break;
62✔
1022

1023
        case 'T':
2✔
1024
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
1025
            break;
3✔
1026

1027
        case 'O':
12✔
1028
            {
1029
                unsigned long val = strtoul_strict(optparse_state.optarg);
18✔
1030
                if (val > 255)
12✔
NEW
1031
                    usage(1);
×
1032
                tos = (int)val;
12✔
1033
            }
1034
            if (socket4 >= 0) {
12✔
1035
                if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
9✔
NEW
1036
                    perror("setting type of service octet IP_TOS");
×
1037
                }
1038
            }
3✔
1039
#if defined(IPV6) && defined(IPV6_TCLASS)
1040
            if (socket6 >= 0) {
12✔
1041
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
9✔
NEW
1042
                    perror("setting type of service octet IPV6_TCLASS");
×
1043
                }
1044
            }
3✔
1045
#endif
1046
            break;
12✔
1047

1048
        case 'o':
6✔
1049
            outage_flag = 1;
7✔
1050
            break;
7✔
1051

1052
        case '?':
4✔
1053
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
1054
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
1055
            exit(1);
6✔
1056
            break;
1057
        }
1058
    }
1059

1060
    /* permanently drop privileges */
1061
    if (suid != getuid() && setuid(getuid())) {
566✔
1062
        perror("fatal: failed to permanently drop privileges");
×
1063
        /* continuing would be a security hole */
1064
        exit(4);
×
1065
    }
1066

1067
    /* validate various option settings */
1068

1069
#ifndef IPV6
1070
    if (socket4 < 0) {
1071
        crash_and_burn("can't create socket (must run as root?)");
1072
    }
1073
#else
1074
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
566✔
1075
        crash_and_burn("can't create socket (must run as root?)");
×
1076
    }
1077
#endif
1078

1079
    if (ttl > 255) {
564✔
1080
        fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
3✔
1081
        exit(1);
3✔
1082
    }
1083

1084
    if (unreachable_flag && alive_flag) {
561✔
1085
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
1086
        exit(1);
3✔
1087
    }
1088

1089
    if (count_flag && loop_flag) {
558✔
1090
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
1091
        exit(1);
3✔
1092
    }
1093

1094
    if (json_flag && !count_flag && !loop_flag) {
555✔
1095
        fprintf(stderr, "%s: option -J, --json requires -c, -C, or -l\n", prog);
3✔
1096
        exit(1);
3✔
1097
    }
1098

1099
    if (interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
552✔
1100
        fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
3✔
1101
        exit(1);
3✔
1102
    }
1103

1104
    if (perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
549✔
1105
        fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
3✔
1106
        exit(1);
3✔
1107
    }
1108

1109
    if (ping_data_size > MAX_PING_DATA) {
546✔
1110
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
1111
            prog, ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
1112
        exit(1);
3✔
1113
    }
1114

1115
    if ((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) {
543✔
1116
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1117
            prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1118
        exit(1);
6✔
1119
    }
1120

1121
    if (icmp_request_typ == 13 && size_flag != 0) {
537✔
1122
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1123
        exit(1);
9✔
1124
    }
1125

1126
    if (count_flag) {
528✔
1127
        if (verbose_flag)
157✔
1128
            per_recv_flag = 1;
99✔
1129

1130
        alive_flag = unreachable_flag = verbose_flag = 0;
157✔
1131
    }
41✔
1132

1133
    if (loop_flag) {
528✔
1134
        if (!report_interval)
14✔
1135
            per_recv_flag = 1;
12✔
1136

1137
        alive_flag = unreachable_flag = verbose_flag = 0;
14✔
1138
    }
4✔
1139

1140
    if (alive_flag || unreachable_flag || min_reachable)
528✔
1141
        verbose_flag = 0;
37✔
1142

1143
    trials = (count > retry + 1) ? count : retry + 1;
544✔
1144

1145
    /* auto-tune default timeout for count/loop modes
1146
     * see also github #32 */
1147
    if (loop_flag || count_flag) {
544✔
1148
        if (!timeout_flag) {
171✔
1149
            timeout = perhost_interval;
159✔
1150
            if (timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
159✔
1151
                timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1152
            }
1153
        }
43✔
1154
    }
45✔
1155

1156
#if defined(DEBUG) || defined(_DEBUG)
1157
    if (debugging & DBG_TRACE)
1158
        trace_flag = 1;
1159

1160
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1161
        randomly_lose_flag = 1;
1162
        lose_factor = 1; /* ie, 1/4 */
1163
    }
1164

1165
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1166
        randomly_lose_flag = 1;
1167
        lose_factor = 5; /* ie, 3/4 */
1168
    }
1169

1170
    if (debugging & DBG_PRINT_PER_SYSTEM)
1171
        print_per_system_flag = 1;
1172

1173
    if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
1174
        report_all_rtts_flag = 1;
1175

1176
    if (trace_flag) {
1177
        fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %.0f ms\n",
1178
            prog, count, retry, interval / 1e6);
1179
        fprintf(stderr, "  perhost_interval: %.0f ms, timeout: %.0f\n",
1180
            perhost_interval / 1e6, timeout / 1e6);
1181
        fprintf(stderr, "  seqmap_timeout: %.0f\n", seqmap_timeout / 1e6);
1182
        fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
1183
            ping_data_size, trials);
1184

1185
        if (verbose_flag)
1186
            fprintf(stderr, "  verbose_flag set\n");
1187
        if (multif_flag)
1188
            fprintf(stderr, "  multif_flag set\n");
1189
        if (name_flag)
1190
            fprintf(stderr, "  name_flag set\n");
1191
        if (addr_flag)
1192
            fprintf(stderr, "  addr_flag set\n");
1193
        if (stats_flag)
1194
            fprintf(stderr, "  stats_flag set\n");
1195
        if (unreachable_flag)
1196
            fprintf(stderr, "  unreachable_flag set\n");
1197
        if (alive_flag)
1198
            fprintf(stderr, "  alive_flag set\n");
1199
        if (elapsed_flag)
1200
            fprintf(stderr, "  elapsed_flag set\n");
1201
        if (version_flag)
1202
            fprintf(stderr, "  version_flag set\n");
1203
        if (count_flag)
1204
            fprintf(stderr, "  count_flag set\n");
1205
        if (loop_flag)
1206
            fprintf(stderr, "  loop_flag set\n");
1207
        if (backoff_flag)
1208
            fprintf(stderr, "  backoff_flag set\n");
1209
        if (per_recv_flag)
1210
            fprintf(stderr, "  per_recv_flag set\n");
1211
        if (report_all_rtts_flag)
1212
            fprintf(stderr, "  report_all_rtts_flag set\n");
1213
        if (randomly_lose_flag)
1214
            fprintf(stderr, "  randomly_lose_flag set\n");
1215
        if (print_per_system_flag)
1216
            fprintf(stderr, "  print_per_system_flag set\n");
1217
        if (outage_flag)
1218
            fprintf(stderr, "  outage_flag set\n");
1219
        if (netdata_flag)
1220
            fprintf(stderr, "  netdata_flag set\n");
1221
        if (json_flag)
1222
            fprintf(stderr, "  json_flag set\n");
1223
    }
1224
#endif /* DEBUG || _DEBUG */
1225

1226
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1227
    if (ttl > 0) {
544✔
1228
        if (socket4 >= 0) {
3✔
1229
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
3✔
1230
                perror("setting time to live");
×
1231
            }
1232
        }
1✔
1233
#ifdef IPV6
1234
        if (socket6 >= 0) {
3✔
1235
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
3✔
1236
                perror("setting time to live");
×
1237
            }
1238
        }
1✔
1239
#endif
1240
    }
1✔
1241

1242
#if HAVE_SO_TIMESTAMPNS
1243
    {
1244
        int opt = 1;
400✔
1245
        if (socket4 >= 0) {
400✔
1246
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
400✔
1247
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1248
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1249
                }
1250
            }
1251
        }
1252
#ifdef IPV6
1253
        if (socket6 >= 0) {
400✔
1254
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
400✔
1255
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1256
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1257
                }
1258
            }
1259
        }
1260
#endif
1261
    }
1262
#endif
1263

1264
    update_current_time();
544✔
1265
    start_time = current_time_ns;
544✔
1266

1267
    /* handle host names supplied on command line or in a file */
1268
    /* if the generate_flag is on, then generate the IP list */
1269

1270
    argv = &argv[optparse_state.optind];
544✔
1271
    argc -= optparse_state.optind;
544✔
1272

1273
    /* calculate how many ping can be in-flight per host */
1274
    if (count_flag) {
544✔
1275
        event_storage_count = count;
172✔
1276
    }
56✔
1277
    else if (loop_flag) {
372✔
1278
        if (perhost_interval > timeout) {
14✔
1279
            event_storage_count = 1;
×
1280
        }
1281
        else {
1282
            event_storage_count = 1 + timeout / perhost_interval;
14✔
1283
        }
1284
    }
4✔
1285
    else {
1286
        event_storage_count = 1;
358✔
1287
    }
1288

1289
    /* file and generate are mutually exclusive */
1290
    /* file and command line are mutually exclusive */
1291
    /* generate requires command line parameters beyond the switches */
1292
    if ((*argv && filename) || (filename && generate_flag) || (generate_flag && !*argv))
544✔
1293
        usage(1);
19✔
1294

1295
    /* if no conditions are specified, then assume input from stdin */
1296
    if (!*argv && !filename && !generate_flag)
526✔
1297
        filename = "-";
69✔
1298

1299
    if (*argv && !generate_flag) {
526✔
1300
        while (*argv) {
675✔
1301
            add_name(*argv);
368✔
1302
            ++argv;
368✔
1303
        }
1304
    }
75✔
1305
    else if (filename) {
219✔
1306
        FILE *ping_file;
1307
        char line[MAX_TARGET_NAME_LEN + 1];
1308
        char host[MAX_TARGET_NAME_LEN + 1];
1309
        char scratch[MAX_TARGET_NAME_LEN + 1];
1310
        int skip, non_empty;
1311

1312
        if (strcmp(filename, "-") == 0)
96✔
1313
            ping_file = fdopen(0, "r");
72✔
1314
        else
1315
            ping_file = fopen(filename, "r");
24✔
1316

1317
        if (!ping_file)
96✔
1318
            errno_crash_and_burn("fopen");
3✔
1319

1320
        /*
1321
         * Read the first word of every non-comment line, skip everything else.
1322
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1323
         * character is a '#' are interpreted as comments and ignored.)
1324
        */
1325
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1326
            skip = non_empty = 0;
309✔
1327

1328
            /* skip over a prefix of the line where sscanf finds nothing */
1329
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1330
                continue;
99✔
1331
            }
1332

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

1384
            if (non_empty)
186✔
1385
                add_name(host);
144✔
1386
        }
1387

1388
        fclose(ping_file);
70✔
1389
    }
24✔
1390
    else if (*argv && generate_flag) {
123✔
1391
        if (argc == 1) {
123✔
1392
            /* one target: we expect a cidr range (n.n.n.n/m) */
1393
            add_cidr(argv[0]);
68✔
1394
        }
14✔
1395
        else if (argc == 2) {
55✔
1396
            add_range(argv[0], argv[1]);
52✔
1397
        }
6✔
1398
        else {
1399
            usage(1);
3✔
1400
        }
1401
    }
21✔
1402
    else {
1403
        usage(1);
×
1404
    }
1405

1406
    if (!num_hosts) {
440✔
1407
        exit(num_noaddress ? 2 : 1);
36✔
1408
    }
1409

1410
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
404✔
1411
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1412
    }
2✔
1413
#ifdef IPV6
1414
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
402✔
1415
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1416
    }
1417
#endif
1418

1419
    /* allocate and initialize array to map host nr to host_entry */
1420
    {
1421
        struct event *cursor = event_queue_ping.first;
400✔
1422
        int i = 0;
400✔
1423
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
400✔
1424
        if (!table)
400✔
1425
            crash_and_burn("Can't malloc array of hosts");
×
1426
        /* initialize table of hosts. we know that we have ping events scheduled
1427
         * for each of them */
1428
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
987✔
1429
            table[i] = cursor->host;
587✔
1430
            cursor->host->i = i;
587✔
1431
            i++;
587✔
1432
        }
155✔
1433
    }
1434

1435
    init_ping_buffer_ipv4(ping_data_size);
400✔
1436
#ifdef IPV6
1437
    init_ping_buffer_ipv6(ping_data_size);
400✔
1438
#endif
1439

1440
#ifdef USE_SIGACTION
1441
    memset(&act, 0, sizeof(act));
400✔
1442
    act.sa_handler = signal_handler;
400✔
1443
    sigemptyset(&act.sa_mask);
400✔
1444
    sigaddset(&act.sa_mask, SIGINT);
400✔
1445
    sigaddset(&act.sa_mask, SIGQUIT);
400✔
1446
    act.sa_flags = SA_RESTART;
400✔
1447
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
400✔
1448
        crash_and_burn("failure to set signal handler");
30✔
1449
    }
30✔
1450
#else
1451
    signal(SIGINT, signal_handler);
1452
    signal(SIGQUIT, signal_handler);
1453
#endif
1454
    setlinebuf(stdout);
370✔
1455

1456
    // Last time we updated current_time_ns was before adding the hosts and
1457
    // possibly doing DNS resolution, which means that it isn't accurate
1458
    // anymore.
1459
    update_current_time();
370✔
1460
    if (report_interval) {
370✔
1461
        next_report_time = current_time_ns + report_interval;
22✔
1462
    }
2✔
1463

1464
    last_send_time = 0;
370✔
1465

1466
    seqmap_init(seqmap_timeout);
370✔
1467

1468
    /* main loop */
1469
    main_loop();
370✔
1470

1471
/* Debug: CPU Performance */
1472
#if defined(DEBUG) || defined(_DEBUG)
1473
    perf_cpu_end = clock();
1474
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1475
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1476
#endif /* DEBUG || _DEBUG */
1477

1478
    finish();
370✔
1479

1480
    return 0;
80✔
1481
}
1482

1483
static inline int64_t timespec_ns(struct timespec *a)
6,707✔
1484
{
1485
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
6,707✔
1486
}
1487

1488
#if HAVE_SO_TIMESTAMPNS
1489
/* convert a struct timeval to nanoseconds */
1490
static inline int64_t timeval_ns(struct timeval *a)
1491
{
1492
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1493
}
1494
#endif /* HAVE_SO_TIMESTAMPNS */
1495

1496
void add_cidr(char *addr)
67✔
1497
{
1498
    char *addr_end;
1499
    char *mask_str;
1500
    unsigned long mask;
1501
    int ret;
1502
    struct addrinfo addr_hints;
1503
    struct addrinfo *addr_res;
1504
    unsigned long net_addr;
1505
#ifdef IPV6
1506
    uint64_t net_upper, net_lower;
1507
    char *scope_str;
1508
#endif /* IPV6 */
1509

1510
    /* Split address from mask */
1511
    addr_end = strrchr(addr, '/');
67✔
1512
    if (addr_end == NULL) {
67✔
1513
        usage(1);
3✔
1514
    }
1✔
1515
    mask_str = addr_end + 1;
65✔
1516

1517
#ifdef IPV6
1518
    /* IPv6 addresses can have a scope */
1519
    scope_str = strchr(addr, '%');
65✔
1520
    if (scope_str && mask_str < scope_str) {
65✔
1521
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1522
        exit(1);
2✔
1523
    }
1524
#endif /*IPV6 */
1525

1526
    *addr_end = '\0';
63✔
1527
    char *endptr;
1528
    errno = 0;
63✔
1529
    mask = strtoul(mask_str, &endptr, 10);
63✔
1530
    if (errno != 0 || mask_str == endptr || *endptr != '\0')
63✔
1531
        usage(1);
13✔
1532

1533
    /* parse address */
1534
    memset(&addr_hints, 0, sizeof(struct addrinfo));
53✔
1535
    addr_hints.ai_family = hints_ai_family;
53✔
1536
    addr_hints.ai_flags = AI_NUMERICHOST;
53✔
1537
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
53✔
1538
    if (ret) {
53✔
1539
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1540
        exit(1);
7✔
1541
    }
1542
    if (addr_res->ai_family == AF_INET) {
46✔
1543
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
24✔
1544
        freeaddrinfo(addr_res);
24✔
1545
        add_cidr_ipv4(net_addr, mask);
24✔
1546
#ifdef IPV6
1547
    } else if (addr_res->ai_family == AF_INET6) {
30✔
1548
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1549
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1550
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1551
        freeaddrinfo(addr_res);
22✔
1552
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1553
#endif /* IPV6 */
1554
    } else {
1555
        freeaddrinfo(addr_res);
×
1556
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1557
        exit(1);
×
1558
    }
1559
}
30✔
1560

1561
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1562
{
1563
    unsigned long bitmask;
1564
    unsigned long net_last;
1565

1566
    /* check mask */
1567
    if (mask < 1 || mask > 32) {
24✔
1568
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1569
        exit(1);
6✔
1570
    }
1571

1572
    /* convert mask integer from 1 to 32 to a bitmask */
1573
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1574

1575
    /* calculate network range */
1576
    net_addr &= bitmask;
18✔
1577
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1578

1579
    /* exclude network and broadcast address for regular prefixes */
1580
    if (mask < 31) {
18✔
1581
        net_last--;
12✔
1582
        net_addr++;
12✔
1583
    }
4✔
1584

1585
    /* add all hosts in that network (net_addr and net_last inclusive) */
1586
    add_addr_range_ipv4(net_addr, net_last);
18✔
1587
}
16✔
1588

1589
#ifdef IPV6
1590
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1591
{
1592
    uint64_t bitmask_lower;
1593
    uint64_t last_lower;
1594

1595
    /* check mask -- 2^63 addresses should suffice for now */
1596
    if (mask < 65 || mask > 128) {
23✔
1597
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1598
        exit(1);
7✔
1599
    }
1600

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

1604
    /* calculate network range */
1605
    net_lower &= bitmask_lower;
16✔
1606
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1607

1608
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1609
}
12✔
1610
#endif /* IPV6 */
1611

1612
void add_range(char *start, char *end)
52✔
1613
{
1614
    struct addrinfo addr_hints;
1615
    struct addrinfo *addr_res;
1616
    unsigned long start_long;
1617
    unsigned long end_long;
1618
    int ret;
1619
#ifdef IPV6
1620
    uint64_t start_upper, start_lower;
1621
    uint64_t end_upper, end_lower;
1622
    char *start_scope_str, *end_scope_str;
1623

1624
    /*
1625
     * The compiler does not know that setting the address family hint to
1626
     * ensure that start and end are from the same address family also
1627
     * ensures that either start_long and end_long are initialized and used,
1628
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1629
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1630
     * supported to suppress compiler warnings.
1631
     */
1632
    start_long = -1;
52✔
1633
    end_long = 0;
52✔
1634
    start_upper = start_lower = -1;
52✔
1635
    end_upper = end_lower = 0;
52✔
1636
    start_scope_str = end_scope_str = NULL;
52✔
1637
#endif /* IPV6 */
1638

1639
    /* parse start address */
1640
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1641
    addr_hints.ai_family = hints_ai_family;
52✔
1642
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1643
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1644
    if (ret) {
52✔
1645
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1646
        exit(1);
9✔
1647
    }
1648
    /* start and end must be from the same address family */
1649
    hints_ai_family = addr_res->ai_family;
43✔
1650
    if (addr_res->ai_family == AF_INET) {
43✔
1651
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1652
        freeaddrinfo(addr_res);
17✔
1653
#ifdef IPV6
1654
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1655
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1656
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1657
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1658
        freeaddrinfo(addr_res);
26✔
1659
#endif /* IPV6 */
1660
    } else {
1661
        freeaddrinfo(addr_res);
×
1662
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1663
        exit(1);
×
1664
    }
1665

1666
#ifdef IPV6
1667
    /* IPv6 addresses can have a scope */
1668
    if (hints_ai_family == AF_INET6) {
43✔
1669
        start_scope_str = strchr(start, '%');
26✔
1670
        end_scope_str = strchr(end, '%');
26✔
1671
        if ((!start_scope_str && end_scope_str) ||
26✔
1672
            (start_scope_str && !end_scope_str) ||
24✔
1673
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1674
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1675
                exit(1);
6✔
1676
        }
1677
    }
1678
#endif
1679

1680
    /* parse end address */
1681
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1682
    addr_hints.ai_family = hints_ai_family;
37✔
1683
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1684
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1685
    if (ret) {
37✔
1686
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1687
        exit(1);
7✔
1688
    }
1689
    if (addr_res->ai_family == AF_INET) {
30✔
1690
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1691
        freeaddrinfo(addr_res);
12✔
1692
        add_addr_range_ipv4(start_long, end_long);
12✔
1693
#ifdef IPV6
1694
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1695
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1696
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1697
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1698
        freeaddrinfo(addr_res);
18✔
1699
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1700
#endif /* IPV6 */
1701
    } else {
1702
        freeaddrinfo(addr_res);
×
1703
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1704
        exit(1);
×
1705
    }
1706
}
24✔
1707

1708
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1709
{
1710
    /* check if generator limit is exceeded */
1711
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1712
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1713
        exit(1);
6✔
1714
    }
1715

1716
    /* generate */
1717
    for (; start_long <= end_long; start_long++) {
93✔
1718
        struct in_addr in_addr_tmp;
1719
        char buffer[20];
1720
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1721
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1722
        add_name(buffer);
69✔
1723
    }
23✔
1724
}
24✔
1725

1726
#ifdef IPV6
1727
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1728
{
1729
    int i;
1730
    uint64_t ret = 0;
134✔
1731
    for (i = 0; i < 8; i++) {
1,206✔
1732
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1733
    }
16✔
1734
    return ret;
134✔
1735
}
1736

1737
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1738
{
1739
    int i;
1740
    for (i = 0; i < 8; i++) {
720✔
1741
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1742
    }
1743
}
80✔
1744

1745
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1746
                         uint64_t end_upper, uint64_t end_lower,
1747
                         const char *scope_str)
1748
{
1749
    struct in6_addr in6_addr_tmp;
1750
    char buffer[100];
1751

1752
    /* prevent generating too many addresses */
1753
    if ((start_upper + 1 < end_upper) ||
34✔
1754
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1755
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1756
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1757
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1758
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1759
        exit(1);
8✔
1760
    }
1761

1762
    while ((start_upper < end_upper) ||
66✔
1763
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1764
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1765
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1766
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1767
        if (scope_str) {
40✔
1768
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1769
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1770
                exit(1);
×
1771
            }
1772
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1773
        }
1774
        add_name(buffer);
40✔
1775
        start_lower++;
40✔
1776
        if (start_lower == 0) {
40✔
1777
            start_upper++;
2✔
1778
        }
1779
    }
1780
}
26✔
1781
#endif /* IPv6 */
1782

1783
void main_loop()
385✔
1784
{
1785
    int64_t lt;
1786
    int64_t wait_time_ns;
1787
    struct event *event;
1788
    struct host_entry *h;
1789

1790
    while (event_queue_ping.first || event_queue_timeout.first) {
2,113✔
1791
        dbg_printf("%s", "# main_loop\n");
1792

1793
        /* timeout event ? */
1794
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,759✔
1795
            event = ev_dequeue(&event_queue_timeout);
101✔
1796
            h = event->host;
101✔
1797

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

1800
            stats_add(h, event->ping_index, 0, -1);
101✔
1801

1802
            if (per_recv_flag) {
101✔
1803
                print_timeout(h, event->ping_index);
25✔
1804
            }
7✔
1805

1806
            /* do we need to send a retry? */
1807
            if (!loop_flag && !count_flag) {
101✔
1808
                if (h->num_sent < retry + 1) {
60✔
1809
                    if (backoff_flag) {
35✔
1810
                        h->timeout *= backoff;
35✔
1811
                    }
9✔
1812
                    send_ping(h, event->ping_index);
35✔
1813
                }
9✔
1814
            }
14✔
1815

1816
            /* note: we process first timeout events, because we might need to
1817
             * wait to process ping events, while we for sure never need to
1818
             * wait for timeout events.
1819
             */
1820
            continue;
101✔
1821
        }
1822

1823
        /* ping event ? */
1824
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,919✔
1825
            /* Make sure that we don't ping more than once every "interval" */
1826
            lt = current_time_ns - last_send_time;
1,264✔
1827
            if (lt < interval)
1,264✔
1828
                goto wait_for_reply;
267✔
1829

1830
            /* Dequeue the event */
1831
            event = ev_dequeue(&event_queue_ping);
997✔
1832
            h = event->host;
997✔
1833

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

1836
            /* Send the ping */
1837
            send_ping(h, event->ping_index);
997✔
1838

1839
            /* Loop and count mode: schedule next ping */
1840
            if (loop_flag || (count_flag && event->ping_index + 1 < count)) {
997✔
1841
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + perhost_interval);
439✔
1842
            }
115✔
1843
        }
261✔
1844

1845
    wait_for_reply:
731✔
1846

1847
        /* When is the next ping next event? */
1848
        wait_time_ns = -1;
1,658✔
1849
        if (event_queue_ping.first) {
1,658✔
1850
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,219✔
1851
            if (wait_time_ns < 0)
1,219✔
1852
                wait_time_ns = 0;
564✔
1853
            /* make sure that we wait enough, so that the inter-ping delay is
1854
             * bigger than 'interval' */
1855
            if (wait_time_ns < interval) {
1,219✔
1856
                lt = current_time_ns - last_send_time;
570✔
1857
                if (lt < interval) {
570✔
1858
                    wait_time_ns = interval - lt;
570✔
1859
                }
214✔
1860
            }
214✔
1861

1862
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1863
        }
331✔
1864

1865
        /* When is the next timeout event? */
1866
        if (event_queue_timeout.first) {
1,658✔
1867
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
1,045✔
1868
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
1,045✔
1869
                wait_time_ns = wait_time_timeout;
433✔
1870
                if (wait_time_ns < 0) {
433✔
1871
                    wait_time_ns = 0;
×
1872
                }
1873
            }
107✔
1874

1875
            dbg_printf("next timeout event in %.0f ms (%s)\n", wait_time_timeout / 1e6, event_queue_timeout.first->host->host);
1876
        }
277✔
1877

1878
        /* When is the next report due? */
1879
        if (report_interval && (loop_flag || count_flag)) {
1,658✔
1880
            int64_t wait_time_next_report = next_report_time - current_time_ns;
213✔
1881
            if (wait_time_next_report < wait_time_ns) {
213✔
1882
                wait_time_ns = wait_time_next_report;
50✔
1883
                if (wait_time_ns < 0) {
50✔
1884
                    wait_time_ns = 0;
×
1885
                }
1886
            }
2✔
1887

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

1891
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1892
        if (wait_time_ns == -1) {
1,658✔
1893
            break;
14✔
1894
        }
1895

1896
        /* end of loop was requested by interrupt signal handler */
1897
        if (finish_requested) {
1,644✔
1898
            break;
17✔
1899
        }
1900

1901
        /* Receive replies */
1902
        /* (this is what sleeps during each loop iteration) */
1903
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1904
        if (wait_for_reply(wait_time_ns)) {
1,627✔
1905
            while (wait_for_reply(0))
1,481✔
1906
                ; /* process other replies in the queue */
1907
        }
249✔
1908

1909
        update_current_time();
1,627✔
1910

1911
        if (status_snapshot) {
1,627✔
1912
            status_snapshot = 0;
3✔
1913
            if (json_flag)
3✔
1914
                print_per_system_splits_json();
×
1915
            else
1916
                print_per_system_splits();
3✔
1917
        }
1✔
1918

1919
        /* Print report */
1920
        if (report_interval && (loop_flag || count_flag) && (current_time_ns >= next_report_time)) {
1,627✔
1921
            if (netdata_flag) {
36✔
1922
                print_netdata();
3✔
1923
            }
1✔
1924
            else if (json_flag) {
33✔
1925
                print_per_system_splits_json();
3✔
1926
            }
1✔
1927
            else {
1928
                print_per_system_splits();
30✔
1929
            }
1930

1931
            while (current_time_ns >= next_report_time) {
72✔
1932
                next_report_time += report_interval;
36✔
1933
            }
1934
        }
2✔
1935
    }
1936
}
385✔
1937

1938
/************************************************************
1939

1940
  Function: signal_handler
1941

1942
*************************************************************
1943

1944
  Inputs:  int signum
1945

1946
  Description:
1947

1948
  SIGQUIT signal handler - set flag and return
1949
  SIGINT signal handler - set flag and return
1950

1951
************************************************************/
1952

1953
void signal_handler(int signum)
17✔
1954
{
1955
    switch (signum) {
17✔
1956
    case SIGINT:
10✔
1957
        finish_requested = 1;
14✔
1958
        break;
14✔
1959

1960
    case SIGQUIT:
2✔
1961
        status_snapshot = 1;
3✔
1962
        break;
3✔
1963
    }
1964
}
17✔
1965

1966
/************************************************************
1967

1968
  Function: update_current_time
1969

1970
*************************************************************/
1971

1972
void update_current_time()
5,475✔
1973
{
1974
    clock_gettime(CLOCKID, &current_time);
5,475✔
1975
    current_time_ns = timespec_ns(&current_time);
5,475✔
1976
}
5,475✔
1977

1978
/************************************************************
1979

1980
  Function: finish
1981

1982
*************************************************************
1983

1984
  Inputs:  void (none)
1985

1986
  Description:
1987

1988
  Main program clean up and exit point
1989

1990
************************************************************/
1991

1992
void finish()
385✔
1993
{
1994
    int i;
1995
    HOST_ENTRY *h;
1996

1997
    update_current_time();
385✔
1998
    end_time = current_time_ns;
385✔
1999

2000
    /* tot up unreachables */
2001
    for (i = 0; i < num_hosts; i++) {
972✔
2002
        h = table[i];
587✔
2003

2004
        if (!h->num_recv) {
587✔
2005
            num_unreachable++;
87✔
2006

2007
            if (verbose_flag || unreachable_flag) {
87✔
2008
                printf("%s", h->host);
47✔
2009

2010
                if (verbose_flag)
47✔
2011
                    printf(" is unreachable");
44✔
2012

2013
                printf("\n");
47✔
2014
            }
3✔
2015
        }
13✔
2016
    }
155✔
2017

2018
    if (count_flag || loop_flag) {
385✔
2019
        if (json_flag)
168✔
2020
            print_per_system_stats_json();
56✔
2021
        else
2022
            print_per_system_stats();
112✔
2023
    }
44✔
2024
#if defined(DEBUG) || defined(_DEBUG)
2025
    else if (print_per_system_flag) {
2026
        if (json_flag)
2027
            print_per_system_stats_json();
2028
        else
2029
            print_per_system_stats();
2030
    }
2031
#endif /* DEBUG || _DEBUG */
2032

2033
    if (stats_flag) {
385✔
2034
        if (json_flag)
12✔
2035
            print_global_stats_json();
3✔
2036
        else
2037
            print_global_stats();
9✔
2038
    }
4✔
2039

2040
    if (min_reachable) {
385✔
2041
        if ((num_hosts - num_unreachable) >= min_reachable) {
9✔
2042
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
6✔
2043
            exit(0);
6✔
2044
        }
2045
        else {
2046
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts - num_unreachable);
3✔
2047
            exit(1);
3✔
2048
        }
2049
    }
2050

2051
    if (num_noaddress)
376✔
2052
        exit(2);
3✔
2053
    else if (num_alive != num_hosts)
373✔
2054
        exit(1);
48✔
2055

2056
    exit(0);
325✔
2057
}
2058

2059
/************************************************************
2060

2061
  Function: print_recv
2062

2063
*************************************************************
2064

2065
  Inputs: HOST_ENTRY *h, int64_t recv_time, int result,
2066
          int this_count, int64_t this_reply, int avg
2067

2068
  Description:
2069

2070
************************************************************/
2071

2072
void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) {
278✔
2073
    if (json_flag) {
278✔
2074
        printf("{\"resp\": {");
35✔
2075

2076
        if (timestamp_flag)
35✔
2077
            print_timestamp_format(recv_time, timestamp_format_flag);
15✔
2078

2079
        printf("\"host\": \"%s\", ", h->host);
35✔
2080
        printf("\"seq\": %d, ", this_count);
35✔
2081
        printf("\"size\": %d, ", result);
35✔
2082
        printf("\"rtt\": %s", sprint_tm(this_reply));
35✔
2083
        return;
35✔
2084
    }
2085

2086
    /* Normal Output */
2087
    if (timestamp_flag)
243✔
2088
        print_timestamp_format(recv_time, timestamp_format_flag);
30✔
2089

2090
    printf("%-*s : [%d], %d bytes, %s ms",
243✔
2091
        max_hostname_len, h->host, this_count, result, sprint_tm(this_reply));
75✔
2092

2093
    printf(" (%s avg, ", sprint_tm(avg));
243✔
2094

2095
    if (h->num_recv <= h->num_sent) {
243✔
2096
        printf("%d%% loss)",
243✔
2097
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
243✔
2098
    }
75✔
2099
    else {
2100
        printf("%d%% return)",
×
2101
            (h->num_recv_total * 100) / h->num_sent);
×
2102
    }
2103
}
86✔
2104

2105
/************************************************************
2106

2107
  Function: print_timeout
2108

2109
*************************************************************
2110

2111
  Inputs: HOST_ENTRY *h, int ping_index
2112

2113
  Description:
2114

2115
************************************************************/
2116

2117
void print_timeout(HOST_ENTRY *h, int ping_index) {
25✔
2118
    if (json_flag) {
25✔
2119
        printf("{\"timeout\": {");
6✔
2120
        if (timestamp_flag)
6✔
2121
            print_timestamp_format(current_time_ns, timestamp_format_flag);
×
2122

2123
        printf("\"host\": \"%s\", ", h->host);
6✔
2124
        printf("\"seq\": %d", ping_index);
6✔
2125
        printf("}}\n");
6✔
2126
        return;
6✔
2127
    }
2128

2129
    /* Normal Output */
2130
    if (timestamp_flag)
19✔
2131
        print_timestamp_format(current_time_ns, timestamp_format_flag);
×
2132

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

2136
    if (h->num_recv > 0) {
19✔
2137
        printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv));
×
2138
    }
2139
    else {
2140
        printf(" (NaN avg, ");
19✔
2141
    }
2142

2143
    if (h->num_recv <= h->num_sent) {
19✔
2144
        printf("%d%% loss)",
19✔
2145
            ((h->num_sent - h->num_recv) * 100) / h->num_sent);
19✔
2146
    }
5✔
2147
    else {
2148
        printf("%d%% return)",
×
2149
            (h->num_recv_total * 100) / h->num_sent);
×
2150
    }
2151
    printf("\n");
19✔
2152
}
7✔
2153

2154
/************************************************************
2155

2156
  Function: print_recv_ext
2157

2158
*************************************************************
2159

2160
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2161
           int64_t recv_time, int64_t this_reply
2162

2163
  Description:
2164

2165
************************************************************/
2166

2167
void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { 
542✔
2168
    if (icmp_request_typ == 13) {
542✔
2169
        printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u",
28✔
2170
            alive_flag ? "" : ",",
14✔
2171
            ip_header_res->otime_ms, ip_header_res->rtime_ms, ip_header_res->ttime_ms,
2172
            ms_since_midnight_utc(recv_time));
2173
    }
2174

2175
    if(print_tos_flag) {
542✔
2176
        if(ip_header_res->tos != -1) {
60✔
2177
            printf(" (TOS %d)", ip_header_res->tos);
60✔
2178
        }
16✔
2179
        else {
2180
            printf(" (TOS unknown)");
×
2181
        }
2182
    }
16✔
2183

2184
    if (print_ttl_flag) {
542✔
2185
        if(ip_header_res->ttl != -1) {
60✔
2186
            printf(" (TTL %d)", ip_header_res->ttl);
60✔
2187
        }
16✔
2188
        else {
2189
            printf(" (TTL unknown)");
×
2190
        }
2191
    }
16✔
2192

2193
    if (elapsed_flag && !per_recv_flag)
542✔
2194
        printf(" (%s ms)", sprint_tm(this_reply));
8✔
2195
    
2196
    printf("\n");
542✔
2197
}
542✔
2198

2199
/************************************************************
2200

2201
  Function: print_recv_ext_json
2202

2203
*************************************************************
2204

2205
  Inputs:  IP_HEADER_RESULT *ip_header_res,
2206
           int64_t recv_time, int64_t this_reply
2207

2208
  Description:
2209

2210
************************************************************/
2211

2212
void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) {
35✔
2213
    if (icmp_request_typ == 13) {
35✔
2214
        printf(", \"timestamps\": {");
2✔
2215
        printf("\"originate\": %u, ", ip_header_res->otime_ms);
2✔
2216
        printf("\"receive\": %u, ", ip_header_res->rtime_ms);
2✔
2217
        printf("\"transmit\": %u, ", ip_header_res->ttime_ms);
2✔
2218
        printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time));
2✔
2219
    }
2220

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

2230
    if (print_ttl_flag) {
35✔
2231
        if(ip_header_res->ttl != -1) {
6✔
2232
            printf(", \"ttl\": %d", ip_header_res->ttl);
6✔
2233
        }
2✔
2234
        else {
2235
            printf(", \"ttl\": -1");
×
2236
        }
2237
    }
2✔
2238

2239
    if (elapsed_flag && !per_recv_flag)
35✔
2240
        printf(" (%s ms)", sprint_tm(this_reply));
×
2241

2242
    printf("}}");
35✔
2243
    printf("\n");
35✔
2244
}
35✔
2245

2246
/************************************************************
2247

2248
  Function: print_per_system_stats
2249

2250
*************************************************************
2251

2252
  Inputs:  void (none)
2253

2254
  Description:
2255

2256

2257
************************************************************/
2258

2259
void print_per_system_stats(void)
112✔
2260
{
2261
    int i, j, avg, outage_ms;
2262
    HOST_ENTRY *h;
2263
    int64_t resp;
2264

2265
    if (verbose_flag || per_recv_flag)
112✔
2266
        fprintf(stderr, "\n");
76✔
2267

2268
    for (i = 0; i < num_hosts; i++) {
258✔
2269
        h = table[i];
146✔
2270
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
146✔
2271

2272
        if (report_all_rtts_flag) {
146✔
2273
            for (j = 0; j < h->num_sent; j++) {
248✔
2274
                if ((resp = h->resp_times[j]) >= 0)
204✔
2275
                    fprintf(stderr, " %s", sprint_tm(resp));
196✔
2276
                else
2277
                    fprintf(stderr, " -");
8✔
2278
            }
64✔
2279

2280
            fprintf(stderr, "\n");
44✔
2281
        }
12✔
2282
        else {
2283
            if (h->num_recv <= h->num_sent) {
102✔
2284
                fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
126✔
2285
                    h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
102✔
2286

2287
                if (outage_flag) {
102✔
2288
                    /* Time outage total */
2289
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
7✔
2290
                    fprintf(stderr, ", outage(ms) = %d", outage_ms);
7✔
2291
                }
1✔
2292
            }
24✔
2293
            else {
2294
                fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2295
                    h->num_sent, h->num_recv,
2296
                    h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2297
            }
2298

2299
            if (h->num_recv) {
102✔
2300
                avg = h->total_time / h->num_recv;
93✔
2301
                fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
93✔
2302
                fprintf(stderr, "/%s", sprint_tm(avg));
93✔
2303
                fprintf(stderr, "/%s", sprint_tm(h->max_reply));
93✔
2304
            }
23✔
2305

2306
            fprintf(stderr, "\n");
102✔
2307
        }
2308
    }
36✔
2309
}
112✔
2310

2311
/************************************************************
2312

2313
  Function: print_per_system_stats_json
2314

2315
*************************************************************
2316

2317
  Inputs:  void (none)
2318

2319
  Description:
2320

2321

2322
************************************************************/
2323

2324
void print_per_system_stats_json(void)
56✔
2325
{
2326
    int i, j, avg, outage_ms;
2327
    HOST_ENTRY *h;
2328
    int64_t resp;
2329

2330
    for (i = 0; i < num_hosts; i++) {
118✔
2331
        h = table[i];
62✔
2332

2333
        if (report_all_rtts_flag)
62✔
2334
            fprintf(stdout, "{\"vSum\": {");
18✔
2335
        else
2336
            fprintf(stdout, "{\"summary\": {");
44✔
2337

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

2340
        if (report_all_rtts_flag) {
62✔
2341
            fprintf(stdout, "\"values\": [");
18✔
2342
            for (j = 0; j < h->num_sent; j++) {
42✔
2343
                if (j > 0)
24✔
2344
                  fprintf(stdout, ", ");
6✔
2345
                
2346
                if ((resp = h->resp_times[j]) >= 0)
24✔
2347
                    fprintf(stdout, "%s", sprint_tm(resp));
21✔
2348
                else
2349
                    fprintf(stdout, "null");
3✔
2350
            }
8✔
2351

2352
            fprintf(stdout, "]}");
18✔
2353
        }
6✔
2354
        else {
2355
            if (h->num_recv <= h->num_sent) {
44✔
2356
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
44✔
2357
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
44✔
2358
                fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0);
44✔
2359

2360
                if (outage_flag) {
44✔
2361
                    /* Time outage total */
2362
                    outage_ms = (h->num_sent - h->num_recv) * perhost_interval / 1e6;
×
2363
                    fprintf(stdout, ", \"outage(ms)\": %d", outage_ms);
×
2364
                }
2365
            }
14✔
2366
            else {
2367
                fprintf(stdout, "\"xmt\": %d, ", h->num_sent);
×
2368
                fprintf(stdout, "\"rcv\": %d, ", h->num_recv);
×
2369
                fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
×
2370
            }
2371

2372
            if (h->num_recv) {
44✔
2373
                avg = h->total_time / h->num_recv;
41✔
2374
                fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply));
41✔
2375
                fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg));
41✔
2376
                fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply));
41✔
2377
            }
13✔
2378

2379
            fprintf(stdout, "}");
44✔
2380
        }
2381
        fprintf(stdout, "}\n");
62✔
2382
    }
20✔
2383
}
56✔
2384

2385
/************************************************************
2386

2387
  Function: print_netdata
2388

2389
*************************************************************
2390

2391
  Inputs:  void (none)
2392

2393
  Description:
2394

2395

2396
************************************************************/
2397

2398
void print_netdata(void)
3✔
2399
{
2400
    static int sent_charts = 0;
2401

2402
    int i;
2403
    int64_t avg;
2404
    HOST_ENTRY *h;
2405

2406
    for (i = 0; i < num_hosts; i++) {
6✔
2407
        h = table[i];
3✔
2408

2409
        if (!sent_charts) {
3✔
2410
            printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2411
            printf("DIMENSION xmt sent absolute 1 1\n");
3✔
2412
            printf("DIMENSION rcv received absolute 1 1\n");
3✔
2413
        }
1✔
2414

2415
        printf("BEGIN fping.%s_packets\n", h->name);
3✔
2416
        printf("SET xmt = %d\n", h->num_sent_i);
3✔
2417
        printf("SET rcv = %d\n", h->num_recv_i);
3✔
2418
        printf("END\n");
3✔
2419

2420
        if (!sent_charts) {
3✔
2421
            printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2422
            printf("DIMENSION returned '' absolute 1 1\n");
3✔
2423
            /* printf("DIMENSION lost '' absolute 1 1\n"); */
2424
        }
1✔
2425

2426
        printf("BEGIN fping.%s_quality\n", h->name);
3✔
2427
        /*
2428
        if( h->num_recv_i <= h->num_sent_i )
2429
            printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 );
2430
        else
2431
            printf("SET lost = 0\n");
2432
*/
2433

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

2437
        if (!sent_charts) {
3✔
2438
            printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
3✔
2439
            printf("DIMENSION min minimum absolute 1 1000000\n");
3✔
2440
            printf("DIMENSION max maximum absolute 1 1000000\n");
3✔
2441
            printf("DIMENSION avg average absolute 1 1000000\n");
3✔
2442
        }
1✔
2443

2444
        printf("BEGIN fping.%s_latency\n", h->name);
3✔
2445
        if (h->num_recv_i) {
3✔
2446
            avg = h->total_time_i / h->num_recv_i;
3✔
2447
            printf("SET min = %" PRId64 "\n", h->min_reply_i);
3✔
2448
            printf("SET avg = %" PRId64 "\n", avg);
3✔
2449
            printf("SET max = %" PRId64 "\n", h->max_reply_i);
3✔
2450
        }
1✔
2451
        printf("END\n");
3✔
2452

2453
        stats_reset_interval(h);
3✔
2454
    }
1✔
2455

2456
    sent_charts = 1;
3✔
2457
}
3✔
2458

2459
/************************************************************
2460

2461
  Function: print_per_system_splits
2462

2463
*************************************************************
2464

2465
  Inputs:  void (none)
2466

2467
  Description:
2468

2469

2470
************************************************************/
2471

2472
void print_per_system_splits(void)
33✔
2473
{
2474
    int i, avg, outage_ms_i;
2475
    HOST_ENTRY *h;
2476
    struct tm *curr_tm;
2477

2478
    if (verbose_flag || per_recv_flag)
33✔
2479
        fprintf(stderr, "\n");
3✔
2480

2481
    update_current_time();
33✔
2482
    curr_tm = localtime((time_t *)&current_time.tv_sec);
33✔
2483
    fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
34✔
2484
        curr_tm->tm_min, curr_tm->tm_sec);
1✔
2485

2486
    for (i = 0; i < num_hosts; i++) {
66✔
2487
        h = table[i];
33✔
2488
        fprintf(stderr, "%-*s :", max_hostname_len, h->host);
33✔
2489

2490
        if (h->num_recv_i <= h->num_sent_i) {
33✔
2491
            fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
34✔
2492
                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✔
2493

2494
            if (outage_flag) {
33✔
2495
                /* Time outage  */
2496
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
8✔
2497
                fprintf(stderr, ", outage(ms) = %d", outage_ms_i);
8✔
2498
            }
2499
        }
1✔
2500
        else {
2501
            fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
×
2502
                h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2503
        }
2504

2505
        if (h->num_recv_i) {
33✔
2506
            avg = h->total_time_i / h->num_recv_i;
25✔
2507
            fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
25✔
2508
            fprintf(stderr, "/%s", sprint_tm(avg));
25✔
2509
            fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
25✔
2510
        }
1✔
2511

2512
        fprintf(stderr, "\n");
33✔
2513
        if (!cumulative_stats_flag) {
33✔
2514
            stats_reset_interval(h);
25✔
2515
        }
1✔
2516
    }
1✔
2517
}
33✔
2518

2519
/************************************************************
2520

2521
  Function: print_per_system_splits_json
2522

2523
*************************************************************
2524

2525
  Inputs:  void (none)
2526

2527
  Description:
2528

2529

2530
************************************************************/
2531

2532
void print_per_system_splits_json(void)
3✔
2533
{
2534
    int i, avg, outage_ms_i;
2535
    HOST_ENTRY *h;
2536

2537
    update_current_time();
3✔
2538

2539
    for (i = 0; i < num_hosts; i++) {
6✔
2540
        h = table[i];
3✔
2541
        fprintf(stdout, "{\"intSum\": {");
3✔
2542
        fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec);
3✔
2543
        fprintf(stdout, "\"host\": \"%s\", ", h->host);
3✔
2544

2545
        if (h->num_recv_i <= h->num_sent_i) {
3✔
2546
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
3✔
2547
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
3✔
2548
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0);
3✔
2549

2550
            if (outage_flag) {
3✔
2551
                /* Time outage  */
2552
                outage_ms_i = (h->num_sent_i - h->num_recv_i) * perhost_interval / 1e6;
×
2553
                fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i);
×
2554
            }
2555
        }
1✔
2556
        else {
2557
            fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i);
×
2558
            fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i);
×
2559
            fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0);
×
2560
        }
2561

2562
        if (h->num_recv_i) {
3✔
2563
            avg = h->total_time_i / h->num_recv_i;
3✔
2564
            fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i));
3✔
2565
            fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg));
3✔
2566
            fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i));
3✔
2567
        }
1✔
2568

2569
        fprintf(stdout, "}}\n");
3✔
2570
        if (!cumulative_stats_flag) {
3✔
2571
            stats_reset_interval(h);
3✔
2572
        }
1✔
2573
    }
1✔
2574
}
3✔
2575

2576
/************************************************************
2577

2578
  Function: print_global_stats
2579

2580
*************************************************************
2581

2582
  Inputs:  void (none)
2583

2584
  Description:
2585

2586

2587
************************************************************/
2588

2589
void print_global_stats(void)
9✔
2590
{
2591
    fprintf(stderr, "\n");
9✔
2592
    fprintf(stderr, " %7d targets\n", num_hosts);
9✔
2593
    fprintf(stderr, " %7d alive\n", num_alive);
9✔
2594
    fprintf(stderr, " %7d unreachable\n", num_unreachable);
9✔
2595
    fprintf(stderr, " %7d unknown addresses\n", num_noaddress);
9✔
2596
    fprintf(stderr, "\n");
9✔
2597
    fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout);
9✔
2598
    fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent);
9✔
2599
    fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived);
9✔
2600
    fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd);
9✔
2601
    fprintf(stderr, "\n");
9✔
2602

2603
    if (total_replies == 0) {
9✔
2604
        min_reply = 0;
3✔
2605
        max_reply = 0;
3✔
2606
        total_replies = 1;
3✔
2607
        sum_replies = 0;
3✔
2608
    }
1✔
2609

2610
    fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply));
9✔
2611
    fprintf(stderr, " %s ms (avg round trip time)\n",
12✔
2612
        sprint_tm(sum_replies / total_replies));
3✔
2613
    fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
9✔
2614
    fprintf(stderr, " %12.3f sec (elapsed real time)\n",
12✔
2615
        (end_time - start_time) / 1e9);
9✔
2616
    fprintf(stderr, "\n");
9✔
2617
}
9✔
2618

2619
/************************************************************
2620

2621
  Function: print_global_stats_json
2622

2623
*************************************************************
2624

2625
  Inputs:  void (none)
2626

2627
  Description:
2628

2629

2630
************************************************************/
2631

2632
void print_global_stats_json(void)
3✔
2633
{
2634
    fprintf(stdout, "{\"stats\": {");
3✔
2635
    fprintf(stdout, "\"targets\": %d, ", num_hosts);
3✔
2636
    fprintf(stdout, "\"alive\": %d, ", num_alive);
3✔
2637
    fprintf(stdout, "\"unreachable\": %d, ", num_unreachable);
3✔
2638
    fprintf(stdout, "\"unknownAddresses\": %d, ", num_noaddress);
3✔
2639
    fprintf(stdout, "\"timeouts\": %d, ", num_timeout);
3✔
2640
    fprintf(stdout, "\"icmpEchosSent\": %d, ", num_pingsent);
3✔
2641
    fprintf(stdout, "\"icmpEchoRepliesReceived\": %d, ", num_pingreceived);
3✔
2642
    fprintf(stdout, "\"otherIcmpReceived\": %d, ", num_othericmprcvd);
3✔
2643

2644
    if (total_replies == 0) {
3✔
2645
        min_reply = 0;
×
2646
        max_reply = 0;
×
2647
        total_replies = 1;
×
2648
        sum_replies = 0;
×
2649
    }
2650

2651
    fprintf(stdout, "\"rttMin\": %s, ", sprint_tm(min_reply));
3✔
2652
    fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(sum_replies / total_replies));
3✔
2653
    fprintf(stdout, "\"rttMax\": %s, ", sprint_tm(max_reply));
3✔
2654
    fprintf(stdout, "\"elapsed\": %.3f", (end_time - start_time) / 1e9);
3✔
2655
    fprintf(stdout, "}}\n");
3✔
2656
}
3✔
2657

2658
/************************************************************
2659

2660
  Function: send_ping
2661

2662
*************************************************************
2663

2664
  Inputs:  int s, HOST_ENTRY *h
2665

2666
  Description:
2667

2668
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
2669
  will be added on by the kernel.  The ID field is our UNIX process ID,
2670
  and the sequence number is an index into an array of outstanding
2671
  ping requests. The sequence number will later be used to quickly
2672
  figure out who the ping reply came from.
2673

2674
************************************************************/
2675

2676
int send_ping(HOST_ENTRY *h, int index)
1,032✔
2677
{
2678
    int n;
2679
    int myseq;
2680
    int ret = 1;
1,032✔
2681
    uint8_t proto = ICMP_ECHO;
1,032✔
2682

2683
    update_current_time();
1,032✔
2684
    h->last_send_time = current_time_ns;
1,032✔
2685
    myseq = seqmap_add(h->i, index, current_time_ns);
1,032✔
2686

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

2689
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
1,032✔
2690
        if(icmp_request_typ == 13)
911✔
2691
            proto = ICMP_TSTAMP;
18✔
2692
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
911✔
2693
    }
255✔
2694
#ifdef IPV6
2695
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
121✔
2696
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
121✔
2697
    }
15✔
2698
#endif
2699
    else {
2700
        return 0;
×
2701
    }
2702

2703
    /* error sending? */
2704
    if (
762✔
2705
        (n < 0)
270✔
2706
#if defined(EHOSTDOWN)
2707
        && errno != EHOSTDOWN
304✔
2708
#endif
2709
    ) {
2710
        if (verbose_flag) {
34✔
2711
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
2712
        }
2713
        else {
2714
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
2715
        }
2716

2717
        h->num_sent++;
34✔
2718
        h->num_sent_i++;
34✔
2719
        if (!loop_flag)
34✔
2720
            h->resp_times[index] = RESP_ERROR;
34✔
2721

2722
        ret = 0;
34✔
2723
    }
2724
    else {
2725
        /* schedule timeout */
2726
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
998✔
2727

2728
        /* mark this trial as outstanding */
2729
        if (!loop_flag) {
998✔
2730
            h->resp_times[index] = RESP_WAITING;
865✔
2731
        }
237✔
2732
    }
2733

2734
    num_pingsent++;
1,032✔
2735
    last_send_time = h->last_send_time;
1,032✔
2736

2737
    return (ret);
1,032✔
2738
}
270✔
2739

2740
int socket_can_read(struct timeval *timeout)
3,108✔
2741
{
2742
    int nfound;
2743
    fd_set readset;
2744
    int socketmax;
2745

2746
#ifndef IPV6
2747
    socketmax = socket4;
2748
#else
2749
    socketmax = socket4 > socket6 ? socket4 : socket6;
3,108✔
2750
#endif
2751

2752
select_again:
2,439✔
2753
    FD_ZERO(&readset);
42,149✔
2754
    if (socket4 >= 0)
3,125✔
2755
        FD_SET(socket4, &readset);
3,125✔
2756
#ifdef IPV6
2757
    if (socket6 >= 0)
3,125✔
2758
        FD_SET(socket6, &readset);
3,125✔
2759
#endif
2760

2761
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
3,125✔
2762
    if (nfound < 0) {
3,125✔
2763
        if (errno == EINTR) {
17✔
2764
            /* interrupted system call: redo the select */
2765
            goto select_again;
17✔
2766
        }
2767
        else {
2768
            perror("select");
×
2769
        }
2770
    }
2771

2772
    if (nfound > 0) {
3,108✔
2773
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
1,481✔
2774
            return socket4;
1,386✔
2775
        }
2776
#ifdef IPV6
2777
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
95✔
2778
            return socket6;
95✔
2779
        }
2780
#endif
2781
    }
2782

2783
    return -1;
1,627✔
2784
}
681✔
2785

2786
int receive_packet(int64_t wait_time,
3,108✔
2787
#if HAVE_SO_TIMESTAMPNS
2788
    int64_t *reply_timestamp,
2789
#else
2790
    int64_t *reply_timestamp __attribute__((unused)),
2791
#endif
2792
    struct sockaddr *reply_src_addr,
2793
    size_t reply_src_addr_len,
2794
    char *reply_buf,
2795
    size_t reply_buf_len,
2796
    int *ip_header_tos,
2797
    int *ip_header_ttl)
2798
{
2799
    struct timeval to;
2800
    int s = 0;
3,108✔
2801
    int recv_len;
2802
    static unsigned char msg_control[128];
2803
    struct iovec msg_iov = {
4,470✔
2804
        reply_buf,
681✔
2805
        reply_buf_len
681✔
2806
    };
2807
    struct msghdr recv_msghdr = {0};
3,108✔
2808
    recv_msghdr.msg_name = reply_src_addr;
3,108✔
2809
    recv_msghdr.msg_namelen = reply_src_addr_len;
3,108✔
2810
    recv_msghdr.msg_iov = &msg_iov;
3,108✔
2811
    recv_msghdr.msg_iovlen = 1;
3,108✔
2812
    recv_msghdr.msg_control = &msg_control;
3,108✔
2813
    recv_msghdr.msg_controllen = sizeof(msg_control);
3,108✔
2814
    struct cmsghdr *cmsg;
2815

2816
    /* Wait for a socket to become ready */
2817
    if (wait_time) {
3,108✔
2818
        to.tv_sec = wait_time / UINT64_C(1000000000);
1,627✔
2819
        to.tv_usec = (wait_time % UINT64_C(1000000000)) / 1000 + 1;
1,627✔
2820
    }
432✔
2821
    else {
2822
        to.tv_sec = 0;
1,481✔
2823
        to.tv_usec = 0;
1,481✔
2824
    }
2825
    s = socket_can_read(&to);
3,108✔
2826
    if (s == -1) {
3,108✔
2827
        return 0; /* timeout */
1,627✔
2828
    }
2829

2830
    recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC);
1,481✔
2831
    if (recv_len <= 0) {
1,481✔
2832
        return 0;
×
2833
    }
2834

2835
    /* ancilliary data */
2836
    {
2837
#if HAVE_SO_TIMESTAMPNS
2838
        struct timespec reply_timestamp_ts;
2839
        struct timeval reply_timestamp_tv;
2840
#endif
2841
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,481✔
2842
             cmsg != NULL;
2,721✔
2843
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,240✔
2844
#if HAVE_SO_TIMESTAMPNS
2845
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,240✔
2846
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,232✔
2847
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,232✔
2848
            }
2849
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,240✔
2850
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2851
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2852
            }
2853
#endif
2854
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,240✔
2855
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2856
            }
2857
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,240✔
2858
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2859
            }
2860
#ifdef IPV6
2861
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,240✔
2862
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2863
            }
2864
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,240✔
2865
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2866
            }
2867
#endif
2868
        }
2869
    }
2870

2871
#if defined(DEBUG) || defined(_DEBUG)
2872
    if (randomly_lose_flag) {
2873
        if ((random() & 0x07) <= lose_factor)
2874
            return 0;
2875
    }
2876
#endif
2877

2878
    return recv_len;
1,481✔
2879
}
681✔
2880

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

2895
    if (!success) {
984✔
2896
        if (!loop_flag && index >= 0) {
101✔
2897
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2898
        }
21✔
2899
        num_timeout++;
101✔
2900
        return;
101✔
2901
    }
2902

2903
    /* received count */
2904
    h->num_recv++;
883✔
2905
    h->num_recv_i++;
883✔
2906

2907
    /* maximum */
2908
    if (!h->max_reply || latency > h->max_reply) {
883✔
2909
        h->max_reply = latency;
647✔
2910
    }
185✔
2911
    if (!h->max_reply_i || latency > h->max_reply_i) {
883✔
2912
        h->max_reply_i = latency;
680✔
2913
    }
188✔
2914

2915
    /* minimum */
2916
    if (!h->min_reply || latency < h->min_reply) {
883✔
2917
        h->min_reply = latency;
551✔
2918
    }
157✔
2919
    if (!h->min_reply_i || latency < h->min_reply_i) {
883✔
2920
        h->min_reply_i = latency;
587✔
2921
    }
159✔
2922

2923
    /* total time (for average) */
2924
    h->total_time += latency;
883✔
2925
    h->total_time_i += latency;
883✔
2926

2927
    /* response time per-packet (count mode) */
2928
    if (!loop_flag && index >= 0) {
883✔
2929
        h->resp_times[index] = latency;
764✔
2930
    }
216✔
2931
}
266✔
2932

2933
/* stats_reset_interval: reset interval statistics
2934
 * h: host entry to update
2935
 */
2936
void stats_reset_interval(HOST_ENTRY *h)
31✔
2937
{
2938
    h->num_sent_i = 0;
31✔
2939
    h->num_recv_i = 0;
31✔
2940
    h->max_reply_i = 0;
31✔
2941
    h->min_reply_i = 0;
31✔
2942
    h->total_time_i = 0;
31✔
2943
}
31✔
2944

2945
int decode_icmp_ipv4(
1,386✔
2946
    struct sockaddr *response_addr,
2947
    size_t response_addr_len,
2948
    char *reply_buf,
2949
    size_t reply_buf_len,
2950
    unsigned short *id,
2951
    unsigned short *seq,
2952
    IP_HEADER_RESULT *ip_header_res)
2953
{
2954
    struct icmp *icp;
2955
    int hlen = 0;
1,386✔
2956

2957
    if (!using_sock_dgram4) {
1,386✔
2958
        struct ip *ip = (struct ip *)reply_buf;
1,378✔
2959
        ip_header_res->tos = ip->ip_tos;
1,378✔
2960
        ip_header_res->ttl = ip->ip_ttl;
1,378✔
2961

2962
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2963
        /* The alpha headers are decidedly broken.
2964
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2965
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2966
         */
2967
        hlen = (ip->ip_vhl & 0x0F) << 2;
2968
#else
2969
        hlen = ip->ip_hl << 2;
1,378✔
2970
#endif
2971
    }
234✔
2972

2973
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,386✔
2974
        /* too short */
2975
        if (verbose_flag) {
×
2976
            char buf[INET6_ADDRSTRLEN];
2977
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2978
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2979
        }
2980
        return -1;
×
2981
    }
2982

2983
    icp = (struct icmp *)(reply_buf + hlen);
1,386✔
2984

2985
    if ((icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,386✔
2986
        (icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
596✔
2987
        /* Handle other ICMP packets */
2988
        struct icmp *sent_icmp;
2989
        SEQMAP_VALUE *seqmap_value;
2990
        char addr_ascii[INET6_ADDRSTRLEN];
2991
        HOST_ENTRY *h;
2992

2993
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2994
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
1,040✔
2995
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2996
            return -1;
×
2997
        }
2998

2999
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
572✔
3000

3001
        if ((icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
572✔
3002
            (icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
3003
            sent_icmp->icmp_id != ident4) {
×
3004
            /* not caused by us */
3005
            return -1;
572✔
3006
        }
3007

3008
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
3009
        if (seqmap_value == NULL) {
×
3010
            return -1;
×
3011
        }
3012

3013
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3014

3015
        switch (icp->icmp_type) {
×
3016
        case ICMP_UNREACH:
3017
            h = table[seqmap_value->host_nr];
×
3018
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
3019
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
3020
                    addr_ascii, h->host);
3021
            }
3022
            else {
3023
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3024
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
3025
            }
3026

3027
            print_warning("\n");
×
3028
            num_othericmprcvd++;
×
3029
            break;
×
3030

3031
        case ICMP_SOURCEQUENCH:
3032
        case ICMP_REDIRECT:
3033
        case ICMP_TIMXCEED:
3034
        case ICMP_PARAMPROB:
3035
            h = table[seqmap_value->host_nr];
×
3036
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
3037
                print_warning("%s from %s for ICMP Echo sent to %s",
×
3038
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
3039
            }
3040
            else {
3041
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
3042
                    icp->icmp_type, addr_ascii, h->host);
×
3043
            }
3044
            print_warning("\n");
×
3045
            num_othericmprcvd++;
×
3046
            break;
×
3047
        }
3048

3049
        return -1;
×
3050
    }
3051

3052
    *id = icp->icmp_id;
814✔
3053
    *seq = ntohs(icp->icmp_seq);
814✔
3054
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
814✔
3055

3056
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
3057
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
3058
            if (verbose_flag) {
×
3059
                char buf[INET6_ADDRSTRLEN];
3060
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3061
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3062
            }
3063
            return -1;
×
3064
        }
3065

3066
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
3067
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
3068
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
3069
    }
3070

3071
    return hlen;
814✔
3072
}
234✔
3073

3074
#ifdef IPV6
3075
int decode_icmp_ipv6(
95✔
3076
    struct sockaddr *response_addr,
3077
    size_t response_addr_len,
3078
    char *reply_buf,
3079
    size_t reply_buf_len,
3080
    unsigned short *id,
3081
    unsigned short *seq)
3082
{
3083
    struct icmp6_hdr *icp;
3084

3085
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
95✔
3086
        if (verbose_flag) {
×
3087
            char buf[INET6_ADDRSTRLEN];
3088
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3089
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
3090
        }
3091
        return 0; /* too short */
×
3092
    }
3093

3094
    icp = (struct icmp6_hdr *)reply_buf;
95✔
3095

3096
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
95✔
3097
        /* Handle other ICMPv6 packets */
3098
        struct ip6_hdr *sent_ipv6;
3099
        struct icmp6_hdr *sent_icmp;
3100
        SEQMAP_VALUE *seqmap_value;
3101
        char addr_ascii[INET6_ADDRSTRLEN];
3102
        HOST_ENTRY *h;
3103

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

3110
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
6✔
3111
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
6✔
3112
            /* discard ICMPv6 message if we can't tell that it was caused by
3113
             * us, because the IPv6 header is not directly followed by an
3114
             * ICMPv6 header
3115
             */
3116
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
3117
            return 0;
×
3118
        }
3119
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
6✔
3120

3121
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
6✔
3122
            /* not caused by us */
3123
            return 0;
6✔
3124
        }
3125

3126
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
3127
        if (seqmap_value == NULL) {
×
3128
            return 0;
×
3129
        }
3130

3131
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3132
        h = table[seqmap_value->host_nr];
×
3133

3134
        switch (icp->icmp6_type) {
×
3135
        case ICMP6_DST_UNREACH:
3136
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
3137
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3138
                    icp->icmp6_code, addr_ascii, h->host);
×
3139
            } else {
3140
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3141
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
3142
            }
3143
            print_warning("\n");
×
3144
            num_othericmprcvd++;
×
3145
            break;
×
3146

3147
        case ICMP6_PACKET_TOO_BIG:
3148
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
3149
                addr_ascii, h->host);
3150
            num_othericmprcvd++;
×
3151
            break;
×
3152

3153
        case ICMP6_TIME_EXCEEDED:
3154
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
3155
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3156
                    icp->icmp6_code, addr_ascii, h->host);
×
3157
            } else {
3158
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3159
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
3160
            }
3161
            print_warning("\n");
×
3162
            num_othericmprcvd++;
×
3163
            break;
×
3164

3165
        case ICMP6_PARAM_PROB:
3166
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
3167
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
3168
                    icp->icmp6_code, addr_ascii, h->host);
×
3169
            } else {
3170
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
3171
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
3172
            }
3173
            print_warning("\n");
×
3174
            num_othericmprcvd++;
×
3175
            break;
×
3176

3177
        default:
3178
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
3179
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
3180
            num_othericmprcvd++;
×
3181
            break;
×
3182
        }
3183

3184
        return 0;
×
3185
    }
3186

3187
    *id = icp->icmp6_id;
89✔
3188
    *seq = ntohs(icp->icmp6_seq);
89✔
3189

3190
    return 1;
89✔
3191
}
15✔
3192
#endif
3193

3194
int wait_for_reply(int64_t wait_time)
3,108✔
3195
{
3196
    int result;
3197
    static char buffer[RECV_BUFSIZE];
3198
    struct sockaddr_storage response_addr;
3199
    int n, avg;
3200
    HOST_ENTRY *h;
3201
    int64_t this_reply;
3202
    int this_count;
3203
    int64_t recv_time = 0;
3,108✔
3204
    SEQMAP_VALUE *seqmap_value;
3205
    unsigned short id;
3206
    unsigned short seq;
3207
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
3,108✔
3208

3209
    /* Receive packet */
3210
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,789✔
3211
        &recv_time, /* reply_timestamp */
3212
        (struct sockaddr *)&response_addr, /* reply_src_addr */
3213
        sizeof(response_addr), /* reply_src_addr_len */
3214
        buffer, /* reply_buf */
3215
        sizeof(buffer), /* reply_buf_len */
3216
        &ip_header_res.tos, /* TOS resp. TC byte */
681✔
3217
        &ip_header_res.ttl /* TTL resp. hop limit */
681✔
3218
    );
3219

3220
    if (result <= 0) {
3,108✔
3221
        return 0;
1,627✔
3222
    }
3223

3224
    update_current_time();
1,481✔
3225
    if (recv_time == 0)
1,481✔
3226
        recv_time = current_time_ns;
249✔
3227

3228
    /* Process ICMP packet and retrieve id/seq */
3229
    if (response_addr.ss_family == AF_INET) {
1,481✔
3230
        int ip_hlen = decode_icmp_ipv4(
1,386✔
3231
            (struct sockaddr *)&response_addr,
3232
            sizeof(response_addr),
3233
            buffer,
3234
            sizeof(buffer),
3235
            &id,
3236
            &seq,
3237
            &ip_header_res);
3238
        if (ip_hlen < 0) {
1,386✔
3239
            return 1;
572✔
3240
        }
3241
        if (id != ident4) {
814✔
3242
            return 1; /* packet received, but not the one we are looking for! */
×
3243
        }
3244
        if (!using_sock_dgram4) {
814✔
3245
            /* do not include IP header in returned size, to be consistent with ping(8) and also
3246
             * with fping with IPv6 hosts */
3247
            result -= ip_hlen;
806✔
3248
        }
234✔
3249
    }
234✔
3250
#ifdef IPV6
3251
    else if (response_addr.ss_family == AF_INET6) {
95✔
3252
        if (!decode_icmp_ipv6(
95✔
3253
                (struct sockaddr *)&response_addr,
3254
                sizeof(response_addr),
3255
                buffer,
3256
                sizeof(buffer),
3257
                &id,
3258
                &seq)) {
3259
            return 1;
6✔
3260
        }
3261
        if (id != ident6) {
89✔
3262
            return 1; /* packet received, but not the one we are looking for! */
×
3263
        }
3264
    }
15✔
3265
#endif
3266
    else {
3267
        return 1;
×
3268
    }
3269

3270
    seqmap_value = seqmap_fetch(seq, current_time_ns);
903✔
3271
    if (seqmap_value == NULL) {
903✔
3272
        return 1;
12✔
3273
    }
3274

3275
    /* find corresponding host_entry */
3276
    n = seqmap_value->host_nr;
891✔
3277
    h = table[n];
891✔
3278
    this_count = seqmap_value->ping_count;
891✔
3279
    this_reply = recv_time - seqmap_value->ping_ts;
891✔
3280

3281
    /* update stats that include invalid replies */
3282
    h->num_recv_total++;
891✔
3283
    num_pingreceived++;
891✔
3284

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

3287
    /* optionally require reply source equal to target address */
3288
    if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
891✔
3289
        dbg_printf("%s\n", "discarding reply from wrong source address");
3290
        return 1;
8✔
3291
    }
3292

3293
    /* discard duplicates */
3294
    if (!loop_flag && !(count_flag && quiet_flag) && h->resp_times[this_count] >= 0) {
883✔
3295
        if (!per_recv_flag) {
×
3296
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
3297
                h->host, this_count, result, sprint_tm(this_reply));
3298

3299
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
3300
                char buf[INET6_ADDRSTRLEN];
3301
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
3302
                fprintf(stderr, " [<- %s]", buf);
×
3303
            }
3304
            fprintf(stderr, "\n");
×
3305
        }
3306
        return 1;
×
3307
    }
3308

3309
    /* discard reply if delay is larger than timeout
3310
     * (see also: github #32) */
3311
    if (this_reply > h->timeout) {
883✔
3312
        return 1;
×
3313
    }
3314

3315
    /* update stats */
3316
    stats_add(h, this_count, 1, this_reply);
883✔
3317
    // TODO: move to stats_add?
3318
    if (!max_reply || this_reply > max_reply)
883✔
3319
        max_reply = this_reply;
516✔
3320
    if (!min_reply || this_reply < min_reply)
883✔
3321
        min_reply = this_reply;
463✔
3322
    sum_replies += this_reply;
883✔
3323
    total_replies++;
883✔
3324

3325
    /* initialize timeout to initial timeout (without backoff) */
3326
    h->timeout = timeout;
883✔
3327

3328
    /* remove timeout event */
3329
    struct event *timeout_event = host_get_timeout_event(h, this_count);
883✔
3330
    if (timeout_event) {
883✔
3331
        ev_remove(&event_queue_timeout, timeout_event);
883✔
3332
    }
245✔
3333

3334
    /* print "is alive" */
3335
    if (h->num_recv == 1) {
883✔
3336
        num_alive++;
500✔
3337
        if (fast_reachable && num_alive >= min_reachable)
500✔
3338
            finish_requested = 1;
3✔
3339

3340
        if (verbose_flag || alive_flag) {
500✔
3341
            printf("%s", h->host);
299✔
3342

3343
            if (verbose_flag)
299✔
3344
                printf(" is alive");
279✔
3345
        }
85✔
3346
    }
142✔
3347

3348
    /* print received ping (unless --quiet) */
3349
    if (per_recv_flag) {
883✔
3350
        avg = h->total_time / h->num_recv;
278✔
3351
        print_recv(h,
364✔
3352
            recv_time,
86✔
3353
            result,
86✔
3354
            this_count,
86✔
3355
            this_reply,
86✔
3356
            avg);
86✔
3357
    }
86✔
3358

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

3379
/************************************************************
3380

3381
  Function: add_name
3382

3383
*************************************************************
3384

3385
  Inputs:  char* name
3386

3387
  Description:
3388

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

3393
************************************************************/
3394

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

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

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

3432
        return;
26✔
3433
    }
3434

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

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

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

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

3490
        if (!multif_flag) {
592✔
3491
            break;
592✔
3492
        }
3493
    }
3494

3495
    freeaddrinfo(res0);
592✔
3496
}
165✔
3497

3498
/************************************************************
3499

3500
  Function: add_addr
3501

3502
*************************************************************
3503

3504
  Description:
3505

3506
  add single address to list of hosts to be pinged
3507

3508
************************************************************/
3509

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

3516
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
592✔
3517
    if (!p)
592✔
3518
        crash_and_burn("can't allocate HOST_ENTRY");
×
3519

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

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

3536
    if (strlen(p->host) > max_hostname_len)
592✔
3537
        max_hostname_len = strlen(p->host);
399✔
3538

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

3549
        for (n = 0; n < trials; n++)
2,871✔
3550
            i[n] = RESP_UNUSED;
2,293✔
3551

3552
        p->resp_times = i;
578✔
3553
    }
152✔
3554

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

3565
    /* schedule first ping */
3566
    host_add_ping_event(p, 0, current_time_ns);
592✔
3567

3568
    num_hosts++;
592✔
3569
}
592✔
3570

3571
/************************************************************
3572

3573
  Function: crash_and_burn
3574

3575
*************************************************************
3576

3577
  Inputs:  char* message
3578

3579
  Description:
3580

3581
************************************************************/
3582

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

3589
/************************************************************
3590

3591
  Function: errno_crash_and_burn
3592

3593
*************************************************************
3594

3595
  Inputs:  char* message
3596

3597
  Description:
3598

3599
************************************************************/
3600

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

3607
/************************************************************
3608

3609
  Function: print_warning
3610

3611
  Description: fprintf(stderr, ...), unless running with -q
3612

3613
*************************************************************/
3614

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

3625
/************************************************************
3626

3627
  Function: sprint_tm
3628

3629
*************************************************************
3630

3631
  render nanosecond int64_t value into milliseconds string with three digits of
3632
  precision.
3633

3634
************************************************************/
3635

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

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

3665
    return (buf);
1,268✔
3666
}
3667

3668
/************************************************************
3669

3670
  Function: addr_cmp
3671

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

3689
    return 0;
×
3690
}
173✔
3691

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

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

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

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

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

3721
/************************************************************
3722

3723
  Function: ev_enqueue
3724

3725
  Enqueue an event
3726

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

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

3734
*************************************************************/
3735
void ev_enqueue(struct event_queue *queue, struct event *event)
2,029✔
3736
{
3737
    struct event *i;
3738
    struct event *i_prev;
3739

3740
    /* Empty list */
3741
    if (queue->last == NULL) {
2,029✔
3742
        event->ev_next = NULL;
1,639✔
3743
        event->ev_prev = NULL;
1,639✔
3744
        queue->first = event;
1,639✔
3745
        queue->last = event;
1,639✔
3746
        return;
1,639✔
3747
    }
3748

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

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

3778
/************************************************************
3779

3780
  Function: ev_dequeue
3781

3782
*************************************************************/
3783
struct event *ev_dequeue(struct event_queue *queue)
1,098✔
3784
{
3785
    struct event *dequeued;
3786

3787
    if (queue->first == NULL) {
1,098✔
3788
        return NULL;
×
3789
    }
3790
    dequeued = queue->first;
1,098✔
3791
    ev_remove(queue, dequeued);
1,098✔
3792

3793
    return dequeued;
1,098✔
3794
}
282✔
3795

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

3798
  Function: ev_remove
3799

3800
*************************************************************/
3801
void ev_remove(struct event_queue *queue, struct event *event)
1,981✔
3802
{
3803
    if (queue->first == event) {
1,981✔
3804
        queue->first = event->ev_next;
1,972✔
3805
    }
524✔
3806
    if (queue->last == event) {
1,981✔
3807
        queue->last = event->ev_prev;
1,612✔
3808
    }
414✔
3809
    if (event->ev_prev) {
1,981✔
3810
        event->ev_prev->ev_next = event->ev_next;
9✔
3811
    }
3✔
3812
    if (event->ev_next) {
1,981✔
3813
        event->ev_next->ev_prev = event->ev_prev;
369✔
3814
    }
113✔
3815
    event->ev_prev = NULL;
1,981✔
3816
    event->ev_next = NULL;
1,981✔
3817
}
1,981✔
3818

3819
/************************************************************
3820

3821
  Function: print_human_readable_time from current_time_ns
3822

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

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

3865
/************************************************************
3866

3867
  Function: ms_since_midnight_utc
3868

3869
*************************************************************
3870

3871
  Input: int64_t: current UTC time in ns
3872

3873
  Output: uint32_t: current time in ms since midnight UTC
3874

3875
  Description:
3876

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

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

3886
/************************************************************
3887

3888
  Function: usage
3889

3890
*************************************************************
3891

3892
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3893

3894
  Description:
3895

3896
************************************************************/
3897

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

© 2025 Coveralls, Inc