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

schweikert / fping / 25245190094

02 May 2026 05:52AM UTC coverage: 87.937% (-0.06%) from 88.0%
25245190094

Pull #469

github

web-flow
Merge e0217dc92 into 780ec4674
Pull Request #469: Options variables converted to a structure

208 of 222 new or added lines in 6 files covered. (93.69%)

3 existing lines in 1 file now uncovered.

1684 of 1915 relevant lines covered (87.94%)

347.5 hits per line

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

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

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

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

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

52
#include "seqmap.h"
53
#include "output.h"
54
#include "stats.h"
55

56
#ifdef HAVE_UNISTD_H
57
#include <unistd.h>
58
#endif /* HAVE_UNISTD_H */
59

60
#ifdef HAVE_STDLIB_H
61
#include <stdlib.h>
62
#endif /* HAVE_STDLIB_H */
63

64
#include <stddef.h>
65
#include <string.h>
66

67
#include <sys/socket.h>
68
#include <sys/time.h>
69
#include <sys/types.h>
70

71
#if HAVE_SYS_FILE_H
72
#include <sys/file.h>
73
#endif /* HAVE_SYS_FILE_H */
74

75
#ifdef IPV6
76
#include <netinet/icmp6.h>
77
#include <netinet/ip6.h>
78
#endif
79
#include <netinet/in_systm.h>
80

81
#include <netinet/ip.h>
82
#include <netinet/ip_icmp.h>
83

84
#include <arpa/inet.h>
85
#include <ctype.h>
86
#include <netdb.h>
87

88
#include <sys/select.h>
89

90
/*** compatibility ***/
91

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

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

110
/*** externals ***/
111

112
extern char *optarg;
113
extern int optind, opterr;
114
#ifndef h_errno
115
extern int h_errno;
116
#endif
117

118
#ifdef __cplusplus
119
}
120
#endif /* __cplusplus */
121

122
/*** Constants ***/
123

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

132
#if !defined(CLOCKID)
133
#if defined(CLOCK_MONOTONIC)
134
#define CLOCKID CLOCK_MONOTONIC
135
#else
136
#define CLOCKID CLOCK_REALTIME
137
#endif
138
#endif
139

140
/*** Ping packet defines ***/
141

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

147
#define MAX_GENERATE 131072 /* maximum number of hosts that -g can generate */
148
#define MAX_TARGET_NAME_LEN 255 /* maximum target name length read from file */
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
IP_HEADER_RESULT default_ip_header_result() {
3,127✔
267
    return (IP_HEADER_RESULT){-1, -1, 0x80000000U, 0x80000000U, 0x80000000U};
3,127✔
268
}
269

270
int event_storage_count;
271

272
/*** globals ***/
273

274
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
275

276
/* we keep two separate queues: a ping queue, for when the next ping should be
277
 * sent, and a timeout queue. the reason for having two separate queues is that
278
 * the ping period and the timeout value are different, so if we put them in
279
 * the same event queue, we would need to scan many more entries when inserting
280
 * into the sorted list.
281
 */
282
struct event_queue event_queue_ping;
283
struct event_queue event_queue_timeout;
284

285
char *prog;
286
int ident4 = 0; /* our icmp identity field */
287
int ident6 = 0;
288
const int sock_opt_on = 1; /* to activate a socket option */
289
int socket4 = -1;
290
int socktype4 = -1;
291
int using_sock_dgram4 = 0;
292
#ifndef IPV6
293
int hints_ai_family = AF_INET;
294
#else
295
int socket6 = -1;
296
int socktype6 = -1;
297
int hints_ai_family = AF_UNSPEC;
298
#endif
299

300
volatile sig_atomic_t status_snapshot = 0;
301
volatile sig_atomic_t finish_requested = 0;
302

303
unsigned int debugging = 0;
304

305
unsigned int trials;
306
int64_t report_interval = 0;
307
int src_addr_set = 0;
308
struct in_addr src_addr;
309
#ifdef IPV6
310
int src_addr6_set = 0;
311
struct in6_addr src_addr6;
312
#endif
313

314
/* global stats */
315
int64_t max_reply = 0;
316
int64_t min_reply = 0;
317
int64_t total_replies = 0;
318
int64_t sum_replies = 0;
319
int max_hostname_len = 0;
320
int num_hosts = 0; /* total number of hosts */
321
int num_alive = 0, /* total number alive */
322
    num_unreachable = 0, /* total number unreachable */
323
    num_noaddress = 0; /* total number of addresses not found */
324
int num_timeout = 0, /* number of times select timed out */
325
    num_pingsent = 0, /* total pings sent */
326
    num_pingreceived = 0, /* total pings received */
327
    num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
328

329
struct timespec current_time; /* current time (pseudo) */
330
int64_t current_time_ns;
331
int64_t start_time;
332
int64_t end_time;
333
int64_t last_send_time; /* time last ping was sent */
334
int64_t next_report_time; /* time next -Q report is expected */
335

336
unsigned int fwmark = 0;
337

338
char *filename = NULL; /* file containing hosts to ping */
339

340
/************************************************************
341

342
  Function: p_setsockopt
343

344
*************************************************************
345

346
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
347

348
  Description:
349

350
  Elevates privileges to p_uid when required, calls
351
  setsockopt, and drops privileges back.
352

353
************************************************************/
354

355
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
22✔
356
    const void *optval, socklen_t optlen)
357
{
358
    const uid_t saved_uid = geteuid();
22✔
359
    int res;
360

361
    if (p_uid != saved_uid && seteuid(p_uid)) {
22✔
362
        perror("cannot elevate privileges for setsockopt");
×
363
    }
364

365
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
366

367
    if (p_uid != saved_uid && seteuid(saved_uid)) {
22✔
368
        perror("fatal error: could not drop privileges after setsockopt");
×
369
        /* continuing would be a security hole */
370
        exit(4);
×
371
    }
372

373
    return res;
22✔
374
}
375

376
unsigned long strtoul_strict(const char *arg, int base)
454✔
377
{
378
    char *endptr;
379
    unsigned long val;
380

381
    while (isspace(*arg))
480✔
382
        arg++;
26✔
383

384
    if (arg[0] == '-')
454✔
385
        usage(1);
52✔
386

387
    errno = 0;
418✔
388
    val = strtoul(arg, &endptr, base);
418✔
389
    if (errno != 0 || arg == endptr || *endptr != '\0')
418✔
390
        usage(1);
100✔
391

392
    return val;
344✔
393
}
394

395
double strtod_strict(const char *arg)
290✔
396
{
397
    char *endptr;
398
    double val;
399

400
    errno = 0;
290✔
401
    val = strtod(arg, &endptr);
290✔
402
    if (errno != 0 || arg == endptr || *endptr != '\0' || val < 0)
290✔
403
        usage(1);
102✔
404

405
    return val;
222✔
406
}
407

408
/************************************************************
409

410
  Function: main
411

412
*************************************************************
413

414
  Inputs:  int argc, char** argv
415

416
  Description:
417

418
  Main program entry point
419

420
************************************************************/
421

422
int main(int argc, char **argv)
972✔
423
{
424
/* Debug: debug_options_init() and CPU Performance */
425
#if defined(DEBUG) || defined(_DEBUG)
426
    clock_t perf_cpu_start, perf_cpu_end;
427
    double perf_cpu_time_used;
428
    perf_cpu_start = clock();
429

430
    dbg_opt = debug_options_init();
431
#endif /* DEBUG || _DEBUG */
432

433
    int c;
434
    char *endptr;
435
    const uid_t suid = geteuid();
972✔
436
    int tos = 0;
972✔
437
    struct optparse optparse_state;
438
#ifdef USE_SIGACTION
439
    struct sigaction act;
440
#endif
441
    opt = options_init();
972✔
442

443
    /* pre-parse -h/--help, so that we also can output help information
444
     * without trying to open the socket, which might fail */
445
    prog = argv[0];
972✔
446
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
972✔
447
        usage(0);
3✔
448
    }
1✔
449

450
    socket4 = open_ping_socket_ipv4(&socktype4);
970✔
451
#ifdef __linux__
452
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
453
     * structure is missing in the message.
454
     */
455
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
642✔
456
#endif
457

458
#ifdef IPV6
459
    socket6 = open_ping_socket_ipv6(&socktype6);
970✔
460
    /* if called (sym-linked) via 'fping6', imply '-6'
461
     * for backward compatibility */
462
    if (strstr(prog, "fping6")) {
970✔
463
        hints_ai_family = AF_INET6;
2✔
464
    }
465
#endif
466

467
    memset(&src_addr, 0, sizeof(src_addr));
970✔
468
#ifdef IPV6
469
    memset(&src_addr6, 0, sizeof(src_addr6));
970✔
470
#endif
471

472
    if (!suid && suid != getuid()) {
970✔
473
        /* *temporarily* drop privileges */
474
        if (seteuid(getuid()) == -1)
869✔
475
            perror("cannot setuid");
×
476
    }
239✔
477

478
    optparse_init(&optparse_state, argv);
970✔
479
    ident4 = ident6 = htons(getpid() & 0xFFFF);
970✔
480
    opt.verbose_on = 1;
970✔
481
    opt.backoff_on = 1;
970✔
482
    opterr = 1;
970✔
483

484
    /* get command line options */
485

486
    struct optparse_long longopts[] = {
970✔
487
        { "ipv4", '4', OPTPARSE_NONE },
488
        { "ipv6", '6', OPTPARSE_NONE },
489
        { "alive", 'a', OPTPARSE_NONE },
490
        { "addr", 'A', OPTPARSE_NONE },
491
        { "size", 'b', OPTPARSE_REQUIRED },
492
        { "backoff", 'B', OPTPARSE_REQUIRED },
493
        { "count", 'c', OPTPARSE_REQUIRED },
494
        { "vcount", 'C', OPTPARSE_REQUIRED },
495
        { "rdns", 'd', OPTPARSE_NONE },
496
        { "timestamp", 'D', OPTPARSE_NONE },
497
        { "timestamp-format", 0, OPTPARSE_REQUIRED },
498
        { "elapsed", 'e', OPTPARSE_NONE },
499
        { "file", 'f', OPTPARSE_REQUIRED },
500
        { "generate", 'g', OPTPARSE_NONE },
501
        { "help", 'h', OPTPARSE_NONE },
502
        { "ttl", 'H', OPTPARSE_REQUIRED },
503
        { "interval", 'i', OPTPARSE_REQUIRED },
504
        { "iface", 'I', OPTPARSE_REQUIRED },
505
        { "oiface", 0, OPTPARSE_REQUIRED },
506
        { "json", 'J', OPTPARSE_NONE },
507
        { "icmp-timestamp", 0, OPTPARSE_NONE },
508
#ifdef SO_MARK
509
        { "fwmark", 'k', OPTPARSE_REQUIRED },
510
#endif
511
        { "loop", 'l', OPTPARSE_NONE },
512
        { "all", 'm', OPTPARSE_NONE },
513
        { "dontfrag", 'M', OPTPARSE_NONE },
514
        { "name", 'n', OPTPARSE_NONE },
515
        { "netdata", 'N', OPTPARSE_NONE },
516
        { "outage", 'o', OPTPARSE_NONE },
517
        { "tos", 'O', OPTPARSE_REQUIRED },
518
        { "period", 'p', OPTPARSE_REQUIRED },
519
        { "quiet", 'q', OPTPARSE_NONE },
520
        { "squiet", 'Q', OPTPARSE_REQUIRED },
521
        { "retry", 'r', OPTPARSE_REQUIRED },
522
        { "random", 'R', OPTPARSE_NONE },
523
        { "stats", 's', OPTPARSE_NONE },
524
        { "src", 'S', OPTPARSE_REQUIRED },
525
        { "timeout", 't', OPTPARSE_REQUIRED },
526
        { NULL, 'T', OPTPARSE_REQUIRED },
527
        { "unreach", 'u', OPTPARSE_NONE },
528
        { "version", 'v', OPTPARSE_NONE },
529
        { "reachable", 'x', OPTPARSE_REQUIRED },
530
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
531
        { "check-source", 0, OPTPARSE_NONE },
532
        { "print-tos", 0, OPTPARSE_NONE },
533
        { "print-ttl", 0, OPTPARSE_NONE },
534
        { "seqmap-timeout", 0, OPTPARSE_REQUIRED },
535
#if defined(DEBUG) || defined(_DEBUG)
536
        { NULL, 'z', OPTPARSE_REQUIRED },
537
#endif
538
        { 0, 0, 0 }
539
    };
540

541
    double opt_val_double;
542
    while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
2,203✔
543
        switch (c) {
1,454✔
544
        case 0:
120✔
545
            if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
157✔
546
                if(strcmp(optparse_state.optarg, "ctime") == 0) {
24✔
547
                    opt.timestamp_format = 1;
6✔
548
                }else if(strcmp(optparse_state.optarg, "iso") == 0) {
20✔
549
                    opt.timestamp_format = 2;
6✔
550
                }else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
14✔
551
                    opt.timestamp_format = 3;
6✔
552
                }else{
2✔
553
                    usage(1);
6✔
554
                }
