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

schweikert / fping / 24923977653

25 Apr 2026 05:49AM UTC coverage: 87.929% (-0.07%) from 88.0%
24923977653

Pull #461

github

web-flow
Merge cad10ab22 into 780ec4674
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%)

2 existing lines in 1 file now uncovered.

1690 of 1922 relevant lines covered (87.93%)

354.31 hits per line

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

87.85
/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,142✔
267
    IP_HEADER_RESULT res;
268
    res.tos = -1;
3,142✔
269
    res.ttl = -1;
3,142✔
270
    res.otime_ms = 0x80000000U;
3,142✔
271
    res.rtime_ms = 0x80000000U;
3,142✔
272
    res.ttime_ms = 0x80000000U;
3,142✔
273
    res.src_addr[0] = '\0';
3,142✔
274
    return res;
3,142✔
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)
977✔
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();
977✔
447
    int tos = 0;
977✔
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];
977✔
456
    if (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
977✔
457
        usage(0);
3✔
458
    }
1✔
459

460
    socket4 = open_ping_socket_ipv4(&socktype4);
975✔
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);
646✔
466
#endif
467

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

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

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

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

494
    /* get command line options */
495

496
    struct optparse_long longopts[] = {
975✔
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
        { "oiface", 0, OPTPARSE_REQUIRED },
516
        { "json", 'J', OPTPARSE_NONE },
517
        { "icmp-timestamp", 0, OPTPARSE_NONE },
518
#ifdef SO_MARK
519
        { "fwmark", 'k', OPTPARSE_REQUIRED },
520
#endif
521
        { "loop", 'l', OPTPARSE_NONE },
522
        { "all", 'm', OPTPARSE_NONE },
523
        { "dontfrag", 'M', OPTPARSE_NONE },
524
        { "name", 'n', OPTPARSE_NONE },
525
        { "netdata", 'N', OPTPARSE_NONE },
526
        { "outage", 'o', OPTPARSE_NONE },
527
        { "tos", 'O', OPTPARSE_REQUIRED },
528
        { "period", 'p', OPTPARSE_REQUIRED },
529
        { "quiet", 'q', OPTPARSE_NONE },
530
        { "squiet", 'Q', OPTPARSE_REQUIRED },
531
        { "retry", 'r', OPTPARSE_REQUIRED },
532
        { "random", 'R', OPTPARSE_NONE },
533
        { "stats", 's', OPTPARSE_NONE },
534
        { "src", 'S', OPTPARSE_REQUIRED },
535
        { "timeout", 't', OPTPARSE_REQUIRED },
536
        { NULL, 'T', OPTPARSE_REQUIRED },
537
        { "unreach", 'u', OPTPARSE_NONE },
538
        { "version", 'v', OPTPARSE_NONE },
539
        { "reachable", 'x', OPTPARSE_REQUIRED },
540
        { "fast-reachable", 'X', OPTPARSE_REQUIRED },
541
        { "check-source", 0, OPTPARSE_NONE },
542
        { "print-tos", 0, OPTPARSE_NONE },
543
        { "print-ttl", 0, OPTPARSE_NONE },
544
        { "print-srcaddr", 0, OPTPARSE_NONE },
545
        { "seqmap-timeout", 0, OPTPARSE_REQUIRED },
546
#if defined(DEBUG) || defined(_DEBUG)
547
        { NULL, 'z', OPTPARSE_REQUIRED },
548
#endif
549
        { 0, 0, 0 }
550
    };
551

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

674
        case 't':
56✔
675
            opt_timeout = strtod_strict(optparse_state.optarg) * 1000000;
66✔
676
            opt_timeout_on = 1;
52✔
677
            break;
52✔
678

679
        case 'r':
44✔
680
            opt_retry = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
51✔
681
            break;
41✔
682

683
        case 'i':
38✔
684
            opt_interval = strtod_strict(optparse_state.optarg) * 1000000;
48✔
685
            break;
34✔
686

687
        case 'p':
90✔
688
            opt_perhost_interval = strtod_strict(optparse_state.optarg) * 1000000;
123✔
689
            break;
109✔
690

691
        case 'c':
104✔
692
            opt_count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
142✔
693
            if (!opt_count)
132✔
694
                usage(1);
3✔
695

696
            opt_count_on = 1;
130✔
697
            break;
130✔
698

699
        case 'C':
42✔
700
            opt_count = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
60✔
701
            if (!opt_count)
50✔
702
                usage(1);
3✔
703

704
            opt_count_on = 1;
48✔
705
            opt_report_all_rtts_on = 1;
48✔
706
            break;
48✔
707

708
        case 'b':
20✔
709
            opt_ping_data_size = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
30✔
710
            opt_size_on = 1;
20✔
711
            break;
20✔
712

713
        case 'h':
2✔
714
            usage(0);
3✔
715
            break;
1✔
716

717
        case 'q':
26✔
718
            opt_verbose_on = 0;
38✔
719
            opt_quiet_on = 1;
38✔
720
            break;
38✔
721

722
        case 'Q':
34✔
723
            opt_verbose_on = 0;
43✔
724
            opt_quiet_on = 1;
43✔
725
            errno = 0;
43✔
726
            opt_val_double = strtod(optparse_state.optarg, &endptr);
43✔
727
            if (errno != 0 || optparse_state.optarg == endptr || (*endptr != '\0' && *endptr != ','))
43✔
728
                usage(1);
12✔
729
            if (opt_val_double < 0) {
35✔
730
                usage(1);
9✔
731
            }
3✔
732
            report_interval = opt_val_double * 1e9;
29✔
733

734
            /* recognize keyword(s) after number, ignore everything else */
735
            {
736
                char *comma = strchr(optparse_state.optarg, ',');
29✔
737
                if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
29✔
738
                    opt_cumulative_stats_on = 1;
4✔
739
                }
740
            }
741

742
            break;
29✔
743

744
        case 'e':
6✔
745
            opt_elapsed_on = 1;
8✔
746
            break;
8✔
747

748
        case 'm':
749
            opt_multif_on = 1;
×
750
            break;
×
751

752
        case 'N':
2✔
753
            opt_print_netdata_on = 1;
3✔
754
            break;
3✔
755

756
        case 'n':
4✔
757
            opt_name_on = 1;
6✔
758
            if (opt_rdns_on) {
6✔
759
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
760
                exit(1);
3✔
761
            }
762
            break;
3✔
763

764
        case 'd':
6✔
765
            opt_rdns_on = 1;
9✔
766
            if (opt_name_on) {
9✔
767
                fprintf(stderr, "%s: use either one of -d or -n\n", prog);
3✔
768
                exit(1);
3✔
769
            }
770
            break;
6✔
771

772
        case 'A':
2✔
773
            opt_addr_on = 1;
3✔
774
            break;
3✔
775

776
        case 'B':
18✔
777
            opt_backoff = strtod_strict(optparse_state.optarg);
26✔
778
            break;
14✔
779

780
        case 's':
8✔
781
            opt_stats_on = 1;
12✔
782
            break;
12✔
783

784
        case 'D':
22✔
785
            opt_timestamp_on = 1;
33✔
786
            break;
33✔
787

788
        case 'R':
4✔
789
            opt_random_data_on = 1;
5✔
790
            break;
5✔
791

792
        case 'l':
12✔
793
            opt_loop_on = 1;
17✔
794
            opt_backoff_on = 0;
17✔
795
            break;
17✔
796

797
        case 'u':
8✔
798
            opt_unreachable_on = 1;
12✔
799
            break;
12✔
800

801
        case 'a':
18✔
802
            opt_alive_on = 1;
26✔
803
            break;
26✔
804

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

811
#if defined(DEBUG) || defined(_DEBUG)
812
        case 'z':
813
            debugging = (unsigned int)strtoul_strict(optparse_state.optarg, 0);
814
            break;
815
#endif /* DEBUG || _DEBUG */
816

817
        case 'v':
4✔
818
            printf("%s: Version %s\n", prog, VERSION);
6✔
819
            exit(0);
6✔
820

821
        case 'x':
16✔
822
            opt_min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
24✔
823
            if (!opt_min_reachable)
14✔
824
                usage(1);
3✔
825
            break;
12✔
826

827
        case 'X':
14✔
828
            opt_min_reachable = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
21✔
829
            if (!opt_min_reachable)
11✔
830
                usage(1);
3✔
831
            opt_fast_reachable_on = 1;
9✔
832
            break;
9✔
833

834
        case 'f':
18✔
835
            filename = optparse_state.optarg;
27✔
836
            break;
27✔
837
#ifdef SO_MARK
838
        case 'k':
18✔
839
            fwmark = (unsigned int)strtoul_strict(optparse_state.optarg, 10);
18✔
840
            if (!fwmark)
8✔
841
                usage(1);
2✔
842

843
            if (socket4 >= 0)
