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

schweikert / fping / 22015104148

14 Feb 2026 09:33AM UTC coverage: 88.245% (+0.2%) from 88.037%
22015104148

Pull #461

github

web-flow
Merge c4b19821b into b8626092d
Pull Request #461: Option to show used IP source address in output

23 of 25 new or added lines in 2 files covered. (92.0%)

1 existing line in 1 file now uncovered.

1644 of 1863 relevant lines covered (88.24%)

363.32 hits per line

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

87.98
/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,132✔
267
    IP_HEADER_RESULT res;
268
    res.tos = -1;
3,132✔
269
    res.ttl = -1;
3,132✔
270
    res.otime_ms = 0x80000000U;
3,132✔
271
    res.rtime_ms = 0x80000000U;
3,132✔
272
    res.ttime_ms = 0x80000000U;
3,132✔
273
    res.src_addr[0] = '\0';
3,132✔
274
    return res;
3,132✔
275
}
276

277
int event_storage_count;
278

279
/*** globals ***/
280

281
HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
282

283
/* we keep two separate queues: a ping queue, for when the next ping should be
284
 * sent, and a timeout queue. the reason for having two separate queues is that
285
 * the ping period and the timeout value are different, so if we put them in
286
 * the same event queue, we would need to scan many more entries when inserting
287
 * into the sorted list.
288
 */
289
struct event_queue event_queue_ping;
290
struct event_queue event_queue_timeout;
291

292
char *prog;
293
int ident4 = 0; /* our icmp identity field */
294
int ident6 = 0;
295
const int sock_opt_on = 1; /* to activate a socket option */
296
int socket4 = -1;
297
int socktype4 = -1;
298
int using_sock_dgram4 = 0;
299
#ifndef IPV6
300
int hints_ai_family = AF_INET;
301
#else
302
int socket6 = -1;
303
int socktype6 = -1;
304
int hints_ai_family = AF_UNSPEC;
305
#endif
306

307
volatile sig_atomic_t status_snapshot = 0;
308
volatile sig_atomic_t finish_requested = 0;
309

310
unsigned int debugging = 0;
311

312
unsigned int trials;
313
int64_t report_interval = 0;
314
int src_addr_set = 0;
315
struct in_addr src_addr;
316
#ifdef IPV6
317
int src_addr6_set = 0;
318
struct in6_addr src_addr6;
319
#endif
320

321
/* global stats */
322
int64_t max_reply = 0;
323
int64_t min_reply = 0;
324
int64_t total_replies = 0;
325
int64_t sum_replies = 0;
326
int max_hostname_len = 0;
327
int num_hosts = 0; /* total number of hosts */
328
int num_alive = 0, /* total number alive */
329
    num_unreachable = 0, /* total number unreachable */
330
    num_noaddress = 0; /* total number of addresses not found */
331
int num_timeout = 0, /* number of times select timed out */
332
    num_pingsent = 0, /* total pings sent */
333
    num_pingreceived = 0, /* total pings received */
334
    num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
335

336
struct timespec current_time; /* current time (pseudo) */
337
int64_t current_time_ns;
338
int64_t start_time;
339
int64_t end_time;
340
int64_t last_send_time; /* time last ping was sent */
341
int64_t next_report_time; /* time next -Q report is expected */
342

343

344
#if defined(DEBUG) || defined(_DEBUG)
345
int opt_debug_randomly_lose_on, opt_debug_trace_on, opt_debug_print_per_system_on;
346
int lose_factor;
347
#endif /* DEBUG || _DEBUG */
348

349
unsigned int fwmark = 0;
350

351
char *filename = NULL; /* file containing hosts to ping */
352

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

355
  Function: p_setsockopt
356

357
*************************************************************
358

359
  Inputs:  p_uid: privileged uid. Others as per setsockopt(2)
360

361
  Description:
362

363
  Elevates privileges to p_uid when required, calls
364
  setsockopt, and drops privileges back.
365

366
************************************************************/
367

368
int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
22✔
369
    const void *optval, socklen_t optlen)
370
{
371
    const uid_t saved_uid = geteuid();
22✔
372
    int res;
373

374
    if (p_uid != saved_uid && seteuid(p_uid)) {
22✔
375
        perror("cannot elevate privileges for setsockopt");
×
376
    }
377

378
    res = setsockopt(sockfd, level, optname, optval, optlen);
22✔
379

380
    if (p_uid != saved_uid && seteuid(saved_uid)) {
22✔
381
        perror("fatal error: could not drop privileges after setsockopt");
×
382
        /* continuing would be a security hole */
383
        exit(4);
×
384
    }
385

386
    return res;
22✔
387
}
388

389
unsigned long strtoul_strict(const char *arg, int base)
457✔
390
{
391
    char *endptr;
392
    unsigned long val;
393

394
    while (isspace(*arg))
483✔
395
        arg++;
26✔
396

397
    if (arg[0] == '-')
457✔
398
        usage(1);
52✔
399

400
    errno = 0;
421✔
401
    val = strtoul(arg, &endptr, base);
421✔
402
    if (errno != 0 || arg == endptr || *endptr != '\0')
421✔
403
        usage(1);
100✔
404

405
    return val;
347✔
406
}
407

408
double strtod_strict(const char *arg)
290✔
409
{
410
    char *endptr;
411
    double val;
412

413
    errno = 0;
290✔
414
    val = strtod(arg, &endptr);
290✔
415
    if (errno != 0 || arg == endptr || *endptr != '\0' || val < 0)
290✔
416
        usage(1);
102✔
417

418
    return val;
222✔
419
}
420

421
/************************************************************
422

423
  Function: main
424

425
*************************************************************
426

427
  Inputs:  int argc, char** argv
428

429
  Description:
430

431
  Main program entry point
432

433
************************************************************/
434

435
int main(int argc, char **argv)
973✔
436
{
437
/* Debug: CPU Performance */
438
#if defined(DEBUG) || defined(_DEBUG)
439
    clock_t perf_cpu_start, perf_cpu_end;
440
    double perf_cpu_time_used;
441
    perf_cpu_start = clock();
442
#endif /* DEBUG || _DEBUG */
443

444
    int c;
445
    char *endptr;
446
    const uid_t suid = geteuid();
973✔
447
    int tos = 0;
973✔
448
    struct optparse optparse_state;
449
#ifdef USE_SIGACTION
450
    struct sigaction act;
451
#endif
452

453
    /* pre-parse -h/--help, so that we also can output help information
454
     * without trying to open the socket, which might fail */
455
    prog = argv[0];
973✔
456
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
973✔
457
        usage(0);
3✔
458
    }
1✔
459

460
    socket4 = open_ping_socket_ipv4(&socktype4);
971✔
461
#ifdef __linux__
462
    /* We only treat SOCK_DGRAM differently on Linux, where the IPv4 header
463
     * structure is missing in the message.
464
     */
465
    using_sock_dgram4 = (socktype4 == SOCK_DGRAM);
642✔
466
#endif
467

468
#ifdef IPV6
469
    socket6 = open_ping_socket_ipv6(&socktype6);
971✔
470
    /* if called (sym-linked) via 'fping6', imply '-6'
471
     * for backward compatibility */
472
    if (strstr(prog, "fping6")) {
971✔
473
        hints_ai_family = AF_INET6;
2✔
474
    }
475
#endif
476

477
    memset(&src_addr, 0, sizeof(src_addr));
971✔
478
#ifdef IPV6
479
    memset(&src_addr6, 0, sizeof(src_addr6));
971✔
480
#endif
481

482
    if (!suid && suid != getuid()) {
971✔
483
        /* *temporarily* drop privileges */
484
        if (seteuid(getuid()) == -1)
868✔
485
            perror("cannot setuid");
×
486
    }
240✔
487

488
    optparse_init(&optparse_state, argv);
971✔
489
    ident4 = ident6 = htons(getpid() & 0xFFFF);
971✔
490
    opt_verbose_on = 1;
971✔
491
    opt_backoff_on = 1;
971✔
492
    opterr = 1;
971✔
493

494
    /* get command line options */
495

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

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

658
        case 't':
56✔
659
            opt_timeout = strtod_strict(optparse_state.optarg) * 1000000;
66✔
660
            opt_timeout_on = 1;
52✔
661
            break;
52✔
662

663
        case 'r':
44✔
664
            opt_retry = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
51✔
665
            break;
41✔
666

667
        case 'i':
38✔
668
            opt_interval = strtod_strict(optparse_state.optarg) * 1000000;
48✔
669
            break;
34✔
670

671
        case 'p':
90✔
672
            opt_perhost_interval = strtod_strict(optparse_state.optarg) * 1000000;
123✔
673
            break;
109✔
674

675
        case 'c':
104✔
676
            opt_count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
142✔
677
            if (!opt_count)
132✔
678
                usage(1);
3✔
679

680
            opt_count_on = 1;
130✔
681
            break;
130✔
682

683
        case 'C':
42✔
684
            opt_count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