555
            } else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
141✔
556
                opt.check_source_on = 1;
9✔
557
            } else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
125✔
558
#ifdef IPV6
559
                if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
29✔
560
                    fprintf(stderr, "%s: ICMP Timestamp is IPv4 only\n", prog);
2✔
561
                    exit(1);
2✔
562
                }
563
                hints_ai_family = AF_INET;
27✔
564
#endif
565
                opt.icmp_request_typ = 13;
27✔
566
                opt.ping_data_size = ICMP_TIMESTAMP_DATA_SIZE;
27✔
567
            } else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
98✔
568
                opt.print_tos_on = 1;
35✔
569
#if defined(HAVE_IP_RECVTOS)
570
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
35✔
571
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTOS, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
572
                        perror("setsockopt IP_RECVTOS");
×
573
                    }
574
                }
575
#endif
576
#if defined(IPV6) && defined(IPV6_RECVTCLASS)
577
                if (socket6 >= 0) {
35✔
578
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVTCLASS, &sock_opt_on, sizeof(sock_opt_on))) {
35✔
579
                        perror("setsockopt IPV6_RECVTCLASS");
×
580
                    }
581
                }
9✔
582
#endif
583
            } else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
69✔
584
                opt.print_ttl_on = 1;
29✔
585
                if (socket4 >= 0 && (socktype4 == SOCK_DGRAM)) {
29✔
586
                    if (setsockopt(socket4, IPPROTO_IP, IP_RECVTTL, &sock_opt_on, sizeof(sock_opt_on))) {
2✔
587
                        perror("setsockopt IP_RECVTTL");
×
588
                    }
589
                }
590
#if defined(IPV6) && defined(IPV6_RECVHOPLIMIT)
591
                if (socket6 >= 0) {
29✔
592
                    if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &sock_opt_on, sizeof(sock_opt_on))) {
29✔
593
                        perror("setsockopt IPV6_RECVHOPLIMIT");
×
594
                    }
595
                }
7✔
596
#endif
597
            } else if (strstr(optparse_state.optlongname, "seqmap-timeout") != NULL) {
38✔
598
                opt.seqmap_timeout = strtod_strict(optparse_state.optarg) * 1000000;
27✔
599
            } else if (strstr(optparse_state.optlongname, "oiface") != NULL) {
13✔
600
                opt.oiface_on = 1;
4✔
601
#ifdef IP_PKTINFO
602
                if (socket4 >= 0) {
4✔
603
                    socket_set_outgoing_iface_ipv4(socket4, optparse_state.optarg);
4✔
604
                }
605
#ifdef IPV6
606
                if (socket6 >= 0) {
4✔
607
                    socket_set_outgoing_iface_ipv6(optparse_state.optarg);
4✔
608
                }
609
#endif
610
#else
611
                fprintf(stderr, "%s: --oiface is not supported on this platform (IP_PKTINFO unavailable)\n", prog);
612
                exit(3);
613
#endif
614
            } else {
615
                usage(1);
×
616
            }
617
            break;
137✔
618
        case '4':
32✔
619
#ifdef IPV6
620
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
43✔
621
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
3✔
622
                exit(1);
3✔
623
            }
624
            hints_ai_family = AF_INET;
40✔
625
#endif
626
            break;
40✔
627
        case '6':
30✔
628
#ifdef IPV6
629
            if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET6) {
33✔
630
                fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
5✔
631
                exit(1);
5✔
632
            }
633
            hints_ai_family = AF_INET6;
28✔
634
#else
635
            fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
636
            exit(1);
637
#endif
638
            break;
28✔
639
        case 'M':
2✔
640
#ifdef IP_MTU_DISCOVER
641
            if (socket4 >= 0) {
2✔
642
                int val = IP_PMTUDISC_DO;
2✔
643
                if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
2✔
644
                    perror("setsockopt IP_MTU_DISCOVER");
645
                }
646
            }
647
#ifdef IPV6
648
            if (socket6 >= 0) {
2✔
649
                int val = IPV6_PMTUDISC_DO;
2✔
650
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
2✔
651
                    perror("setsockopt IPV6_MTU_DISCOVER");
652
                }
653
            }
654
#endif
655
#else
656
            fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
657
            exit(1);
658
#endif
659
            break;
2✔
660

661
        case 't':
56✔
662
            opt.timeout = strtod_strict(optparse_state.optarg) * 1000000;
66✔
663
            opt.timeout_on = 1;
52✔
664
            break;
52✔
665

666
        case 'r':
44✔
667
            opt.retry = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
51✔
668
            break;
41✔
669

670
        case 'i':
38✔
671
            opt.interval = strtod_strict(optparse_state.optarg) * 1000000;
48✔
672
            break;
34✔
673

674
        case 'p':
90✔
675
            opt.perhost_interval = strtod_strict(optparse_state.optarg) * 1000000;
123✔
676
            break;
109✔
677

678
        case 'c':
102✔
679
            opt.count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
139✔
680
            if (!opt.count)
129✔
681
                usage(1);
3✔
682

683
            opt.count_on = 1;
127✔
684
            break;
127✔
685

686
        case 'C':
42✔
687
            opt.count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
60✔
688
            if (!opt.count)
50✔
689
                usage(1);
3✔
690

691
            opt.count_on = 1;
48✔
692
            opt.report_all_rtts_on = 1;
48✔
693
            break;
48✔
694

695
        case 'b':
20✔
696
            opt.ping_data_size = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
30✔
697
            opt.size_on = 1;
20✔
698
            break;
20✔
699

700
        case 'h':
2✔
701
            usage(0);
3✔
702
            break;
1✔
703

704
        case 'q':
26✔
705
            opt.verbose_on = 0;
38✔
706
            opt.quiet_on = 1;
38✔
707
            break;
38✔
708

709
        case 'Q':
34✔
710
            opt.verbose_on = 0;
43✔
711
            opt.quiet_on = 1;
43✔
712
            errno = 0;
43✔
713
            opt_val_double = strtod(optparse_state.optarg, &endptr);
43✔
714
            if (errno != 0 || optparse_state.optarg == endptr || (*endptr != '\0' && *endptr != ','))
43✔
715
                usage(1);
12✔
716
            if (opt_val_double < 0) {
35✔
717
                usage(1);
9✔
718
            }
3✔
719
            report_interval = opt_val_double * 1e9;
29✔
720

721
            /* recognize keyword(s) after number, ignore everything else */
722
            {
723
                char *comma = strchr(optparse_state.optarg, ',');
29✔
724
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
29✔
725
                    opt.cumulative_stats_on = 1;
4✔
726
                }
727
            }
728

729
            break;
29✔
730

731
        case 'e':
6✔
732
            opt.elapsed_on = 1;
8✔
733
            break;
8✔
734

735
        case 'm':
NEW
736
            opt.multif_on = 1;
×
737
            break;
×
738

739
        case 'N':
2✔
740
            opt.print_netdata_on = 1;
3✔
741
            break;
3✔
742

743
        case 'n':
4✔
744
            opt.name_on = 1;
6✔
745
            if (opt.rdns_on) {
6✔
746
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
747
                exit(1);
3✔
748
            }
749
            break;
3✔
750

751
        case 'd':
6✔
752
            opt.rdns_on = 1;
9✔
753
            if (opt.name_on) {
9✔
754
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
755
                exit(1);
3✔
756
            }
757
            break;
6✔
758

759
        case 'A':
2✔
760
            opt.addr_on = 1;
3✔
761
            break;
3✔
762

763
        case 'B':
18✔
764
            opt.backoff = strtod_strict(optparse_state.optarg);
26✔
765
            break;
14✔
766

767
        case 's':
8✔
768
            opt.stats_on = 1;
12✔
769
            break;
12✔
770

771
        case 'D':
22✔
772
            opt.timestamp_on = 1;
33✔
773
            break;
33✔
774

775
        case 'R':
4✔
776
            opt.random_data_on = 1;
5✔
777
            break;
5✔
778

779
        case 'l':
12✔
780
            opt.loop_on = 1;
17✔
781
            opt.backoff_on = 0;
17✔
782
            break;
17✔
783

784
        case 'u':
8✔
785
            opt.unreachable_on = 1;
12✔
786
            break;
12✔
787

788
        case 'a':
18✔
789
            opt.alive_on = 1;
26✔
790
            break;
26✔
791

792
        case 'H':
16✔
793
            opt.ttl = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
794
            if (!opt.ttl)
14✔
795
                usage(1);
3✔
796
            break;
12✔
797

798
#if defined(DEBUG) || defined(_DEBUG)
799
        case 'z':
800
            debugging = (unsigned int)strtoul_strict(optparse_state.optarg, 0);
801
            break;
802
#endif /* DEBUG || _DEBUG */
803

804
        case 'v':
4✔
805
            printf("%s: Version %s\n", prog, VERSION);
6✔
806
            exit(0);
6✔
807

808
        case 'x':
16✔
809
            opt.min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
810
            if (!opt.min_reachable)
14✔
811
                usage(1);
3✔
812
            break;
12✔
813

814
        case 'X':
14✔
815
            opt.min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
21✔
816
            if (!opt.min_reachable)
11✔
817
                usage(1);
3✔
818
            opt.fast_reachable_on = 1;
9✔
819
            break;
9✔
820

821
        case 'f':
18✔
822
            filename = optparse_state.optarg;
27✔
823
            break;
27✔
824
#ifdef SO_MARK
825
        case 'k':
18✔
826
            fwmark = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
18✔
827
            if (!fwmark)
8✔
828
                usage(1);
2✔
829

830
            if (socket4 >= 0)
6✔
831
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
832
                    perror("fwmark ipv4");
4✔
833

834
#ifdef IPV6
835
            if (socket6 >= 0)
6✔
836
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
837
                    perror("fwmark ipv6");
4✔
838
#endif
839

840
            break;
6✔
841
#endif
842

843
        case 'g':
106✔
844
            /* use IP list generation */
845
            /* mutually exclusive with using file input or command line targets */
846
            opt.generate_on = 1;
129✔
847
            break;
129✔
848

849
        case 'S':
10✔
850
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
851
                src_addr_set = 1;
6✔
852
                break;
6✔
853
            }
854
#ifdef IPV6
855
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
856
                src_addr6_set = 1;
4✔
857
                break;
4✔
858
            }
859
#endif
860
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
861
            exit(1);
3✔
862

863
        case 'I':
6✔
864
            opt.bindiface_on = 1;
6✔
865
#ifdef SO_BINDTODEVICE
866
            if (socket4 >= 0) {
6✔
867
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
868
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
869
                    exit(1);
2✔
870
                }
871
            }
872
#ifdef IPV6
873
            if (socket6 >= 0) {
4✔
874
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
875
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
×
876
                    exit(1);
×
877
                }
878
            }
879
#endif
880
#else
881
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
882
            exit(3);
883
            ;
884
#endif
885
            break;
4✔
886

887
        case 'J':
42✔
888
            opt.print_json_on = 1;
62✔
889
            break;
62✔
890

891
        case 'T':
2✔
892
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
893
            break;
3✔
894

895
        case 'O':
16✔
896
            {
897
                unsigned long val = strtoul_strict(optparse_state.optarg, 0);
24✔
898
                if (val > 255)
16✔
899
                    usage(1);
×
900
                tos = (int)val;
16✔
901
            }
902
            if (socket4 >= 0) {
16✔
903
                if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
12✔
904
                    perror("setting type of service octet IP_TOS");
×
905
                }
906
            }
4✔
907
#if defined(IPV6) && defined(IPV6_TCLASS)
908
            if (socket6 >= 0) {
16✔
909
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
12✔
910
                    perror("setting type of service octet IPV6_TCLASS");
×
911
                }
912
            }
4✔
913
#endif
914
            break;
16✔
915

916
        case 'o':
6✔
917
            opt.outage_on = 1;
7✔
918
            break;
7✔
919

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

928
    /* permanently drop privileges */
929
    if (suid != getuid() && setuid(getuid())) {
573✔
930
        perror("fatal: failed to permanently drop privileges");
×
931
        /* continuing would be a security hole */
932
        exit(4);
×
933
    }
934

935
    /* validate various option settings */
936

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

947
    if (opt.ttl > 255) {
571✔
948
        fprintf(stderr, "%s: ttl %u out of range\n", prog, opt.ttl);
3✔
949
        exit(1);
3✔
950
    }
951

952
    if (opt.unreachable_on && opt.alive_on) {
568✔
953
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
954
        exit(1);
3✔
955
    }
956

957
    if (opt.oiface_on && opt.bindiface_on) {
565✔
958
        fprintf(stderr, "%s: specify only --oiface or -I, --iface\n", prog);
×
959
        exit(1);
×
960
    }