6✔
844
                if(-1 == p_setsockopt(suid, socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
845
                    perror("fwmark ipv4");
4✔
846

847
#ifdef IPV6
848
            if (socket6 >= 0)
6✔
849
                if(-1 == p_setsockopt(suid, socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
6✔
850
                    perror("fwmark ipv6");
4✔
851
#endif
852

853
            break;
6✔
854
#endif
855

856
        case 'g':
106✔
857
            /* use IP list generation */
858
            /* mutually exclusive with using file input or command line targets */
859
            opt_generate_on = 1;
129✔
860
            break;
129✔
861

862
        case 'S':
10✔
863
            if (inet_pton(AF_INET, optparse_state.optarg, &src_addr)) {
13✔
864
                src_addr_set = 1;
6✔
865
                break;
6✔
866
            }
867
#ifdef IPV6
868
            if (inet_pton(AF_INET6, optparse_state.optarg, &src_addr6)) {
7✔
869
                src_addr6_set = 1;
4✔
870
                break;
4✔
871
            }
872
#endif
873
            fprintf(stderr, "%s: can't parse source address: %s\n", prog, optparse_state.optarg);
3✔
874
            exit(1);
3✔
875

876
        case 'I':
6✔
877
            opt_bindiface_on = 1;
6✔
878
#ifdef SO_BINDTODEVICE
879
            if (socket4 >= 0) {
6✔
880
                if (p_setsockopt(suid, socket4, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
6✔
881
                    perror("binding to specific interface (SO_BINDTODEVICE)");
2✔
882
                    exit(1);
2✔
883
                }
884
            }
885
#ifdef IPV6
886
            if (socket6 >= 0) {
4✔
887
                if (p_setsockopt(suid, socket6, SOL_SOCKET, SO_BINDTODEVICE, optparse_state.optarg, strlen(optparse_state.optarg))) {
4✔
888
                    perror("binding to specific interface (SO_BINDTODEVICE), IPV6");
×
889
                    exit(1);
×
890
                }
891
            }
892
#endif
893
#else
894
            printf("%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", prog);
895
            exit(3);
896
            ;
897
#endif
898
            break;
4✔
899

900
        case 'J':
44✔
901
            opt_print_json_on = 1;
65✔
902
            break;
65✔
903

904
        case 'T':
2✔
905
            /* This option is ignored for compatibility reasons ("select timeout" is not meaningful anymore) */
906
            break;
3✔
907

908
        case 'O':
16✔
909
            {
910
                unsigned long val = strtoul_strict(optparse_state.optarg, 0);
24✔
911
                if (val > 255)
16✔
912
                    usage(1);
×
913
                tos = (int)val;
16✔
914
            }
915
            if (socket4 >= 0) {
16✔
916
                if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
12✔
917
                    perror("setting type of service octet IP_TOS");
×
918
                }
919
            }
4✔
920
#if defined(IPV6) && defined(IPV6_TCLASS)
921
            if (socket6 >= 0) {
16✔
922
                if (setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
12✔
923
                    perror("setting type of service octet IPV6_TCLASS");
×
924
                }
925
            }
4✔
926
#endif
927
            break;
16✔
928

929
        case 'o':
6✔
930
            opt_outage_on = 1;
7✔
931
            break;
7✔
932

933
        case '?':
4✔
934
            fprintf(stderr, "%s: %s\n", argv[0], optparse_state.errmsg);
6✔
935
            fprintf(stderr, "see 'fping -h' for usage information\n");
6✔
936
            exit(1);
6✔
937
            break;
938
        }
939
    }
940

941
    /* permanently drop privileges */
942
    if (suid != getuid() && setuid(getuid())) {
578✔
943
        perror("fatal: failed to permanently drop privileges");
×
944
        /* continuing would be a security hole */
945
        exit(4);
×
946
    }
947

948
    /* validate various option settings */
949

950
#ifndef IPV6
951
    if (socket4 < 0) {
952
        crash_and_burn("can't create socket (must run as root?)");
953
    }
954
#else
955
    if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
578✔
956
        crash_and_burn("can't create socket (must run as root?)");
×
957
    }
958
#endif
959

960
    if (opt_ttl > 255) {
576✔
961
        fprintf(stderr, "%s: ttl %u out of range\n", prog, opt_ttl);
3✔
962
        exit(1);
3✔
963
    }
964

965
    if (opt_unreachable_on && opt_alive_on) {
573✔
966
        fprintf(stderr, "%s: specify only one of a, u\n", prog);
3✔
967
        exit(1);
3✔
968
    }
969

970
    if (opt_oiface_on && opt_bindiface_on) {
570✔
971
        fprintf(stderr, "%s: specify only --oiface or -I, --iface\n", prog);
×
972
        exit(1);
×
973
    }
974

975
    if (opt_count_on && opt_loop_on) {
570✔
976
        fprintf(stderr, "%s: specify only one of c, l\n", prog);
3✔
977
        exit(1);
3✔
978
    }
979

980
    if (opt_print_json_on && !opt_count_on && !opt_loop_on) {
567✔
981
        fprintf(stderr, "%s: option -J, --json requires -c, -C, or -l\n", prog);
3✔
982
        exit(1);
3✔
983
    }
984

985
    if (opt_interval < (float)MIN_INTERVAL_MS * 1000000 && getuid()) {
564✔
986
        fprintf(stderr, "%s: -i must be >= %g\n", prog, (float)MIN_INTERVAL_MS);
3✔
987
        exit(1);
3✔
988
    }
989

990
    if (opt_perhost_interval < (float)MIN_PERHOST_INTERVAL_MS * 1000000 && getuid()) {
561✔
991
        fprintf(stderr, "%s: -p must be >= %g\n", prog, (float)MIN_PERHOST_INTERVAL_MS);
3✔
992
        exit(1);
3✔
993
    }
994

995
    if (opt_ping_data_size > MAX_PING_DATA) {
558✔
996
        fprintf(stderr, "%s: data size %u not valid, must not be larger than %u\n",
4✔
997
            prog, opt_ping_data_size, (unsigned int)MAX_PING_DATA);
1✔
998
        exit(1);
3✔
999
    }
1000

1001
    if ((opt_backoff > MAX_BACKOFF_FACTOR) || (opt_backoff < MIN_BACKOFF_FACTOR)) {
555✔
1002
        fprintf(stderr, "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
8✔
1003
            prog, opt_backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
2✔
1004
        exit(1);
6✔
1005
    }
1006

1007
    if (opt_icmp_request_typ == 13 && opt_size_on != 0) {
549✔
1008
        fprintf(stderr, "%s: cannot change ICMP Timestamp size\n", prog);
9✔
1009
        exit(1);
9✔
1010
    }
1011

1012
    if (opt_count_on) {
540✔
1013
        if (opt_verbose_on)
160✔
1014
            opt_per_recv_on = 1;
102✔
1015

1016
        opt_alive_on = opt_unreachable_on = opt_verbose_on = 0;
160✔
1017
    }
42✔
1018

1019
    if (opt_loop_on) {
540✔
1020
        if (!report_interval)
14✔
1021
            opt_per_recv_on = 1;
12✔
1022

1023
        opt_alive_on = opt_unreachable_on = opt_verbose_on = 0;
14✔
1024
    }
4✔
1025

1026
    if (opt_alive_on || opt_unreachable_on || opt_min_reachable)
540✔
1027
        opt_verbose_on = 0;
37✔
1028

1029
    trials = (opt_count > opt_retry + 1) ? opt_count : opt_retry + 1;
556✔
1030

1031
    /* auto-tune default timeout for count/loop modes
1032
     * see also github #32 */
1033
    if (opt_loop_on || opt_count_on) {
556✔
1034
        if (!opt_timeout_on) {
174✔
1035
            opt_timeout = opt_perhost_interval;
162✔
1036
            if (opt_timeout > (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000) {
162✔
1037
                opt_timeout = (int64_t)AUTOTUNE_TIMEOUT_MAX * 1000000;
×
1038
            }
1039
        }
44✔
1040
    }
46✔
1041

1042
#if defined(DEBUG) || defined(_DEBUG)
1043
    if (debugging & DBG_TRACE)
1044
        opt_debug_trace_on = 1;
1045

1046
    if (debugging & DBG_RANDOM_LOSE_FEW) {
1047
        opt_debug_randomly_lose_on = 1;
1048
        lose_factor = 1; /* ie, 1/4 */
1049
    }
1050

1051
    if (debugging & DBG_RANDOM_LOSE_MANY) {
1052
        opt_debug_randomly_lose_on = 1;
1053
        lose_factor = 5; /* ie, 3/4 */
1054
    }
1055

1056
    if (debugging & DBG_PRINT_PER_SYSTEM)
1057
        opt_debug_print_per_system_on = 1;
1058

1059
    if ((debugging & DBG_REPORT_ALL_RTTS) && !opt_loop_on)
1060
        opt_report_all_rtts_on = 1;
1061

1062
    if (opt_debug_trace_on) {
1063
        fprintf(stderr, "%s:\n  opt_count: %u, opt_retry: %u, opt_interval: %.0f ms\n",
1064
            prog, opt_count, opt_retry, opt_interval / 1e6);
1065
        fprintf(stderr, "  opt_perhost_interval: %.0f ms, opt_timeout: %.0f\n",
1066
            opt_perhost_interval / 1e6, opt_timeout / 1e6);
1067
        fprintf(stderr, "  opt_seqmap_timeout: %.0f\n", opt_seqmap_timeout / 1e6);
1068
        fprintf(stderr, "  opt_ping_data_size = %u, trials = %u\n",
1069
            opt_ping_data_size, trials);
1070

1071
        if (opt_verbose_on)
1072
            fprintf(stderr, "  opt_verbose_on set\n");
1073
        if (opt_multif_on)
1074
            fprintf(stderr, "  opt_multif_on set\n");
1075
        if (opt_name_on)
1076
            fprintf(stderr, "  opt_name_on set\n");
1077
        if (opt_addr_on)
1078
            fprintf(stderr, "  opt_addr_on set\n");
1079
        if (opt_stats_on)
1080
            fprintf(stderr, "  opt_stats_on set\n");
1081
        if (opt_unreachable_on)
1082
            fprintf(stderr, "  opt_unreachable_on set\n");
1083
        if (opt_alive_on)
1084
            fprintf(stderr, "  opt_alive_on set\n");
1085
        if (opt_elapsed_on)
1086
            fprintf(stderr, "  opt_elapsed_on set\n");
1087
        if (opt_version_on)
1088
            fprintf(stderr, "  opt_version_on set\n");
1089
        if (opt_count_on)
1090
            fprintf(stderr, "  opt_count_on set\n");
1091
        if (opt_loop_on)
1092
            fprintf(stderr, "  opt_loop_on set\n");
1093
        if (opt_backoff_on)
1094
            fprintf(stderr, "  opt_backoff_on set\n");
1095
        if (opt_per_recv_on)
1096
            fprintf(stderr, "  opt_per_recv_on set\n");
1097
        if (opt_report_all_rtts_on)
1098
            fprintf(stderr, "  opt_report_all_rtts_on set\n");
1099
        if (opt_debug_randomly_lose_on)
1100
            fprintf(stderr, "  opt_debug_randomly_lose_on set\n");
1101
        if (opt_debug_print_per_system_on)
1102
            fprintf(stderr, "  opt_debug_print_per_system_on set\n");
1103
        if (opt_outage_on)
1104
            fprintf(stderr, "  opt_outage_on set\n");
1105
        if (opt_print_netdata_on)
1106
            fprintf(stderr, "  opt_print_netdata_on set\n");
1107
        if (opt_print_json_on)
1108
            fprintf(stderr, "  opt_print_json_on set\n");
1109
    }
1110
#endif /* DEBUG || _DEBUG */
1111

1112
    /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
1113
    if (opt_ttl > 0) {
556✔
1114
        if (socket4 >= 0) {
3✔
1115
            if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &opt_ttl, sizeof(opt_ttl))) {
3✔
1116
                perror("setting time to live");
×
1117
            }
1118
        }
1✔
1119
#ifdef IPV6
1120
        if (socket6 >= 0) {
3✔
1121
            if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &opt_ttl, sizeof(opt_ttl))) {
3✔
1122
                perror("setting time to live");
×
1123
            }
1124
        }
1✔
1125
#endif
1126
    }
1✔
1127

1128
#if HAVE_SO_TIMESTAMPNS
1129
    {
1130
        int opt = 1;
410✔
1131
        if (socket4 >= 0) {
410✔
1132
            if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
410✔
1133
                if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1134
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
1135
                }
1136
            }
1137
        }
1138
#ifdef IPV6
1139
        if (socket6 >= 0) {
410✔
1140
            if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
410✔
1141
                if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
1142
                    perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
1143
                }
1144
            }
1145
        }
1146
#endif
1147
    }
1148
#endif
1149

1150
    update_current_time();
556✔
1151
    start_time = current_time_ns;
556✔
1152

1153
    /* handle host names supplied on command line or in a file */
1154
    /* if the opt_generate_on is on, then generate the IP list */
1155

1156
    argv = &argv[optparse_state.optind];
556✔
1157
    argc -= optparse_state.optind;
556✔
1158

1159
    /* calculate how many ping can be in-flight per host */
1160
    if (opt_count_on) {
556✔
1161
        event_storage_count = opt_count;
175✔
1162
    }
57✔
1163
    else if (opt_loop_on) {
381✔
1164
        if (opt_perhost_interval > opt_timeout) {
14✔
1165
            event_storage_count = 1;
×
1166
        }
1167
        else {
1168
            event_storage_count = 1 + opt_timeout / opt_perhost_interval;
14✔
1169
        }
1170
    }
4✔
1171
    else {
1172
        event_storage_count = 1;
367✔
1173
    }
1174

1175
    /* file and generate are mutually exclusive */
1176
    /* file and command line are mutually exclusive */
1177
    /* generate requires command line parameters beyond the switches */
1178
    if ((*argv && filename) || (filename && opt_generate_on) || (opt_generate_on && !*argv))
556✔
1179
        usage(1);
19✔
1180

1181
    /* if no conditions are specified, then assume input from stdin */
1182
    if (!*argv && !filename && !opt_generate_on)
538✔
1183
        filename = "-";
69✔
1184

1185
    if (*argv && !opt_generate_on) {
538✔
1186
        while (*argv) {
699✔
1187
            add_name(*argv);
380✔
1188
            ++argv;
380✔
1189
        }
1190
    }
