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

schweikert / fping / 23642461283

27 Mar 2026 10:41AM UTC coverage: 88.077% (-0.08%) from 88.152%
23642461283

Pull #461

github

web-flow
Merge a494e712b into fd87284ca
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.

1640 of 1862 relevant lines covered (88.08%)

363.25 hits per line

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

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

277
int event_storage_count;
278

279
/*** globals ***/
280

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

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

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

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

310
unsigned int debugging = 0;
311

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

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

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

343

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

349
unsigned int fwmark = 0;
350

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

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

355
  Function: p_setsockopt
356

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

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

361
  Description:
362

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

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

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

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

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

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

386
    return res;
22✔
387
}
388

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

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

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

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

405
    return val;
347✔
406
}
407

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

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

418
    return val;
222✔
419
}
420

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

423
  Function: main
424

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

427
  Inputs:  int argc, char** argv
428

429
  Description:
430

431
  Main program entry point
432

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

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

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

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

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

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

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

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

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

494
    /* get command line options */
495

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

726
            break;
29✔
727

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

837
            break;
6✔
838
#endif
839

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

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

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

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

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

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

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

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

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

931
    /* validate various option settings */
932

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1328
    last_send_time = 0;
378✔
1329

1330
    seqmap_init(opt_seqmap_timeout);
378✔
1331

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

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

1342
    finish();
378✔
1343

1344
    return 0;
82✔
1345
}
1346

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1705
    wait_for_reply:
738✔
1706

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

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

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

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

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

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

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

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

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

1769
        update_current_time();
1,636✔
1770

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

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

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

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

1800
  Function: signal_handler
1801

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

1804
  Inputs:  int signum
1805

1806
  Description:
1807

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

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

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

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

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

1828
  Function: update_current_time
1829

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

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

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

1840
  Function: finish
1841

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

1844
  Inputs:  void (none)
1845

1846
  Description:
1847

1848
  Main program clean up and exit point
1849

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

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

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

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

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

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

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

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

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

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

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

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

1916
    exit(0);
333✔
1917
}
1918

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

1921
  Function: send_ping
1922

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

1925
  Inputs:  int s, HOST_ENTRY *h
1926

1927
  Description:
1928

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

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

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

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

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

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

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

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

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

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

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

1998
    return (ret);
1,040✔
1999
}
272✔
2000

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

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

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

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

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

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

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

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

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

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

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

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

2153
    return recv_len;
1,493✔
2154
}
685✔
2155

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

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

2178
    /* received count */
2179
    h->num_recv++;
891✔
2180
    h->num_recv_i++;
891✔
2181

2182
    /* maximum */
2183
    if (!h->max_reply || latency > h->max_reply) {
891✔
2184
        h->max_reply = latency;
660✔
2185
    }
188✔
2186
    if (!h->max_reply_i || latency > h->max_reply_i) {
891✔
2187
        h->max_reply_i = latency;
685✔
2188
    }
190✔
2189

2190
    /* minimum */
2191
    if (!h->min_reply || latency < h->min_reply) {
891✔
2192
        h->min_reply = latency;
560✔
2193
    }
157✔
2194
    if (!h->min_reply_i || latency < h->min_reply_i) {
891✔
2195
        h->min_reply_i = latency;
598✔
2196
    }
162✔
2197

2198
    /* total time (for average) */
2199
    h->total_time += latency;
891✔
2200
    h->total_time_i += latency;
891✔
2201

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

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

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

2233
    if (!using_sock_dgram4) {
1,398✔
2234
        ip = (struct ip *)reply_buf;
1,388✔
2235
        ip_header_res->tos = ip->ip_tos;
1,388✔
2236
        ip_header_res->ttl = ip->ip_ttl;
1,388✔
2237

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

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

2259
    icp = (struct icmp *)(reply_buf + hlen);
1,398✔
2260

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

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

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

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

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

2289
        getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
×
2290

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

2303
            print_warning("\n");
×
2304
            num_othericmprcvd++;
×
2305
            break;
×
2306

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

2325
        return -1;
×
2326
    }
2327

2328
    *id = icp->icmp_id;
822✔
2329
    *seq = ntohs(icp->icmp_seq);
822✔
2330
    if(icp->icmp_type == ICMP_TSTAMPREPLY) {
822✔
2331

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

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

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

2354
    return hlen;
822✔
2355
}
236✔
2356

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2468
        return 0;
×
2469
    }
2470

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

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

2479
    return 1;
89✔
2480
}
15✔
2481
#endif
2482

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

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

2509
    if (result <= 0) {
3,129✔
2510
        return 0;
1,636✔
2511
    }
2512

2513
    update_current_time();
1,493✔
2514
    if (recv_time == 0)
1,493✔
2515
        recv_time = current_time_ns;
251✔
2516

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

2560
    seqmap_value = seqmap_fetch(seq, current_time_ns);
911✔
2561
    if (seqmap_value == NULL) {
911✔
2562
        return 1;
12✔
2563
    }
2564

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

2571
    /* update stats that include invalid replies */
2572
    h->num_recv_total++;
899✔
2573
    num_pingreceived++;
899✔
2574

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

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

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

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

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

2605
    /* update stats */