961

962
    if (opt.count_on && opt.loop_on) {
565✔
963
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
964
        exit(1);
3✔
965
    }
966

967
    if (opt.print_json_on && !opt.count_on && !opt.loop_on) {
562✔
968
        fprintf(stderr, "%s: option -J, --json requires -c, -C, or -l\n", prog);
3✔
969
        exit(1);
3✔
970
    }
971

972
    if (opt.interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
559✔
973
        fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
3✔
974
        exit(1);
3✔
975
    }
976

977
    if (opt.perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
556✔
978
        fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
3✔
979
        exit(1);
3✔
980
    }
981

982
    if (opt.ping_data_size > MAX_PING_DATA) {
553✔
983
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
984
            prog, opt.ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
985
        exit(1);
3✔
986
    }
987

988
    if ((opt.backoff > MAX_BACKOFF_FACTOR) || (opt.backoff < MIN_BACKOFF_FACTOR)) {
550✔
989
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
990
            prog, opt.backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
6✔
991
        exit(1);
6✔
992
    }
993

994
    if (opt.icmp_request_typ == 13 && opt.size_on != 0) {
544✔
995
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
996
        exit(1);
9✔
997
    }
998

999
    if (opt.count_on) {
535✔
1000
        if (opt.verbose_on)
157✔
1001
            opt.per_recv_on = 1;
99✔
1002

1003
        opt.alive_on = opt.unreachable_on = opt.verbose_on = 0;
157✔
1004
    }
41✔
1005

1006
    if (opt.loop_on) {
535✔
1007
        if (!report_interval)
14✔
1008
            opt.per_recv_on = 1;
12✔
1009

1010
        opt.alive_on = opt.unreachable_on = opt.verbose_on = 0;
14✔
1011
    }
4✔
1012

1013
    if (opt.alive_on || opt.unreachable_on || opt.min_reachable)
535✔
1014
        opt.verbose_on = 0;
37✔
1015

1016
    trials = (opt.count > opt.retry + 1) ? opt.count : opt.retry + 1;
551✔
1017

1018
    /* auto-tune default timeout for count/loop modes
1019
     * see also github #32 */
1020
    if (opt.loop_on || opt.count_on) {
551✔
1021
        if (!opt.timeout_on) {
171✔
1022
            opt.timeout = opt.perhost_interval;
159✔
1023
            if (opt.timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
159✔
NEW
1024
                opt.timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1025
            }
1026
        }
43✔
1027
    }
45✔
1028

1029
#if defined(DEBUG) || defined(_DEBUG)
1030
    if (debugging & DBG_TRACE)
1031
        dbg_opt.trace_on = 1;
1032

1033
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1034
        dbg_opt.randomly_lose_on = 1;
1035
        dbg_opt.lose_factor = 1; /* ie, 1/4 */
1036
    }
1037

1038
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1039
        dbg_opt.randomly_lose_on = 1;
1040
        dbg_opt.lose_factor = 5; /* ie, 3/4 */
1041
    }
1042

1043
    if (debugging & DBG_PRINT_PER_SYSTEM)
1044
        dbg_opt.print_per_system_on = 1;
1045

1046
    if ((debugging & DBG_REPORT_ALL_RTTS) && !opt.loop_on)
1047
        opt.report_all_rtts_on = 1;
1048

1049
    if (dbg_opt.trace_on) {
1050
        fprintf(stderr, "%s:\n  opt.count: %u, opt.retry: %u, opt.interval: %.0f ms\n",
1051
            prog, opt.count, opt.retry, opt.interval / 1e6);
1052
        fprintf(stderr, "  opt.perhost_interval: %.0f ms, opt.timeout: %.0f\n",
1053
            opt.perhost_interval / 1e6, opt.timeout / 1e6);
1054
        fprintf(stderr, "  opt.seqmap_timeout: %.0f\n", opt.seqmap_timeout / 1e6);
1055
        fprintf(stderr, "  opt.ping_data_size = %u, trials = %u\n",
1056
            opt.ping_data_size, trials);
1057

1058
        if (opt.verbose_on)
1059
            fprintf(stderr, "  opt.verbose_on set\n");
1060
        if (opt.multif_on)
1061
            fprintf(stderr, "  opt.multif_on set\n");
1062
        if (opt.name_on)
1063
            fprintf(stderr, "  opt.name_on set\n");
1064
        if (opt.addr_on)
1065
            fprintf(stderr, "  opt.addr_on set\n");
1066
        if (opt.stats_on)
1067
            fprintf(stderr, "  opt.stats_on set\n");
1068
        if (opt.unreachable_on)
1069
            fprintf(stderr, "  opt.unreachable_on set\n");
1070
        if (opt.alive_on)
1071
            fprintf(stderr, "  opt.alive_on set\n");
1072
        if (opt.elapsed_on)
1073
            fprintf(stderr, "  opt.elapsed_on set\n");
1074
        if (opt.version_on)
1075
            fprintf(stderr, "  opt.version_on set\n");
1076
        if (opt.count_on)
1077
            fprintf(stderr, "  opt.count_on set\n");
1078
        if (opt.loop_on)
1079
            fprintf(stderr, "  opt.loop_on set\n");
1080
        if (opt.backoff_on)
1081
            fprintf(stderr, "  opt.backoff_on set\n");
1082
        if (opt.per_recv_on)
1083
            fprintf(stderr, "  opt.per_recv_on set\n");
1084
        if (opt.report_all_rtts_on)
1085
            fprintf(stderr, "  opt.report_all_rtts_on set\n");
1086
        if (dbg_opt.randomly_lose_on)
1087
            fprintf(stderr, "  dbg_opt.randomly_lose_on set\n");
1088
        if (dbg_opt.print_per_system_on)
1089
            fprintf(stderr, "  dbg_opt.print_per_system_on set\n");
1090
        if (opt.outage_on)
1091
            fprintf(stderr, "  opt.outage_on set\n");
1092
        if (opt.print_netdata_on)
1093
            fprintf(stderr, "  opt.print_netdata_on set\n");
1094
        if (opt.print_json_on)
1095
            fprintf(stderr, "  opt.print_json_on set\n");
1096
    }
1097
#endif /* DEBUG || _DEBUG */
1098

1099
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1100
    if (opt.ttl > 0) {
551✔
1101
        if (socket4 >= 0) {
3✔
1102
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &opt.ttl, sizeof(opt.ttl))) {
3✔
1103
                perror("setting time to live");
×
1104
            }
1105
        }
1✔
1106
#ifdef IPV6
1107
        if (socket6 >= 0) {
3✔
1108
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &opt.ttl, sizeof(opt.ttl))) {
3✔
1109
                perror("setting time to live");
×
1110
            }
1111
        }
1✔
1112
#endif
1113
    }
1✔
1114

1115
#if HAVE_SO_TIMESTAMPNS
1116
    {
1117
        int opt = 1;
406✔
1118
        if (socket4 >= 0) {
406✔
1119
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
406✔
1120
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1121
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1122
                }
1123
            }
1124
        }
1125
#ifdef IPV6
1126
        if (socket6 >= 0) {
406✔
1127
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
406✔
1128
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1129
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1130
                }
1131
            }
1132
        }
1133
#endif
1134
    }
1135
#endif
1136

1137
    update_current_time();
551✔
1138
    start_time = current_time_ns;
551✔
1139

1140
    /* handle host names supplied on command line or in a file */
1141
    /* if the opt.generate_on is on, then generate the IP list */
1142

1143
    argv = &argv[optparse_state.optind];
551✔
1144
    argc -= optparse_state.optind;
551✔
1145

1146
    /* calculate how many ping can be in-flight per host */
1147
    if (opt.count_on) {
551✔
1148
        event_storage_count = opt.count;
172✔
1149
    }
56✔
1150
    else if (opt.loop_on) {
379✔
1151
        if (opt.perhost_interval > opt.timeout) {
14✔
UNCOV
1152
            event_storage_count = 1;
×
1153
        }
1154
        else {
1155
            event_storage_count = 1 + opt.timeout / opt.perhost_interval;
14✔
1156
        }
1157
    }
4✔
1158
    else {
1159
        event_storage_count = 1;
365✔
1160
    }
1161

1162
    /* file and generate are mutually exclusive */
1163
    /* file and command line are mutually exclusive */
1164
    /* generate requires command line parameters beyond the switches */
1165
    if ((*argv && filename) || (filename && opt.generate_on) || (opt.generate_on && !*argv))
551✔
1166
        usage(1);
19✔
1167

1168
    /* if no conditions are specified, then assume input from stdin */
1169
    if (!*argv && !filename && !opt.generate_on)
533✔
1170
        filename = "-";
69✔
1171

1172
    if (*argv && !opt.generate_on) {
533✔
1173
        while (*argv) {
689✔
1174
            add_name(*argv);
375✔
1175
            ++argv;
375✔
1176
        }
1177
    }
76✔
1178
    else if (filename) {
219✔
1179
        FILE *ping_file;
1180
        char line[MAX_TARGET_NAME_LEN + 1];
1181
        char host[MAX_TARGET_NAME_LEN + 1];
1182
        char scratch[MAX_TARGET_NAME_LEN + 1];
1183
        int skip, non_empty;
1184

1185
        if (strcmp(filename, "-") == 0)
96✔
1186
            ping_file = fdopen(0, "r");
72✔
1187
        else
1188
            ping_file = fopen(filename, "r");
24✔
1189

1190
        if (!ping_file)
96✔
1191
            errno_crash_and_burn("fopen");
3✔
1192

1193
        /*
1194
         * Read the first word of every non-comment line, skip everything else.
1195
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1196
         * character is a '#' are interpreted as comments and ignored.)
1197
        */
1198
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1199
            skip = non_empty = 0;
309✔
1200

1201
            /* skip over a prefix of the line where sscanf finds nothing */
1202
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1203
                continue;
99✔
1204
            }
1205

1206
            /* the first word of the line can indicate a comment line */
1207
            if (host[0] == '#') {
210✔
1208
                skip = 1; /* skip remainder of line */
42✔
1209
            } else {
14✔
1210
                non_empty = 1; /* we have something to add as a target name */
168✔
1211
                /*
1212
                 * We have found the start of a word.
1213
                 * This part of the line may contain all of the first word.
1214
                 */
1215
                if (!strchr(line, '\n') && (strlen(line) == sizeof(line) - 1)) {
168✔
1216
                    char discard1[MAX_TARGET_NAME_LEN + 1];
1217
                    char discard2[MAX_TARGET_NAME_LEN + 1];
1218
                    if (sscanf(line, "%s%s", discard1, discard2) == 2) {
102✔
1219
                        skip = 1; /* a second word starts in this part */
6✔
1220
                    }
2✔
1221
                    if (isspace(line[sizeof(line) - 2])) {
102✔
1222
                        skip = 1; /* the first word ends in this part */
42✔
1223
                    }
14✔
1224
                }
34✔
1225
            }
1226
            /* read remainder of this input line */
1227
            while (!strchr(line, '\n') && fgets(line, sizeof(line), ping_file)) {
336✔
1228
                if (skip) {
150✔
1229
                    continue; /* skip rest of data in this input line */
102✔
1230
                }
1231
                if (isspace(line[0])) {
48✔
1232
                    skip = 1; /* first word ended in previous part */
6✔
1233
                    continue;
6✔
1234
                }
1235
                if ((sscanf(line, "%s", scratch) != 1) || (!*scratch)) {
42✔
1236
                    skip = 1; /* empty or blank part of line, skip the rest */
×
1237
                    continue;
×
1238
                }
1239
                if (sizeof(host) - strlen(host) < strlen(scratch) + 1) {
42✔
1240
                    fprintf(stderr, "%s: target name too long\n", prog);
24✔
1241
                    exit(1);
24✔
1242
                }
1243
                /* append remainder of word started in previous line part */
1244
                strncat(host, scratch, sizeof(host) - strlen(host) - 1);
18✔
1245
                /*
1246
                 * Since the "host" buffer is the same size as the "line"
1247
                 * buffer, a target name that fits into the "host" buffer
1248
                 * cannot use more than two consecutive line parts.
1249
                 * A target name that uses two consecutive line parts
1250
                 * and fits into the "host" buffer must end before the
1251
                 * end of the second "line" buffer.  Thus the rest of
1252
                 * the line can be skipped.
1253
                 */
1254
                skip = 1;
18✔
1255
            }
1256

1257
            if (non_empty)
186✔
1258
                add_name(host);
144✔
1259
        }
1260

1261
        fclose(ping_file);
70✔
1262
    }