77✔
1191
    else if (filename) {
219✔
1192
        FILE *ping_file;
1193
        char line[MAX_TARGET_NAME_LEN + 1];
1194
        char host[MAX_TARGET_NAME_LEN + 1];
1195
        char scratch[MAX_TARGET_NAME_LEN + 1];
1196
        int skip, non_empty;
1197

1198
        if (strcmp(filename, "-") == 0)
96✔
1199
            ping_file = fdopen(0, "r");
72✔
1200
        else
1201
            ping_file = fopen(filename, "r");
24✔
1202

1203
        if (!ping_file)
96✔
1204
            errno_crash_and_burn("fopen");
3✔
1205

1206
        /*
1207
         * Read the first word of every non-comment line, skip everything else.
1208
         * (Empty and blank lines are ignored.  Lines where the first non-blank
1209
         * character is a '#' are interpreted as comments and ignored.)
1210
        */
1211
        while (fgets(line, sizeof(line), ping_file)) {
379✔
1212
            skip = non_empty = 0;
309✔
1213

1214
            /* skip over a prefix of the line where sscanf finds nothing */
1215
            if ((sscanf(line, "%s", host) != 1) || (!*host)) {
309✔
1216
                continue;
99✔
1217
            }
1218

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

1270
            if (non_empty)
186✔
1271
                add_name(host);
144✔
1272
        }
1273

1274
        fclose(ping_file);
70✔
1275
    }
24✔
1276
    else if (*argv && opt_generate_on) {
123✔
1277
        if (argc == 1) {
123✔
1278
            /* one target: we expect a cidr range (n.n.n.n/m) */
1279
            add_cidr(argv[0]);
68✔
1280
        }
14✔
1281
        else if (argc == 2) {
55✔
1282
            add_range(argv[0], argv[1]);
52✔
1283
        }
6✔
1284
        else {
1285
            usage(1);
3✔
1286
        }
1287
    }
21✔
1288
    else {
1289
        usage(1);
×
1290
    }
1291

1292
    if (!num_hosts) {
452✔
1293
        exit(num_noaddress ? 2 : 1);
36✔
1294
    }
1295

1296
    if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
416✔
1297
        socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
20✔
1298
    }
2✔
1299
#ifdef IPV6
1300
    if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
414✔
1301
        socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
18✔
1302
    }
1303
#endif
1304

1305
    /* allocate and initialize array to map host nr to host_entry */
1306
    {
1307
        struct event *cursor = event_queue_ping.first;
412✔
1308
        int i = 0;
412✔
1309
        table = (HOST_ENTRY **)calloc(num_hosts, sizeof(HOST_ENTRY *));
412✔
1310
        if (!table)
412✔
1311
            crash_and_burn("Can't malloc array of hosts");
×
1312
        /* initialize table of hosts. we know that we have ping events scheduled
1313
         * for each of them */
1314
        for (cursor = event_queue_ping.first; cursor; cursor = cursor->ev_next) {
1,011✔
1315
            table[i] = cursor->host;
599✔
1316
            cursor->host->i = i;
599✔
1317
            i++;
599✔
1318
        }
157✔
1319
    }
1320

1321
    init_ping_buffer_ipv4(opt_ping_data_size);
412✔
1322
#ifdef IPV6
1323
    init_ping_buffer_ipv6(opt_ping_data_size);
412✔
1324
#endif
1325

1326
#ifdef USE_SIGACTION
1327
    memset(&act, 0, sizeof(act));
412✔
1328
    act.sa_handler = signal_handler;
412✔
1329
    sigemptyset(&act.sa_mask);
412✔
1330
    sigaddset(&act.sa_mask, SIGINT);
412✔
1331
    sigaddset(&act.sa_mask, SIGQUIT);
412✔
1332
    act.sa_flags = SA_RESTART;
412✔
1333
    if (sigaction(SIGQUIT, &act, NULL) || sigaction(SIGINT, &act, NULL)) {
412✔
1334
        crash_and_burn("failure to set signal handler");
30✔
1335
    }
30✔
1336
#else
1337
    signal(SIGINT, signal_handler);
1338
    signal(SIGQUIT, signal_handler);
1339
#endif
1340
    setlinebuf(stdout);
382✔
1341

1342
    // Last time we updated current_time_ns was before adding the hosts and
1343
    // possibly doing DNS resolution, which means that it isn't accurate
1344
    // anymore.
1345
    update_current_time();
382✔
1346
    if (report_interval) {
382✔
1347
        next_report_time = current_time_ns + report_interval;
22✔
1348
    }
2✔
1349

1350
    last_send_time = 0;
382✔
1351

1352
    seqmap_init(opt_seqmap_timeout);
382✔
1353

1354
    /* main loop */
1355
    main_loop();
382✔
1356

1357
/* Debug: CPU Performance */
1358
#if defined(DEBUG) || defined(_DEBUG)
1359
    perf_cpu_end = clock();
1360
    perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
1361
    printf("[DEBUG] CPU time used: %f sec\n", perf_cpu_time_used);
1362
#endif /* DEBUG || _DEBUG */
1363

1364
    finish();
382✔
1365

1366
    return 0;
82✔
1367
}
1368

1369
static inline int64_t timespec_ns(struct timespec *a)
6,805✔
1370
{
1371
    return ((int64_t)a->tv_sec * 1000000000) + a->tv_nsec;
6,805✔
1372
}
1373

1374
#if HAVE_SO_TIMESTAMPNS
1375
/* convert a struct timeval to nanoseconds */
1376
static inline int64_t timeval_ns(struct timeval *a)
1377
{
1378
    return ((int64_t)a->tv_sec * 1000000000) + ((int64_t)a->tv_usec * 1000);
1379
}
1380
#endif /* HAVE_SO_TIMESTAMPNS */
1381

1382
void add_cidr(char *addr)
63✔
1383
{
1384
    char *addr_end;
1385
    char *mask_str;
1386
    unsigned long mask;
1387
    int ret;
1388
    struct addrinfo addr_hints;
1389
    struct addrinfo *addr_res;
1390
    unsigned long net_addr;
1391
#ifdef IPV6
1392
    uint64_t net_upper, net_lower;
1393
    char *scope_str;
1394
#endif /* IPV6 */
1395

1396
    /* Split address from mask */
1397
    addr_end = strrchr(addr, '/');
63✔
1398
    if (addr_end == NULL) {
63✔
1399
        usage(1);
3✔
1400
    }
1✔
1401
    mask_str = addr_end + 1;
61✔
1402

1403
#ifdef IPV6
1404
    /* IPv6 addresses can have a scope */
1405
    scope_str = strchr(addr, '%');
61✔
1406
    if (scope_str && mask_str < scope_str) {
61✔
1407
        fprintf(stderr, "%s: address scope must precede prefix length\n", prog);
2✔
1408
        exit(1);
2✔
1409
    }
1410
#endif /*IPV6 */
1411

1412
    *addr_end = '\0';
59✔
1413
    mask = strtoul_strict(mask_str, 10);
59✔
1414

1415
    /* parse address */
1416
    memset(&addr_hints, 0, sizeof(struct addrinfo));
53✔
1417
    addr_hints.ai_family = hints_ai_family;
53✔
1418
    addr_hints.ai_flags = AI_NUMERICHOST;
53✔
1419
    ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
53✔
1420
    if (ret) {
53✔
1421
        fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
7✔
1422
        exit(1);
7✔
1423
    }
1424
    if (addr_res->ai_family == AF_INET) {
46✔
1425
        net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
24✔
1426
        freeaddrinfo(addr_res);
24✔
1427
        add_cidr_ipv4(net_addr, mask);
24✔
1428
#ifdef IPV6
1429
    } else if (addr_res->ai_family == AF_INET6) {
30✔
1430
        uint8_t *ipv6_addr = ((struct sockaddr_in6*)addr_res->ai_addr)->sin6_addr.s6_addr;
22✔
1431
        net_upper = be_octets_to_uint64(ipv6_addr);
22✔
1432
        net_lower = be_octets_to_uint64(ipv6_addr + 8);
22✔
1433
        freeaddrinfo(addr_res);
22✔
1434
        add_cidr_ipv6(net_upper, net_lower, mask, scope_str);
22✔
1435
#endif /* IPV6 */
1436
    } else {
1437
        freeaddrinfo(addr_res);
×
1438
        fprintf(stderr, "%s: -g does not support this address family\n", prog);
×
1439
        exit(1);
×
1440
    }
1441
}
30✔
1442

1443
void add_cidr_ipv4(unsigned long net_addr, unsigned long mask)
24✔
1444
{
1445
    unsigned long bitmask;
1446
    unsigned long net_last;
1447

1448
    /* check mask */
1449
    if (mask < 1 || mask > 32) {
24✔
1450
        fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %lu)\n", prog, mask);
6✔
1451
        exit(1);
6✔
1452
    }
1453

1454
    /* convert mask integer from 1 to 32 to a bitmask */
1455
    bitmask = ((unsigned long)0xFFFFFFFF) << (32 - mask);
18✔
1456

1457
    /* calculate network range */
1458
    net_addr &= bitmask;
18✔
1459
    net_last = net_addr + ((unsigned long)0x1 << (32 - mask)) - 1;
18✔
1460

1461
    /* exclude network and broadcast address for regular prefixes */
1462
    if (mask < 31) {
18✔
1463
        net_last--;
12✔
1464
        net_addr++;
12✔
1465
    }
4✔
1466

1467
    /* add all hosts in that network (net_addr and net_last inclusive) */
1468
    add_addr_range_ipv4(net_addr, net_last);
18✔
1469
}
16✔
1470

1471
#ifdef IPV6
1472
void add_cidr_ipv6(uint64_t net_upper, uint64_t net_lower, unsigned long mask, const char *scope_str)
23✔
1473
{
1474
    uint64_t bitmask_lower;
1475
    uint64_t last_lower;
1476

1477
    /* check mask -- 2^63 addresses should suffice for now */
1478
    if (mask < 65 || mask > 128) {
23✔
1479
        fprintf(stderr, "%s: netmask must be between 65 and 128 (is: %lu)\n", prog, mask);
7✔
1480
        exit(1);
7✔
1481
    }
1482

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

1486
    /* calculate network range */
1487
    net_lower &= bitmask_lower;
16✔
1488
    last_lower = net_lower + ((uint64_t)1 << (128 - mask)) - 1;
16✔
1489

1490
    add_addr_range_ipv6(net_upper, net_lower, net_upper, last_lower, scope_str);
16✔
1491
}
12✔
1492
#endif /* IPV6 */
1493

