|
|
|
@ -34,17 +34,17 @@
|
|
|
|
|
extern "C" {
|
|
|
|
|
#endif /* __cplusplus */
|
|
|
|
|
|
|
|
|
|
#include "fping.h"
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#include "fping.h"
|
|
|
|
|
#include "options.h"
|
|
|
|
|
#include "optparse.h"
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
|
|
#include "seqmap.h"
|
|
|
|
@ -60,6 +60,8 @@ extern "C" {
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
@ -116,16 +118,15 @@ extern int h_errno;
|
|
|
|
|
|
|
|
|
|
/*** Constants ***/
|
|
|
|
|
|
|
|
|
|
/* CLOCK_MONTONIC starts under macOS, OpenBSD and FreeBSD with undefined positive point and can not be use
|
|
|
|
|
* see github PR #217
|
|
|
|
|
* The configure script detect the predefined operating systems an set CLOCK_REALTIME using over ONLY_CLOCK_REALTIME variable
|
|
|
|
|
*/
|
|
|
|
|
#if HAVE_SO_TIMESTAMPNS || ONLY_CLOCK_REALTIME
|
|
|
|
|
#if HAVE_SO_TIMESTAMPNS
|
|
|
|
|
#define CLOCKID CLOCK_REALTIME
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* CLOCK_MONTONIC starts under macOS, OpenBSD and FreeBSD with undefined positive point and can not be use
|
|
|
|
|
* see github PR #217
|
|
|
|
|
*/
|
|
|
|
|
#if !defined(CLOCKID)
|
|
|
|
|
#if defined(CLOCK_MONOTONIC)
|
|
|
|
|
#if defined(CLOCK_MONOTONIC) && !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
|
|
|
|
|
#define CLOCKID CLOCK_MONOTONIC
|
|
|
|
|
#else
|
|
|
|
|
#define CLOCKID CLOCK_REALTIME
|
|
|
|
@ -139,7 +140,7 @@ extern int h_errno;
|
|
|
|
|
#define SIZE_ICMP_HDR 8 /* from ip_icmp.h */
|
|
|
|
|
#define MAX_PING_DATA (MAX_IP_PACKET - SIZE_IP_HDR - SIZE_ICMP_HDR)
|
|
|
|
|
|
|
|
|
|
#define MAX_GENERATE 131070 /* maximum number of hosts that -g can generate */
|
|
|
|
|
#define MAX_GENERATE 100000 /* maximum number of hosts that -g can generate */
|
|
|
|
|
|
|
|
|
|
/* sized so as to be like traditional ping */
|
|
|
|
|
#define DEFAULT_PING_DATA_SIZE 56
|
|
|
|
@ -354,19 +355,15 @@ int generate_flag = 0; /* flag for IP list generation */
|
|
|
|
|
int verbose_flag, quiet_flag, stats_flag, unreachable_flag, alive_flag;
|
|
|
|
|
int elapsed_flag, version_flag, count_flag, loop_flag, netdata_flag;
|
|
|
|
|
int per_recv_flag, report_all_rtts_flag, name_flag, addr_flag, backoff_flag, rdns_flag;
|
|
|
|
|
int multif_flag, timeout_flag, fast_reachable;
|
|
|
|
|
int multif_flag, timeout_flag;
|
|
|
|
|
int outage_flag = 0;
|
|
|
|
|
int timestamp_flag = 0;
|
|
|
|
|
int timestamp_format_flag = 0;
|
|
|
|
|
int nonzero_payload_flag = 0;
|
|
|
|
|
int cumulative_stats_flag = 0;
|
|
|
|
|
int random_data_flag = 0;
|
|
|
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
|
|
|
int randomly_lose_flag, trace_flag, print_per_system_flag;
|
|
|
|
|
int lose_factor;
|
|
|
|
|
#endif /* DEBUG || _DEBUG */
|
|
|
|
|
|
|
|
|
|
unsigned int fwmark = 0;
|
|
|
|
|
|
|
|
|
|
char* filename = NULL; /* file containing hosts to ping */
|
|
|
|
|
|
|
|
|
|
/*** forward declarations ***/
|
|
|
|
@ -382,7 +379,6 @@ void usage(int);
|
|
|
|
|
int wait_for_reply(int64_t);
|
|
|
|
|
void print_per_system_stats(void);
|
|
|
|
|
void print_per_system_splits(void);
|
|
|
|
|
void stats_reset_interval(HOST_ENTRY *h);
|
|
|
|
|
void print_netdata(void);
|
|
|
|
|
void print_global_stats(void);
|
|
|
|
|
void main_loop();
|
|
|
|
@ -394,7 +390,6 @@ struct event *ev_dequeue(struct event_queue *queue);
|
|
|
|
|
void ev_remove(struct event_queue *queue, struct event *event);
|
|
|
|
|
void add_cidr(char*);
|
|
|
|
|
void add_range(char*, char*);
|
|
|
|
|
void add_addr_range_ipv4(unsigned long, unsigned long);
|
|
|
|
|
void print_warning(char* fmt, ...);
|
|
|
|
|
int addr_cmp(struct sockaddr* a, struct sockaddr* b);
|
|
|
|
|
void host_add_ping_event(HOST_ENTRY *h, int index, int64_t ev_time);
|
|
|
|
@ -402,7 +397,6 @@ void host_add_timeout_event(HOST_ENTRY *h, int index, int64_t ev_time);
|
|
|
|
|
struct event *host_get_timeout_event(HOST_ENTRY *h, int index);
|
|
|
|
|
void stats_add(HOST_ENTRY *h, int index, int success, int64_t latency);
|
|
|
|
|
void update_current_time();
|
|
|
|
|
void print_timestamp_format(int64_t current_time_ns, int timestamp_format);
|
|
|
|
|
|
|
|
|
|
/************************************************************
|
|
|
|
|
|
|
|
|
@ -456,13 +450,6 @@ int p_setsockopt(uid_t p_uid, int sockfd, int level, int optname,
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv)
|
|
|
|
|
{
|
|
|
|
|
/* Debug: CPU Performance */
|
|
|
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
|
|
|
clock_t perf_cpu_start, perf_cpu_end;
|
|
|
|
|
double perf_cpu_time_used;
|
|
|
|
|
perf_cpu_start = clock();
|
|
|
|
|
#endif /* DEBUG || _DEBUG */
|
|
|
|
|
|
|
|
|
|
int c;
|
|
|
|
|
const uid_t suid = geteuid();
|
|
|
|
|
int tos = 0;
|
|
|
|
@ -525,7 +512,6 @@ int main(int argc, char **argv)
|
|
|
|
|
{ "vcount", 'C', OPTPARSE_REQUIRED },
|
|
|
|
|
{ "rdns", 'd', OPTPARSE_NONE },
|
|
|
|
|
{ "timestamp", 'D', OPTPARSE_NONE },
|
|
|
|
|
{ "timestamp-format", '0', OPTPARSE_REQUIRED },
|
|
|
|
|
{ "elapsed", 'e', OPTPARSE_NONE },
|
|
|
|
|
{ "file", 'f', OPTPARSE_REQUIRED },
|
|
|
|
|
{ "generate", 'g', OPTPARSE_NONE },
|
|
|
|
@ -533,9 +519,6 @@ int main(int argc, char **argv)
|
|
|
|
|
{ "ttl", 'H', OPTPARSE_REQUIRED },
|
|
|
|
|
{ "interval", 'i', OPTPARSE_REQUIRED },
|
|
|
|
|
{ "iface", 'I', OPTPARSE_REQUIRED },
|
|
|
|
|
#ifdef SO_MARK
|
|
|
|
|
{ "fwmark", 'k', OPTPARSE_REQUIRED },
|
|
|
|
|
#endif
|
|
|
|
|
{ "loop", 'l', OPTPARSE_NONE },
|
|
|
|
|
{ "all", 'm', OPTPARSE_NONE },
|
|
|
|
|
{ "dontfrag", 'M', OPTPARSE_NONE },
|
|
|
|
@ -547,7 +530,7 @@ int main(int argc, char **argv)
|
|
|
|
|
{ "quiet", 'q', OPTPARSE_NONE },
|
|
|
|
|
{ "squiet", 'Q', OPTPARSE_REQUIRED },
|
|
|
|
|
{ "retry", 'r', OPTPARSE_REQUIRED },
|
|
|
|
|
{ "nonzero", 'Z', OPTPARSE_NONE },
|
|
|
|
|
{ "random", 'R', OPTPARSE_NONE },
|
|
|
|
|
{ "stats", 's', OPTPARSE_NONE },
|
|
|
|
|
{ "src", 'S', OPTPARSE_REQUIRED },
|
|
|
|
|
{ "timeout", 't', OPTPARSE_REQUIRED },
|
|
|
|
@ -555,7 +538,6 @@ int main(int argc, char **argv)
|
|
|
|
|
{ "unreach", 'u', OPTPARSE_NONE },
|
|
|
|
|
{ "version", 'v', OPTPARSE_NONE },
|
|
|
|
|
{ "reachable", 'x', OPTPARSE_REQUIRED },
|
|
|
|
|
{ "fast-reachable", 'X', OPTPARSE_REQUIRED },
|
|
|
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
|
|
|
{ NULL, 'z', OPTPARSE_REQUIRED },
|
|
|
|
|
#endif
|
|
|
|
@ -565,19 +547,6 @@ int main(int argc, char **argv)
|
|
|
|
|
float opt_value_float;
|
|
|
|
|
while ((c = optparse_long(&optparse_state, longopts, NULL)) != EOF) {
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '0':
|
|
|
|
|
if(strstr(optparse_state.optlongname, "timestamp-format") != NULL) {
|
|
|
|
|
if(strcmp(optparse_state.optarg, "ctime") == 0) {
|
|
|
|
|
timestamp_format_flag = 1;
|
|
|
|
|
}else if(strcmp(optparse_state.optarg, "iso") == 0) {
|
|
|
|
|
timestamp_format_flag = 2;
|
|
|
|
|
}else if(strcmp(optparse_state.optarg, "rfc3339") == 0) {
|
|
|
|
|
timestamp_format_flag = 3;
|
|
|
|
|
}else{
|
|
|
|
|
usage(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case '4':
|
|
|
|
|
#ifdef IPV6
|
|
|
|
|
if (hints_ai_family != AF_UNSPEC && hints_ai_family != AF_INET) {
|
|
|
|
@ -622,7 +591,7 @@ int main(int argc, char **argv)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
|
if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
|
|
|
|
|
if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
|
|
|
|
|
usage(1);
|
|
|
|
|
if (opt_value_float < 0) {
|
|
|
|
|
usage(1);
|
|
|
|
@ -632,12 +601,12 @@ int main(int argc, char **argv)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'r':
|
|
|
|
|
if (sscanf(optparse_state.optarg, "%u", &retry) != 1)
|
|
|
|
|
if (!sscanf(optparse_state.optarg, "%u", &retry))
|
|
|
|
|
usage(1);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'i':
|
|
|
|
|
if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
|
|
|
|
|
if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
|
|
|
|
|
usage(1);
|
|
|
|
|
if (opt_value_float < 0) {
|
|
|
|
|
usage(1);
|
|
|
|
@ -646,7 +615,7 @@ int main(int argc, char **argv)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
|
if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
|
|
|
|
|
if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
|
|
|
|
|
usage(1);
|
|
|
|
|
if (opt_value_float < 0) {
|
|
|
|
|
usage(1);
|
|
|
|
@ -671,7 +640,7 @@ int main(int argc, char **argv)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'b':
|
|
|
|
|
if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
|
|
|
|
|
if (!sscanf(optparse_state.optarg, "%u", &ping_data_size))
|
|
|
|
|
usage(1);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
@ -688,21 +657,13 @@ int main(int argc, char **argv)
|
|
|
|
|
case 'Q':
|
|
|
|
|
verbose_flag = 0;
|
|
|
|
|
quiet_flag = 1;
|
|
|
|
|
if (sscanf(optparse_state.optarg, "%f", &opt_value_float) != 1)
|
|
|
|
|
if (!sscanf(optparse_state.optarg, "%f", &opt_value_float))
|
|
|
|
|
usage(1);
|
|
|
|
|
if (opt_value_float < 0) {
|
|
|
|
|
usage(1);
|
|
|
|
|
}
|
|
|
|
|
report_interval = opt_value_float * 1e9;
|
|
|
|
|
|
|
|
|
|
/* recognize keyword(s) after number, ignore everything else */
|
|
|
|
|
{
|
|
|
|
|
char *comma = strchr(optparse_state.optarg, ',');
|
|
|
|
|
if ((comma != NULL) && (strcmp(++comma, "cumulative") == 0)) {
|
|
|
|
|
cumulative_stats_flag = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'e':
|
|
|
|
@ -751,8 +712,8 @@ int main(int argc, char **argv)
|
|
|
|
|
timestamp_flag = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'Z':
|
|
|
|
|
nonzero_payload_flag = 1;
|
|
|
|
|
case 'R':
|
|
|
|
|
random_data_flag = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'l':
|
|
|
|
@ -791,32 +752,9 @@ int main(int argc, char **argv)
|
|
|
|
|
usage(1);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'X':
|
|
|
|
|
if (!(min_reachable = (unsigned int)atoi(optparse_state.optarg)))
|
|
|
|
|
usage(1);
|
|
|
|
|
fast_reachable = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'f':
|
|
|
|
|
filename = optparse_state.optarg;
|
|
|
|
|
break;
|
|
|
|
|
#ifdef SO_MARK
|
|
|
|
|
case 'k':
|
|
|
|
|
if (!(fwmark = (unsigned int)atol(optparse_state.optarg)))
|
|
|
|
|
usage(1);
|
|
|
|
|
|
|
|
|
|
if (socket4 >= 0)
|
|
|
|
|
if(-1 == setsockopt(socket4, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
|
|
|
|
|
perror("fwmark ipv4");
|
|
|
|
|
|
|
|
|
|
#ifdef IPV6
|
|
|
|
|
if (socket6 >= 0)
|
|
|
|
|
if(-1 == setsockopt(socket6, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark))
|
|
|
|
|
perror("fwmark ipv6");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
case 'g':
|
|
|
|
|
/* use IP list generation */
|
|
|
|
@ -866,7 +804,7 @@ int main(int argc, char **argv)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'O':
|
|
|
|
|
if (sscanf(optparse_state.optarg, "%i", &tos) == 1) {
|
|
|
|
|
if (sscanf(optparse_state.optarg, "%i", &tos)) {
|
|
|
|
|
if (socket4 >= 0) {
|
|
|
|
|
if (setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
|
|
|
|
|
perror("setting type of service octet IP_TOS");
|
|
|
|
@ -908,13 +846,11 @@ int main(int argc, char **argv)
|
|
|
|
|
|
|
|
|
|
#ifndef IPV6
|
|
|
|
|
if (socket4 < 0) {
|
|
|
|
|
crash_and_burn("can't create socket (must run as root?)");
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
if ((socket4 < 0 && socket6 < 0) || (hints_ai_family == AF_INET6 && socket6 < 0)) {
|
|
|
|
|
#endif
|
|
|
|
|
crash_and_burn("can't create socket (must run as root?)");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (ttl > 255) {
|
|
|
|
|
fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
|
|
|
|
@ -953,6 +889,9 @@ int main(int argc, char **argv)
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (alive_flag || unreachable_flag || min_reachable)
|
|
|
|
|
verbose_flag = 0;
|
|
|
|
|
|
|
|
|
|
if (count_flag) {
|
|
|
|
|
if (verbose_flag)
|
|
|
|
|
per_recv_flag = 1;
|
|
|
|
@ -967,9 +906,6 @@ int main(int argc, char **argv)
|
|
|
|
|
alive_flag = unreachable_flag = verbose_flag = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (alive_flag || unreachable_flag || min_reachable)
|
|
|
|
|
verbose_flag = 0;
|
|
|
|
|
|
|
|
|
|
trials = (count > retry + 1) ? count : retry + 1;
|
|
|
|
|
|
|
|
|
|
/* auto-tune default timeout for count/loop modes
|
|
|
|
@ -1071,17 +1007,13 @@ int main(int argc, char **argv)
|
|
|
|
|
int opt = 1;
|
|
|
|
|
if (socket4 >= 0) {
|
|
|
|
|
if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
|
|
|
|
|
if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
|
|
|
|
|
perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option");
|
|
|
|
|
}
|
|
|
|
|
perror("setting SO_TIMESTAMPNS option");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#ifdef IPV6
|
|
|
|
|
if (socket6 >= 0) {
|
|
|
|
|
if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
|
|
|
|
|
if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
|
|
|
|
|
perror("setting SO_TIMESTAMPNS and SO_TIMESTAMP option (IPv6)");
|
|
|
|
|
}
|
|
|
|
|
perror("setting SO_TIMESTAMPNS option (IPv6)");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
@ -1174,11 +1106,11 @@ int main(int argc, char **argv)
|
|
|
|
|
exit(num_noaddress ? 2 : 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (socket4 >= 0 && (src_addr_set || socktype4 == SOCK_DGRAM)) {
|
|
|
|
|
if (src_addr_set && socket4 >= 0) {
|
|
|
|
|
socket_set_src_addr_ipv4(socket4, &src_addr, (socktype4 == SOCK_DGRAM) ? &ident4 : NULL);
|
|
|
|
|
}
|
|
|
|
|
#ifdef IPV6
|
|
|
|
|
if (socket6 >= 0 && (src_addr6_set || socktype6 == SOCK_DGRAM)) {
|
|
|
|
|
if (src_addr6_set && socket6 >= 0) {
|
|
|
|
|
socket_set_src_addr_ipv6(socket6, &src_addr6, (socktype6 == SOCK_DGRAM) ? &ident6 : NULL);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
@ -1231,13 +1163,6 @@ int main(int argc, char **argv)
|
|
|
|
|
/* main loop */
|
|
|
|
|
main_loop();
|
|
|
|
|
|
|
|
|
|
/* Debug: CPU Performance */
|
|
|
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
|
|
|
perf_cpu_end = clock();
|
|
|
|
|
perf_cpu_time_used = ((double) (perf_cpu_end - perf_cpu_start)) / CLOCKS_PER_SEC;
|
|
|
|
|
printf("[DEBUG] CPU time used: %f sec", perf_cpu_time_used);
|
|
|
|
|
#endif /* DEBUG || _DEBUG */
|
|
|
|
|
|
|
|
|
|
finish();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
@ -1283,7 +1208,6 @@ void add_cidr(char *addr)
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
net_addr = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
|
|
|
|
|
freeaddrinfo(addr_res);
|
|
|
|
|
|
|
|
|
|
/* check mask */
|
|
|
|
|
if (mask < 1 || mask > 32) {
|
|
|
|
@ -1305,7 +1229,15 @@ void add_cidr(char *addr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* add all hosts in that network (net_addr and net_last inclusive) */
|
|
|
|
|
add_addr_range_ipv4(net_addr, net_last);
|
|
|
|
|
for (; net_addr <= net_last; net_addr++) {
|
|
|
|
|
struct in_addr in_addr_tmp;
|
|
|
|
|
char buffer[20];
|
|
|
|
|
in_addr_tmp.s_addr = htonl(net_addr);
|
|
|
|
|
inet_ntop(AF_INET, &in_addr_tmp, buffer, sizeof(buffer));
|
|
|
|
|
add_name(buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
freeaddrinfo(addr_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void add_range(char* start, char* end)
|
|
|
|
@ -1349,14 +1281,7 @@ void add_range(char *start, char *end)
|
|
|
|
|
end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr);
|
|
|
|
|
freeaddrinfo(addr_res);
|
|
|
|
|
|
|
|
|
|
/* add IPv4 addresses from closed interval [start_long,end_long] */
|
|
|
|
|
add_addr_range_ipv4(start_long, end_long);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void add_addr_range_ipv4(unsigned long start_long, unsigned long end_long)
|
|
|
|
|
{
|
|
|
|
|
/* check if generator limit is exceeded */
|
|
|
|
|
if (end_long >= start_long + MAX_GENERATE) {
|
|
|
|
|
if (end_long > start_long + MAX_GENERATE) {
|
|
|
|
|
fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
@ -1382,7 +1307,9 @@ void main_loop()
|
|
|
|
|
dbg_printf("%s", "# main_loop\n");
|
|
|
|
|
|
|
|
|
|
/* timeout event ? */
|
|
|
|
|
if (event_queue_timeout.first && event_queue_timeout.first->ev_time - current_time_ns <= 0) {
|
|
|
|
|
if (event_queue_timeout.first &&
|
|
|
|
|
event_queue_timeout.first->ev_time - current_time_ns <= 0)
|
|
|
|
|
{
|
|
|
|
|
event = ev_dequeue(&event_queue_timeout);
|
|
|
|
|
h = event->host;
|
|
|
|
|
|
|
|
|
@ -1392,7 +1319,7 @@ void main_loop()
|
|
|
|
|
|
|
|
|
|
if (per_recv_flag) {
|
|
|
|
|
if (timestamp_flag) {
|
|
|
|
|
print_timestamp_format(current_time_ns, timestamp_format_flag);
|
|
|
|
|
printf("[%.5f] ", (double)current_time_ns / 1e9);
|
|
|
|
|
}
|
|
|
|
|
printf("%-*s : [%d], timed out",
|
|
|
|
|
max_hostname_len, h->host, event->ping_index);
|
|
|
|
@ -1431,7 +1358,9 @@ void main_loop()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ping event ? */
|
|
|
|
|
if (event_queue_ping.first && event_queue_ping.first->ev_time - current_time_ns <= 0) {
|
|
|
|
|
if (event_queue_ping.first &&
|
|
|
|
|
event_queue_ping.first->ev_time - current_time_ns <= 0)
|
|
|
|
|
{
|
|
|
|
|
/* Make sure that we don't ping more than once every "interval" */
|
|
|
|
|
lt = current_time_ns - last_send_time;
|
|
|
|
|
if (lt < interval)
|
|
|
|
@ -1577,6 +1506,7 @@ void update_current_time()
|
|
|
|
|
current_time_ns = timespec_ns(¤t_time);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************************
|
|
|
|
|
|
|
|
|
|
Function: finish
|
|
|
|
@ -1631,8 +1561,7 @@ void finish()
|
|
|
|
|
if ((num_hosts-num_unreachable) >= min_reachable) {
|
|
|
|
|
printf("Enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts-num_unreachable);
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
} else {
|
|
|
|
|
printf("Not enough hosts reachable (required: %d, reachable: %d)\n", min_reachable, num_hosts-num_unreachable);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
@ -1696,7 +1625,7 @@ void print_per_system_stats(void)
|
|
|
|
|
else {
|
|
|
|
|
fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
|
|
|
|
|
h->num_sent, h->num_recv,
|
|
|
|
|
h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0);
|
|
|
|
|
((h->num_recv * 100) / h->num_sent));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (h->num_recv) {
|
|
|
|
@ -1736,7 +1665,7 @@ void print_netdata(void)
|
|
|
|
|
h = table[i];
|
|
|
|
|
|
|
|
|
|
if (!sent_charts) {
|
|
|
|
|
printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9);
|
|
|
|
|
printf("CHART fping.%s_packets '' 'FPing Packets for host %s' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, h->host, report_interval / 1e9);
|
|
|
|
|
printf("DIMENSION xmt sent absolute 1 1\n");
|
|
|
|
|
printf("DIMENSION rcv received absolute 1 1\n");
|
|
|
|
|
}
|
|
|
|
@ -1747,7 +1676,7 @@ void print_netdata(void)
|
|
|
|
|
printf("END\n");
|
|
|
|
|
|
|
|
|
|
if (!sent_charts) {
|
|
|
|
|
printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9);
|
|
|
|
|
printf("CHART fping.%s_quality '' 'FPing Quality for host %s' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, h->host, report_interval / 1e9);
|
|
|
|
|
printf("DIMENSION returned '' absolute 1 1\n");
|
|
|
|
|
/* printf("DIMENSION lost '' absolute 1 1\n"); */
|
|
|
|
|
}
|
|
|
|
@ -1764,7 +1693,7 @@ void print_netdata(void)
|
|
|
|
|
printf("END\n");
|
|
|
|
|
|
|
|
|
|
if (!sent_charts) {
|
|
|
|
|
printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9);
|
|
|
|
|
printf("CHART fping.%s_latency '' 'FPing Latency for host %s' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, h->host, report_interval / 1e9);
|
|
|
|
|
printf("DIMENSION min minimum absolute 1 1000000\n");
|
|
|
|
|
printf("DIMENSION max maximum absolute 1 1000000\n");
|
|
|
|
|
printf("DIMENSION avg average absolute 1 1000000\n");
|
|
|
|
@ -1779,7 +1708,7 @@ void print_netdata(void)
|
|
|
|
|
}
|
|
|
|
|
printf("END\n");
|
|
|
|
|
|
|
|
|
|
stats_reset_interval(h);
|
|
|
|
|
h->num_sent_i = h->num_recv_i = h->max_reply_i = h->min_reply_i = h->total_time_i = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sent_charts = 1;
|
|
|
|
@ -1839,9 +1768,7 @@ void print_per_system_splits(void)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
if (!cumulative_stats_flag) {
|
|
|
|
|
stats_reset_interval(h);
|
|
|
|
|
}
|
|
|
|
|
h->num_sent_i = h->num_recv_i = h->max_reply_i = h->min_reply_i = h->total_time_i = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1981,11 +1908,9 @@ int socket_can_read(struct timeval *timeout)
|
|
|
|
|
|
|
|
|
|
select_again:
|
|
|
|
|
FD_ZERO(&readset);
|
|
|
|
|
if (socket4 >= 0)
|
|
|
|
|
FD_SET(socket4, &readset);
|
|
|
|
|
if(socket4 >= 0) FD_SET(socket4, &readset);
|
|
|
|
|
#ifdef IPV6
|
|
|
|
|
if (socket6 >= 0)
|
|
|
|
|
FD_SET(socket6, &readset);
|
|
|
|
|
if(socket6 >= 0) FD_SET(socket6, &readset);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
|
|
|
|
@ -2028,13 +1953,15 @@ int receive_packet(int64_t wait_time,
|
|
|
|
|
reply_buf,
|
|
|
|
|
reply_buf_len
|
|
|
|
|
};
|
|
|
|
|
struct msghdr recv_msghdr = {0};
|
|
|
|
|
recv_msghdr.msg_name = reply_src_addr;
|
|
|
|
|
recv_msghdr.msg_namelen = reply_src_addr_len;
|
|
|
|
|
recv_msghdr.msg_iov = &msg_iov;
|
|
|
|
|
recv_msghdr.msg_iovlen = 1;
|
|
|
|
|
recv_msghdr.msg_control = &msg_control;
|
|
|
|
|
recv_msghdr.msg_controllen = sizeof(msg_control);
|
|
|
|
|
struct msghdr recv_msghdr = {
|
|
|
|
|
reply_src_addr,
|
|
|
|
|
reply_src_addr_len,
|
|
|
|
|
&msg_iov,
|
|
|
|
|
1,
|
|
|
|
|
&msg_control,
|
|
|
|
|
sizeof(msg_control),
|
|
|
|
|
0
|
|
|
|
|
};
|
|
|
|
|
#if HAVE_SO_TIMESTAMPNS
|
|
|
|
|
struct cmsghdr* cmsg;
|
|
|
|
|
#endif
|
|
|
|
@ -2064,7 +1991,8 @@ int receive_packet(int64_t wait_time,
|
|
|
|
|
struct timespec reply_timestamp_ts;
|
|
|
|
|
for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
|
|
|
|
|
cmsg != NULL;
|
|
|
|
|
cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
|
|
|
|
|
cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg))
|
|
|
|
|
{
|
|
|
|
|
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
|
|
|
|
|
memcpy(&reply_timestamp_ts, CMSG_DATA(cmsg), sizeof(reply_timestamp_ts));
|
|
|
|
|
*reply_timestamp = timespec_ns(&reply_timestamp_ts);
|
|
|
|
@ -2161,7 +2089,7 @@ int decode_icmp_ipv4(
|
|
|
|
|
if (!using_sock_dgram4) {
|
|
|
|
|
struct ip* ip = (struct ip*)reply_buf;
|
|
|
|
|
|
|
|
|
|
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
|
|
|
|
|
#if defined(__alpha__) && __STDC__ && !defined(__GLIBC__)
|
|
|
|
|
/* The alpha headers are decidedly broken.
|
|
|
|
|
* Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
|
|
|
|
|
* ip_v. So, to get ip_hl, we mask off the bottom four bits.
|
|
|
|
@ -2172,11 +2100,12 @@ int decode_icmp_ipv4(
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (reply_buf_len < hlen + ICMP_MINLEN) {
|
|
|
|
|
/* too short */
|
|
|
|
|
if (verbose_flag) {
|
|
|
|
|
char buf[INET6_ADDRSTRLEN];
|
|
|
|
|
getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
|
|
|
|
|
getnameinfo( response_addr, sizeof( struct sockaddr_in ), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
|
|
|
|
|
printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
@ -2209,7 +2138,7 @@ int decode_icmp_ipv4(
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
|
|
|
|
|
getnameinfo(response_addr, sizeof( struct sockaddr_in ), addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
|
|
|
|
|
|
|
|
|
|
switch (icp->icmp_type) {
|
|
|
|
|
case ICMP_UNREACH:
|
|
|
|
@ -2268,7 +2197,7 @@ int decode_icmp_ipv6(
|
|
|
|
|
if (reply_buf_len < sizeof(struct icmp6_hdr)) {
|
|
|
|
|
if (verbose_flag) {
|
|
|
|
|
char buf[INET6_ADDRSTRLEN];
|
|
|
|
|
getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
|
|
|
|
|
getnameinfo((struct sockaddr*)&response_addr, sizeof(response_addr), buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
|
|
|
|
|
printf("received packet too short for ICMP (%d bytes from %s)\n", (int)reply_buf_len, buf);
|
|
|
|
|
}
|
|
|
|
|
return 0; /* too short */
|
|
|
|
@ -2375,8 +2304,7 @@ int wait_for_reply(int64_t wait_time)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update_current_time();
|
|
|
|
|
if (recv_time == 0)
|
|
|
|
|
recv_time = current_time_ns;
|
|
|
|
|
if(recv_time==0) recv_time = current_time_ns;
|
|
|
|
|
|
|
|
|
|
/* Process ICMP packet and retrieve id/seq */
|
|
|
|
|
if (response_addr.ss_family == AF_INET) {
|
|
|
|
@ -2480,9 +2408,6 @@ int wait_for_reply(int64_t wait_time)
|
|
|
|
|
/* print "is alive" */
|
|
|
|
|
if (h->num_recv == 1) {
|
|
|
|
|
num_alive++;
|
|
|
|
|
if (fast_reachable && num_alive >= min_reachable)
|
|
|
|
|
finish_requested = 1;
|
|
|
|
|
|
|
|
|
|
if (verbose_flag || alive_flag) {
|
|
|
|
|
printf("%s", h->host);
|
|
|
|
|
|
|
|
|
@ -2505,7 +2430,7 @@ int wait_for_reply(int64_t wait_time)
|
|
|
|
|
/* print received ping (unless --quiet) */
|
|
|
|
|
if (per_recv_flag) {
|
|
|
|
|
if (timestamp_flag) {
|
|
|
|
|
print_timestamp_format(recv_time, timestamp_format_flag);
|
|
|
|
|
printf("[%.5f] ", (double)recv_time / 1e9);
|
|
|
|
|
}
|
|
|
|
|
avg = h->total_time / h->num_recv;
|
|
|
|
|
printf("%-*s : [%d], %d bytes, %s ms",
|
|
|
|
@ -2722,7 +2647,9 @@ void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_
|
|
|
|
|
|
|
|
|
|
void crash_and_burn(char* message)
|
|
|
|
|
{
|
|
|
|
|
if (verbose_flag)
|
|
|
|
|
fprintf(stderr, "%s: %s\n", prog, message);
|
|
|
|
|
|
|
|
|
|
exit(4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2858,6 +2785,7 @@ struct event *host_get_timeout_event(HOST_ENTRY *h, int index)
|
|
|
|
|
return &h->event_storage_timeout[index % event_storage_count];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************************
|
|
|
|
|
|
|
|
|
|
Function: ev_enqueue
|
|
|
|
@ -2956,40 +2884,6 @@ void ev_remove(struct event_queue *queue, struct event *event)
|
|
|
|
|
event->ev_next = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************
|
|
|
|
|
|
|
|
|
|
Function: print_human_readable_time from current_time_ns
|
|
|
|
|
|
|
|
|
|
*************************************************************/
|
|
|
|
|
void print_timestamp_format(int64_t current_time_ns, int timestamp_format)
|
|
|
|
|
{
|
|
|
|
|
char time_buffer[100];
|
|
|
|
|
time_t current_time_s;
|
|
|
|
|
struct tm *local_time;
|
|
|
|
|
|
|
|
|
|
current_time_s = current_time_ns / 1000000000;
|
|
|
|
|
local_time = localtime(¤t_time_s);
|
|
|
|
|
switch(timestamp_format) {
|
|
|
|
|
case 1:
|
|
|
|
|
// timestamp-format ctime
|
|
|
|
|
strftime(time_buffer, sizeof(time_buffer), "%c", local_time);
|
|
|
|
|
printf("[%s] ", time_buffer);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
// timestamp-format iso
|
|
|
|
|
strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time);
|
|
|
|
|
printf("[%s] ", time_buffer);
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
// timestamp-format rfc3339
|
|
|
|
|
strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time);
|
|
|
|
|
printf("[%s] ", time_buffer);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
printf("[%.5f] ", (double)current_time_ns / 1e9);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************
|
|
|
|
|
|
|
|
|
|
Function: usage
|
|
|
|
@ -3012,19 +2906,14 @@ void usage(int is_error)
|
|
|
|
|
fprintf(out, " -6, --ipv6 only ping IPv6 addresses\n");
|
|
|
|
|
fprintf(out, " -b, --size=BYTES amount of ping data to send, in bytes (default: %d)\n", DEFAULT_PING_DATA_SIZE);
|
|
|
|
|
fprintf(out, " -B, --backoff=N set exponential backoff factor to N (default: 1.5)\n");
|
|
|
|
|
fprintf(out, " -c, --count=N count mode: send N pings to each target and report stats\n");
|
|
|
|
|
fprintf(out, " -c, --count=N count mode: send N pings to each target\n");
|
|
|
|
|
fprintf(out, " -f, --file=FILE read list of targets from a file ( - means stdin)\n");
|
|
|
|
|
fprintf(out, " -g, --generate generate target list (only if no -f specified),\n");
|
|
|
|
|
fprintf(out, " limited to at most %d targets\n", MAX_GENERATE);
|
|
|
|
|
fprintf(out, " -g, --generate generate target list (only if no -f specified)\n");
|
|
|
|
|
fprintf(out, " (give start and end IP in the target list, or a CIDR address)\n");
|
|
|
|
|
fprintf(out, " (ex. %s -g 192.168.1.0 192.168.1.255 or %s -g 192.168.1.0/24)\n", prog, prog);
|
|
|
|
|
fprintf(out, " -H, --ttl=N set the IP TTL value (Time To Live hops)\n");
|
|
|
|
|
fprintf(out, " -i, --interval=MSEC interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
|
|
|
|
|
#ifdef SO_BINDTODEVICE
|
|
|
|
|
fprintf(out, " -I, --iface=IFACE bind to a particular interface\n");
|
|
|
|
|
#endif
|
|
|
|
|
#ifdef SO_MARK
|
|
|
|
|
fprintf(out, " -k, --fwmark=FWMARK set the routing mark\n");
|
|
|
|
|
#endif
|
|
|
|
|
fprintf(out, " -l, --loop loop mode: send pings forever\n");
|
|
|
|
|
fprintf(out, " -m, --all use all IPs of provided hostnames (e.g. IPv4 and IPv6), use with -A\n");
|
|
|
|
@ -3033,7 +2922,7 @@ void usage(int is_error)
|
|
|
|
|
fprintf(out, " -p, --period=MSEC interval between ping packets to one target (in ms)\n");
|
|
|
|
|
fprintf(out, " (in loop and count modes, default: %.0f ms)\n", perhost_interval / 1e6);
|
|
|
|
|
fprintf(out, " -r, --retry=N number of retries (default: %d)\n", DEFAULT_RETRY);
|
|
|
|
|
fprintf(out, " -Z, --nonzero non-zero packet data (same payload as ping 0xA ...)\n");
|
|
|
|
|
fprintf(out, " -R, --random random packet data (to foil link data compression)\n");
|
|
|
|
|
fprintf(out, " -S, --src=IP set source address\n");
|
|
|
|
|
fprintf(out, " -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
|
|
|
|
|
fprintf(out, " except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
|
|
|
|
@ -3041,21 +2930,19 @@ void usage(int is_error)
|
|
|
|
|
fprintf(out, "Output options:\n");
|
|
|
|
|
fprintf(out, " -a, --alive show targets that are alive\n");
|
|
|
|
|
fprintf(out, " -A, --addr show targets by address\n");
|
|
|
|
|
fprintf(out, " -C, --vcount=N same as -c, report results (not stats) in verbose format\n");
|
|
|
|
|
fprintf(out, " -C, --vcount=N same as -c, report results in verbose format\n");
|
|
|
|
|
fprintf(out, " -d, --rdns show targets by name (force reverse-DNS lookup)\n");
|
|
|
|
|
fprintf(out, " -D, --timestamp print timestamp before each output line\n");
|
|
|
|
|
fprintf(out, " --timestamp-format=FORMAT show timestamp in the given format (-D required): ctime|iso|rfc3339\n");
|
|
|
|
|
fprintf(out, " -e, --elapsed show elapsed time on return packets\n");
|
|
|
|
|
fprintf(out, " -i, --interval=MSEC interval between sending ping packets (default: %.0f ms)\n", interval / 1e6);
|
|
|
|
|
fprintf(out, " -n, --name show targets by name (reverse-DNS lookup for target IPs)\n");
|
|
|
|
|
fprintf(out, " -N, --netdata output compatible for netdata (-l -Q are required)\n");
|
|
|
|
|
fprintf(out, " -o, --outage show the accumulated outage time (lost packets * packet interval)\n");
|
|
|
|
|
fprintf(out, " -q, --quiet quiet (don't show per-target/per-ping results)\n");
|
|
|
|
|
fprintf(out, " -Q, --squiet=SECS[,cumulative] same as -q, but add interval summary every SECS seconds,\n");
|
|
|
|
|
fprintf(out, " with 'cumulative', print stats since beginning\n");
|
|
|
|
|
fprintf(out, " -Q, --squiet=SECS same as -q, but add interval summary every SECS seconds\n");
|
|
|
|
|
fprintf(out, " -s, --stats print final stats\n");
|
|
|
|
|
fprintf(out, " -u, --unreach show targets that are unreachable\n");
|
|
|
|
|
fprintf(out, " -v, --version show version\n");
|
|
|
|
|
fprintf(out, " -x, --reachable=N shows if >=N hosts are reachable or not\n");
|
|
|
|
|
fprintf(out, " -X, --fast-reachable=N exits true immediately when N hosts are found\n");
|
|
|
|
|
exit(is_error);
|
|
|
|
|
}
|
|
|
|
|