24✔
1263
    else if (*argv && opt.generate_on) {
123✔
1264
        if (argc == 1) {
123✔
1265
            /* one target: we expect a cidr range (n.n.n.n/m) */
1266
            add_cidr(argv[0]);
68✔
1267
        }
14✔
1268
        else if (argc == 2) {
55✔
1269
            add_range(argv[0], argv[1]);
52✔
1270
        }
6✔
1271
        else {
1272
            usage(1);
3✔
1273
        }
1274
    }
21✔
1275
    else {
1276
        usage(1);
×
1277
    }
1278

1279
    if (!num_hosts) {
447✔
1280
        exit(num_noaddress ? 2 : 1);
36✔
1281
    }
1282

1283
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
411✔
1284
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
18✔
1285
    }
2✔
1286
#ifdef IPV6
1287
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
409✔
1288
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
16✔
1289
    }
1290
#endif
1291

1292
    /* allocate and initialize array to map host nr to host_entry */
1293
    {
1294
        struct event *cursor = event_queue_ping.first;
407✔
1295
        int i = 0;
407✔
1296
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
407✔
1297
        if (!table)
407✔
1298
            crash_and_burn("Can't malloc array of hosts");
×
1299
        /* initialize table of hosts. we know that we have ping events scheduled
1300
         * for each of them */
1301
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
1,001✔
1302
            table[i] = cursor->host;
594✔
1303
            cursor->host->i = i;
594✔
1304
            i++;
594✔
1305
        }
156✔
1306
    }
1307

1308
    init_ping_buffer_ipv4(opt.ping_data_size);
407✔
1309
#ifdef IPV6
1310
    init_ping_buffer_ipv6(opt.ping_data_size);
407✔
1311
#endif
1312

1313
#ifdef USE_SIGACTION
1314
    memset(&act, 0, sizeof(act));
407✔
1315
    act.sa_handler = signal_handler;
407✔
1316
    sigemptyset(&act.sa_mask);
407✔
1317
    sigaddset(&act.sa_mask, SIGINT);
407✔
1318
    sigaddset(&act.sa_mask, SIGQUIT);
407✔
1319
    act.sa_flags = SA_RESTART;
407✔
1320
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
407✔
1321
        crash_and_burn("failure to set signal handler");
30✔
1322
    }
30✔
1323
#else
1324
    signal(SIGINT, signal_handler);
1325
    signal(SIGQUIT, signal_handler);
1326
#endif
1327
    setlinebuf(stdout);
377✔
1328

1329
    // Last time we updated current_time_ns was before adding the hosts and
1330
    // possibly doing DNS resolution, which means that it isn't accurate
1331
    // anymore.
1332
    update_current_time();
377✔
1333
    if (report_interval) {
377✔
1334
        next_report_time = current_time_ns + report_interval;
22✔
1335
    }
2✔
1336

1337
    last_send_time = 0;
377✔
1338

1339
    seqmap_init(opt.seqmap_timeout);
377✔
1340

1341
    /* main loop */
1342
    main_loop();
377✔
1343

1344
/* Debug: CPU Performance */
1345
#if defined(DEBUG) || defined(_DEBUG)
1346
    perf_cpu_end = clock();
1347
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1348
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1349
#endif /* DEBUG || _DEBUG */
1350

1351
    finish();
377✔
1352

1353
    return 0;
81✔
1354
}
1355

1356
static inline int64_t timespec_ns(struct timespec *a)
6,763✔
1357
{
1358
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
6,763✔
1359
}
1360

1361
#if HAVE_SO_TIMESTAMPNS
1362
/* convert a struct timeval to nanoseconds */
1363
static inline int64_t timeval_ns(struct timeval *a)
1364
{
1365
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1366
}
1367
#endif /* HAVE_SO_TIMESTAMPNS */
1368

1369
void add_cidr(char *addr)
63✔
1370
{
1371
    char *addr_end;
1372
    char *mask_str;
1373
    unsigned long mask;
1374
    int ret;
1375
    struct addrinfo addr_hints;
1376
    struct addrinfo *addr_res;
1377
    unsigned long net_addr;
1378
#ifdef IPV6
1379
    uint64_t net_upper, net_lower;
1380
    char *scope_str;
1381
#endif /* IPV6 */
1382

1383
    /* Split address from mask */
1384
    addr_end = strrchr(addr, '/');
63✔
1385
    if (addr_end == NULL) {
63✔
1386
        usage(1);
3✔
1387
    }
1✔
1388
    mask_str = addr_end + 1;
61✔
1389

1390
#ifdef IPV6
1391
    /* IPv6 addresses can have a scope */
1392
    scope_str = strchr(addr, '%');
61✔
1393
    if (scope_str && mask_str < scope_str) {
61✔
1394
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1395
        exit(1);
2✔
1396
    }
1397
#endif /*IPV6 */
1398

1399
    *addr_end = '\0';
59✔
1400
    mask = strtoul_strict(mask_str, 10);
59✔
1401

1402
    /* parse address */
1403
    memset(&addr_hints, 0, sizeof(struct addrinfo));
53✔
1404
    addr_hints.ai_family = hints_ai_family;
53✔
1405
    addr_hints.ai_flags = AI_NUMERICHOST;
53✔
1406
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
53✔
1407
    if (ret) {
53✔
1408
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1409
        exit(1);
7✔
1410
    }
1411
    if (addr_res->ai_family == AF_INET) {
46✔
1412
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
24✔
1413
        freeaddrinfo(addr_res);
24✔
1414
        add_cidr_ipv4(net_addr, mask);
24✔
1415
#ifdef IPV6
1416
    } else if (addr_res->ai_family == AF_INET6) {
30✔
1417
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1418
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1419
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1420
        freeaddrinfo(addr_res);
22✔
1421
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1422
#endif /* IPV6 */
1423
    } else {
1424
        freeaddrinfo(addr_res);
×
1425
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1426
        exit(1);
×
1427
    }
1428
}
30✔
1429

1430
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1431
{
1432
    unsigned long bitmask;
1433
    unsigned long net_last;
1434

1435
    /* check mask */
1436
    if (mask < 1 || mask > 32) {
24✔
1437
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1438
        exit(1);
6✔
1439
    }
1440

1441
    /* convert mask integer from 1 to 32 to a bitmask */
1442
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1443

1444
    /* calculate network range */
1445
    net_addr &= bitmask;
18✔
1446
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1447

1448
    /* exclude network and broadcast address for regular prefixes */
1449
    if (mask < 31) {
18✔
1450
        net_last--;
12✔
1451
        net_addr++;
12✔
1452
    }
4✔
1453

1454
    /* add all hosts in that network (net_addr and net_last inclusive) */
1455
    add_addr_range_ipv4(net_addr, net_last);
18✔
1456
}
16✔
1457

1458
#ifdef IPV6
1459
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1460
{
1461
    uint64_t bitmask_lower;
1462
    uint64_t last_lower;
1463

1464
    /* check mask -- 2^63 addresses should suffice for now */
1465
    if (mask < 65 || mask > 128) {
23✔
1466
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1467
        exit(1);
7✔
1468
    }
1469

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

1473
    /* calculate network range */
1474
    net_lower &= bitmask_lower;
16✔
1475
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1476

1477
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1478
}
12✔
1479
#endif /* IPV6 */
1480

1481
void add_range(char *start, char *end)
52✔
1482
{
1483
    struct addrinfo addr_hints;
1484
    struct addrinfo *addr_res;
1485
    unsigned long start_long;
1486
    unsigned long end_long;
1487
    int ret;
1488
#ifdef IPV6
1489
    uint64_t start_upper, start_lower;
1490
    uint64_t end_upper, end_lower;
1491
    char *start_scope_str, *end_scope_str;
1492

1493
    /*
1494
     * The compiler does not know that setting the address family hint to
1495
     * ensure that start and end are from the same address family also
1496
     * ensures that either start_long and end_long are initialized and used,
1497
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1498
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1499
     * supported to suppress compiler warnings.
1500
     */
1501
    start_long = -1;
52✔
1502
    end_long = 0;
52✔
1503
    start_upper = start_lower = -1;
52✔
1504
    end_upper = end_lower = 0;
52✔
1505
    start_scope_str = end_scope_str = NULL;
52✔
1506
#endif /* IPV6 */
1507

1508
    /* parse start address */
1509
    memset(&addr_hints, 0, sizeof(struct addrinfo));
52✔
1510
    addr_hints.ai_family = hints_ai_family;
52✔
1511
    addr_hints.ai_flags = AI_NUMERICHOST;
52✔
1512
    ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
52✔
1513
    if (ret) {
52✔
1514
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
9✔
1515
        exit(1);
9✔
1516
    }
1517
    /* start and end must be from the same address family */
1518
    hints_ai_family = addr_res->ai_family;
43✔
1519
    if (addr_res->ai_family == AF_INET) {
43✔
1520
        start_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
17✔
1521
        freeaddrinfo(addr_res);
17✔
1522
#ifdef IPV6
1523
    } else if (addr_res->ai_family == AF_INET6) {
31✔
1524
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
26✔
1525
        start_upper = be_octets_to_uint64(ipv6_addr);
26✔
1526
        start_lower = be_octets_to_uint64(ipv6_addr + 8);
26✔
1527
        freeaddrinfo(addr_res);
26✔
1528
#endif /* IPV6 */
1529
    } else {
1530
        freeaddrinfo(addr_res);
×
1531
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1532
        exit(1);
×
1533
    }
1534

1535
#ifdef IPV6
1536
    /* IPv6 addresses can have a scope */
1537
    if (hints_ai_family == AF_INET6) {
43✔
1538
        start_scope_str = strchr(start, '%');
26✔
1539
        end_scope_str = strchr(end, '%');
26✔
1540
        if ((!start_scope_str && end_scope_str) ||
26✔
1541
            (start_scope_str && !end_scope_str) ||
24✔
1542
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1543
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1544
                exit(1);
6✔
1545
        }
1546
    }
1547
#endif
1548

1549
    /* parse end address */
1550
    memset(&addr_hints, 0, sizeof(struct addrinfo));
37✔
1551
    addr_hints.ai_family = hints_ai_family;
37✔
1552
    addr_hints.ai_flags = AI_NUMERICHOST;
37✔
1553
    ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
37✔
1554
    if (ret) {
37✔
1555
        fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
7✔
1556
        exit(1);
7✔
1557
    }
1558
    if (addr_res->ai_family == AF_INET) {
30✔
1559
        end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
12✔
1560
        freeaddrinfo(addr_res);
12✔
1561
        add_addr_range_ipv4(start_long, end_long);
12✔
1562
#ifdef IPV6
1563
    } else if (addr_res->ai_family == AF_INET6) {
22✔
1564
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
18✔
1565
        end_upper = be_octets_to_uint64(ipv6_addr);
18✔
1566
        end_lower = be_octets_to_uint64(ipv6_addr + 8);
18✔
1567
        freeaddrinfo(addr_res);
18✔
1568
        add_addr_range_ipv6(start_upper, start_lower, end_upper, end_lower, start_scope_str);
18✔
1569
#endif /* IPV6 */
1570
    } else {
1571
        freeaddrinfo(addr_res);
×
1572
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1573
        exit(1);
×
1574
    }
1575
}
24✔
1576

1577
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1578
{
1579
    /* check if generator limit is exceeded */
1580
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1581
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1582
        exit(1);
6✔
1583
    }
1584

1585
    /* generate */
1586
    for (; start_long <= end_long; start_long++) {
93✔
1587
        struct in_addr in_addr_tmp;
1588
        char buffer[20];
1589
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1590
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1591
        add_name(buffer);
69✔
1592
    }
23✔
1593
}
24✔
1594

1595
#ifdef IPV6
1596
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1597
{
1598
    int i;
1599
    uint64_t ret = 0;
134✔
1600
    for (i = 0; i < 8; i++) {
1,206✔
1601
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1602
    }
16✔
1603
    return ret;
134✔
1604
}
1605

1606
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1607
{
1608
    int i;
1609
    for (i = 0; i < 8; i++) {
720✔
1610
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1611
    }
1612
}
80✔
1613

1614
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1615
                         uint64_t end_upper, uint64_t end_lower,
1616
                         const char *scope_str)
1617
{
1618
    struct in6_addr in6_addr_tmp;
1619
    char buffer[100];
1620

1621
    /* prevent generating too many addresses */
1622
    if ((start_upper + 1 < end_upper) ||
34✔
1623
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1624
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1625
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1626
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1627
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1628
        exit(1);
8✔
1629
    }
1630

1631
    while ((start_upper < end_upper) ||
66✔
1632
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1633
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1634
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1635
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1636
        if (scope_str) {
40✔
1637
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1638
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1639
                exit(1);
×
1640
            }
1641
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1642
        }
1643
        add_name(buffer);
40✔
1644
        start_lower++;
40✔
1645
        if (start_lower == 0) {
40✔
1646
            start_upper++;
2✔
1647
        }
1648
    }
1649
}
26✔
1650
#endif /* IPv6 */
1651