60✔
685
            if (!opt_count)
50✔
686
                usage(1);
3✔
687

688
            opt_count_on = 1;
48✔
689
            opt_report_all_rtts_on = 1;
48✔
690
            break;
48✔
691

692
        case 'b':
20✔
693
            opt_ping_data_size = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
30✔
694
            opt_size_on = 1;
20✔
695
            break;
20✔
696

697
        case 'h':
2✔
698
            usage(0);
3✔
699
            break;
1✔
700

701
        case 'q':
26✔
702
            opt_verbose_on = 0;
38✔
703
            opt_quiet_on = 1;
38✔
704
            break;
38✔
705

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

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

726
            break;
29✔
727

728
        case 'e':
6✔
729
            opt_elapsed_on = 1;
8✔
730
            break;
8✔
731

732
        case 'm':
733
            opt_multif_on = 1;
×
734
            break;
×
735

736
        case 'N':
2✔
737
            opt_print_netdata_on = 1;
3✔
738
            break;
3✔
739

740
        case 'n':
4✔
741
            opt_name_on = 1;
6✔
742
            if (opt_rdns_on) {
6✔
743
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
744
                exit(1);
3✔
745
            }
746
            break;
3✔
747

748
        case 'd':
6✔
749
            opt_rdns_on = 1;
9✔
750
            if (opt_name_on) {
9✔
751
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
752
                exit(1);
3✔
753
            }
754
            break;
6✔
755

756
        case 'A':
2✔
757
            opt_addr_on = 1;
3✔
758
            break;
3✔
759

760
        case 'B':
18✔
761
            opt_backoff = strtod_strict(optparse_state.optarg);
26✔
762
            break;
14✔
763

764
        case 's':
8✔
765
            opt_stats_on = 1;
12✔
766
            break;
12✔
767

768
        case 'D':
22✔
769
            opt_timestamp_on = 1;
33✔
770
            break;
33✔
771

772
        case 'R':
4✔
773
            opt_random_data_on = 1;
5✔
774
            break;
5✔
775

776
        case 'l':
12✔
777
            opt_loop_on = 1;
17✔
778
            opt_backoff_on = 0;
17✔
779
            break;
17✔
780

781
        case 'u':
8✔
782
            opt_unreachable_on = 1;
12✔
783
            break;
12✔
784

785
        case 'a':
18✔
786
            opt_alive_on = 1;
26✔
787
            break;
26✔
788

789
        case 'H':
16✔
790
            opt_ttl = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
791
            if (!opt_ttl)
14✔
792
                usage(1);
3✔
793
            break;
12✔
794

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

801
        case 'v':
4✔
802
            printf("%s: Version %s\n", prog, VERSION);
6✔
803
            exit(0);
6✔
804

805
        case 'x':
16✔
806
            opt_min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
807
            if (!opt_min_reachable)
14✔
808
                usage(1);
3✔
809
            break;
12✔
810

811
        case 'X':
14✔
812
            opt_min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
21✔
813
            if (!opt_min_reachable)
11✔
814
                usage(1);
3✔
815
            opt_fast_reachable_on = 1;
9✔
816
            break;
9✔
817

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

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

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

837
            break;
6✔
838
#endif
839

840
        case 'g':
106✔
841
            /* use IP list generation */
842
            /* mutually exclusive with using file input or command line targets */
843
            opt_generate_on = 1;
129✔
844
            break;
129✔
845

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

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

883
        case 'J':
44✔
884
            opt_print_json_on = 1;
65✔
885
            break;
65✔
886

887
        case 'T':
2✔
888
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
889
            break;
3✔
890

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

912
        case 'o':
6✔
913
            opt_outage_on = 1;
7✔
914
            break;
7✔
915

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

924
    /* permanently drop privileges */
925
    if (suid != getuid() && setuid(getuid())) {
574✔
926
        perror("fatal: failed to permanently drop privileges");
×
927
        /* continuing would be a security hole */
928
        exit(4);
×
929
    }
930

931
    /* validate various option settings */
932

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

943
    if (opt_ttl > 255) {
572✔
944
        fprintf(stderr, "%s: ttl %u out of range\n", prog, opt_ttl);
3✔
945
        exit(1);
3✔
946
    }
947

948
    if (opt_unreachable_on && opt_alive_on) {
569✔
949
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
950
        exit(1);
3✔
951
    }
952

953
    if (opt_count_on && opt_loop_on) {
566✔
954
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
955
        exit(1);
3✔
956
    }
957

958
    if (opt_print_json_on && !opt_count_on && !opt_loop_on) {
563✔
959
        fprintf(stderr, "%s: option -J, --json requires -c, -C, or -l\n", prog);
3✔
960
        exit(1);
3✔
961
    }
962

963
    if (opt_interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
560✔
964
        fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
3✔
965
        exit(1);
3✔
966
    }
967

968
    if (opt_perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
557✔
969
        fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
3✔
970
        exit(1);
3✔
971
    }
972

973
    if (opt_ping_data_size > MAX_PING_DATA) {
554✔
974
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
975
            prog, opt_ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
976
        exit(1);
3✔
977
    }
978

979
    if ((opt_backoff > MAX_BACKOFF_FACTOR) || (opt_backoff < MIN_BACKOFF_FACTOR)) {
551✔
980
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
981
            prog, opt_backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
982
        exit(1);
6✔
983
    }
984

985
    if (opt_icmp_request_typ == 13 && opt_size_on != 0) {
545✔
986
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
987
        exit(1);
9✔
988
    }
989

990
    if (opt_count_on) {
536✔
991
        if (opt_verbose_on)
160✔
992
            opt_per_recv_on = 1;
102✔
993

994
        opt_alive_on = opt_unreachable_on = opt_verbose_on = 0;
160✔
995
    }
42✔
996

997
    if (opt_loop_on) {
536✔
998
        if (!report_interval)
14✔
999
            opt_per_recv_on = 1;
12✔
1000

1001
        opt_alive_on = opt_unreachable_on = opt_verbose_on = 0;
14✔
1002
    }
4✔
1003

1004
    if (opt_alive_on || opt_unreachable_on || opt_min_reachable)
536✔
1005
        opt_verbose_on = 0;
37✔
1006

1007
    trials = (opt_count > opt_retry + 1) ? opt_count : opt_retry + 1;
552✔
1008

1009
    /* auto-tune default timeout for count/loop modes
1010
     * see also github #32 */
1011
    if (opt_loop_on || opt_count_on) {
552✔
1012
        if (!opt_timeout_on) {
174✔
1013
            opt_timeout = opt_perhost_interval;
162✔
1014
            if (opt_timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
162✔
1015
                opt_timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1016
            }
1017
        }
44✔
1018
    }
46✔
1019

1020
#if defined(DEBUG) || defined(_DEBUG)
1021
    if (debugging & DBG_TRACE)
1022
        opt_debug_trace_on = 1;
1023

1024
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1025
        opt_debug_randomly_lose_on = 1;
1026
        lose_factor = 1; /* ie, 1/4 */
1027
    }
1028

1029
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1030
        opt_debug_randomly_lose_on = 1;
1031
        lose_factor = 5; /* ie, 3/4 */
1032
    }
1033

1034
    if (debugging & DBG_PRINT_PER_SYSTEM)
1035
        opt_debug_print_per_system_on = 1;
1036

1037
    if ((debugging & DBG_REPORT_ALL_RTTS) && !opt_loop_on)
1038
        opt_report_all_rtts_on = 1;
1039

1040
    if (opt_debug_trace_on) {
1041
        fprintf(stderr, "%s:\n  opt_count: %u, opt_retry: %u, opt_interval: %.0f ms\n",
1042
            prog, opt_count, opt_retry, opt_interval / 1e6);
1043
        fprintf(stderr, "  opt_perhost_interval: %.0f ms, opt_timeout: %.0f\n",
1044
            opt_perhost_interval / 1e6, opt_timeout / 1e6);
1045
        fprintf(stderr, "  opt_seqmap_timeout: %.0f\n", opt_seqmap_timeout / 1e6);
1046
        fprintf(stderr, "  opt_ping_data_size = %u, trials = %u\n",
1047
            opt_ping_data_size, trials);
1048

1049
        if (opt_verbose_on)
1050
            fprintf(stderr, "  opt_verbose_on set\n");
1051
        if (opt_multif_on)
1052
            fprintf(stderr, "  opt_multif_on set\n");
1053
        if (opt_name_on)
1054
            fprintf(stderr, "  opt_name_on set\n");
1055
        if (opt_addr_on)
1056
            fprintf(stderr, "  opt_addr_on set\n");
1057
        if (opt_stats_on)
1058
            fprintf(stderr, "  opt_stats_on set\n");
1059
        if (opt_unreachable_on)
1060
            fprintf(stderr, "  opt_unreachable_on set\n");
1061
        if (opt_alive_on)
1062
            fprintf(stderr, "  opt_alive_on set\n");
1063
        if (opt_elapsed_on)
1064
            fprintf(stderr, "  opt_elapsed_on set\n");
1065
        if (opt_version_on)
1066
            fprintf(stderr, "  opt_version_on set\n");
1067
        if (opt_count_on)
1068
            fprintf(stderr, "  opt_count_on set\n");
1069
        if (opt_loop_on)
1070
            fprintf(stderr, "  opt_loop_on set\n");
1071
        if (opt_backoff_on)
1072
            fprintf(stderr, "  opt_backoff_on set\n");
1073
        if (opt_per_recv_on)
1074
            fprintf(stderr, "  opt_per_recv_on set\n");
1075
        if (opt_report_all_rtts_on)
1076
            fprintf(stderr, "  opt_report_all_rtts_on set\n");
1077
        if (opt_debug_randomly_lose_on)
1078
            fprintf(stderr, "  opt_debug_randomly_lose_on set\n");
1079
        if (opt_debug_print_per_system_on)
1080
            fprintf(stderr, "  opt_debug_print_per_system_on set\n");
1081
        if (opt_outage_on)
1082
            fprintf(stderr, "  opt_outage_on set\n");
1083
        if (opt_print_netdata_on)
1084
            fprintf(stderr, "  opt_print_netdata_on set\n");
1085
        if (opt_print_json_on)
1086
            fprintf(stderr, "  opt_print_json_on set\n");
1087
    }