1494
void add_range(char *start, char *end)
52✔
1495
{
1496
    struct addrinfo addr_hints;
1497
    struct addrinfo *addr_res;
1498
    unsigned long start_long;
1499
    unsigned long end_long;
1500
    int ret;
1501
#ifdef IPV6
1502
    uint64_t start_upper, start_lower;
1503
    uint64_t end_upper, end_lower;
1504
    char *start_scope_str, *end_scope_str;
1505

1506
    /*
1507
     * The compiler does not know that setting the address family hint to
1508
     * ensure that start and end are from the same address family also
1509
     * ensures that either start_long and end_long are initialized and used,
1510
     * or start_upper, start_lower, end_upper, and end_lower are initialized
1511
     * and used.  Thus initialize all variables when both IPv4 and IPv6 are
1512
     * supported to suppress compiler warnings.
1513
     */
1514
    start_long = -1;
52✔
1515
    end_long = 0;
52✔
1516
    start_upper = start_lower = -1;
52✔
1517
    end_upper = end_lower = 0;
52✔
1518
    start_scope_str = end_scope_str = NULL;
52✔
1519
#endif /* IPV6 */
1520

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

1548
#ifdef IPV6
1549
    /* IPv6 addresses can have a scope */
1550
    if (hints_ai_family == AF_INET6) {
43✔
1551
        start_scope_str = strchr(start, '%');
26✔
1552
        end_scope_str = strchr(end, '%');
26✔
1553
        if ((!start_scope_str && end_scope_str) ||
26✔
1554
            (start_scope_str && !end_scope_str) ||
24✔
1555
            (start_scope_str && end_scope_str && strcmp(start_scope_str, end_scope_str) != 0)) {
4✔
1556
                fprintf(stderr, "%s: different scopes for start and end addresses\n", prog);
6✔
1557
                exit(1);
6✔
1558
        }
1559
    }
1560
#endif
1561

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

1590
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
30✔
1591
{
1592
    /* check if generator limit is exceeded */
1593
    if (end_long >= start_long + MAX_GENERATE) {
30✔
1594
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
6✔
1595
        exit(1);
6✔
1596
    }
1597

1598
    /* generate */
1599
    for (; start_long <= end_long; start_long++) {
93✔
1600
        struct in_addr in_addr_tmp;
1601
        char buffer[20];
1602
        in_addr_tmp.s_addr = htonl(start_long);
69✔
1603
        inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
69✔
1604
        add_name(buffer);
69✔
1605
    }
23✔
1606
}
24✔
1607

1608
#ifdef IPV6
1609
uint64_t be_octets_to_uint64(uint8_t *be_octets)
134✔
1610
{
1611
    int i;
1612
    uint64_t ret = 0;
134✔
1613
    for (i = 0; i < 8; i++) {
1,206✔
1614
        ret |= (uint64_t)be_octets[7 - i] << (i * 8);
1,072✔
1615
    }
16✔
1616
    return ret;
134✔
1617
}
1618

1619
void uint64_to_be_octets(uint64_t num, uint8_t *be_octets)
80✔
1620
{
1621
    int i;
1622
    for (i = 0; i < 8; i++) {
720✔
1623
        be_octets[7 - i] = (uint8_t)((num >> (i * 8)) & 0xff);
640✔
1624
    }
1625
}
80✔
1626

1627
void add_addr_range_ipv6(uint64_t start_upper, uint64_t start_lower,
34✔
1628
                         uint64_t end_upper, uint64_t end_lower,
1629
                         const char *scope_str)
1630
{
1631
    struct in6_addr in6_addr_tmp;
1632
    char buffer[100];
1633

1634
    /* prevent generating too many addresses */
1635
    if ((start_upper + 1 < end_upper) ||
34✔
1636
        (start_upper + 1 == end_upper && end_lower >= start_lower) ||
34✔
1637
        (start_upper + 1 == end_upper && end_lower - MAX_GENERATE >= start_lower) ||
32✔
1638
        (start_upper == end_upper && end_lower - MAX_GENERATE >= start_lower &&
28✔
1639
                                     start_lower + MAX_GENERATE <= end_lower)) {
18✔
1640
        fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
8✔
1641
        exit(1);
8✔
1642
    }
1643

1644
    while ((start_upper < end_upper) ||
66✔
1645
           (start_upper == end_upper && start_lower <= end_lower)) {
60✔
1646
        uint64_to_be_octets(start_upper, in6_addr_tmp.s6_addr);
40✔
1647
        uint64_to_be_octets(start_lower, in6_addr_tmp.s6_addr + 8);
40✔
1648
        inet_ntop(AF_INET6, &in6_addr_tmp, buffer, sizeof(buffer));
40✔
1649
        if (scope_str) {
40✔
1650
            if (strlen(buffer) + strlen(scope_str) + 1 > sizeof(buffer)) {
6✔
1651
                fprintf(stderr, "%s: scope identifier is too long\n", prog);
×
1652
                exit(1);
×
1653
            }
1654
            strncat(buffer, scope_str, sizeof(buffer) - strlen(buffer) - 1);
6✔
1655
        }
1656
        add_name(buffer);
40✔
1657
        start_lower++;
40✔
1658
        if (start_lower == 0) {
40✔
1659
            start_upper++;
2✔
1660
        }
1661
    }
1662
}
26✔
1663
#endif /* IPv6 */
1664

1665
void main_loop()
397✔
1666
{
1667
    int64_t lt;
1668
    int64_t wait_time_ns;
1669
    struct event *event;
1670
    struct host_entry *h;
1671

1672
    while (event_queue_ping.first || event_queue_timeout.first) {
2,140✔
1673
        dbg_printf("%s", "# main_loop\n");
1674

1675
        /* timeout event ? */
1676
        if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
1,774✔
1677
            event = ev_dequeue(&event_queue_timeout);
101✔
1678
            h = event->host;
101✔
1679

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

1682
            stats_add(h, event->ping_index, 0, -1);
101✔
1683

1684
            if (opt_per_recv_on) {
101✔
1685
                print_timeout(h, event->ping_index);
25✔
1686
            }
7✔
1687

1688
            /* do we need to send a retry? */
1689
            if (!opt_loop_on && !opt_count_on) {
101✔
1690
                if (h->num_sent < opt_retry + 1) {
60✔
1691
                    if (opt_backoff_on) {
35✔
1692
                        h->timeout *= opt_backoff;
35✔
1693
                    }
9✔
1694
                    send_ping(h, event->ping_index);
35✔
1695
                }
9✔
1696
            }
14✔
1697

1698
            /* note: we process first timeout events, because we might need to
1699
             * wait to process ping events, while we for sure never need to
1700
             * wait for timeout events.
1701
             */
1702
            continue;
101✔
1703
        }
1704

1705
        /* ping event ? */
1706
        if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
1,936✔
1707
            /* Make sure that we don't ping more than once every "interval" */
1708
            lt = current_time_ns - last_send_time;
1,265✔
1709
            if (lt < opt_interval)
1,265✔
1710
                goto wait_for_reply;
256✔
1711

1712
            /* Dequeue the event */
1713
            event = ev_dequeue(&event_queue_ping);
1,009✔
1714
            h = event->host;
1,009✔
1715

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

1718
            /* Send the ping */
1719
            send_ping(h, event->ping_index);
1,009✔
1720

1721
            /* Loop and count mode: schedule next ping */
1722
            if (opt_loop_on || (opt_count_on && event->ping_index + 1 < opt_count)) {
1,009✔
1723
                host_add_ping_event(h, event->ping_index + 1, event->ev_time + opt_perhost_interval);
439✔
1724
            }
115✔
1725
        }
263✔
1726

1727
    wait_for_reply:
743✔
1728

1729
        /* When is the next ping next event? */
1730
        wait_time_ns = -1;
1,673✔
1731
        if (event_queue_ping.first) {
1,673✔
1732
            wait_time_ns = event_queue_ping.first->ev_time - current_time_ns;
1,220✔
1733
            if (wait_time_ns < 0)
1,220✔
1734
                wait_time_ns = 0;
540✔
1735
            /* make sure that we wait enough, so that the inter-ping delay is
1736
             * bigger than 'interval' */
1737
            if (wait_time_ns < opt_interval) {
1,220✔
1738
                lt = current_time_ns - last_send_time;
543✔
1739
                if (lt < opt_interval) {
543✔
1740
                    wait_time_ns = opt_interval - lt;
542✔
1741
                }
186✔
1742
            }
186✔
1743

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

1747
        /* When is the next timeout event? */
1748
        if (event_queue_timeout.first) {
1,673✔
1749
            int64_t wait_time_timeout = event_queue_timeout.first->ev_time - current_time_ns;
1,059✔
1750
            if (wait_time_ns < 0 || wait_time_timeout < wait_time_ns) {
1,059✔
1751
                wait_time_ns = wait_time_timeout;
448✔
1752
                if (wait_time_ns < 0) {
448✔
1753
                    wait_time_ns = 0;
×
1754
                }
1755
            }
111✔
1756

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

1760
        /* When is the next report due? */
1761
        if (report_interval && (opt_loop_on || opt_count_on)) {
1,673✔
1762
            int64_t wait_time_next_report = next_report_time - current_time_ns;
214✔
1763
            if (wait_time_next_report < wait_time_ns) {
214✔
1764
                wait_time_ns = wait_time_next_report;
50✔
1765
                if (wait_time_ns < 0) {
50✔
1766
                    wait_time_ns = 0;
×
1767
                }
1768
            }
2✔
1769

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

1773
        /* if wait_time is still -1, it means that we are waiting for nothing... */
1774
        if (wait_time_ns == -1) {
1,673✔
1775
            break;
14✔
1776
        }
1777

1778
        /* end of loop was requested by interrupt signal handler */
1779
        if (finish_requested) {
1,659✔
1780
            break;
17✔
1781
        }
1782

1783
        /* Receive replies */
1784
        /* (this is what sleeps during each loop iteration) */
1785
        dbg_printf("waiting up to %.0f ms\n", wait_time_ns / 1e6);
1786
        if (wait_for_reply(wait_time_ns)) {
1,642✔
1787
            while (wait_for_reply(0))
1,500✔
1788
                ; /* process other replies in the queue */
1789
        }
252✔
1790

1791
        update_current_time();
1,642✔
1792

1793
        if (status_snapshot) {
1,642✔
1794
            status_snapshot = 0;
3✔
1795
            if (opt_print_json_on)
3✔
1796
                print_per_system_splits_json();
×
1797
            else
1798
                print_per_system_splits();
3✔
1799
        }
1✔
1800

1801
        /* Print report */
1802
        if (report_interval && (opt_loop_on || opt_count_on) && (current_time_ns >= next_report_time)) {
1,642✔
1803
            if (opt_print_netdata_on) {
36✔
1804
                print_netdata();
3✔
1805
            }
1✔
1806
            else if (opt_print_json_on) {
33✔
1807
                print_per_system_splits_json();
3✔
1808
            }
1✔
1809
            else {
1810
                print_per_system_splits();
30✔
1811
            }
1812

1813
            while (current_time_ns >= next_report_time) {
72✔
1814
                next_report_time += report_interval;
36✔
1815
            }
1816
        }
2✔
1817
    }
1818
}
397✔
1819

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

1822
  Function: signal_handler
1823

1824
*************************************************************
1825

1826
  Inputs:  int signum
1827

1828
  Description:
1829

1830
  SIGQUIT signal handler - set flag and return
1831
  SIGINT signal handler - set flag and return
1832

1833
************************************************************/
1834

1835
void signal_handler(int signum)
17✔
1836
{
1837
    switch (signum) {
17✔
1838
    case SIGINT:
10✔
1839
        finish_requested = 1;
14✔
1840
        break;
14✔
1841

1842
    case SIGQUIT:
2✔
1843
        status_snapshot = 1;
3✔
1844
        break;
3✔
1845
    }
1846
}
17✔
1847

1848
/************************************************************
1849

1850
  Function: update_current_time
1851

1852
*************************************************************/
1853

1854
void update_current_time()
5,557✔
1855
{
1856
    clock_gettime(CLOCKID, &current_time);
5,557✔
1857
    current_time_ns = timespec_ns(&current_time);
5,557✔
1858
}
5,557✔
1859

1860
/************************************************************
1861

1862
  Function: finish
1863

1864
*************************************************************
1865

1866
  Inputs:  void (none)
1867

1868
  Description:
1869

1870
  Main program clean up and exit point
1871

1872
************************************************************/
1873

1874
void finish()
397✔
1875
{
1876
    int i;
1877
    HOST_ENTRY *h;
1878

1879
    update_current_time();
397✔
1880
    end_time = current_time_ns;
397✔
1881

1882
    /* tot up unreachables */
1883
    for (i = 0; i < num_hosts; i++) {
996✔
1884
        h = table[i];
599✔
1885

1886
        if (!h->num_recv) {
599✔
1887
            num_unreachable++;
87✔
1888

1889
            if (opt_verbose_on || opt_unreachable_on) {
87✔
1890
                printf("%s", h->host);
47✔
1891

1892
                if (opt_verbose_on)
47✔
1893
                    printf(" is unreachable");
44✔
1894

1895
                printf("\n");
47✔
1896
            }
3✔
1897
        }
13✔
1898
    }
157✔
1899

1900
    if (opt_count_on || opt_loop_on) {
397✔
1901
        if (opt_print_json_on)
171✔
1902
            print_per_system_stats_json();
59✔
1903
        else
1904
            print_per_system_stats();
112✔
1905
    }
45✔
1906
#if defined(DEBUG) || defined(_DEBUG)
1907
    else if (opt_debug_print_per_system_on) {
1908
        if (opt_print_json_on)
1909
            print_per_system_stats_json();
1910
        else
1911
            print_per_system_stats();
1912
    }
1913
#endif /* DEBUG || _DEBUG */
1914

1915
    if (opt_stats_on) {
397✔
1916
        if (opt_print_json_on)
12✔
1917
            print_global_stats_json();
3✔
1918
        else
1919
            print_global_stats();
9✔
1920
    }
4✔
1921

1922
    if (opt_min_reachable) {
397✔
1923
        if ((num_hosts - num_unreachable) >= opt_min_reachable) {
9✔
1924
            printf("Enough hosts reachable (required: %d, reachable: %d)\n", opt_min_reachable, num_hosts - num_unreachable);
6✔
1925
            exit(0);
6✔
1926
        }
1927
        else {
1928
            printf("Not enough hosts reachable (required: %d, reachable: %d)\n", opt_min_reachable, num_hosts - num_unreachable);
3✔
1929
            exit(1);
3✔
1930
        }
1931
    }
1932

1933
    if (num_noaddress)
388✔
1934
        exit(2);
3✔
1935
    else if (num_alive != num_hosts)
385✔
1936
        exit(1);
48✔
1937

1938
    exit(0);
337✔
1939
}
1940

1941
/************************************************************
1942

1943
  Function: send_ping
1944

1945
*************************************************************
1946

1947
  Inputs:  int s, HOST_ENTRY *h
1948

1949
  Description:
1950

1951
  Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
1952
  will be added on by the kernel.  The ID field is our UNIX process ID,
1953
  and the sequence number is an index into an array of outstanding
1954
  ping requests. The sequence number will later be used to quickly
1955
  figure out who the ping reply came from.
1956

1957
************************************************************/
1958

1959
int send_ping(HOST_ENTRY *h, int index)
1,044✔
1960
{
1961
    int n;
1962
    int myseq;
1963
    int ret = 1;
1,044✔
1964
    uint8_t proto = ICMP_ECHO;
1,044✔
1965

1966
    update_current_time();
1,044✔
1967
    h->last_send_time = current_time_ns;
1,044✔
1968
    myseq = seqmap_add(h->i, index, current_time_ns);
1,044✔
1969

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

1972
    if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
1,044✔
1973
        if(opt_icmp_request_typ == 13)
921✔
1974
            proto = ICMP_TSTAMP;
18✔
1975
        n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
921✔
1976
    }
257✔
1977
#ifdef IPV6
1978
    else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
123✔
1979
        n = socket_sendto_ping_ipv6(socket6, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident6);
123✔
1980
    }
15✔
1981
#endif
1982
    else {
1983
        return 0;
×
1984
    }
1985

1986
    /* error sending? */
1987
    if (
772✔
1988
        (n < 0)
272✔
1989
#if defined(EHOSTDOWN)
1990
        && errno != EHOSTDOWN
306✔
1991
#endif
1992
    ) {
1993
        if (opt_verbose_on) {
34✔
1994
            print_warning("%s: error while sending ping: %s\n", h->host, strerror(errno));
28✔
1995
        }
1996
        else {
1997
            dbg_printf("%s: error while sending ping: %s\n", h->host, strerror(errno));
1998
        }
1999

2000
        h->num_sent++;
34✔
2001
        h->num_sent_i++;
34✔
2002
        if (!opt_loop_on)
34✔
2003
            h->resp_times[index] = RESP_ERROR;
34✔
2004

2005
        ret = 0;
34✔
2006
    }
2007
    else {
2008
        /* schedule timeout */
2009
        host_add_timeout_event(h, index, current_time_ns + h->timeout);
1,010✔
2010

2011
        /* mark this trial as outstanding */
2012
        if (!opt_loop_on) {
1,010✔
2013
            h->resp_times[index] = RESP_WAITING;
877✔
2014
        }
239✔
2015
    }
2016

2017
    num_pingsent++;
1,044✔
2018
    last_send_time = h->last_send_time;
1,044✔
2019

2020
    return (ret);
1,044✔
2021
}
272✔
2022