1652
void main_loop()
392✔
1653
{
1654
    int64_t lt;
1655
    int64_t wait_time_ns;
1656
    struct event *event;
1657
    struct host_entry *h;
1658

1659
    while (event_queue_ping.first || event_queue_timeout.first) {
2,126✔
1660
        dbg_printf("%s", "# main_loop\n");
1661

1662
        /* timeout event ? */
1663
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,765✔
1664
            event = ev_dequeue(&event_queue_timeout);
101✔
1665
            h = event->host;
101✔
1666

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

1669
            stats_add(h, event->ping_index, 0, -1);
101✔
1670

1671
            if (opt.per_recv_on) {
101✔
1672
                print_timeout(h, event->ping_index);
25✔
1673
            }
7✔
1674

1675
            /* do we need to send a retry? */
1676
            if (!opt.loop_on && !opt.count_on) {
101✔
1677
                if (h->num_sent < opt.retry + 1) {
60✔
1678
                    if (opt.backoff_on) {
35✔
1679
                        h->timeout *= opt.backoff;
35✔
1680
                    }
9✔
1681
                    send_ping(h, event->ping_index);
35✔
1682
                }
9✔
1683
            }
14✔
1684

1685
            /* note: we process first timeout events, because we might need to
1686
             * wait to process ping events, while we for sure never need to
1687
             * wait for timeout events.
1688
             */
1689
            continue;
101✔
1690
        }
1691

1692
        /* ping event ? */
1693
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,925✔
1694
            /* Make sure that we don't ping more than once every "interval" */
1695
            lt = current_time_ns - last_send_time;
1,256✔
1696
            if (lt < opt.interval)
1,256✔
1697
                goto wait_for_reply;
253✔
1698

1699
            /* Dequeue the event */
1700
            event = ev_dequeue(&event_queue_ping);
1,003✔
1701
            h = event->host;
1,003✔
1702

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

1705
            /* Send the ping */
1706
            send_ping(h, event->ping_index);
1,003✔
1707

1708
            /* Loop and count mode: schedule next ping */
1709
            if (opt.loop_on || (opt.count_on && event->ping_index + 1 < opt.count)) {
1,003✔
1710
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + opt.perhost_interval);
438✔
1711
            }
114✔
1712
        }
261✔
1713

1714
    wait_for_reply:
738✔
1715

1716
        /* When is the next ping next event? */
1717
        wait_time_ns = -1;
1,664✔
1718
        if (event_queue_ping.first) {
1,664✔
1719
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,219✔
1720
            if (wait_time_ns < 0)
1,219✔
1721
                wait_time_ns = 0;
535✔
1722
            /* make sure that we wait enough, so that the inter-ping delay is
1723
             * bigger than 'interval' */
1724
            if (wait_time_ns < opt.interval) {
1,219✔
1725
                lt = current_time_ns - last_send_time;
537✔
1726
                if (lt < opt.interval) {
537✔
1727
                    wait_time_ns = opt.interval - lt;
537✔
1728
                }
181✔
1729
            }
181✔
1730

1731
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1732
        }
329✔
1733

1734
        /* When is the next timeout event? */
1735
        if (event_queue_timeout.first) {
1,664✔
1736
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
1,051✔
1737
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
1,051✔
1738
                wait_time_ns = wait_time_timeout;
441✔
1739
                if (wait_time_ns < 0) {
441✔
1740
                    wait_time_ns = 0;
×
1741
                }
1742
            }
109✔
1743

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

1747
        /* When is the next report due? */
1748
        if (report_interval && (opt.loop_on || opt.count_on)) {
1,664✔
1749
            int64_t wait_time_next_report = next_report_time - current_time_ns;
214✔
1750
            if (wait_time_next_report < wait_time_ns) {
214✔
1751
                wait_time_ns = wait_time_next_report;
50✔
1752
                if (wait_time_ns < 0) {
50✔
1753
                    wait_time_ns = 0;
×
1754
                }
1755
            }
2✔
1756

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

1760
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1761
        if (wait_time_ns == -1) {
1,664✔
1762
            break;
14✔
1763
        }
1764

1765
        /* end of loop was requested by interrupt signal handler */
1766
        if (finish_requested) {
1,650✔
1767
            break;
17✔
1768
        }
1769

1770
        /* Receive replies */
1771
        /* (this is what sleeps during each loop iteration) */
1772
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1773
        if (wait_for_reply(wait_time_ns)) {
1,633✔
1774
            while (wait_for_reply(0))
1,494✔
1775
                ; /* process other replies in the queue */
1776
        }
250✔
1777

1778
        update_current_time();
1,633✔
1779

1780
        if (status_snapshot) {
1,633✔
1781
            status_snapshot = 0;
3✔
1782
            if (opt.print_json_on)
3✔
1783
                print_per_system_splits_json();
×
1784
            else
1785
                print_per_system_splits();
3✔
1786
        }
1✔
1787

1788
        /* Print report */
1789
        if (report_interval && (opt.loop_on || opt.count_on) && (current_time_ns >= next_report_time)) {
1,633✔
1790
            if (opt.print_netdata_on) {
36✔
1791
                print_netdata();
3✔
1792
            }
1✔
1793
            else if (opt.print_json_on) {
33✔
1794
                print_per_system_splits_json();
3✔
1795
            }
1✔
1796
            else {
1797
                print_per_system_splits();
30✔
1798
            }
1799

1800
            while (current_time_ns >= next_report_time) {
72✔
1801
                next_report_time += report_interval;
36✔
1802
            }
1803
        }
2✔
1804
    }
1805
}
392✔
1806

1807
/************************************************************
1808

1809
  Function: signal_handler
1810

1811
*************************************************************
1812

1813
  Inputs:  int signum
1814

1815
  Description:
1816

1817
  SIGQUIT signal handler - set flag and return
1818
  SIGINT signal handler - set flag and return
1819

1820
************************************************************/
1821

1822
void signal_handler(int signum)
17✔
1823
{
1824
    switch (signum) {
17✔
1825
    case SIGINT:
10✔
1826
        finish_requested = 1;
14✔
1827
        break;
14✔
1828

1829
    case SIGQUIT:
2✔
1830
        status_snapshot = 1;
3✔
1831
        break;
3✔
1832
    }
1833
}
17✔
1834

1835
/************************************************************
1836

1837
  Function: update_current_time
1838

1839
*************************************************************/
1840

1841
void update_current_time()
5,521✔
1842
{
1843
    clock_gettime(CLOCKID, &current_time);
5,521✔
1844
    current_time_ns = timespec_ns(&current_time);
5,521✔
1845
}
5,521✔
1846

1847
/************************************************************
1848

1849
  Function: finish
1850

1851
*************************************************************
1852

1853
  Inputs:  void (none)
1854

1855
  Description:
1856

1857
  Main program clean up and exit point
1858

1859
************************************************************/
1860

1861
void finish()
392✔
1862
{
1863
    int i;
1864
    HOST_ENTRY *h;
1865

1866
    update_current_time();
392✔
1867
    end_time = current_time_ns;
392✔
1868

1869
    /* tot up unreachables */
1870
    for (i = 0; i < num_hosts; i++) {
986✔
1871
        h = table[i];
594✔
1872

1873
        if (!h->num_recv) {
594✔
1874
            num_unreachable++;
87✔
1875

1876
            if (opt.verbose_on || opt.unreachable_on) {
87✔
1877
                printf("%s", h->host);
47✔
1878

1879
                if (opt.verbose_on)
47✔
1880
                    printf(" is unreachable");
44✔
1881

1882
                printf("\n");
47✔
1883
            }
3✔
1884
        }
13✔
1885
    }
156✔
1886

1887
    if (opt.count_on || opt.loop_on) {
392✔
1888
        if (opt.print_json_on)
168✔
1889
            print_per_system_stats_json();
56✔
1890
        else
1891
            print_per_system_stats();
112✔
1892
    }
44✔
1893
#if defined(DEBUG) || defined(_DEBUG)
1894
    else if (dbg_opt.print_per_system_on) {
1895
        if (opt.print_json_on)
1896
            print_per_system_stats_json();
1897
        else
1898
            print_per_system_stats();
1899
    }
1900
#endif /* DEBUG || _DEBUG */
1901

1902
    if (opt.stats_on) {
392✔
1903
        if (opt.print_json_on)
12✔
1904
            print_global_stats_json();
3✔
1905
        else
1906
            print_global_stats();
9✔
1907
    }
4✔
1908

1909
    if (opt.min_reachable) {
392✔
1910
        if ((num_hosts - num_unreachable) >= opt.min_reachable) {
9✔
1911
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", opt.min_reachable, num_hosts - num_unreachable);
6✔
1912
            exit(0);
6✔
1913
        }
1914
        else {
1915
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", opt.min_reachable, num_hosts - num_unreachable);
3✔
1916
            exit(1);
3✔
1917
        }
1918
    }
1919

1920
    if (num_noaddress)
383✔
1921
        exit(2);
3✔
1922
    else if (num_alive != num_hosts)
380✔
1923
        exit(1);
48✔
1924

1925
    exit(0);
332✔
1926
}
1927

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

1930
  Function: send_ping
1931

1932
*************************************************************
1933

1934
  Inputs:  int s, HOST_ENTRY *h
1935

1936
  Description:
1937

1938
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
1939
  will be added on by the kernel.  The ID field is our UNIX process ID,
1940
  and the sequence number is an index into an array of outstanding
1941
  ping requests. The sequence number will later be used to quickly
1942
  figure out who the ping reply came from.
1943

1944
************************************************************/
1945

1946
int send_ping(HOST_ENTRY *h, int index)
1,038✔
1947
{
1948
    int n;
1949
    int myseq;
1950
    int ret = 1;
1,038✔
1951
    uint8_t proto = ICMP_ECHO;
1,038✔
1952

1953
    update_current_time();
1,038✔
1954
    h->last_send_time = current_time_ns;
1,038✔
1955
    myseq = seqmap_add(h->i, index, current_time_ns);
1,038✔
1956

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

1959
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
1,038✔
1960
        if(opt.icmp_request_typ == 13)
915✔
1961
            proto = ICMP_TSTAMP;
18✔
1962
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
915✔
1963
    }
255✔
1964
#ifdef IPV6
1965
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
1966
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
1967
    }
15✔
1968
#endif
1969
    else {
1970
        return 0;
×
1971
    }
1972

1973
    /* error sending? */
1974
    if (
768✔
1975
        (n < 0)
270✔
1976
#if defined(EHOSTDOWN)
1977
        && errno != EHOSTDOWN
304✔
1978
#endif
1979
    ) {
1980
        if (opt.verbose_on) {
34✔
1981
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
1982
        }
1983
        else {
1984
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
1985
        }
1986

1987
        h->num_sent++;
34✔
1988
        h->num_sent_i++;
34✔
1989
        if (!opt.loop_on)
34✔
1990
            h->resp_times[index] = RESP_ERROR;
34✔
1991

1992
        ret = 0;
34✔
1993
    }
1994
    else {
1995
        /* schedule timeout */
1996
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
1,004✔
1997

1998
        /* mark this trial as outstanding */
1999
        if (!opt.loop_on) {
1,004✔
2000
            h->resp_times[index] = RESP_WAITING;
872✔
2001
        }
238✔
2002
    }
2003

2004
    num_pingsent++;
1,038✔
2005
    last_send_time = h->last_send_time;
1,038✔
2006

2007
    return (ret);
1,038✔
2008
}
270✔
2009

2010
int socket_can_read(struct timeval *timeout)
1,633✔
2011
{
2012
    int nfound;
2013
    fd_set readset;
2014
    int socketmax;
2015

2016
#ifndef IPV6
2017
    socketmax = socket4;
2018
#else
2019
    socketmax = socket4 > socket6 ? socket4 : socket6;
1,633✔
2020
#endif
2021

2022
select_again:
1,214✔
2023
    FD_ZERO(&readset);
21,074✔
2024
    if (socket4 >= 0)
1,650✔
2025
        FD_SET(socket4, &readset);
1,650✔
2026
#ifdef IPV6
2027
    if (socket6 >= 0)
1,650✔
2028
        FD_SET(socket6, &readset);
1,650✔
2029
#endif
2030

2031
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
1,650✔
2032
    if (nfound < 0) {
1,650✔
2033
        if (errno == EINTR) {
17✔
2034
            /* interrupted system call: redo the select */
2035
            goto select_again;
17✔
2036
        }
2037
        else {
2038
            perror("select");
×
2039
        }
2040
    }
2041

2042
    if (nfound > 0) {
1,633✔
2043
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
914✔
2044
            return socket4;
819✔
2045
        }
2046
#ifdef IPV6
2047
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
95✔
2048
            return socket6;
95✔
2049
        }
2050
#endif
2051
    }
2052

2053
    return -1;
719✔
2054
}
431✔
2055