1088
#endif /* DEBUG || _DEBUG */
1089

1090
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1091
    if (opt_ttl > 0) {
552✔
1092
        if (socket4 >= 0) {
3✔
1093
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &opt_ttl, sizeof(opt_ttl))) {
3✔
1094
                perror("setting time to live");
×
1095
            }
1096
        }
1✔
1097
#ifdef IPV6
1098
        if (socket6 >= 0) {
3✔
1099
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &opt_ttl, sizeof(opt_ttl))) {
3✔
1100
                perror("setting time to live");
×
1101
            }
1102
        }
1✔
1103
#endif
1104
    }
1✔
1105

1106
#if HAVE_SO_TIMESTAMPNS
1107
    {
1108
        int opt = 1;
406✔
1109
        if (socket4 >= 0) {
406✔
1110
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
406✔
1111
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1112
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1113
                }
1114
            }
1115
        }
1116
#ifdef IPV6
1117
        if (socket6 >= 0) {
406✔
1118
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
406✔
1119
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1120
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1121
                }
1122
            }
1123
        }
1124
#endif
1125
    }
1126
#endif
1127

1128
    update_current_time();
552✔
1129
    start_time = current_time_ns;
552✔
1130

1131
    /* handle host names supplied on command line or in a file */
1132
    /* if the opt_generate_on is on, then generate the IP list */
1133

1134
    argv = &argv[optparse_state.optind];
552✔
1135
    argc -= optparse_state.optind;
552✔
1136

1137
    /* calculate how many ping can be in-flight per host */
1138
    if (opt_count_on) {
552✔
1139
        event_storage_count = opt_count;
175✔
1140
    }
57✔
1141
    else if (opt_loop_on) {
377✔
1142
        if (opt_perhost_interval > opt_timeout) {
14✔
1143
            event_storage_count = 1;
×
1144
        }
1145
        else {
1146
            event_storage_count = 1 + opt_timeout / opt_perhost_interval;
14✔
1147
        }
1148
    }
4✔
1149
    else {
1150
        event_storage_count = 1;
363✔
1151
    }
1152

1153
    /* file and generate are mutually exclusive */
1154
    /* file and command line are mutually exclusive */
1155
    /* generate requires command line parameters beyond the switches */
1156
    if ((*argv && filename) || (filename && opt_generate_on) || (opt_generate_on && !*argv))
552✔
1157
        usage(1);
19✔
1158

1159
    /* if no conditions are specified, then assume input from stdin */
1160
    if (!*argv && !filename && !opt_generate_on)
534✔
1161
        filename = "-";
69✔
1162

1163
    if (*argv && !opt_generate_on) {
534✔
1164
        while (*argv) {
691✔
1165
            add_name(*argv);
376✔
1166
            ++argv;
376✔
1167
        }
1168
    }
77✔
1169
    else if (filename) {
219✔
1170
        FILE *ping_file;
1171
        char line[MAX_TARGET_NAME_LEN + 1];
1172
        char host[MAX_TARGET_NAME_LEN + 1];
1173
        char scratch[MAX_TARGET_NAME_LEN + 1];
1174
        int skip, non_empty;
1175

1176
        if (strcmp(filename, "-") == 0)
96✔
1177
            ping_file = fdopen(0, "r");
72✔
1178
        else
1179
            ping_file = fopen(filename, "r");
24✔
1180

1181
        if (!ping_file)
96✔
1182
            errno_crash_and_burn("fopen");
3✔
1183

1184
        /*
1185
         * Read the first word of every non-comment line, skip everything else.
1186
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1187
         * character is a '#' are interpreted as comments and ignored.)
1188
        */
1189
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1190
            skip = non_empty = 0;
309✔
1191

1192
            /* skip over a prefix of the line where sscanf finds nothing */
1193
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1194
                continue;
99✔
1195
            }
1196

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

1248
            if (non_empty)
186✔
1249
                add_name(host);
144✔
1250
        }
1251

1252
        fclose(ping_file);
70✔
1253
    }
24✔
1254
    else if (*argv && opt_generate_on) {
123✔
1255
        if (argc == 1) {
123✔
1256
            /* one target: we expect a cidr range (n.n.n.n/m) */
1257
            add_cidr(argv[0]);
68✔
1258
        }
14✔
1259
        else if (argc == 2) {
55✔
1260
            add_range(argv[0], argv[1]);
52✔
1261
        }
6✔
1262
        else {
1263
            usage(1);
3✔
1264
        }
1265
    }
21✔
1266
    else {
1267
        usage(1);
×
1268
    }
1269

1270
    if (!num_hosts) {
448✔
1271
        exit(num_noaddress ? 2 : 1);
36✔
1272
    }
1273

1274
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
412✔
1275
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
20✔
1276
    }
2✔
1277
#ifdef IPV6
1278
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
410✔
1279
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
18✔
1280
    }
1281
#endif
1282

1283
    /* allocate and initialize array to map host nr to host_entry */
1284
    {
1285
        struct event *cursor = event_queue_ping.first;
408✔
1286
        int i = 0;
408✔
1287
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
408✔
1288
        if (!table)
408✔
1289
            crash_and_burn("Can't malloc array of hosts");
×
1290
        /* initialize table of hosts. we know that we have ping events scheduled
1291
         * for each of them */
1292
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
1,003✔
1293
            table[i] = cursor->host;
595✔
1294
            cursor->host->i = i;
595✔
1295
            i++;
595✔
1296
        }
157✔
1297
    }
1298

1299
    init_ping_buffer_ipv4(opt_ping_data_size);
408✔
1300
#ifdef IPV6
1301
    init_ping_buffer_ipv6(opt_ping_data_size);
408✔
1302
#endif
1303

1304
#ifdef USE_SIGACTION
1305
    memset(&act, 0, sizeof(act));
408✔
1306
    act.sa_handler = signal_handler;
408✔
1307
    sigemptyset(&act.sa_mask);
408✔
1308
    sigaddset(&act.sa_mask, SIGINT);
408✔
1309
    sigaddset(&act.sa_mask, SIGQUIT);
408✔
1310
    act.sa_flags = SA_RESTART;
408✔
1311
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
408✔
1312
        crash_and_burn("failure to set signal handler");
30✔
1313
    }
30✔
1314
#else
1315
    signal(SIGINT, signal_handler);
1316
    signal(SIGQUIT, signal_handler);
1317
#endif
1318
    setlinebuf(stdout);
378✔
1319

1320
    // Last time we updated current_time_ns was before adding the hosts and
1321
    // possibly doing DNS resolution, which means that it isn't accurate
1322
    // anymore.
1323
    update_current_time();
378✔
1324
    if (report_interval) {
378✔
1325
        next_report_time = current_time_ns + report_interval;
22✔
1326
    }
2✔
1327

1328
    last_send_time = 0;
378✔
1329

1330
    seqmap_init(opt_seqmap_timeout);
378✔
1331

1332
    /* main loop */
1333
    main_loop();
378✔
1334

1335
/* Debug: CPU Performance */
1336
#if defined(DEBUG) || defined(_DEBUG)
1337
    perf_cpu_end = clock();
1338
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1339
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1340
#endif /* DEBUG || _DEBUG */
1341

1342
    finish();
378✔
1343

1344
    return 0;
82✔
1345
}
1346

1347
static inline int64_t timespec_ns(struct timespec *a)
6,774✔
1348
{
1349
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
6,774✔
1350
}
1351

1352
#if HAVE_SO_TIMESTAMPNS
1353
/* convert a struct timeval to nanoseconds */
1354
static inline int64_t timeval_ns(struct timeval *a)
1355
{
1356
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1357
}
1358
#endif /* HAVE_SO_TIMESTAMPNS */
1359