2023
int socket_can_read(struct timeval *timeout)
1,642✔
2024
{
2025
    int nfound;
2026
    fd_set readset;
2027
    int socketmax;
2028

2029
#ifndef IPV6
2030
    socketmax = socket4;
2031
#else
2032
    socketmax = socket4 > socket6 ? socket4 : socket6;
1,642✔
2033
#endif
2034

2035
select_again:
1,219✔
2036
    FD_ZERO(&readset);
21,163✔
2037
    if (socket4 >= 0)
1,659✔
2038
        FD_SET(socket4, &readset);
1,659✔
2039
#ifdef IPV6
2040
    if (socket6 >= 0)
1,659✔
2041
        FD_SET(socket6, &readset);
1,659✔
2042
#endif
2043

2044
    nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
1,659✔
2045
    if (nfound < 0) {
1,659✔
2046
        if (errno == EINTR) {
17✔
2047
            /* interrupted system call: redo the select */
2048
            goto select_again;
17✔
2049
        }
2050
        else {
2051
            perror("select");
×
2052
        }
2053
    }
2054

2055
    if (nfound > 0) {
1,642✔
2056
        if (socket4 >= 0 && FD_ISSET(socket4, &readset)) {
920✔
2057
            return socket4;
825✔
2058
        }
2059
#ifdef IPV6
2060
        if (socket6 >= 0 && FD_ISSET(socket6, &readset)) {
95✔
2061
            return socket6;
95✔
2062
        }
2063
#endif
2064
    }
2065

2066
    return -1;
722✔
2067
}
435✔
2068

2069
int receive_packet(int64_t wait_time,
3,142✔
2070
#if HAVE_SO_TIMESTAMPNS
2071
    int64_t *reply_timestamp,
2072
#else
2073
    int64_t *reply_timestamp __attribute__((unused)),
2074
#endif
2075
    struct sockaddr *reply_src_addr,
2076
    size_t reply_src_addr_len,
2077
    char *reply_buf,
2078
    size_t reply_buf_len,
2079
    int *ip_header_tos,
2080
    int *ip_header_ttl)
2081
{
2082
    struct timeval to;
2083
    int s = 0;
3,142✔
2084
    int recv_len;
2085
    static unsigned char msg_control[128];
2086
    struct iovec msg_iov = {
4,516✔
2087
        reply_buf,
687✔
2088
        reply_buf_len
687✔
2089
    };
2090
    struct msghdr recv_msghdr = {0};
3,142✔
2091
    recv_msghdr.msg_name = reply_src_addr;
3,142✔
2092
    recv_msghdr.msg_namelen = reply_src_addr_len;
3,142✔
2093
    recv_msghdr.msg_iov = &msg_iov;
3,142✔
2094
    recv_msghdr.msg_iovlen = 1;
3,142✔
2095
    recv_msghdr.msg_control = &msg_control;
3,142✔
2096
    recv_msghdr.msg_controllen = sizeof(msg_control);
3,142✔
2097
    struct cmsghdr *cmsg;
2098

2099
    /* Wait for a socket to become ready */
2100
#if HAVE_MSG_DONTWAIT
2101
    if (wait_time == 0) {
3,142✔
2102
        /* Optimization: if wait_time is 0, we can skip select() and just try to
2103
         * read from the sockets with MSG_DONTWAIT */
2104
        recv_len = (socket4 >= 0) ? recvmsg(socket4, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT) : -1;
1,500✔
2105
#ifdef IPV6
2106
        if (recv_len <= 0 && socket6 >= 0) {
1,500✔
2107
            /* Reset fields potentially modified by failed recvmsg */
2108
            recv_msghdr.msg_namelen = reply_src_addr_len;
922✔
2109
            recv_msghdr.msg_controllen = sizeof(msg_control);
922✔
2110
            recv_len = recvmsg(socket6, &recv_msghdr, MSG_TRUNC | MSG_DONTWAIT);
922✔
2111
        }
252✔
2112
#endif
2113
        if (recv_len > 0) {
1,500✔
2114
            goto packet_received;
580✔
2115
        }
2116
        return 0;
920✔
2117
    }
2118
#endif
2119

2120
    int64_t wait_us = (wait_time + 999) / 1000; // round up (1 ns -> 1 us)
1,642✔
2121
    to.tv_sec = wait_us / 1000000;
1,642✔
2122
    to.tv_usec = wait_us % 1000000;
1,642✔
2123

2124
    s = socket_can_read(&to);
1,642✔
2125
    if (s == -1 || (recv_len = recvmsg(s, &recv_msghdr, MSG_TRUNC)) <= 0) {
1,642✔
2126
        return 0;
722✔
2127
    }
2128

2129
packet_received:
668✔
2130
    /* ancilliary data */
2131
    {
2132
#if HAVE_SO_TIMESTAMPNS
2133
        struct timespec reply_timestamp_ts;
2134
        struct timeval reply_timestamp_tv;
2135
#endif
2136
        for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
1,500✔
2137
             cmsg != NULL;
2,760✔
2138
             cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
1,260✔
2139
#if HAVE_SO_TIMESTAMPNS
2140
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
1,260✔
2141
                memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
1,248✔
2142
                *reply_timestamp = timespec_ns(&reply_timestamp_ts);
1,248✔
2143
            }
2144
            if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
1,260✔
2145
                memcpy(&reply_timestamp_tv, CMSG_DATA(cmsg), sizeof(reply_timestamp_tv));
2146
                *reply_timestamp = timeval_ns(&reply_timestamp_tv);
2147
            }
2148
#endif
2149
#if defined(HAVE_IP_RECVTOS)
2150
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
1,260✔
2151
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2152
            }
2153
#endif
2154
            if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) {
1,260✔
2155
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2156
            }
2157
#ifdef IPV6
2158
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS) {
1,260✔
2159
                memcpy(ip_header_tos, CMSG_DATA(cmsg), sizeof(*ip_header_tos));
2✔
2160
            }
2161
            if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
1,260✔
2162
                memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl));
2✔
2163
            }
2164
#endif
2165
        }
2166
    }
2167

2168
#if defined(DEBUG) || defined(_DEBUG)
2169
    if (opt_debug_randomly_lose_on) {
2170
        if ((random() & 0x07) <= lose_factor)
2171
            return 0;
2172
    }
2173
#endif
2174

2175
    return recv_len;
1,500✔
2176
}
687✔
2177

2178
/* stats_add: update host statistics for a single packet that was received (or timed out)
2179
 * h: host entry to update
2180
 * index: if in count mode: index number for this ping packet (-1 otherwise)
2181
 * success: 1 if response received, 0 otherwise
2182
 * latency: response time, in ns
2183
 */