2056
int receive_packet(int64_t wait_time,
3,127✔
2057
#if HAVE_SO_TIMESTAMPNS
2058
    int64_t *reply_timestamp,
2059
#else
2060
    int64_t *reply_timestamp __attribute__((unused)),
2061
#endif
2062
    struct sockaddr *reply_src_addr,
2063
    size_t reply_src_addr_len,
2064
    char *reply_buf,
2065
    size_t reply_buf_len,
2066
    int *ip_header_tos,
2067
    int *ip_header_ttl)
2068
{
2069
    struct timeval to;
2070
    int s = 0;
3,127✔
2071
    int recv_len;
2072
    static unsigned char msg_control[128];
2073
    struct iovec msg_iov = {
4,493✔
2074
        reply_buf,
683✔
2075
        reply_buf_len
683✔
2076
    };
2077
    struct msghdr recv_msghdr = {0};
3,127✔
2078
    recv_msghdr.msg_name = reply_src_addr;
3,127✔
2079
    recv_msghdr.msg_namelen = reply_src_addr_len;
3,127✔
2080
    recv_msghdr.msg_iov = &msg_iov;
3,127✔
2081
    recv_msghdr.msg_iovlen = 1;
3,127✔
2082
    recv_msghdr.msg_control = &msg_control;
3,127✔
2083
    recv_msghdr.msg_controllen = sizeof(msg_control);
3,127✔
2084
    struct cmsghdr *cmsg;
2085

2086
    /* Wait for a socket to become ready */
2087
#if HAVE_MSG_DONTWAIT
2088
    if (wait_time == 0) {
3,127✔
2089
        /* Optimization: if wait_time is 0, we can skip select() and just try to
2090
         * read from the sockets with MSG_DONTWAIT */
2091
        recv_len = (socket4 >= 0) ? recvmsg(socket4, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT) : -1;
1,494✔
2092
#ifdef IPV6
2093
        if (recv_len <= 0 && socket6 >= 0) {
1,494✔
2094
            /* Reset fields potentially modified by failed recvmsg */
2095
            recv_msghdr.msg_namelen = reply_src_addr_len;
916✔
2096
            recv_msghdr.msg_controllen = sizeof(msg_control);
916✔
2097
            recv_len = recvmsg(socket6, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT);
916✔
2098
        }
250✔
2099
#endif
2100
        if (recv_len > 0) {
1,494✔
2101
            goto packet_received;
580✔
2102
        }
2103
        return 0;
914✔
2104
    }
2105
#endif
2106

2107
    int64_t wait_us = (wait_time + 999) / 1000; // round up (1 ns -> 1 us)
1,633✔
2108
    to.tv_sec = wait_us / 1000000;
1,633✔
2109
    to.tv_usec = wait_us % 1000000;
1,633✔
2110

2111
    s = socket_can_read(&to);
1,633✔
2112
    if (s == -1 || (recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC)) <= 0) {
1,633✔
2113
        return 0;
719✔
2114
    }
2115

2116
packet_received:
664✔
2117
    /* ancilliary data */
2118
    {
2119
#if HAVE_SO_TIMESTAMPNS
2120
        struct timespec reply_timestamp_ts;
2121
        struct timeval reply_timestamp_tv;
2122
#endif
2123
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,494✔
2124
             cmsg != NULL;
2,748✔
2125
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,254✔
2126
#if HAVE_SO_TIMESTAMPNS
2127
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,254✔
2128
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,242✔
2129
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,242✔
2130
            }
2131
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,254✔
2132
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2133
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2134
            }
2135
#endif
2136
#if defined(HAVE_IP_RECVTOS)
2137
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,254✔
2138
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2139
            }
2140
#endif
2141
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,254✔
2142
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2143
            }
2144
#ifdef IPV6
2145
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,254✔
2146
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2147
            }
2148
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,254✔
2149
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2150
            }
2151
#endif
2152
        }
2153
    }
2154

2155
#if defined(DEBUG) || defined(_DEBUG)
2156
    if (dbg_opt.randomly_lose_on) {
2157
        if ((random() & 0x07) <= dbg_opt.lose_factor)
2158
            return 0;
2159
    }
2160
#endif
2161

2162
    return recv_len;
1,494✔
2163
}
683✔
2164

2165
/* stats_add: update host statistics for a single packet that was received (or timed out)
2166
 * h: host entry to update
2167
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2168
 * success: 1 if response received, 0 otherwise
2169
 * latency: response time, in ns
2170
 */
2171
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
990✔
2172
{
2173
    /* sent count - we update only on receive/timeout, so that we don't get
2174
     * weird loss percentage, just because a packet was note recived yet.
2175
     */
2176
    h->num_sent++;
990✔
2177
    h->num_sent_i++;
990✔
2178

2179
    if (!success) {
990✔
2180
        if (!opt.loop_on && index >= 0) {
101✔
2181
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2182
        }
21✔
2183
        num_timeout++;
101✔
2184
        return;
101✔
2185
    }
2186

2187
    /* received count */
2188
    h->num_recv++;
889✔
2189
    h->num_recv_i++;
889✔
2190

2191
    /* maximum */
2192
    if (!h->max_reply || latency > h->max_reply) {
889✔
2193
        h->max_reply = latency;
683✔
2194
    }
179✔
2195
    if (!h->max_reply_i || latency > h->max_reply_i) {
889✔
2196
        h->max_reply_i = latency;
703✔
2197
    }
182✔
2198

2199
    /* minimum */
2200
    if (!h->min_reply || latency < h->min_reply) {
889✔
2201
        h->min_reply = latency;
535✔
2202
    }
161✔
2203
    if (!h->min_reply_i || latency < h->min_reply_i) {
889✔
2204
        h->min_reply_i = latency;
573✔
2205
    }
165✔
2206

2207
    /* total time (for average) */
2208
    h->total_time += latency;
889✔
2209
    h->total_time_i += latency;
889✔
2210

2211
    /* response time per-packet (count mode) */
2212
    if (!opt.loop_on && index >= 0) {
889✔
2213
        h->resp_times[index] = latency;
771✔
2214
    }
217✔
2215
}
266✔
2216

2217
/* stats_reset_interval: reset interval statistics
2218
 * h: host entry to update
2219
 */
2220
void stats_reset_interval(HOST_ENTRY *h)
31✔
2221
{
2222
    h->num_sent_i = 0;
31✔
2223
    h->num_recv_i = 0;
31✔
2224
    h->max_reply_i = 0;
31✔
2225
    h->min_reply_i = 0;
31✔
2226
    h->total_time_i = 0;
31✔
2227
}
31✔
2228

2229
int decode_icmp_ipv4(
1,397✔
2230
    struct sockaddr *response_addr,
2231
    size_t response_addr_len,
2232
    char *reply_buf,
2233
    size_t reply_buf_len,
2234
    unsigned short *id,
2235
    unsigned short *seq,
2236
    IP_HEADER_RESULT *ip_header_res)
2237
{
2238
    struct icmp *icp;
2239
    int hlen = 0;
1,397✔
2240

2241
    if (!using_sock_dgram4) {
1,397✔
2242
        struct ip *ip = (struct ip *)reply_buf;
1,389✔
2243
        ip_header_res->tos = ip->ip_tos;
1,389✔
2244
        ip_header_res->ttl = ip->ip_ttl;
1,389✔
2245

2246
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2247
        /* The alpha headers are decidedly broken.
2248
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2249
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2250
         */
2251
        hlen = (ip->ip_vhl & 0x0F) << 2;
2252
#else
2253
        hlen = ip->ip_hl << 2;
1,389✔
2254
#endif
2255
    }
237✔
2256

2257
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,397✔
2258
        /* too short */
NEW
2259
        if (opt.verbose_on) {
×
2260
            char buf[INET6_ADDRSTRLEN];
2261
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2262
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2263
        }
2264
        return -1;
×
2265
    }
2266

2267
    icp = (struct icmp *)(reply_buf + hlen);
1,397✔
2268

2269
    if ((opt.icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,397✔
2270
        (opt.icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
600✔
2271
        /* Handle other ICMP packets */
2272
        struct icmp *sent_icmp;
2273
        SEQMAP_VALUE *seqmap_value;
2274
        char addr_ascii[INET6_ADDRSTRLEN];
2275
        HOST_ENTRY *h;
2276

2277
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2278
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
1,047✔
2279
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2280
            return -1;
×
2281
        }
2282

2283
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
579✔
2284

2285
        if ((opt.icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
579✔
2286
            (opt.icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
UNCOV
2287
            sent_icmp->icmp_id != ident4) {
×
2288
            /* not caused by us */
2289
            return -1;
579✔
2290
        }
2291

2292
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2293
        if (seqmap_value == NULL) {
×
2294
            return -1;
×
2295
        }
2296

2297
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2298

2299
        switch (icp->icmp_type) {
×
2300
        case ICMP_UNREACH:
2301
            h = table[seqmap_value->host_nr];
×
2302
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2303
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2304
                    addr_ascii, h->host);
2305
            }
2306
            else {
2307
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2308
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2309
            }
2310

2311
            print_warning("\n");
×
2312
            num_othericmprcvd++;
×
2313
            break;
×
2314

2315
        case ICMP_SOURCEQUENCH:
2316
        case ICMP_REDIRECT:
2317
        case ICMP_TIMXCEED:
2318
        case ICMP_PARAMPROB:
2319
            h = table[seqmap_value->host_nr];
×
2320
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2321
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2322
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2323
            }
2324
            else {
2325
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2326
                    icp->icmp_type, addr_ascii, h->host);
×
2327
            }
2328
            print_warning("\n");
×
2329
            num_othericmprcvd++;
×
2330
            break;
×
2331
        }
2332

2333
        return -1;
×
2334
    }
2335

2336
    *id = icp->icmp_id;
818✔
2337
    *seq = ntohs(icp->icmp_seq);
818✔
2338
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
818✔
2339

2340
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2341
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
NEW
2342
            if (opt.verbose_on) {
×
2343
                char buf[INET6_ADDRSTRLEN];
2344
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2345
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2346
            }
2347
            return -1;
×
2348
        }
2349

2350
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
2351
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
2352
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
2353
    }
2354

2355
    return hlen;
818✔
2356
}
237✔
2357

2358
#ifdef IPV6
2359
int decode_icmp_ipv6(
97✔
2360
    struct sockaddr *response_addr,
2361
    size_t response_addr_len,
2362
    char *reply_buf,
2363
    size_t reply_buf_len,
2364
    unsigned short *id,
2365
    unsigned short *seq)
2366
{
2367
    struct icmp6_hdr *icp;
2368

2369
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
97✔
NEW
2370
        if (opt.verbose_on) {
×
2371
            char buf[INET6_ADDRSTRLEN];
2372
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2373
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2374
        }
2375
        return 0; /* too short */
×
2376
    }
2377

2378
    icp = (struct icmp6_hdr *)reply_buf;
97✔
2379

2380
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
97✔
2381
        /* Handle other ICMPv6 packets */
2382
        struct ip6_hdr *sent_ipv6;
2383
        struct icmp6_hdr *sent_icmp;
2384
        SEQMAP_VALUE *seqmap_value;
2385
        char addr_ascii[INET6_ADDRSTRLEN];
2386
        HOST_ENTRY *h;
2387

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

2394
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
6✔
2395
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
6✔
2396
            /* discard ICMPv6 message if we can't tell that it was caused by
2397
             * us, because the IPv6 header is not directly followed by an
2398
             * ICMPv6 header
2399
             */
2400
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
2401
            return 0;
×
2402
        }
2403
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
6✔
2404

2405
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
6✔
2406
            /* not caused by us */
2407
            return 0;
6✔
2408
        }
2409

2410
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2411
        if (seqmap_value == NULL) {
×
2412
            return 0;
×
2413
        }
2414

2415
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2416
        h = table[seqmap_value->host_nr];
×
2417

2418
        switch (icp->icmp6_type) {
×
2419
        case ICMP6_DST_UNREACH:
2420
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
2421
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2422
                    icp->icmp6_code, addr_ascii, h->host);
×
2423
            } else {
2424
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2425
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2426
            }
2427
            print_warning("\n");
×
2428
            num_othericmprcvd++;
×
2429
            break;
×
2430

2431
        case ICMP6_PACKET_TOO_BIG:
2432
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
2433
                addr_ascii, h->host);
2434
            num_othericmprcvd++;
×
2435
            break;
×
2436

2437
        case ICMP6_TIME_EXCEEDED:
2438
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
2439
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2440
                    icp->icmp6_code, addr_ascii, h->host);
×
2441
            } else {
2442
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2443
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
2444
            }
2445
            print_warning("\n");
×
2446
            num_othericmprcvd++;
×
2447
            break;
×
2448

2449
        case ICMP6_PARAM_PROB:
2450
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
2451
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2452
                    icp->icmp6_code, addr_ascii, h->host);
×
2453
            } else {
2454
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2455
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
2456
            }