1360
void add_cidr(char *addr)
63✔
1361
{
1362
    char *addr_end;
1363
    char *mask_str;
1364
    unsigned long mask;
1365
    int ret;
1366
    struct addrinfo addr_hints;
1367
    struct addrinfo *addr_res;
1368
    unsigned long net_addr;
1369
#ifdef IPV6
1370
    uint64_t net_upper, net_lower;
1371
    char *scope_str;
1372
#endif /* IPV6 */
1373

1374
    /* Split address from mask */
1375
    addr_end = strrchr(addr, '/');
63✔
1376
    if (addr_end == NULL) {
63✔
1377
        usage(1);
3✔
1378
    }
1✔
1379
    mask_str = addr_end + 1;
61✔
1380

1381
#ifdef IPV6
1382
    /* IPv6 addresses can have a scope */
1383
    scope_str = strchr(addr, '%');
61✔
1384
    if (scope_str && mask_str < scope_str) {
61✔
1385
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1386
        exit(1);
2✔
1387
    }
1388
#endif /*IPV6 */
1389

1390
    *addr_end = '\0';
59✔
1391
    mask = strtoul_strict(mask_str, 10);
59✔
1392

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

1421
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1422
{
1423
    unsigned long bitmask;
1424
    unsigned long net_last;
1425

1426
    /* check mask */
1427
    if (mask < 1 || mask > 32) {
24✔
1428
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1429
        exit(1);
6✔
1430
    }
1431

1432
    /* convert mask integer from 1 to 32 to a bitmask */
1433
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1434

1435
    /* calculate network range */
1436
    net_addr &= bitmask;
18✔
1437
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1438

1439
    /* exclude network and broadcast address for regular prefixes */
1440
    if (mask < 31) {
18✔
1441
        net_last--;
12✔
1442
        net_addr++;
12✔
1443
    }
4✔
1444

1445
    /* add all hosts in that network (net_addr and net_last inclusive) */
1446
    add_addr_range_ipv4(net_addr, net_last);
18✔
1447
}
16✔
1448

1449
#ifdef IPV6
1450
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1451
{
1452
    uint64_t bitmask_lower;
1453
    uint64_t last_lower;
1454

1455
    /* check mask -- 2^63 addresses should suffice for now */
1456
    if (mask < 65 || mask > 128) {
23✔
1457
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1458
        exit(1);
7✔
1459
    }
1460

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

1464
    /* calculate network range */
1465
    net_lower &= bitmask_lower;
16✔
1466
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1467

1468
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1469
}
12✔
1470
#endif /* IPV6 */
1471

1472
void add_range(char *start, char *end)
52✔
1473
{
1474
    struct addrinfo addr_hints;
1475
    struct addrinfo *addr_res;
1476
    unsigned long start_long;
1477
    unsigned long end_long;
1478
    int ret;
1479
#ifdef IPV6
1480
    uint64_t start_upper, start_lower;
1481
    uint64_t end_upper, end_lower;
1482
    char *start_scope_str, *end_scope_str;
1483

1484
    /*
1485
     * The compiler does not know that setting the address family hint to
1486
     * ensure that start and end are from the same address family also
1487
     * ensures that either start_long and end_long are initialized and used,
1488
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1489
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1490
     * supported to suppress compiler warnings.
1491
     */
1492
    start_long = -1;
52✔
1493
    end_long = 0;
52✔
1494
    start_upper = start_lower = -1;
52✔
1495
    end_upper = end_lower = 0;
52✔
1496
    start_scope_str = end_scope_str = NULL;
52✔
1497
#endif /* IPV6 */
1498

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

1526
#ifdef IPV6
1527
    /* IPv6 addresses can have a scope */
1528
    if (hints_ai_family == AF_INET6) {
43✔
1529
        start_scope_str = strchr(start, '%');
26✔
1530
        end_scope_str = strchr(end, '%');
26✔
1531
        if ((!start_scope_str && end_scope_str) ||
26✔
1532
            (start_scope_str && !end_scope_str) ||
24✔
1533
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1534
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1535
                exit(1);
6✔
1536
        }
1537
    }
1538
#endif
1539

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

1568
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1569
{
1570
    /* check if generator limit is exceeded */
1571
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1572
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1573
        exit(1);
6✔
1574
    }
1575

1576
    /* generate */
1577
    for (; start_long <= end_long; start_long++) {
93✔
1578
        struct in_addr in_addr_tmp;
1579
        char buffer[20];
1580
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1581
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1582
        add_name(buffer);
69✔
1583
    }
23✔
1584
}
24✔
1585

1586
#ifdef IPV6
1587
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1588
{
1589
    int i;
1590
    uint64_t ret = 0;
134✔
1591
    for (i = 0; i < 8; i++) {
1,206✔
1592
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1593
    }
16✔
1594
    return ret;
134✔
1595
}
1596

1597
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1598
{
1599
    int i;
1600
    for (i = 0; i < 8; i++) {
720✔
1601
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1602
    }
1603
}
80✔
1604

1605
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1606
                         uint64_t end_upper, uint64_t end_lower,
1607
                         const char *scope_str)
1608
{
1609
    struct in6_addr in6_addr_tmp;
1610
    char buffer[100];
1611

1612
    /* prevent generating too many addresses */
1613
    if ((start_upper + 1 < end_upper) ||
34✔
1614
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1615
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1616
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1617
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1618
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1619
        exit(1);
8✔
1620
    }
1621

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

1643
void main_loop()
393✔
1644
{
1645
    int64_t lt;
1646
    int64_t wait_time_ns;
1647
    struct event *event;
1648
    struct host_entry *h;
1649

1650
    while (event_queue_ping.first || event_queue_timeout.first) {
2,132✔
1651
        dbg_printf("%s", "# main_loop\n");
1652

1653
        /* timeout event ? */
1654
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,770✔
1655
            event = ev_dequeue(&event_queue_timeout);
101✔
1656
            h = event->host;
101✔
1657

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

1660
            stats_add(h, event->ping_index, 0, -1);
101✔
1661

1662
            if (opt_per_recv_on) {
101✔
1663
                print_timeout(h, event->ping_index);
25✔
1664
            }
7✔
1665

1666
            /* do we need to send a retry? */
1667
            if (!opt_loop_on && !opt_count_on) {
101✔
1668
                if (h->num_sent < opt_retry + 1) {
60✔
1669
                    if (opt_backoff_on) {
35✔
1670
                        h->timeout *= opt_backoff;
35✔
1671
                    }
9✔
1672
                    send_ping(h, event->ping_index);
35✔
1673
                }
9✔
1674
            }
14✔
1675

1676
            /* note: we process first timeout events, because we might need to
1677
             * wait to process ping events, while we for sure never need to
1678
             * wait for timeout events.
1679
             */
1680
            continue;
101✔
1681
        }
1682

1683
        /* ping event ? */
1684
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,933✔
1685
            /* Make sure that we don't ping more than once every "interval" */
1686
            lt = current_time_ns - last_send_time;
1,275✔
1687
            if (lt < opt_interval)
1,275✔
1688
                goto wait_for_reply;
269✔
1689

1690
            /* Dequeue the event */
1691
            event = ev_dequeue(&event_queue_ping);
1,006✔
1692
            h = event->host;
1,006✔
1693

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

1696
            /* Send the ping */
1697
            send_ping(h, event->ping_index);
1,006✔
1698

1699
            /* Loop and count mode: schedule next ping */
1700
            if (opt_loop_on || (opt_count_on && event->ping_index + 1 < opt_count)) {
1,006✔
1701
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + opt_perhost_interval);
440✔
1702
            }
116✔
1703
        }
264✔
1704

1705
    wait_for_reply:
738✔
1706

1707
        /* When is the next ping next event? */
1708
        wait_time_ns = -1;
1,669✔
1709
        if (event_queue_ping.first) {
1,669✔
1710
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,222✔
1711
            if (wait_time_ns < 0)
1,222✔
1712
                wait_time_ns = 0;
569✔
1713
            /* make sure that we wait enough, so that the inter-ping delay is
1714
             * bigger than 'interval' */
1715
            if (wait_time_ns < opt_interval) {
1,222✔
1716
                lt = current_time_ns - last_send_time;
581✔
1717
                if (lt < opt_interval) {
581✔
1718
                    wait_time_ns = opt_interval - lt;
580✔
1719
                }
224✔
1720
            }
224✔
1721

1722
            dbg_printf("next ping event in %.0f ms (%s)\n", wait_time_ns / 1e6, event_queue_ping.first->host->host);
1723
        }
333✔
1724

1725
        /* When is the next timeout event? */
1726
        if (event_queue_timeout.first) {
1,669✔
1727
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
1,054✔
1728
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
1,054✔
1729
                wait_time_ns = wait_time_timeout;
441✔
1730
                if (wait_time_ns < 0) {
441✔
1731
                    wait_time_ns = 0;
1✔
1732
                }
1733
            }