2184
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency)
996✔
2185
{
2186
    /* sent count - we update only on receive/timeout, so that we don't get
2187
     * weird loss percentage, just because a packet was note recived yet.
2188
     */
2189
    h->num_sent++;
996✔
2190
    h->num_sent_i++;
996✔
2191

2192
    if (!success) {
996✔
2193
        if (!opt_loop_on && index >= 0) {
101✔
2194
            h->resp_times[index] = RESP_TIMEOUT;
101✔
2195
        }
21✔
2196
        num_timeout++;
101✔
2197
        return;
101✔
2198
    }
2199

2200
    /* received count */
2201
    h->num_recv++;
895✔
2202
    h->num_recv_i++;
895✔
2203

2204
    /* maximum */
2205
    if (!h->max_reply || latency > h->max_reply) {
895✔
2206
        h->max_reply = latency;
688✔
2207
    }
189✔
2208
    if (!h->max_reply_i || latency > h->max_reply_i) {
895✔
2209
        h->max_reply_i = latency;
716✔
2210
    }
192✔
2211

2212
    /* minimum */
2213
    if (!h->min_reply || latency < h->min_reply) {
895✔
2214
        h->min_reply = latency;
549✔
2215
    }
164✔
2216
    if (!h->min_reply_i || latency < h->min_reply_i) {
895✔
2217
        h->min_reply_i = latency;
578✔
2218
    }
166✔
2219

2220
    /* total time (for average) */
2221
    h->total_time += latency;
895✔
2222
    h->total_time_i += latency;
895✔
2223

2224
    /* response time per-packet (count mode) */
2225
    if (!opt_loop_on && index >= 0) {
895✔
2226
        h->resp_times[index] = latency;
776✔
2227
    }
218✔
2228
}
268✔
2229

2230
/* stats_reset_interval: reset interval statistics
2231
 * h: host entry to update
2232
 */
2233
void stats_reset_interval(HOST_ENTRY *h)
31✔
2234
{
2235
    h->num_sent_i = 0;
31✔
2236
    h->num_recv_i = 0;
31✔
2237
    h->max_reply_i = 0;
31✔
2238
    h->min_reply_i = 0;
31✔
2239
    h->total_time_i = 0;
31✔
2240
}
31✔
2241

2242
int decode_icmp_ipv4(
1,403✔
2243
    struct sockaddr *response_addr,
2244
    size_t response_addr_len,
2245
    char *reply_buf,
2246
    size_t reply_buf_len,
2247
    unsigned short *id,
2248
    unsigned short *seq,
2249
    IP_HEADER_RESULT *ip_header_res)
2250
{
2251
    struct icmp *icp;
2252
    int hlen = 0;
1,403✔
2253
    struct ip *ip = NULL;
1,403✔
2254

2255
    if (!using_sock_dgram4) {
1,403✔
2256
        ip = (struct ip *)reply_buf;
1,393✔
2257
        ip_header_res->tos = ip->ip_tos;
1,393✔
2258
        ip_header_res->ttl = ip->ip_ttl;
1,393✔
2259

2260
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
2261
        /* The alpha headers are decidedly broken.
2262
         * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
2263
         * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
2264
         */
2265
        hlen = (ip->ip_vhl & 0x0F) << 2;
2266
#else
2267
        hlen = ip->ip_hl << 2;
1,393✔
2268
#endif
2269
    }
237✔
2270

2271
    if (reply_buf_len < hlen + ICMP_MINLEN) {
1,403✔
2272
        /* too short */
2273
        if (opt_verbose_on) {
×
2274
            char buf[INET6_ADDRSTRLEN];
2275
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2276
            printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2277
        }
2278
        return -1;
×
2279
    }
2280

2281
    icp = (struct icmp *)(reply_buf + hlen);
1,403✔
2282

2283
    if ((opt_icmp_request_typ == 0 && icp->icmp_type != ICMP_ECHOREPLY) ||
1,403✔
2284
        (opt_icmp_request_typ == 13 && icp->icmp_type != ICMP_TSTAMPREPLY)) {
604✔
2285
        /* Handle other ICMP packets */
2286
        struct icmp *sent_icmp;
2287
        SEQMAP_VALUE *seqmap_value;
2288
        char addr_ascii[INET6_ADDRSTRLEN];
2289
        HOST_ENTRY *h;
2290

2291
        /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */
2292
        if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) {
1,051✔
2293
            /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */
2294
            return -1;
×
2295
        }
2296

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

2299
        if ((opt_icmp_request_typ == 0 && sent_icmp->icmp_type != ICMP_ECHO) ||
579✔
2300
            (opt_icmp_request_typ == 13 && sent_icmp->icmp_type != ICMP_TSTAMP) ||
16✔
2301
            sent_icmp->icmp_id != ident4) {
×
2302
            /* not caused by us */
2303
            return -1;
579✔
2304
        }
2305

2306
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), current_time_ns);
×
2307
        if (seqmap_value == NULL) {
×
2308
            return -1;
×
2309
        }
2310

2311
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2312

2313
        switch (icp->icmp_type) {
×
2314
        case ICMP_UNREACH:
2315
            h = table[seqmap_value->host_nr];
×
2316
            if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) {
×
2317
                print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s",
×
2318
                    addr_ascii, h->host);
2319
            }
2320
            else {
2321
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2322
                    icmp_unreach_str[icp->icmp_code], addr_ascii, h->host);
×
2323
            }
2324

2325
            print_warning("\n");
×
2326
            num_othericmprcvd++;
×
2327
            break;
×
2328

2329
        case ICMP_SOURCEQUENCH:
2330
        case ICMP_REDIRECT:
2331
        case ICMP_TIMXCEED:
2332
        case ICMP_PARAMPROB:
2333
            h = table[seqmap_value->host_nr];
×
2334
            if (icp->icmp_type <= ICMP_TYPE_STR_MAX) {
×
2335
                print_warning("%s from %s for ICMP Echo sent to %s",
×
2336
                    icmp_type_str[icp->icmp_type], addr_ascii, h->host);
×
2337
            }
2338
            else {
2339
                print_warning("ICMP %d from %s for ICMP Echo sent to %s",
×
2340
                    icp->icmp_type, addr_ascii, h->host);
×
2341
            }
2342
            print_warning("\n");
×
2343
            num_othericmprcvd++;
×
2344
            break;
×
2345
        }
2346

2347
        return -1;
×
2348
    }
2349

2350
    *id = icp->icmp_id;
824✔
2351
    *seq = ntohs(icp->icmp_seq);
824✔
2352
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
824✔
2353

2354
        /* Check that reply_buf_len is sufficiently big to contain the timestamps */
2355
        if (reply_buf_len < hlen + ICMP_MINLEN + ICMP_TIMESTAMP_DATA_SIZE) {
16✔
2356
            if (opt_verbose_on) {
×
2357
                char buf[INET6_ADDRSTRLEN];
2358
                getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2359
                printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2360
            }
2361
            return -1;
×
2362
        }
2363

2364
        ip_header_res->otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
16✔
2365
        ip_header_res->rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
16✔
2366
        ip_header_res->ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
16✔
2367
    }
2368

2369
    if (opt_print_srcaddr_on) {
824✔
2370
        if (ip == NULL || inet_ntop(AF_INET, &ip->ip_dst, ip_header_res->src_addr, sizeof(ip_header_res->src_addr)) == NULL) {
5✔
2371
            strncpy(ip_header_res->src_addr, "unknown", sizeof(ip_header_res->src_addr) - 1);
2✔
2372
            ip_header_res->src_addr[sizeof(ip_header_res->src_addr) - 1] = '\0';
2✔
2373
        }
2374
    }
1✔
2375

2376
    return hlen;
824✔
2377
}
237✔
2378

2379
#ifdef IPV6
2380
int decode_icmp_ipv6(
97✔
2381
    struct sockaddr *response_addr,
2382
    size_t response_addr_len,
2383
    char *reply_buf,
2384
    size_t reply_buf_len,
2385
    unsigned short *id,
2386
    unsigned short *seq,
2387
    IP_HEADER_RESULT *ip_header_res)
2388
{
2389
    struct icmp6_hdr *icp;
2390

2391
    if (reply_buf_len < sizeof(struct icmp6_hdr)) {
97✔
2392
        if (opt_verbose_on) {
×
2393
            char buf[INET6_ADDRSTRLEN];
2394
            getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2395
            printf("received packet too short for ICMPv6 (%d bytes from %s)\n", (int)reply_buf_len, buf);
×
2396
        }
2397
        return 0; /* too short */
×
2398
    }
2399

2400
    icp = (struct icmp6_hdr *)reply_buf;
97✔
2401

2402
    if (icp->icmp6_type != ICMP6_ECHO_REPLY) {
97✔
2403
        /* Handle other ICMPv6 packets */
2404
        struct ip6_hdr *sent_ipv6;
2405
        struct icmp6_hdr *sent_icmp;
2406
        SEQMAP_VALUE *seqmap_value;
2407
        char addr_ascii[INET6_ADDRSTRLEN];
2408
        HOST_ENTRY *h;
2409

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

2416
        sent_ipv6 = (struct ip6_hdr *)(reply_buf + sizeof(struct icmp6_hdr));
6✔
2417
        if (sent_ipv6->ip6_nxt != IPPROTO_ICMPV6) {
6✔
2418
            /* discard ICMPv6 message if we can't tell that it was caused by
2419
             * us, because the IPv6 header is not directly followed by an
2420
             * ICMPv6 header
2421
             */
2422
            dbg_printf("invoking packet next header is %d\n", sent_ipv6->ip6_nxt);
2423
            return 0;
×
2424
        }
2425
        sent_icmp = (struct icmp6_hdr *)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr));
6✔
2426

2427
        if (sent_icmp->icmp6_type != ICMP6_ECHO_REQUEST || sent_icmp->icmp6_id != ident6) {
6✔
2428
            /* not caused by us */
2429
            return 0;
6✔
2430
        }
2431

2432
        seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), current_time_ns);
×
2433
        if (seqmap_value == NULL) {
×
2434
            return 0;
×
2435
        }
2436

2437
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2438
        h = table[seqmap_value->host_nr];
×
2439

2440
        switch (icp->icmp6_type) {
×
2441
        case ICMP6_DST_UNREACH:
2442
            if (icp->icmp6_code > ICMP6_UNREACH_MAXCODE) {
×
2443
                print_warning("ICMPv6 Destination Unreachable (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2444
                    icp->icmp6_code, addr_ascii, h->host);
×
2445
            } else {
2446
                print_warning("ICMPv6 Destination Unreachable (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2447
                    icmp6_unreach_str[icp->icmp6_code], addr_ascii, h->host);
×
2448
            }
2449
            print_warning("\n");
×
2450
            num_othericmprcvd++;
×
2451
            break;
×
2452

2453
        case ICMP6_PACKET_TOO_BIG:
2454
            print_warning("ICMPv6 Packet Too Big from %s for ICMPv6 Echo Request sent to %s\n",
×
2455
                addr_ascii, h->host);
2456
            num_othericmprcvd++;
×
2457
            break;
×
2458

2459
        case ICMP6_TIME_EXCEEDED:
2460
            if (icp->icmp6_code > ICMP6_TIME_EXCEEDED_MAXCODE) {
×
2461
                print_warning("ICMPv6 Time Exceeded (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2462
                    icp->icmp6_code, addr_ascii, h->host);
×
2463
            } else {
2464
                print_warning("ICMPv6 Time Exceeded (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2465
                    icmp6_time_exceeded_str[icp->icmp6_code], addr_ascii, h->host);
×
2466
            }
2467
            print_warning("\n");
×
2468
            num_othericmprcvd++;
×
2469
            break;
×
2470

2471
        case ICMP6_PARAM_PROB:
2472
            if (icp->icmp6_code > ICMP6_PARAM_PROB_MAXCODE) {
×
2473
                print_warning("ICMPv6 Parameter Problem (Code %d) from %s for ICMPv6 Echo Request sent to %s",
×
2474
                    icp->icmp6_code, addr_ascii, h->host);
×
2475
            } else {
2476
                print_warning("ICMPv6 Parameter Problem (%s) from %s for ICMPv6 Echo Request sent to %s",
×
2477
                    icmp6_param_prob_str[icp->icmp6_code], addr_ascii, h->host);
×
2478
            }
2479
            print_warning("\n");
×
2480
            num_othericmprcvd++;
×
2481
            break;
×
2482

2483
        default:
2484
            print_warning("ICMPv6 Type %d Code %d from %s for ICMPv6 Echo Request sent to %s\n",
×
2485
                icp->icmp6_type, icp->icmp6_code, addr_ascii, h->host);
×
2486
            num_othericmprcvd++;
×
2487
            break;
×
2488
        }
2489

2490
        return 0;
×
2491
    }
2492

2493
    *id = icp->icmp6_id;
91✔
2494
    *seq = ntohs(icp->icmp6_seq);
91✔
2495

2496
    if (opt_print_srcaddr_on) {
91✔
NEW
2497
        strncpy(ip_header_res->src_addr, "not supported", sizeof(ip_header_res->src_addr) - 1);
×
NEW
2498
        ip_header_res->src_addr[sizeof(ip_header_res->src_addr) - 1] = '\0';
×
2499
    }
2500

2501
    return 1;
91✔
2502
}
15✔
2503
#endif
2504

2505
int wait_for_reply(int64_t wait_time)
3,142✔
2506
{
2507
    int result;
2508
    static char buffer[RECV_BUFSIZE];
2509
    struct sockaddr_storage response_addr;
2510
    int n, avg;
2511
    HOST_ENTRY *h;
2512
    int64_t this_reply;
2513
    int this_count;
2514
    int64_t recv_time = 0;
3,142✔
2515
    SEQMAP_VALUE *seqmap_value;
2516
    unsigned short id;
2517
    unsigned short seq;
2518
    IP_HEADER_RESULT ip_header_res = default_ip_header_result();
3,142✔
2519

2520
    /* Receive packet */
2521
    result = receive_packet(wait_time, /* max. wait time, in ns */
3,829✔
2522
        &recv_time, /* reply_timestamp */
2523
        (struct sockaddr *)&response_addr, /* reply_src_addr */
2524
        sizeof(response_addr), /* reply_src_addr_len */
2525
        buffer, /* reply_buf */
2526
        sizeof(buffer), /* reply_buf_len */
2527
        &ip_header_res.tos, /* TOS resp. TC byte */
687✔
2528
        &ip_header_res.ttl /* TTL resp. hop limit */
687✔
2529
    );
2530

2531
    if (result <= 0) {
3,142✔
2532
        return 0;
1,642✔
2533
    }
2534

2535
    update_current_time();
1,500✔
2536
    if (recv_time == 0)
1,500✔
2537
        recv_time = current_time_ns;
252✔
2538

2539
    /* Process ICMP packet and retrieve id/seq */
2540
    if (response_addr.ss_family == AF_INET) {
1,500✔
2541
        int ip_hlen = decode_icmp_ipv4(
1,403✔
2542
            (struct sockaddr *)&response_addr,
2543
            sizeof(response_addr),
2544
            buffer,
2545
            sizeof(buffer),
2546
            &id,
2547
            &seq,
2548
            &ip_header_res);
2549
        if (ip_hlen < 0) {
1,403✔
2550
            return 1;
579✔
2551
        }
2552
        if (id != ident4) {
824✔
2553
            return 1; /* packet received, but not the one we are looking for! */
×
2554
        }
2555
        if (!using_sock_dgram4) {
824✔
2556
            /* do not include IP header in returned size, to be consistent with ping(8) and also
2557
             * with fping with IPv6 hosts */
2558
            result -= ip_hlen;
814✔
2559
        }
236✔
2560
    }
236✔
2561
#ifdef IPV6
2562
    else if (response_addr.ss_family == AF_INET6) {
97✔
2563
        if (!decode_icmp_ipv6(
97✔
2564
                (struct sockaddr *)&response_addr,
2565
                sizeof(response_addr),
2566
                buffer,
2567
                sizeof(buffer),
2568
                &id,
2569
                &seq,
2570
                &ip_header_res)) {
2571
            return 1;
6✔
2572
        }
2573
        if (id != ident6) {
91✔
2574
            return 1; /* packet received, but not the one we are looking for! */
×
2575
        }
2576
    }
15✔
2577
#endif
2578
    else {
2579
        return 1;
×
2580
    }
2581

2582
    seqmap_value = seqmap_fetch(seq, current_time_ns);
915✔
2583
    if (seqmap_value == NULL) {
915✔
2584
        return 1;
12✔
2585
    }
2586

2587
    /* find corresponding host_entry */
2588
    n = seqmap_value->host_nr;
903✔
2589
    h = table[n];
903✔
2590
    this_count = seqmap_value->ping_count;
903✔
2591
    this_reply = recv_time - seqmap_value->ping_ts;
903✔
2592

2593
    /* update stats that include invalid replies */
2594
    h->num_recv_total++;
903✔
2595
    num_pingreceived++;
903✔
2596

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

2599
    /* optionally require reply source equal to target address */
2600
    if (opt_check_source_on && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
903✔
2601
        dbg_printf("%s\n", "discarding reply from wrong source address");
2602
        return 1;
8✔
2603
    }
2604

2605
    /* discard duplicates */
2606
    if (!opt_loop_on && !(opt_count_on && opt_quiet_on) && h->resp_times[this_count] >= 0) {
895✔
2607
        if (!opt_per_recv_on) {
×
2608
            fprintf(stderr, "%s : duplicate for [%d], %d bytes, %s ms",
×
2609
                h->host, this_count, result, sprint_tm(this_reply));
2610

2611
            if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
×
2612
                char buf[INET6_ADDRSTRLEN];
2613
                getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2614
                fprintf(stderr, " [<- %s]", buf);
×
2615
            }
2616
            fprintf(stderr, "\n");
×
2617
        }
2618
        return 1;
×
2619
    }
2620

2621
    /* discard reply if delay is larger than timeout
2622
     * (see also: github #32) */
2623
    if (this_reply > h->timeout) {
895✔
2624
        return 1;
×
2625
    }
2626

2627
    /* update stats */
2628
    stats_add(h, this_count, 1, this_reply);
895✔
2629
    // TODO: move to stats_add?
2630
    if (!max_reply || this_reply > max_reply)
895✔
2631
        max_reply = this_reply;
547✔
2632
    if (!min_reply || this_reply < min_reply)
895✔
2633
        min_reply = this_reply;
470✔
2634
    sum_replies += this_reply;
895✔
2635
    total_replies++;
895✔
2636

2637
    /* initialize timeout to initial timeout (without backoff) */
2638
    h->timeout = opt_timeout;
895✔
2639

2640
    /* remove timeout event */
2641
    struct event *timeout_event = host_get_timeout_event(h, this_count);
895✔
2642
    if (timeout_event) {
895✔
2643
        ev_remove(&event_queue_timeout, timeout_event);
895✔
2644
    }
247✔
2645

2646
    /* print "is alive" */
2647
    if (h->num_recv == 1) {
895✔
2648
        num_alive++;
512✔
2649
        if (opt_fast_reachable_on && num_alive >= opt_min_reachable)
512✔
2650
            finish_requested = 1;
3✔
2651

2652
        if (opt_verbose_on || opt_alive_on) {
512✔
2653
            printf("%s", h->host);
308✔
2654

2655
            if (opt_verbose_on)
308✔
2656
                printf(" is alive");
288✔
2657
        }
86✔
2658
    }
144✔
2659

2660
    /* print received ping (unless --quiet) */
2661
    if (opt_per_recv_on) {
895✔
2662
        avg = h->total_time / h->num_recv;
281✔
2663
        print_recv(h,
368✔
2664
            recv_time,
87✔
2665
            result,
87✔
2666
            this_count,
87✔
2667
            this_reply,
87✔
2668
            avg);
87✔
2669
    }
87✔
2670

2671
    if (opt_verbose_on || opt_alive_on || opt_per_recv_on) {
895✔
2672
        if (addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) {
589✔
2673
            char buf[INET6_ADDRSTRLEN];
2674
            getnameinfo((struct sockaddr *)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
6✔
2675
            fprintf(stderr, " [<- %s]", buf);
6✔
2676
        }
2677
        if (opt_print_json_on) {
589✔
2678
            print_recv_ext_json(&ip_header_res,
38✔
2679
                recv_time,
12✔
2680
                this_reply);
12✔
2681
        }
12✔
2682
        else {
2683
            print_recv_ext(&ip_header_res,
551✔
2684
                recv_time,
161✔
2685
                this_reply);
161✔
2686
        }
2687
    }
173✔
2688
    return 1;
895✔
2689
}
687✔
2690

2691
/************************************************************
2692

2693
  Function: add_name
2694

2695
*************************************************************
2696

2697
  Inputs:  char* name
2698

2699
  Description:
2700

2701
  process input name for addition to target list
2702
  name can turn into multiple targets via multiple interfaces (-m)
2703
  or via NIS groups
2704

2705
************************************************************/
2706

2707
void add_name(char *name)
633✔
2708
{
2709
    struct addrinfo *res0, *res, hints;
2710
    int ret_ga;
2711
    char *printname;
2712
    char namebuf[256];
2713
    char addrbuf[256];
2714

2715
    /* getaddrinfo */
2716
    memset(&hints, 0, sizeof(struct addrinfo));
633✔
2717
    hints.ai_flags = AI_UNUSABLE;
633✔
2718
    hints.ai_socktype = SOCK_RAW;
633✔
2719
    hints.ai_family = hints_ai_family;
633✔
2720
    if (hints_ai_family == AF_INET) {
633✔
2721
        hints.ai_protocol = IPPROTO_ICMP;
81✔
2722
    }
21✔
2723
#ifdef IPV6
2724
    else if (hints_ai_family == AF_INET6) {
552✔
2725
        hints.ai_protocol = IPPROTO_ICMPV6;
35✔
2726
    }
1✔
2727
#endif
2728
    else {
2729
        hints.ai_socktype = SOCK_STREAM;
517✔
2730
        hints.ai_protocol = 0;
517✔
2731
    }
2732
    ret_ga = getaddrinfo(name, NULL, &hints, &res0);
633✔
2733
    if (ret_ga) {
633✔
2734
        if (!opt_quiet_on)
29✔
2735
            print_warning("%s: %s\n", name, gai_strerror(ret_ga));
26✔
2736
        num_noaddress++;
29✔
2737

2738
        // Handle JSON output for invalid hosts
2739
        if (opt_print_json_on) {
29✔
2740
            fprintf(stdout, "{\"warning\": {\"host\": \"%s\", \"message\": \"%s\"}}\n", name, gai_strerror(ret_ga));
3✔
2741
            return;
21✔
2742
        }
2743

2744
        return;
26✔
2745
    }
2746

2747
    /* NOTE: we could/should loop with res on all addresses like this:
2748
     * for (res = res0; res; res = res->ai_next) {
2749
     * We don't do it yet, however, because is is an incompatible change
2750
     * (need to implement a separate option for this)
2751
     */
2752
    for (res = res0; res; res = res->ai_next) {
604✔
2753
        /* opt_name_on: addr -> name lookup requested) */
2754
        if (opt_name_on || opt_rdns_on) {
606✔
2755
            int do_rdns = opt_rdns_on ? 1 : 0;
3✔
2756
            if (opt_name_on) {
3✔
2757
                /* Was it a numerical address? Only then do a rdns-query */
2758
                struct addrinfo *nres;
2759
                hints.ai_flags = AI_NUMERICHOST;
×
2760
                if (getaddrinfo(name, NULL, &hints, &nres) == 0) {
×
2761
                    do_rdns = 1;
×
2762
                    freeaddrinfo(nres);
×
2763
                }
2764
            }
2765

2766
            if (do_rdns && getnameinfo(res->ai_addr, res->ai_addrlen, namebuf, sizeof(namebuf) / sizeof(char), NULL, 0, 0) == 0) {
3✔
2767
                printname = namebuf;
3✔
2768
            }
1✔
2769
            else {
2770
                printname = name;
×
2771
            }
2772
        }
1✔
2773
        else {
2774
            printname = name;
601✔
2775
        }
2776

2777
        /* opt_addr_on: name -> addr lookup requested */
2778
        if (opt_addr_on) {
604✔
2779
            int ret;
2780
            ret = getnameinfo(res->ai_addr, res->ai_addrlen, addrbuf,
3✔
2781
                sizeof(addrbuf) / sizeof(char), NULL, 0, NI_NUMERICHOST);
2782
            if (ret) {
3✔
2783
                if (!opt_quiet_on) {
×
2784
                    print_warning("%s: can't forward-lookup address (%s)\n", name, gai_strerror(ret));
×
2785
                }
2786
                continue;
×
2787
            }
2788

2789
            if (opt_name_on || opt_rdns_on) {
3✔
2790
                char nameaddrbuf[512 + 3];
2791
                snprintf(nameaddrbuf, sizeof(nameaddrbuf) / sizeof(char), "%s (%s)", printname, addrbuf);
×
2792
                add_addr(name, nameaddrbuf, res->ai_addr, res->ai_addrlen);
×
2793
            }
2794
            else {
2795
                add_addr(name, addrbuf, res->ai_addr, res->ai_addrlen);
3✔
2796
            }
2797
        }
1✔
2798
        else {
2799
            add_addr(name, printname, res->ai_addr, res->ai_addrlen);
601✔
2800
        }
2801

2802
        if (!opt_multif_on) {
604✔
2803
            break;
604✔
2804
        }
2805
    }
2806

2807
    freeaddrinfo(res0);
604✔
2808
}
167✔
2809