2457
            print_warning("\n");
×
2458
            num_othericmprcvd++;
×
2459
            break;
×
2460

2461
        default:
2462
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
2463
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
2464
            num_othericmprcvd++;
×
2465
            break;
×
2466
        }
2467

2468
        return 0;
×
2469
    }
2470

2471
    *id = icp->icmp6_id;
91✔
2472
    *seq = ntohs(icp->icmp6_seq);
91✔
2473

2474
    return 1;
91✔
2475
}
15✔
2476
#endif
2477

2478
int wait_for_reply(int64_t wait_time)
3,127✔
2479
{
2480
    int result;
2481
    static char buffer[RECV_BUFSIZE];
2482
    struct sockaddr_storage response_addr;
2483
    int n, avg;
2484
    HOST_ENTRY *h;
2485
    int64_t this_reply;
2486
    int this_count;
2487
    int64_t recv_time = 0;
3,127✔
2488
    SEQMAP_VALUE *seqmap_value;
2489
    unsigned short id;
2490
    unsigned short seq;
2491
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
3,127✔
2492

2493
    /* Receive packet */
2494
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,810✔
2495
        &recv_time, /* reply_timestamp */
2496
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2497
        sizeof(response_addr), /* reply_src_addr_len */
2498
        buffer, /* reply_buf */
2499
        sizeof(buffer), /* reply_buf_len */
2500
        &ip_header_res.tos, /* TOS resp. TC byte */
683✔
2501
        &ip_header_res.ttl /* TTL resp. hop limit */
683✔
2502
    );
2503

2504
    if (result <= 0) {
3,127✔
2505
        return 0;
1,633✔
2506
    }
2507

2508
    update_current_time();
1,494✔
2509
    if (recv_time == 0)
1,494✔
2510
        recv_time = current_time_ns;
252✔
2511

2512
    /* Process ICMP packet and retrieve id/seq */
2513
    if (response_addr.ss_family == AF_INET) {
1,494✔
2514
        int ip_hlen = decode_icmp_ipv4(
1,397✔
2515
            (struct sockaddr *)&response_addr,
2516
            sizeof(response_addr),
2517
            buffer,
2518
            sizeof(buffer),
2519
            &id,
2520
            &seq,
2521
            &ip_header_res);
2522
        if (ip_hlen < 0) {
1,397✔
2523
            return 1;
579✔
2524
        }
2525
        if (id != ident4) {
818✔
2526
            return 1; /* packet received, but not the one we are looking for! */
×
2527
        }
2528
        if (!using_sock_dgram4) {
818✔
2529
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2530
             * with fping with IPv6 hosts */
2531
            result -= ip_hlen;
810✔
2532
        }
234✔
2533
    }
234✔
2534
#ifdef IPV6
2535
    else if (response_addr.ss_family == AF_INET6) {
97✔
2536
        if (!decode_icmp_ipv6(
97✔
2537
                (struct sockaddr *)&response_addr,
2538
                sizeof(response_addr),
2539
                buffer,
2540
                sizeof(buffer),
2541
                &id,
2542
                &seq)) {
2543
            return 1;
6✔
2544
        }
2545
        if (id != ident6) {
91✔
2546
            return 1; /* packet received, but not the one we are looking for! */
×
2547
        }
2548
    }
15✔
2549
#endif
2550
    else {
2551
        return 1;
×
2552
    }
2553

2554
    seqmap_value = seqmap_fetch(seq, current_time_ns);
909✔
2555
    if (seqmap_value == NULL) {
909✔
2556
        return 1;
12✔
2557
    }
2558

2559
    /* find corresponding host_entry */
2560
    n = seqmap_value->host_nr;
897✔
2561
    h = table[n];
897✔
2562
    this_count = seqmap_value->ping_count;
897✔
2563
    this_reply = recv_time - seqmap_value->ping_ts;
897✔
2564

2565
    /* update stats that include invalid replies */
2566
    h->num_recv_total++;
897✔
2567
    num_pingreceived++;
897✔
2568

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

2571
    /* optionally require reply source equal to target address */
2572
    if (opt.check_source_on && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
897✔
2573
        dbg_printf("%s\n", "discarding reply from wrong source address");
2574
        return 1;
8✔
2575
    }
2576

2577
    /* discard duplicates */
2578
    if (!opt.loop_on && !(opt.count_on && opt.quiet_on) && h->resp_times[this_count] >= 0) {
889✔
NEW
2579
        if (!opt.per_recv_on) {
×
UNCOV
2580
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2581
                h->host, this_count, result, sprint_tm(this_reply));
2582

2583
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2584
                char buf[INET6_ADDRSTRLEN];
2585
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2586
                fprintf(stderr, " [<- %s]", buf);
×
2587
            }
2588
            fprintf(stderr, "\n");
×
2589
        }
2590
        return 1;
×
2591
    }
2592

2593
    /* discard reply if delay is larger than timeout
2594
     * (see also: github #32) */
2595
    if (this_reply > h->timeout) {
889✔
2596
        return 1;
×
2597
    }
2598

2599
    /* update stats */
2600
    stats_add(h, this_count, 1, this_reply);
889✔
2601
    // TODO: move to stats_add?
2602
    if (!max_reply || this_reply > max_reply)
889✔
2603
        max_reply = this_reply;
561✔
2604
    if (!min_reply || this_reply < min_reply)
889✔
2605
        min_reply = this_reply;
445✔
2606
    sum_replies += this_reply;
889✔
2607
    total_replies++;
889✔
2608

2609
    /* initialize timeout to initial timeout (without backoff) */
2610
    h->timeout = opt.timeout;
889✔
2611

2612
    /* remove timeout event */
2613
    struct event *timeout_event = host_get_timeout_event(h, this_count);
889✔
2614
    if (timeout_event) {
889✔
2615
        ev_remove(&event_queue_timeout, timeout_event);
889✔
2616
    }
245✔
2617

2618
    /* print "is alive" */
2619
    if (h->num_recv == 1) {
889✔
2620
        num_alive++;
507✔
2621
        if (opt.fast_reachable_on && num_alive >= opt.min_reachable)
507✔
2622
            finish_requested = 1;
3✔
2623

2624
        if (opt.verbose_on || opt.alive_on) {
507✔
2625
            printf("%s", h->host);
306✔
2626

2627
            if (opt.verbose_on)
306✔
2628
                printf(" is alive");
286✔
2629
        }
86✔
2630
    }
143✔
2631

2632
    /* print received ping (unless --quiet) */
2633
    if (opt.per_recv_on) {
889✔
2634
        avg = h->total_time / h->num_recv;
277✔
2635
        print_recv(h,
362✔
2636
            recv_time,
85✔
2637
            result,
85✔
2638
            this_count,
85✔
2639
            this_reply,
85✔
2640
            avg);
85✔
2641
    }
85✔
2642

2643
    if (opt.verbose_on || opt.alive_on || opt.per_recv_on) {
889✔
2644
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
583✔
2645
            char buf[INET6_ADDRSTRLEN];
2646
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2647
            fprintf(stderr, " [<- %s]", buf);
6✔
2648
        }
2649
        if (opt.print_json_on) {
583✔
2650
            print_recv_ext_json(&ip_header_res,
35✔
2651
                recv_time,
11✔
2652
                this_reply);
11✔
2653
        }
11✔
2654
        else {
2655
            print_recv_ext(&ip_header_res,
548✔
2656
                recv_time,
160✔
2657
                this_reply);
160✔
2658
        }
2659
    }
171✔
2660
    return 1;
889✔
2661
}
683✔
2662

2663
/************************************************************
2664

2665
  Function: add_name
2666

2667
*************************************************************
2668

2669
  Inputs:  char* name
2670

2671
  Description:
2672

2673
  process input name for addition to target list
2674
  name can turn into multiple targets via multiple interfaces (-m)
2675
  or via NIS groups
2676

2677
************************************************************/
2678

2679
void add_name(char *name)
628✔
2680
{
2681
    struct addrinfo *res0, *res, hints;
2682
    int ret_ga;
2683
    char *printname;
2684
    char namebuf[256];
2685
    char addrbuf[256];
2686

2687
    /* getaddrinfo */
2688
    memset(&hints, 0, sizeof(struct addrinfo));
628✔
2689
    hints.ai_flags = AI_UNUSABLE;
628✔
2690
    hints.ai_socktype = SOCK_RAW;
628✔
2691
    hints.ai_family = hints_ai_family;
628✔
2692
    if (hints_ai_family == AF_INET) {
628✔
2693
        hints.ai_protocol = IPPROTO_ICMP;
81✔
2694
    }
21✔
2695
#ifdef IPV6
2696
    else if (hints_ai_family == AF_INET6) {
547✔
2697
        hints.ai_protocol = IPPROTO_ICMPV6;
35✔
2698
    }
1✔
2699
#endif
2700
    else {
2701
        hints.ai_socktype = SOCK_STREAM;
512✔
2702
        hints.ai_protocol = 0;
512✔
2703
    }
2704
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
628✔
2705
    if (ret_ga) {
628✔
2706
        if (!opt.quiet_on)
29✔
2707
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
2708
        num_noaddress++;
29✔
2709

2710
        // Handle JSON output for invalid hosts
2711
        if (opt.print_json_on) {
29✔
2712
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
2713
            return;
21✔
2714
        }
2715

2716
        return;
26✔
2717
    }
2718

2719
    /* NOTE: we could/should loop with res on all addresses like this:
2720
     * for (res = res0; res; res = res->ai_next) {
2721
     * We don't do it yet, however, because is is an incompatible change
2722
     * (need to implement a separate option for this)
2723
     */
2724
    for (res = res0; res; res = res->ai_next) {
599✔
2725
        /* opt.name_on: addr -> name lookup requested) */
2726
        if (opt.name_on || opt.rdns_on) {
601✔
2727
            int do_rdns = opt.rdns_on ? 1 : 0;
3✔
2728
            if (opt.name_on) {
3✔
2729
                /* Was it a numerical address? Only then do a rdns-query */
2730
                struct addrinfo *nres;
2731
                hints.ai_flags = AI_NUMERICHOST;
×
2732
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
2733
                    do_rdns = 1;
×
2734
                    freeaddrinfo(nres);
×
2735
                }
2736
            }
2737

2738
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2739
                printname = namebuf;
3✔
2740
            }
1✔
2741
            else {
2742
                printname = name;
×
2743
            }
2744
        }
1✔
2745
        else {
2746
            printname = name;
596✔
2747
        }
2748

2749
        /* opt.addr_on: name -> addr lookup requested */
2750
        if (opt.addr_on) {
599✔
2751
            int ret;
2752
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2753
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2754
            if (ret) {
3✔
NEW
2755
                if (!opt.quiet_on) {
×
2756
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2757
                }
2758
                continue;
×
2759
            }
2760

2761
            if (opt.name_on || opt.rdns_on) {
3✔
2762
                char nameaddrbuf[512 + 3];
2763
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
2764
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2765
            }
2766
            else {
2767
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2768
            }
2769
        }
1✔
2770
        else {
2771
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
596✔
2772
        }
2773

2774
        if (!opt.multif_on) {
599✔
2775
            break;
599✔
2776
        }
2777
    }
2778

2779
    freeaddrinfo(res0);
599✔
2780
}
166✔
2781

2782
/************************************************************
2783

2784
  Function: add_addr
2785

2786
*************************************************************
2787

2788
  Description:
2789

2790
  add single address to list of hosts to be pinged
2791

2792
************************************************************/
2793

2794
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
599✔
2795
{
2796
    HOST_ENTRY *p;
2797
    int n;
2798
    int64_t *i;
2799

2800
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
599✔
2801
    if (!p)
599✔
2802
        crash_and_burn("can't allocate HOST_ENTRY");
×
2803

2804
    p->name = strdup(name);
599✔
2805
    p->host = strdup(host);
599✔
2806
    memcpy(&p->saddr, ipaddr, ipaddr_len);
599✔
2807
    p->saddr_len = ipaddr_len;
599✔
2808
    p->timeout = opt.timeout;
599✔
2809
    p->min_reply = 0;
599✔
2810

2811
    if (opt.print_netdata_on) {
599✔
2812
        char *s = p->name;
3✔
2813
        while (*s) {
30✔
2814
            if (!isalnum(*s))
27✔
2815
                *s = '_';
9✔
2816
            s++;
27✔
2817
        }
2818
    }
1✔
2819

2820
    if (strlen(p->host) > max_hostname_len)
599✔
2821
        max_hostname_len = strlen(p->host);
406✔
2822

2823
    /* array for response time results */
2824
    if (!opt.loop_on) {
599✔
2825
#if SIZE_MAX <= UINT_MAX
2826
        if (trials > (SIZE_MAX / sizeof(int64_t)))
2827
            crash_and_burn("resp_times array too large for memory");
2828
#endif
2829
        i = (int64_t *)malloc(trials * sizeof(int64_t));
585✔
2830
        if (!i)
585✔
2831
            crash_and_burn("can't allocate resp_times array");
×
2832

2833
        for (n = 0; n < trials; n++)
2,906✔
2834
            i[n] = RESP_UNUSED;
2,321✔
2835

2836
        p->resp_times = i;
585✔
2837
    }
153✔
2838

2839
    /* allocate event storage */
2840
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
599✔
2841
    if (!p->event_storage_ping) {
599✔
2842
        errno_crash_and_burn("can't allocate event_storage_ping");
×
2843
    }
2844
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
599✔
2845
    if (!p->event_storage_timeout) {
599✔
2846
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
2847
    }
2848

2849
    /* schedule first ping */
2850
    host_add_ping_event(p, 0, current_time_ns);
599✔
2851

2852
    num_hosts++;
599✔
2853
}
599✔
2854