109✔
1734

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

1738
        /* When is the next report due? */
1739
        if (report_interval && (opt_loop_on || opt_count_on)) {
1,669✔
1740
            int64_t wait_time_next_report = next_report_time - current_time_ns;
213✔
1741
            if (wait_time_next_report < wait_time_ns) {
213✔
1742
                wait_time_ns = wait_time_next_report;
50✔
1743
                if (wait_time_ns < 0) {
50✔
1744
                    wait_time_ns = 0;
×
1745
                }
1746
            }
2✔
1747

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

1751
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1752
        if (wait_time_ns == -1) {
1,669✔
1753
            break;
14✔
1754
        }
1755

1756
        /* end of loop was requested by interrupt signal handler */
1757
        if (finish_requested) {
1,655✔
1758
            break;
17✔
1759
        }
1760

1761
        /* Receive replies */
1762
        /* (this is what sleeps during each loop iteration) */
1763
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1764
        if (wait_for_reply(wait_time_ns)) {
1,638✔
1765
            while (wait_for_reply(0))
1,494✔
1766
                ; /* process other replies in the queue */
1767
        }
252✔
1768

1769
        update_current_time();
1,638✔
1770

1771
        if (status_snapshot) {
1,638✔
1772
            status_snapshot = 0;
3✔
1773
            if (opt_print_json_on)
3✔
1774
                print_per_system_splits_json();
×
1775
            else
1776
                print_per_system_splits();
3✔
1777
        }
1✔
1778

1779
        /* Print report */
1780
        if (report_interval && (opt_loop_on || opt_count_on) && (current_time_ns >= next_report_time)) {
1,638✔
1781
            if (opt_print_netdata_on) {
36✔
1782
                print_netdata();
3✔
1783
            }
1✔
1784
            else if (opt_print_json_on) {
33✔
1785
                print_per_system_splits_json();
3✔
1786
            }
1✔
1787
            else {
1788
                print_per_system_splits();
30✔
1789
            }
1790

1791
            while (current_time_ns >= next_report_time) {
72✔
1792
                next_report_time += report_interval;
36✔
1793
            }
1794
        }
2✔
1795
    }
1796
}
393✔
1797

1798
/************************************************************
1799

1800
  Function: signal_handler
1801

1802
*************************************************************
1803

1804
  Inputs:  int signum
1805

1806
  Description:
1807

1808
  SIGQUIT signal handler - set flag and return
1809
  SIGINT signal handler - set flag and return
1810

1811
************************************************************/
1812

1813
void signal_handler(int signum)
17✔
1814
{
1815
    switch (signum) {
17✔
1816
    case SIGINT:
10✔
1817
        finish_requested = 1;
14✔
1818
        break;
14✔
1819

1820
    case SIGQUIT:
2✔
1821
        status_snapshot = 1;
3✔
1822
        break;
3✔
1823
    }
1824
}
17✔
1825

1826
/************************************************************
1827

1828
  Function: update_current_time
1829

1830
*************************************************************/
1831

1832
void update_current_time()
5,532✔
1833
{
1834
    clock_gettime(CLOCKID, &current_time);
5,532✔
1835
    current_time_ns = timespec_ns(&current_time);
5,532✔
1836
}
5,532✔
1837

1838
/************************************************************
1839

1840
  Function: finish
1841

1842
*************************************************************
1843

1844
  Inputs:  void (none)
1845

1846
  Description:
1847

1848
  Main program clean up and exit point
1849

1850
************************************************************/
1851

1852
void finish()
393✔
1853
{
1854
    int i;
1855
    HOST_ENTRY *h;
1856

1857
    update_current_time();
393✔
1858
    end_time = current_time_ns;
393✔
1859

1860
    /* tot up unreachables */
1861
    for (i = 0; i < num_hosts; i++) {
988✔
1862
        h = table[i];
595✔
1863

1864
        if (!h->num_recv) {
595✔
1865
            num_unreachable++;
87✔
1866

1867
            if (opt_verbose_on || opt_unreachable_on) {
87✔
1868
                printf("%s", h->host);
47✔
1869

1870
                if (opt_verbose_on)
47✔
1871
                    printf(" is unreachable");
44✔
1872

1873
                printf("\n");
47✔
1874
            }
3✔
1875
        }
13✔
1876
    }
157✔
1877

1878
    if (opt_count_on || opt_loop_on) {
393✔
1879
        if (opt_print_json_on)
171✔
1880
            print_per_system_stats_json();
59✔
1881
        else
1882
            print_per_system_stats();
112✔
1883
    }
45✔
1884
#if defined(DEBUG) || defined(_DEBUG)
1885
    else if (opt_debug_print_per_system_on) {
1886
        if (opt_print_json_on)
1887
            print_per_system_stats_json();
1888
        else
1889
            print_per_system_stats();
1890
    }
1891
#endif /* DEBUG || _DEBUG */
1892

1893
    if (opt_stats_on) {
393✔
1894
        if (opt_print_json_on)
12✔
1895
            print_global_stats_json();
3✔
1896
        else
1897
            print_global_stats();
9✔
1898
    }
4✔
1899

1900
    if (opt_min_reachable) {
393✔
1901
        if ((num_hosts - num_unreachable) >= opt_min_reachable) {
9✔
1902
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", opt_min_reachable, num_hosts - num_unreachable);
6✔
1903
            exit(0);
6✔
1904
        }
1905
        else {
1906
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", opt_min_reachable, num_hosts - num_unreachable);
3✔
1907
            exit(1);
3✔
1908
        }
1909
    }
1910

1911
    if (num_noaddress)
384✔
1912
        exit(2);
3✔
1913
    else if (num_alive != num_hosts)
381✔
1914
        exit(1);
48✔
1915

1916
    exit(0);
333✔
1917
}
1918

1919
/************************************************************
1920

1921
  Function: send_ping
1922

1923
*************************************************************
1924

1925
  Inputs:  int s, HOST_ENTRY *h
1926

1927
  Description:
1928

1929
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
1930
  will be added on by the kernel.  The ID field is our UNIX process ID,
1931
  and the sequence number is an index into an array of outstanding
1932
  ping requests. The sequence number will later be used to quickly
1933
  figure out who the ping reply came from.
1934

1935
************************************************************/
1936

1937
int send_ping(HOST_ENTRY *h, int index)
1,041✔
1938
{
1939
    int n;
1940
    int myseq;
1941
    int ret = 1;
1,041✔
1942
    uint8_t proto = ICMP_ECHO;
1,041✔
1943

1944
    update_current_time();
1,041✔
1945
    h->last_send_time = current_time_ns;
1,041✔
1946
    myseq = seqmap_add(h->i, index, current_time_ns);
1,041✔
1947

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

1950
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
1,041✔
1951
        if(opt_icmp_request_typ == 13)
920✔
1952
            proto = ICMP_TSTAMP;
18✔
1953
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
920✔
1954
    }
258✔
1955
#ifdef IPV6
1956
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
121✔
1957
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
121✔
1958
    }
15✔
1959
#endif
1960
    else {
1961
        return 0;
×
1962
    }
1963

1964
    /* error sending? */
1965
    if (
768✔
1966
        (n < 0)
273✔
1967
#if defined(EHOSTDOWN)
1968
        && errno != EHOSTDOWN
307✔
1969
#endif
1970
    ) {
1971
        if (opt_verbose_on) {
34✔
1972
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
1973
        }
1974
        else {
1975
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
1976
        }
1977

1978
        h->num_sent++;
34✔
1979
        h->num_sent_i++;
34✔
1980
        if (!opt_loop_on)
34✔
1981
            h->resp_times[index] = RESP_ERROR;
34✔
1982

1983
        ret = 0;
34✔
1984
    }
1985
    else {
1986
        /* schedule timeout */
1987
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
1,007✔
1988

1989
        /* mark this trial as outstanding */
1990
        if (!opt_loop_on) {
1,007✔
1991
            h->resp_times[index] = RESP_WAITING;
873✔
1992
        }
239✔
1993
    }
1994

1995
    num_pingsent++;
1,041✔
1996
    last_send_time = h->last_send_time;
1,041✔
1997

1998
    return (ret);
1,041✔
1999
}
273✔
2000

2001
int socket_can_read(struct timeval *timeout)
1,637✔
2002
{
2003
    int nfound;
2004
    fd_set readset;
2005
    int socketmax;
2006

2007
#ifndef IPV6
2008
    socketmax = socket4;
2009
#else
2010
    socketmax = socket4 > socket6 ? socket4 : socket6;
1,637✔
2011
#endif
2012

2013
select_again:
1,213✔
2014
    FD_ZERO(&readset);
21,062✔
2015
    if (socket4 >= 0)
1,654✔
2016
        FD_SET(socket4, &readset);
1,654✔
2017
#ifdef IPV6
2018
    if (socket6 >= 0)
1,654✔
2019
        FD_SET(socket6, &readset);
1,654✔
2020
#endif
2021

2022
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
1,654✔
2023
    if (nfound < 0) {
1,654✔
2024
        if (errno == EINTR) {
17✔
2025
            /* interrupted system call: redo the select */
2026
            goto select_again;
17✔
2027
        }
2028
        else {
2029
            perror("select");
×
2030
        }
2031
    }
2032

2033
    if (nfound > 0) {
1,637✔
2034
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
916✔
2035
            return socket4;
823✔
2036
        }
2037
#ifdef IPV6
2038
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
93✔
2039
            return socket6;
93✔
2040
        }
2041
#endif
2042
    }
2043

2044
    return -1;
721✔
2045
}
436✔
2046

2047
int receive_packet(int64_t wait_time,
3,132✔
2048
#if HAVE_SO_TIMESTAMPNS
2049
    int64_t *reply_timestamp,
2050
#else
2051
    int64_t *reply_timestamp __attribute__((unused)),
2052
#endif
2053
    struct sockaddr *reply_src_addr,
2054
    size_t reply_src_addr_len,
2055
    char *reply_buf,
2056
    size_t reply_buf_len,
2057
    int *ip_header_tos,
2058
    int *ip_header_ttl)
2059
{
2060
    struct timeval to;
2061
    int s = 0;
3,132✔
2062
    int recv_len;
2063
    static unsigned char msg_control[128];
2064
    struct iovec msg_iov = {
4,508✔
2065
        reply_buf,
688✔
2066
        reply_buf_len
688✔
2067
    };
2068
    struct msghdr recv_msghdr = {0};
3,132✔
2069
    recv_msghdr.msg_name = reply_src_addr;
3,132✔
2070
    recv_msghdr.msg_namelen = reply_src_addr_len;
3,132✔
2071
    recv_msghdr.msg_iov = &msg_iov;
3,132✔
2072
    recv_msghdr.msg_iovlen = 1;
3,132✔
2073
    recv_msghdr.msg_control = &msg_control;
3,132✔
2074
    recv_msghdr.msg_controllen = sizeof(msg_control);
3,132✔
2075
    struct cmsghdr *cmsg;
2076

2077
    /* Wait for a socket to become ready */
2078
#if HAVE_MSG_DONTWAIT
2079
    if (wait_time == 0) {
3,132✔
2080
        /* Optimization: if wait_time is 0, we can skip select() and just try to
2081
         * read from the sockets with MSG_DONTWAIT */
2082
        recv_len = (socket4 >= 0) ? recvmsg(socket4, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT) : -1;
1,495✔
2083
#ifdef IPV6
2084
        if (recv_len <= 0 && socket6 >= 0) {
1,495✔
2085
            /* Reset fields potentially modified by failed recvmsg */
2086
            recv_msghdr.msg_namelen = reply_src_addr_len;
919✔
2087
            recv_msghdr.msg_controllen = sizeof(msg_control);
919✔
2088
            recv_len = recvmsg(socket6, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT);
919✔
2089
        }
252✔
2090
#endif
2091
        if (recv_len > 0) {
1,495✔
2092
            goto packet_received;
578✔
2093
        }
2094
        return 0;
917✔
2095
    }
2096
#endif
2097

2098
    int64_t wait_us = (wait_time + 999) / 1000; // round up (1 ns -> 1 us)
1,637✔
2099
    to.tv_sec = wait_us / 1000000;
1,637✔
2100
    to.tv_usec = wait_us % 1000000;
1,637✔
2101

2102
    s = socket_can_read(&to);
1,637✔
2103
    if (s == -1 || (recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC)) <= 0) {
1,637✔
2104
        return 0;
721✔
2105
    }
2106

2107
packet_received:
664✔
2108
    /* ancilliary data */
2109
    {
2110
#if HAVE_SO_TIMESTAMPNS
2111
        struct timespec reply_timestamp_ts;
2112
        struct timeval reply_timestamp_tv;
2113
#endif
2114
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,494✔
2115
             cmsg != NULL;
2,744✔
2116
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,250✔
2117
#if HAVE_SO_TIMESTAMPNS
2118
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,250✔
2119
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,242✔
2120
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,242✔
2121
            }
2122
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,250✔
2123
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2124
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2125
            }
2126
#endif
2127
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,250✔
2128
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2129
            }
2130
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,250✔
2131
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2132
            }
2133
#ifdef IPV6
2134
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,250✔
2135
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2136
            }
2137
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,250✔
2138
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2139
            }
2140
#endif
2141
        }
2142
    }
2143

2144
#if defined(DEBUG) || defined(_DEBUG)
2145
    if (opt_debug_randomly_lose_on) {
2146
        if ((random() & 0x07) <= lose_factor)
2147
            return 0;
2148
    }
2149
#endif
2150

2151
    return recv_len;
1,494✔
2152
}
688✔
2153

2154
/* stats_add: update host statistics for a single packet that was received (or timed out)
2155
 * h: host entry to update
2156
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2157
 * success: 1 if response received, 0 otherwise
2158
 * latency: response time, in ns
2159
 */
2160
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
993✔
2161
{
2162
    /* sent count - we update only on receive/timeout, so that we don't get
2163
     * weird loss percentage, just because a packet was note recived yet.
2164
     */
2165
    h->num_sent++;
993✔
2166
    h->num_sent_i++;
993✔
2167

2168
    if (!success) {
993✔
2169
        if (!opt_loop_on && index >= 0) {
101✔
2170
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2171
        }
21✔
2172
        num_timeout++;
101✔
2173
        return;
101✔
2174
    }
2175

2176
    /* received count */
2177
    h->num_recv++;
892✔
2178
    h->num_recv_i++;
892✔
2179

2180
    /* maximum */
2181
    if (!h->max_reply || latency > h->max_reply) {
892✔
2182
        h->max_reply = latency;
672✔
2183
    }
185✔
2184
    if (!h->max_reply_i || latency > h->max_reply_i) {
892✔
2185
        h->max_reply_i = latency;
700✔
2186
    }
188✔
2187

2188
    /* minimum */
2189
    if (!h->min_reply || latency < h->min_reply) {
892✔
2190
        h->min_reply = latency;
552✔
2191
    }
162✔
2192
    if (!h->min_reply_i || latency < h->min_reply_i) {
892✔
2193
        h->min_reply_i = latency;
580✔
2194
    }
166✔
2195

2196
    /* total time (for average) */
2197
    h->total_time += latency;
892✔
2198
    h->total_time_i += latency;
892✔
2199

2200
    /* response time per-packet (count mode) */
2201
    if (!opt_loop_on && index >= 0) {
892✔
2202
        h->resp_times[index] = latency;
772✔
2203
    }
218✔
2204
}
269✔
2205

2206
/* stats_reset_interval: reset interval statistics
2207
 * h: host entry to update
2208
 */
2209
void stats_reset_interval(HOST_ENTRY *h)
31✔
2210
{
2211
    h->num_sent_i = 0;
31✔
2212
    h->num_recv_i = 0;
31✔
2213
    h->max_reply_i = 0;
31✔
2214
    h->min_reply_i = 0;
31✔
2215
    h->total_time_i = 0;
31✔
2216
}
31✔
2217

2218
int decode_icmp_ipv4(
1,399✔
2219
    struct sockaddr *response_addr,
2220
    size_t response_addr_len,
2221
    char *reply_buf,
2222
    size_t reply_buf_len,
2223
    unsigned short *id,
2224
    unsigned short *seq,
2225
    IP_HEADER_RESULT *ip_header_res)
2226
{
2227
    struct icmp *icp;
2228
    int hlen = 0;
1,399✔
2229
    struct ip *ip = NULL;
1,399✔
2230

2231
    if (!using_sock_dgram4) {
1,399✔
2232
        ip = (struct ip *)reply_buf;
1,389✔
2233
        ip_header_res->tos = ip->ip_tos;
1,389✔
2234
        ip_header_res->ttl = ip->ip_ttl;
1,389✔
2235

2236
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2237
        /* The alpha headers are decidedly broken.
2238
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2239
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2240
         */
2241
        hlen = (ip->ip_vhl & 0x0F) << 2;
2242
#else
2243
        hlen = ip->ip_hl << 2;
1,389✔
2244
#endif
2245
    }
237✔
2246

2247
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,399✔
2248
        /* too short */
2249
        if (opt_verbose_on) {
×
2250
            char buf[INET6_ADDRSTRLEN];
2251
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2252
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2253
        }
2254
        return -1;
×
2255
    }
2256

2257
    icp = (struct icmp *)(reply_buf + hlen);
1,399✔
2258

2259
    if ((opt_icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,399✔
2260
        (opt_icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
602✔
2261
        /* Handle other ICMP packets */
2262
        struct icmp *sent_icmp;
2263
        SEQMAP_VALUE *seqmap_value;
2264
        char addr_ascii[INET6_ADDRSTRLEN];
2265
        HOST_ENTRY *h;
2266

2267
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2268
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
1,050✔
2269
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2270
            return -1;
×
2271
        }
2272

2273
        sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));
576✔
2274

2275
        if ((opt_icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
576✔
2276
            (opt_icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
2277
            sent_icmp->icmp_id != ident4) {
×
2278
            /* not caused by us */
2279
            return -1;
576✔
2280
        }
2281

2282
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2283
        if (seqmap_value == NULL) {
×
2284
            return -1;
×
2285
        }
2286

2287
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2288

2289
        switch (icp->icmp_type) {
×
2290
        case ICMP_UNREACH:
2291
            h = table[seqmap_value->host_nr];
×
2292
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2293
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2294
                    addr_ascii, h->host);
2295
            }
2296
            else {
2297
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2298
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2299
            }
2300

2301
            print_warning("\n");
×
2302
            num_othericmprcvd++;
×
2303
            break;
×
2304

2305
        case ICMP_SOURCEQUENCH:
2306
        case ICMP_REDIRECT:
2307
        case ICMP_TIMXCEED:
2308
        case ICMP_PARAMPROB:
2309
            h = table[seqmap_value->host_nr];
×
2310
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2311
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2312
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2313
            }
2314
            else {
2315
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2316
                    icp->icmp_type, addr_ascii, h->host);
×
2317
            }
2318
            print_warning("\n");
×
2319
            num_othericmprcvd++;
×
2320
            break;
×
2321
        }
2322

2323
        return -1;
×
2324
    }
2325

2326
    *id = icp->icmp_id;
823✔
2327
    *seq = ntohs(icp->icmp_seq);
823✔
2328
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
823✔
2329

2330
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2331
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
2332
            if (opt_verbose_on) {
×
2333
                char buf[INET6_ADDRSTRLEN];
2334
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2335
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2336
            }
2337
            return -1;
×
2338
        }
2339

2340
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
2341
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
2342
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
2343
    }
2344

2345
    if (opt_print_srcaddr_on) {
823✔
2346
        if (ip == NULL || inet_ntop(AF_INET, &ip->ip_dst, ip_header_res->src_addr, sizeof(ip_header_res->src_addr)) == NULL) {
5✔
2347
            strncpy(ip_header_res->src_addr, "unknown", sizeof(ip_header_res->src_addr) - 1);
2✔
2348
            ip_header_res->src_addr[sizeof(ip_header_res->src_addr) - 1] = '\0';
2✔
2349
        }
2350
    }
1✔
2351

2352
    return hlen;
823✔
2353
}
237✔
2354

2355
#ifdef IPV6
2356
int decode_icmp_ipv6(
95✔
2357
    struct sockaddr *response_addr,
2358
    size_t response_addr_len,
2359
    char *reply_buf,
2360
    size_t reply_buf_len,
2361
    unsigned short *id,
2362
    unsigned short *seq,
2363
    IP_HEADER_RESULT *ip_header_res)
2364
{
2365
    struct icmp6_hdr *icp;
2366

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

2376
    icp = (struct icmp6_hdr *)reply_buf;
95✔
2377

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

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

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

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

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

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

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

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

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

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

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

2466
        return 0;
×
2467
    }
2468

2469
    *id = icp->icmp6_id;
89✔
2470
    *seq = ntohs(icp->icmp6_seq);
89✔
2471

2472
    if (opt_print_srcaddr_on) {
89✔
NEW
UNCOV
2473
        strncpy(ip_header_res->src_addr, "not supported", sizeof(ip_header_res->src_addr) - 1);
×
NEW
2474
        ip_header_res->src_addr[sizeof(ip_header_res->src_addr) - 1] = '\0';
×
2475
    }
2476

2477
    return 1;
89✔
2478
}
15✔
2479
#endif
2480

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

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

2507
    if (result <= 0) {
3,132✔
2508
        return 0;
1,638✔
2509
    }
2510

2511
    update_current_time();
1,494✔
2512
    if (recv_time == 0)
1,494✔
2513
        recv_time = current_time_ns;
252✔
2514

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

2558
    seqmap_value = seqmap_fetch(seq, current_time_ns);
912✔
2559
    if (seqmap_value == NULL) {
912✔
2560
        return 1;
12✔
2561
    }
2562

2563
    /* find corresponding host_entry */
2564
    n = seqmap_value->host_nr;
900✔
2565
    h = table[n];
900✔
2566
    this_count = seqmap_value->ping_count;
900✔
2567
    this_reply = recv_time - seqmap_value->ping_ts;
900✔
2568

2569
    /* update stats that include invalid replies */
2570
    h->num_recv_total++;
900✔
2571
    num_pingreceived++;
900✔
2572

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

2575
    /* optionally require reply source equal to target address */
2576
    if (opt_check_source_on && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
900✔
2577
        dbg_printf("%s\n", "discarding reply from wrong source address");
2578
        return 1;
8✔
2579
    }
2580

2581
    /* discard duplicates */
2582
    if (!opt_loop_on && !(opt_count_on && opt_quiet_on) && h->resp_times[this_count] >= 0) {
892✔
2583
        if (!opt_per_recv_on) {
×
2584
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2585
                h->host, this_count, result, sprint_tm(this_reply));
2586

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

2597
    /* discard reply if delay is larger than timeout
2598
     * (see also: github #32) */
2599
    if (this_reply > h->timeout) {
892✔
2600
        return 1;
×
2601
    }
2602

2603
    /* update stats */
2604
    stats_add(h, this_count, 1, this_reply);
892✔
2605
    // TODO: move to stats_add?
2606
    if (!max_reply || this_reply > max_reply)
892✔
2607
        max_reply = this_reply;
544✔
2608
    if (!min_reply || this_reply < min_reply)
892✔
2609
        min_reply = this_reply;
473✔
2610
    sum_replies += this_reply;
892✔
2611
    total_replies++;
892✔
2612

2613
    /* initialize timeout to initial timeout (without backoff) */
2614
    h->timeout = opt_timeout;
892✔
2615

2616
    /* remove timeout event */
2617
    struct event *timeout_event = host_get_timeout_event(h, this_count);
892✔
2618
    if (timeout_event) {
892✔
2619
        ev_remove(&event_queue_timeout, timeout_event);
892✔
2620
    }
248✔
2621

2622
    /* print "is alive" */
2623
    if (h->num_recv == 1) {
892✔
2624
        num_alive++;
508✔
2625
        if (opt_fast_reachable_on && num_alive >= opt_min_reachable)
508✔
2626
            finish_requested = 1;
3✔
2627

2628
        if (opt_verbose_on || opt_alive_on) {
508✔
2629
            printf("%s", h->host);
304✔
2630

2631
            if (opt_verbose_on)
304✔
2632
                printf(" is alive");
284✔
2633
        }
86✔
2634
    }
144✔
2635

2636
    /* print received ping (unless --quiet) */
2637
    if (opt_per_recv_on) {
892✔
2638
        avg = h->total_time / h->num_recv;
282✔
2639
        print_recv(h,
370✔
2640
            recv_time,
88✔
2641
            result,
88✔
2642
            this_count,
88✔
2643
            this_reply,
88✔
2644
            avg);
88✔
2645
    }
88✔
2646

2647
    if (opt_verbose_on || opt_alive_on || opt_per_recv_on) {
892✔
2648
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
586✔
2649
            char buf[INET6_ADDRSTRLEN];
2650
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2651
            fprintf(stderr, " [<- %s]", buf);
6✔
2652
        }
2653
        if (opt_print_json_on) {
586✔
2654
            print_recv_ext_json(&ip_header_res,
38✔
2655
                recv_time,
12✔
2656
                this_reply);
12✔
2657
        }
12✔
2658
        else {
2659
            print_recv_ext(&ip_header_res,
548✔
2660
                recv_time,
162✔
2661
                this_reply);
162✔
2662
        }
2663
    }
174✔
2664
    return 1;
892✔
2665
}
688✔
2666

2667
/************************************************************
2668

2669
  Function: add_name
2670

2671
*************************************************************
2672

2673
  Inputs:  char* name
2674

2675
  Description:
2676

2677
  process input name for addition to target list
2678
  name can turn into multiple targets via multiple interfaces (-m)
2679
  or via NIS groups
2680

2681
************************************************************/
2682

2683
void add_name(char *name)
629✔
2684
{
2685
    struct addrinfo *res0, *res, hints;
2686
    int ret_ga;
2687
    char *printname;
2688
    char namebuf[256];
2689
    char addrbuf[256];
2690

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

2714
        // Handle JSON output for invalid hosts
2715
        if (opt_print_json_on) {
29✔
2716
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
2717
            return;
21✔
2718
        }
2719

2720
        return;
26✔
2721
    }
2722

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

2742
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2743
                printname = namebuf;
3✔
2744
            }
1✔
2745
            else {
2746
                printname = name;
×
2747
            }
2748
        }
1✔
2749
        else {
2750
            printname = name;
597✔
2751
        }
2752

2753
        /* opt_addr_on: name -> addr lookup requested */