2810
/************************************************************
2811

2812
  Function: add_addr
2813

2814
*************************************************************
2815

2816
  Description:
2817

2818
  add single address to list of hosts to be pinged
2819

2820
************************************************************/
2821

2822
void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len)
604✔
2823
{
2824
    HOST_ENTRY *p;
2825
    int n;
2826
    int64_t *i;
2827

2828
    p = (HOST_ENTRY *)calloc(1, sizeof(HOST_ENTRY));
604✔
2829
    if (!p)
604✔
2830
        crash_and_burn("can't allocate HOST_ENTRY");
×
2831

2832
    p->name = strdup(name);
604✔
2833
    p->host = strdup(host);
604✔
2834
    memcpy(&p->saddr, ipaddr, ipaddr_len);
604✔
2835
    p->saddr_len = ipaddr_len;
604✔
2836
    p->timeout = opt_timeout;
604✔
2837
    p->min_reply = 0;
604✔
2838

2839
    if (opt_print_netdata_on) {
604✔
2840
        char *s = p->name;
3✔
2841
        while (*s) {
30✔
2842
            if (!isalnum(*s))
27✔
2843
                *s = '_';
9✔
2844
            s++;
27✔
2845
        }
2846
    }
1✔
2847

2848
    if (strlen(p->host) > max_hostname_len)
604✔
2849
        max_hostname_len = strlen(p->host);
411✔
2850

2851
    /* array for response time results */
2852
    if (!opt_loop_on) {
604✔
2853
#if SIZE_MAX <= UINT_MAX
2854
        if (trials > (SIZE_MAX / sizeof(int64_t)))
2855
            crash_and_burn("resp_times array too large for memory");
2856
#endif
2857
        i = (int64_t *)malloc(trials * sizeof(int64_t));
590✔
2858
        if (!i)
590✔
2859
            crash_and_burn("can't allocate resp_times array");
×
2860

2861
        for (n = 0; n < trials; n++)
2,931✔
2862
            i[n] = RESP_UNUSED;
2,341✔
2863

2864
        p->resp_times = i;
590✔
2865
    }
154✔
2866

2867
    /* allocate event storage */
2868
    p->event_storage_ping = (struct event *)calloc(event_storage_count, sizeof(struct event));
604✔
2869
    if (!p->event_storage_ping) {
604✔
2870
        errno_crash_and_burn("can't allocate event_storage_ping");
×
2871
    }
2872
    p->event_storage_timeout = (struct event *)calloc(event_storage_count, sizeof(struct event));
604✔
2873
    if (!p->event_storage_timeout) {
604✔
2874
        errno_crash_and_burn("can't allocate event_storage_timeout");
×
2875
    }
2876

2877
    /* schedule first ping */
2878
    host_add_ping_event(p, 0, current_time_ns);
604✔
2879

2880
    num_hosts++;
604✔
2881
}
604✔
2882

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

2885
  Function: crash_and_burn
2886

2887
*************************************************************
2888

2889
  Inputs:  char* message
2890

2891
  Description:
2892

2893
************************************************************/
2894

2895
void crash_and_burn(char *message)
×
2896
{
2897
    fprintf(stderr, "%s: %s\n", prog, message);
×
2898
    exit(4);
×
2899
}
2900

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

2903
  Function: errno_crash_and_burn
2904

2905
*************************************************************
2906

2907
  Inputs:  char* message
2908

2909
  Description:
2910

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

2913
void errno_crash_and_burn(char *message)
8✔
2914
{
2915
    fprintf(stderr, "%s: %s : %s\n", prog, message, strerror(errno));
8✔
2916
    exit(4);
8✔
2917
}
2918

2919
/************************************************************
2920

2921
  Function: print_warning
2922

2923
  Description: fprintf(stderr, ...), unless running with -q
2924

2925
*************************************************************/
2926

2927
void print_warning(char *format, ...)
54✔
2928
{
2929
    va_list args;
2930
    if (!opt_quiet_on) {
54✔
2931
        va_start(args, format);
54✔
2932
        vfprintf(stderr, format, args);
54✔
2933
        va_end(args);
54✔
2934
    }
8✔
2935
}
54✔
2936

2937

2938

2939
/************************************************************
2940

2941
  Function: addr_cmp
2942

2943
*************************************************************/
2944
int addr_cmp(struct sockaddr *a, struct sockaddr *b)
607✔
2945
{
2946
    if (a->sa_family != b->sa_family) {
607✔
2947
        return a->sa_family - b->sa_family;
×
2948
    }
2949
    else {
2950
        if (a->sa_family == AF_INET) {
607✔
2951
            return ((struct sockaddr_in *)a)->sin_addr.s_addr - ((struct sockaddr_in *)b)->sin_addr.s_addr;
543✔
2952
        }
2953
        else if (a->sa_family == AF_INET6) {
64✔
2954
            return memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
72✔
2955
                &((struct sockaddr_in6 *)b)->sin6_addr,
64✔
2956
                sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
2957
        }
2958
    }
2959

2960
    return 0;
×
2961
}
175✔
2962

2963
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,043✔
2964
{
2965
    struct event *event = &h->event_storage_ping[index % event_storage_count];
1,043✔
2966
    event->host = h;
1,043✔
2967
    event->ping_index = index;
1,043✔
2968
    event->ev_time = ev_time;
1,043✔
2969
    ev_enqueue(&event_queue_ping, event);
1,043✔
2970

2971
    dbg_printf("%s [%d]: add ping event in %.0f ms\n",
2972
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2973
}
1,043✔
2974

2975
void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time)
1,010✔
2976
{
2977
    struct event *event = &h->event_storage_timeout[index % event_storage_count];
1,010✔
2978
    event->host = h;
1,010✔
2979
    event->ping_index = index;
1,010✔
2980
    event->ev_time = ev_time;
1,010✔
2981
    ev_enqueue(&event_queue_timeout, event);
1,010✔
2982

2983
    dbg_printf("%s [%d]: add timeout event in %.0f ms\n",
2984
        event->host->host, index, (ev_time - current_time_ns) / 1e6);
2985
}
1,010✔
2986

2987
struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
895✔
2988
{
2989
    return &h->event_storage_timeout[index % event_storage_count];
895✔
2990
}
2991

2992
/************************************************************
2993

2994
  Function: ev_enqueue
2995

2996
  Enqueue an event
2997

2998
  The queue is sorted by event->ev_time, so that queue->first always points to
2999
  the earliest event.
3000

3001
  We start scanning the queue from the tail, because we assume
3002
  that new events mostly get inserted with a event time higher
3003
  than the others.
3004

3005
*************************************************************/
3006
void ev_enqueue(struct event_queue *queue, struct event *event)
2,053✔
3007
{
3008
    struct event *i;
3009
    struct event *i_prev;
3010

3011
    /* Empty list */
3012
    if (queue->last == NULL) {
2,053✔
3013
        event->ev_next = NULL;
1,661✔
3014
        event->ev_prev = NULL;
1,661✔
3015
        queue->first = event;
1,661✔
3016
        queue->last = event;
1,661✔
3017
        return;
1,661✔
3018
    }
3019

3020
    /* Insert on tail? */
3021
    if (event->ev_time - queue->last->ev_time >= 0) {
392✔
3022
        event->ev_next = NULL;
392✔
3023
        event->ev_prev = queue->last;
392✔
3024
        queue->last->ev_next = event;
392✔
3025
        queue->last = event;
392✔
3026
        return;
392✔
3027
    }
3028

3029
    /* Find insertion point */
3030
    i = queue->last;
×
3031
    while (1) {
3032
        i_prev = i->ev_prev;
×
3033
        if (i_prev == NULL || event->ev_time - i_prev->ev_time >= 0) {
×
3034
            event->ev_prev = i_prev;
×
3035
            event->ev_next = i;
×
3036
            i->ev_prev = event;
×
3037
            if (i_prev != NULL) {
×
3038
                i_prev->ev_next = event;
×
3039
            }
3040
            else {
3041
                queue->first = event;
×
3042
            }
3043
            return;
×
3044
        }
3045
        i = i_prev;
×
3046
    }
3047
}
545✔
3048

3049
/************************************************************
3050

3051
  Function: ev_dequeue
3052

3053
*************************************************************/
3054
struct event *ev_dequeue(struct event_queue *queue)
1,110✔
3055
{
3056
    struct event *dequeued;
3057

3058
    if (queue->first == NULL) {
1,110✔
3059
        return NULL;
×
3060
    }
3061
    dequeued = queue->first;
1,110✔
3062
    ev_remove(queue, dequeued);
1,110✔
3063

3064
    return dequeued;
1,110✔
3065
}
284✔
3066

3067
/************************************************************
3068

3069
  Function: ev_remove
3070

3071
*************************************************************/
3072
void ev_remove(struct event_queue *queue, struct event *event)
2,005✔
3073
{
3074
    if (queue->first == event) {
2,005✔
3075
        queue->first = event->ev_next;
1,996✔
3076
    }
528✔
3077
    if (queue->last == event) {
2,005✔
3078
        queue->last = event->ev_prev;
1,634✔
3079
    }
417✔
3080
    if (event->ev_prev) {
2,005✔
3081
        event->ev_prev->ev_next = event->ev_next;
9✔
3082
    }
3✔
3083
    if (event->ev_next) {
2,005✔
3084
        event->ev_next->ev_prev = event->ev_prev;
371✔
3085
    }
114✔
3086
    event->ev_prev = NULL;
2,005✔
3087
    event->ev_next = NULL;
2,005✔
3088
}
2,005✔
3089

3090

3091

3092
/************************************************************
3093

3094
  Function: usage
3095

3096
*************************************************************
3097

3098
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3099

3100
  Description:
3101

3102
************************************************************/
3103

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