2606
    stats_add(h, this_count, 1, this_reply);
891✔
2607
    // TODO: move to stats_add?
2608
    if (!max_reply || this_reply > max_reply)
891✔
2609
        max_reply = this_reply;
523✔
2610
    if (!min_reply || this_reply < min_reply)
891✔
2611
        min_reply = this_reply;
472✔
2612
    sum_replies += this_reply;
891✔
2613
    total_replies++;
891✔
2614

2615
    /* initialize timeout to initial timeout (without backoff) */
2616
    h->timeout = opt_timeout;
891✔
2617

2618
    /* remove timeout event */
2619
    struct event *timeout_event = host_get_timeout_event(h, this_count);
891✔
2620
    if (timeout_event) {
891✔
2621
        ev_remove(&event_queue_timeout, timeout_event);
891✔
2622
    }
247✔
2623

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

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

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

2638
    /* print received ping (unless --quiet) */
2639
    if (opt_per_recv_on) {
891✔
2640
        avg = h->total_time / h->num_recv;
281✔
2641
        print_recv(h,
368✔
2642
            recv_time,
87✔
2643
            result,
87✔
2644
            this_count,
87✔
2645
            this_reply,
87✔
2646
            avg);
87✔
2647
    }
87✔
2648

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

2669
/************************************************************
2670

2671
  Function: add_name
2672

2673
*************************************************************
2674

2675
  Inputs:  char* name
2676

2677
  Description:
2678

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

2683
************************************************************/
2684

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

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

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

2722
        return;
26✔
2723
    }
2724

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

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

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

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

2780
        if (!opt_multif_on) {
600✔
2781
            break;
600✔
2782
        }
2783
    }
2784

2785
    freeaddrinfo(res0);
600✔
2786
}
167✔
2787

2788
/************************************************************
2789

2790
  Function: add_addr
2791

2792
*************************************************************
2793

2794
  Description:
2795

2796
  add single address to list of hosts to be pinged
2797

2798
************************************************************/
2799

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

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

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

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

2826
    if (strlen(p->host) > max_hostname_len)
600✔
2827
        max_hostname_len = strlen(p->host);
407✔
2828

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

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

2842
        p->resp_times = i;
586✔
2843
    }
154✔
2844

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

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

2858
    num_hosts++;
600✔
2859
}
600✔
2860

2861
/************************************************************
2862

2863
  Function: crash_and_burn
2864

2865
*************************************************************
2866

2867
  Inputs:  char* message
2868

2869
  Description:
2870

2871
************************************************************/
2872

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

2879
/************************************************************
2880

2881
  Function: errno_crash_and_burn
2882

2883
*************************************************************
2884

2885
  Inputs:  char* message
2886

2887
  Description:
2888

2889
************************************************************/
2890

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

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

2899
  Function: print_warning
2900

2901
  Description: fprintf(stderr, ...), unless running with -q
2902

2903
*************************************************************/
2904

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

2915

2916

2917
/************************************************************
2918

2919
  Function: addr_cmp
2920

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

2938
    return 0;
×
2939
}
175✔
2940

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

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

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

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

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

2970
/************************************************************
2971

2972
  Function: ev_enqueue
2973

2974
  Enqueue an event
2975

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

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

2983
*************************************************************/
2984
void ev_enqueue(struct event_queue *queue, struct event *event)
2,045✔
2985
{
2986
    struct event *i;
2987
    struct event *i_prev;
2988

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

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

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

3027
/************************************************************
3028

3029
  Function: ev_dequeue
3030

3031
*************************************************************/
3032
struct event *ev_dequeue(struct event_queue *queue)
1,106✔
3033
{
3034
    struct event *dequeued;
3035

3036
    if (queue->first == NULL) {
1,106✔
3037
        return NULL;
×
3038
    }
3039
    dequeued = queue->first;
1,106✔
3040
    ev_remove(queue, dequeued);
1,106✔
3041

3042
    return dequeued;
1,106✔
3043
}
284✔
3044

3045
/************************************************************
3046

3047
  Function: ev_remove
3048

3049
*************************************************************/
3050
void ev_remove(struct event_queue *queue, struct event *event)
1,997✔
3051
{
3052
    if (queue->first == event) {
1,997✔
3053
        queue->first = event->ev_next;
1,988✔
3054
    }
528✔
3055
    if (queue->last == event) {
1,997✔
3056
        queue->last = event->ev_prev;
1,626✔
3057
    }
416✔
3058
    if (event->ev_prev) {
1,997✔
3059
        event->ev_prev->ev_next = event->ev_next;
9✔
3060
    }
3✔
3061
    if (event->ev_next) {
1,997✔
3062
        event->ev_next->ev_prev = event->ev_prev;
371✔
3063
    }
115✔
3064
    event->ev_prev = NULL;
1,997✔
3065
    event->ev_next = NULL;
1,997✔
3066
}
1,997✔
3067

3068

3069

3070
/************************************************************
3071

3072
  Function: usage
3073

3074
*************************************************************
3075

3076
  Inputs:  int: 0 if output on request, 1 if output because of wrong argument
3077

3078
  Description:
3079

3080
************************************************************/
3081

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