2754
        if (opt_addr_on) {
600✔
2755
            int ret;
2756
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2757
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2758
            if (ret) {
3✔
2759
                if (!opt_quiet_on) {
×
2760
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2761
                }
2762
                continue;
×
2763
            }
2764

2765
            if (opt_name_on || opt_rdns_on) {
3✔
2766
                char nameaddrbuf[512 + 3];
2767
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
2768
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2769
            }
2770
            else {
2771
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2772
            }
2773
        }
1✔
2774
        else {
2775
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
597✔
2776
        }
2777

2778
        if (!opt_multif_on) {
600✔
2779
            break;
600✔
2780
        }
2781
    }
2782

2783
    freeaddrinfo(res0);
600✔
2784
}
167✔
2785

2786
/************************************************************
2787

2788
  Function: add_addr
2789

2790
*************************************************************
2791

2792
  Description:
2793

2794
  add single address to list of hosts to be pinged
2795

2796
************************************************************/
2797

2798
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
600✔
2799
{
2800
    HOST_ENTRY *p;
2801
    int n;
2802
    int64_t *i;
2803

2804
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
600✔
2805
    if (!p)
600✔
2806
        crash_and_burn("can't allocate HOST_ENTRY");
×
2807

2808
    p->name = strdup(name);
600✔
2809
    p->host = strdup(host);
600✔
2810
    memcpy(&p->saddr, ipaddr, ipaddr_len);
600✔
2811
    p->saddr_len = ipaddr_len;
600✔
2812
    p->timeout = opt_timeout;
600✔
2813
    p->min_reply = 0;
600✔
2814

2815
    if (opt_print_netdata_on) {
600✔
2816
        char *s = p->name;
3✔
2817
        while (*s) {
30✔
2818
            if (!isalnum(*s))
27✔
2819
                *s = '_';
9✔
2820
            s++;
27✔
2821
        }
2822
    }
1✔
2823

2824
    if (strlen(p->host) > max_hostname_len)
600✔
2825
        max_hostname_len = strlen(p->host);
407✔
2826

2827
    /* array for response time results */
2828
    if (!opt_loop_on) {
600✔
2829
#if SIZE_MAX <= UINT_MAX
2830
        if (trials > (SIZE_MAX / sizeof(int64_t)))
2831
            crash_and_burn("resp_times array too large for memory");
2832
#endif
2833
        i = (int64_t *)malloc(trials * sizeof(int64_t));
586✔
2834
        if (!i)
586✔
2835
            crash_and_burn("can't allocate resp_times array");
×
2836

2837
        for (n = 0; n < trials; n++)
2,911✔
2838
            i[n] = RESP_UNUSED;
2,325✔
2839

2840
        p->resp_times = i;
586✔
2841
    }
154✔
2842

2843
    /* allocate event storage */
2844
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
600✔
2845
    if (!p->event_storage_ping) {
600✔
2846
        errno_crash_and_burn("can't allocate event_storage_ping");
×
2847
    }
2848
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
600✔
2849
    if (!p->event_storage_timeout) {
600✔
2850
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
2851
    }
2852

2853
    /* schedule first ping */
2854
    host_add_ping_event(p, 0, current_time_ns);
600✔
2855

2856
    num_hosts++;
600✔
2857
}
600✔
2858

2859
/************************************************************
2860

2861
  Function: crash_and_burn
2862

2863
*************************************************************
2864

2865
  Inputs:  char* message
2866

2867
  Description:
2868

2869
************************************************************/
2870

2871
void crash_and_burn(char *message)
×
2872
{
2873
    fprintf(stderr, "%s: %s\n", prog, message);
×
2874
    exit(4);
×
2875
}
2876

2877
/************************************************************
2878

2879
  Function: errno_crash_and_burn
2880

2881
*************************************************************
2882

2883
  Inputs:  char* message
2884

2885
  Description:
2886

2887
************************************************************/
2888

2889
void errno_crash_and_burn(char *message)
8✔
2890
{
2891
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
2892
    exit(4);
8✔
2893
}
2894

2895
/************************************************************
2896

2897
  Function: print_warning
2898

2899
  Description: fprintf(stderr, ...), unless running with -q
2900

2901
*************************************************************/
2902

2903
void print_warning(char *format, ...)
54✔
2904
{
2905
    va_list args;
2906
    if (!opt_quiet_on) {
54✔
2907
        va_start(args, format);
54✔
2908
        vfprintf(stderr, format, args);
54✔
2909
        va_end(args);
54✔
2910
    }
8✔
2911
}
54✔
2912

2913

2914

2915
/************************************************************
2916

2917
  Function: addr_cmp
2918

2919
*************************************************************/
2920
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
604✔
2921
{
2922
    if (a->sa_family != b->sa_family) {
604✔
2923
        return a->sa_family - b->sa_family;
×
2924
    }
2925
    else {
2926
        if (a->sa_family == AF_INET) {
604✔
2927
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
542✔
2928
        }
2929
        else if (a->sa_family == AF_INET6) {
62✔
2930
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
70✔
2931
                &((struct sockaddr_in6 *)b)->sin6_addr,
62✔
2932
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
2933
        }
2934
    }
2935

2936
    return 0;
×
2937
}
176✔
2938

2939
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,040✔
2940
{
2941
    struct event *event = &h->event_storage_ping[index % event_storage_count];
1,040✔
2942
    event->host = h;
1,040✔
2943
    event->ping_index = index;
1,040✔
2944
    event->ev_time = ev_time;
1,040✔
2945
    ev_enqueue(&event_queue_ping, event);
1,040✔
2946

2947
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
2948
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2949
}
1,040✔
2950

2951
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,007✔
2952
{
2953
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
1,007✔
2954
    event->host = h;
1,007✔
2955
    event->ping_index = index;
1,007✔
2956
    event->ev_time = ev_time;
1,007✔
2957
    ev_enqueue(&event_queue_timeout, event);
1,007✔
2958

2959
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
2960
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2961
}
1,007✔
2962

2963
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
892✔
2964
{
2965
    return &h->event_storage_timeout[index % event_storage_count];
892✔
2966
}
2967

2968
/************************************************************
2969

2970
  Function: ev_enqueue
2971

2972
  Enqueue an event
2973

2974
  The queue is sorted by event->ev_time, so that queue->first always points to
2975
  the earliest event.
2976

2977
  We start scanning the queue from the tail, because we assume
2978
  that new events mostly get inserted with a event time higher
2979
  than the others.
2980

2981
*************************************************************/
2982
void ev_enqueue(struct event_queue *queue, struct event *event)
2,047✔
2983
{
2984
    struct event *i;
2985
    struct event *i_prev;
2986

2987
    /* Empty list */
2988
    if (queue->last == NULL) {
2,047✔
2989
        event->ev_next = NULL;
1,655✔
2990
        event->ev_prev = NULL;
1,655✔
2991
        queue->first = event;
1,655✔
2992
        queue->last = event;
1,655✔
2993
        return;
1,655✔
2994
    }
2995

2996
    /* Insert on tail? */
2997
    if (event->ev_time - queue->last->ev_time >= 0) {
392✔
2998
        event->ev_next = NULL;
392✔
2999
        event->ev_prev = queue->last;
392✔
3000
        queue->last->ev_next = event;
392✔
3001
        queue->last = event;
392✔
3002
        return;
392✔
3003
    }
3004

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

3025
/************************************************************
3026

3027
  Function: ev_dequeue
3028

3029
*************************************************************/
3030
struct event *ev_dequeue(struct event_queue *queue)
1,107✔
3031
{
3032
    struct event *dequeued;
3033

3034
    if (queue->first == NULL) {
1,107✔
3035
        return NULL;
×
3036
    }
3037
    dequeued = queue->first;
1,107✔
3038
    ev_remove(queue, dequeued);
1,107✔
3039

3040
    return dequeued;
1,107✔
3041
}
285✔
3042

3043
/************************************************************
3044

3045
  Function: ev_remove
3046

3047
*************************************************************/
3048
void ev_remove(struct event_queue *queue, struct event *event)
1,999✔
3049
{
3050
    if (queue->first == event) {
1,999✔
3051
        queue->first = event->ev_next;
1,990✔
3052
    }
530✔
3053
    if (queue->last == event) {
1,999✔
3054
        queue->last = event->ev_prev;
1,628✔
3055
    }
418✔
3056
    if (event->ev_prev) {
1,999✔
3057
        event->ev_prev->ev_next = event->ev_next;
9✔
3058
    }
3✔
3059
    if (event->ev_next) {
1,999✔
3060
        event->ev_next->ev_prev = event->ev_prev;
371✔
3061
    }
115✔
3062
    event->ev_prev = NULL;
1,999✔
3063
    event->ev_next = NULL;
1,999✔
3064
}
1,999✔
3065

3066

3067

3068
/************************************************************
3069

3070
  Function: usage
3071

3072
*************************************************************
3073

3074
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3075

3076
  Description:
3077

3078
************************************************************/
3079

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