2855
/************************************************************
2856

2857
  Function: crash_and_burn
2858

2859
*************************************************************
2860

2861
  Inputs:  char* message
2862

2863
  Description:
2864

2865
************************************************************/
2866

2867
void crash_and_burn(char *message)
×
2868
{
2869
    fprintf(stderr, "%s: %s\n", prog, message);
×
2870
    exit(4);
×
2871
}
2872

2873
/************************************************************
2874

2875
  Function: errno_crash_and_burn
2876

2877
*************************************************************
2878

2879
  Inputs:  char* message
2880

2881
  Description:
2882

2883
************************************************************/
2884

2885
void errno_crash_and_burn(char *message)
8✔
2886
{
2887
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
2888
    exit(4);
8✔
2889
}
2890

2891
/************************************************************
2892

2893
  Function: print_warning
2894

2895
  Description: fprintf(stderr, ...), unless running with -q
2896

2897
*************************************************************/
2898

2899
void print_warning(char *format, ...)
54✔
2900
{
2901
    va_list args;
2902
    if (!opt.quiet_on) {
54✔
2903
        va_start(args, format);
54✔
2904
        vfprintf(stderr, format, args);
54✔
2905
        va_end(args);
54✔
2906
    }
8✔
2907
}
54✔
2908

2909

2910

2911
/************************************************************
2912

2913
  Function: addr_cmp
2914

2915
*************************************************************/
2916
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
601✔
2917
{
2918
    if (a->sa_family != b->sa_family) {
601✔
2919
        return a->sa_family - b->sa_family;
×
2920
    }
2921
    else {
2922
        if (a->sa_family == AF_INET) {
601✔
2923
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
537✔
2924
        }
2925
        else if (a->sa_family == AF_INET6) {
64✔
2926
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
2927
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
2928
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
2929
        }
2930
    }
2931

2932
    return 0;
×
2933
}
173✔
2934

2935
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,037✔
2936
{
2937
    struct event *event = &h->event_storage_ping[index % event_storage_count];
1,037✔
2938
    event->host = h;
1,037✔
2939
    event->ping_index = index;
1,037✔
2940
    event->ev_time = ev_time;
1,037✔
2941
    ev_enqueue(&event_queue_ping, event);
1,037✔
2942

2943
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
2944
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2945
}
1,037✔
2946

2947
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,004✔
2948
{
2949
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
1,004✔
2950
    event->host = h;
1,004✔
2951
    event->ping_index = index;
1,004✔
2952
    event->ev_time = ev_time;
1,004✔
2953
    ev_enqueue(&event_queue_timeout, event);
1,004✔
2954

2955
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
2956
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2957
}
1,004✔
2958

2959
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
889✔
2960
{
2961
    return &h->event_storage_timeout[index % event_storage_count];
889✔
2962
}
2963

2964
/************************************************************
2965

2966
  Function: ev_enqueue
2967

2968
  Enqueue an event
2969

2970
  The queue is sorted by event->ev_time, so that queue->first always points to
2971
  the earliest event.
2972

2973
  We start scanning the queue from the tail, because we assume
2974
  that new events mostly get inserted with a event time higher
2975
  than the others.
2976

2977
*************************************************************/
2978
void ev_enqueue(struct event_queue *queue, struct event *event)
2,041✔
2979
{
2980
    struct event *i;
2981
    struct event *i_prev;
2982

2983
    /* Empty list */
2984
    if (queue->last == NULL) {
2,041✔
2985
        event->ev_next = NULL;
1,650✔
2986
        event->ev_prev = NULL;
1,650✔
2987
        queue->first = event;
1,650✔
2988
        queue->last = event;
1,650✔
2989
        return;
1,650✔
2990
    }
2991

2992
    /* Insert on tail? */
2993
    if (event->ev_time - queue->last->ev_time >= 0) {
391✔
2994
        event->ev_next = NULL;
391✔
2995
        event->ev_prev = queue->last;
391✔
2996
        queue->last->ev_next = event;
391✔
2997
        queue->last = event;
391✔
2998
        return;
391✔
2999
    }
3000

3001
    /* Find insertion point */
3002
    i = queue->last;
×
3003
    while (1) {
3004
        i_prev = i->ev_prev;
×
3005
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3006
            event->ev_prev = i_prev;
×
3007
            event->ev_next = i;
×
3008
            i->ev_prev = event;
×
3009
            if (i_prev != NULL) {
×
3010
                i_prev->ev_next = event;
×
3011
            }
3012
            else {
3013
                queue->first = event;
×
3014
            }
3015
            return;
×
3016
        }
3017
        i = i_prev;
×
3018
    }
3019
}
541✔
3020

3021
/************************************************************
3022

3023
  Function: ev_dequeue
3024

3025
*************************************************************/
3026
struct event *ev_dequeue(struct event_queue *queue)
1,104✔
3027
{
3028
    struct event *dequeued;
3029

3030
    if (queue->first == NULL) {
1,104✔
3031
        return NULL;
×
3032
    }
3033
    dequeued = queue->first;
1,104✔
3034
    ev_remove(queue, dequeued);
1,104✔
3035

3036
    return dequeued;
1,104✔
3037
}
282✔
3038

3039
/************************************************************
3040

3041
  Function: ev_remove
3042

3043
*************************************************************/
3044
void ev_remove(struct event_queue *queue, struct event *event)
1,993✔
3045
{
3046
    if (queue->first == event) {
1,993✔
3047
        queue->first = event->ev_next;
1,984✔
3048
    }
524✔
3049
    if (queue->last == event) {
1,993✔
3050
        queue->last = event->ev_prev;
1,623✔
3051
    }
413✔
3052
    if (event->ev_prev) {
1,993✔
3053
        event->ev_prev->ev_next = event->ev_next;
9✔
3054
    }
3✔
3055
    if (event->ev_next) {
1,993✔
3056
        event->ev_next->ev_prev = event->ev_prev;
370✔
3057
    }
114✔
3058
    event->ev_prev = NULL;
1,993✔
3059
    event->ev_next = NULL;
1,993✔
3060
}
1,993✔
3061

3062

3063

3064
/************************************************************
3065

3066
  Function: usage
3067

3068
*************************************************************
3069

3070
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3071

3072
  Description:
3073

3074
************************************************************/
3075

3076
void usage(int is_error)
297✔
3077
{
3078
    FILE *out = is_error ? stderr : stdout;
297✔
3079
    fprintf(out, "Usage: %s [options] [targets...]\n", prog);
297✔
3080
    fprintf(out, "\n");
297✔
3081
    fprintf(out, "Probing options:\n");
297✔
3082
    fprintf(out, "   -4, --ipv4         only ping IPv4 addresses\n");
297✔
3083
    fprintf(out, "   -6, --ipv6         only ping IPv6 addresses\n");
297✔
3084
    fprintf(out, "   -b, --size=BYTES   amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
297✔
3085
    fprintf(out, "   -B, --backoff=N    set exponential backoff factor to N (default: 1.5)\n");
297✔
3086
    fprintf(out, "   -c, --count=N      count mode: send N pings to each target and report stats\n");
297✔
3087
    fprintf(out, "   -f, --file=FILE    read list of targets from a file ( - means stdin)\n");
297✔
3088
    fprintf(out, "   -g, --generate     generate target list (only if no -f specified),\n");
297✔
3089
    fprintf(out, "                      limited to at most %d targets\n", MAX_GENERATE);
297✔
3090
    fprintf(out, "                      (give start and end IP in the target list, or a CIDR address)\n");
297✔
3091
    fprintf(out, "                      (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
297✔
3092
    fprintf(out, "   -H, --ttl=N        set the IP TTL value (Time To Live hops)\n");
297✔
3093
    fprintf(out, "   -i, --interval=MSEC  interval between sending ping packets (default: %.0f ms)\n", opt.interval / 1e6);
297✔
3094
#ifdef SO_BINDTODEVICE
3095
    fprintf(out, "   -I, --iface=IFACE  bind to a particular interface\n");
297✔
3096
#endif
3097
#ifdef IP_PKTINFO
3098
    fprintf(out, "       --oiface=IFACE  send pings via a specific outgoing interface (receive from any)\n");
297✔
3099
#endif
3100
#ifdef SO_MARK
3101
    fprintf(out, "   -k, --fwmark=FWMARK set the routing mark\n");
202✔
3102
#endif
3103
    fprintf(out, "   -l, --loop         loop mode: send pings forever\n");
297✔
3104
    fprintf(out, "   -m, --all          use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
297✔
3105
    fprintf(out, "   -M, --dontfrag     set the Don't Fragment flag\n");
297✔
3106
    fprintf(out, "   -O, --tos=N        set the type of service (tos) flag on the ICMP packets\n");
297✔
3107
    fprintf(out, "   -p, --period=MSEC  interval between ping packets to one target (in ms)\n");
297✔
3108
    fprintf(out, "                      (in loop and count modes, default: %.0f ms)\n", opt.perhost_interval / 1e6);
297✔
3109
    fprintf(out, "   -r, --retry=N      number of retries (default: %d)\n", DEFAULT_RETRY);
297✔
3110
    fprintf(out, "   -R, --random       random packet data (to foil link data compression)\n");
297✔
3111
    fprintf(out, "   -S, --src=IP       set source address\n");
297✔
3112
    fprintf(out, "       --seqmap-timeout=MSEC sequence number mapping timeout (default: %.0f ms)\n", opt.seqmap_timeout / 1e6);
297✔
3113
    fprintf(out, "   -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", opt.timeout / 1e6);
297✔
3114
    fprintf(out, "                      except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
297✔
3115
    fprintf(out, "       --check-source discard replies not from target address\n");
297✔
3116
    fprintf(out, "       --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
297✔
3117
    fprintf(out, "\n");
297✔
3118
    fprintf(out, "Output options:\n");
297✔
3119
    fprintf(out, "   -a, --alive        show targets that are alive\n");
297✔
3120
    fprintf(out, "   -A, --addr         show targets by address\n");
297✔
3121
    fprintf(out, "   -C, --vcount=N     same as -c, report results (not stats) in verbose format\n");
297✔
3122
    fprintf(out, "   -d, --rdns         show targets by name (force reverse-DNS lookup)\n");
297✔
3123
    fprintf(out, "   -D, --timestamp    print timestamp before each output line\n");
297✔
3124
    fprintf(out, "       --timestamp-format=FORMAT  show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
297✔
3125
    fprintf(out, "   -e, --elapsed      show elapsed time on return packets\n");
297✔
3126
    fprintf(out, "   -J, --json         output in JSON format (-c, -C, or -l required)\n");
297✔
3127
    fprintf(out, "   -n, --name         show targets by name (reverse-DNS lookup for target IPs)\n");
297✔
3128
    fprintf(out, "   -N, --netdata      output compatible for netdata (-l -Q are required)\n");
297✔
3129
    fprintf(out, "   -o, --outage       show the accumulated outage time (lost packets * packet interval)\n");
297✔
3130
    fprintf(out, "   -q, --quiet        quiet (don't show per-target/per-ping results)\n");
297✔
3131
    fprintf(out, "   -Q, --squiet=SECS[,cumulative]  same as -q, but add interval summary every SECS seconds,\n");
297✔
3132
    fprintf(out, "                                   with 'cumulative', print stats since beginning\n");
297✔
3133
    fprintf(out, "   -s, --stats        print final stats\n");
297✔
3134
    fprintf(out, "   -u, --unreach      show targets that are unreachable\n");
297✔
3135
    fprintf(out, "   -v, --version      show version\n");
297✔
3136
    fprintf(out, "   -x, --reachable=N  shows if >=N hosts are reachable or not\n");
297✔
3137
    fprintf(out, "   -X, --fast-reachable=N exits true immediately when N hosts are found\n");
297✔
3138
    fprintf(out, "       --print-tos    show received TOS value\n");
297✔
3139
    fprintf(out, "       --print-ttl    show IP TTL value\n");
297✔
3140
    exit(is_error);
297✔
3141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc