diff --git a/ci/test-02-help.pl b/ci/test-02-help.pl index 5a146fb8..0f66d13d 100755 --- a/ci/test-02-help.pl +++ b/ci/test-02-help.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -use Test::Command tests => 12; +use Test::Command tests => 15; my $I_HELP = " -I, --iface=IFACE bind to a particular interface\n"; $I_HELP = '' if $^O eq 'darwin'; @@ -38,3 +38,9 @@ $cmd3->exit_is_num(1); $cmd3->stdout_is_eq(""); $cmd3->stderr_like(qr{^fping: (illegal|invalid) option -- '?Z'?\nsee 'fping -h' for usage information\n$}); + +# fping with unknown long option +my $cmd5 = Test::Command->new(cmd => "fping --unknown-long-option"); +$cmd5->exit_is_num(1); +$cmd5->stdout_is_eq(""); +$cmd5->stderr_like(qr{^fping: (illegal|invalid) option -- '?unknown-long-option'?\nsee 'fping -h' for usage information\n$}); diff --git a/ci/test-05-options-c-e.pl b/ci/test-05-options-c-e.pl index 9884d116..400cd319 100755 --- a/ci/test-05-options-c-e.pl +++ b/ci/test-05-options-c-e.pl @@ -1,11 +1,13 @@ #!/usr/bin/perl -w -use Test::Command tests => 51; +use Test::Command tests => 63; +use Test::More; -# -c n count of pings to send to each target (default 1) -# -C n same as -c, report results in verbose format -# -D print timestamp before each output line -# -e show elapsed time on return packets +# -c n count of pings to send to each target (default 1) +# -C n same as -c, report results in verbose format +# --check-source discard replies not from target address +# -D print timestamp before each output line +# -e show elapsed time on return packets # fping -c n { @@ -126,6 +128,57 @@ }); } +# fping --check-source +{ +my $cmd = Test::Command->new(cmd => "fping --check-source 127.0.0.1 127.0.0.2"); +$cmd->exit_is_num(0); +$cmd->stdout_is_eq("127.0.0.1 is alive\n127.0.0.2 is alive\n"); +$cmd->stderr_is_eq(""); +} + +# fping --check-source (to IPv6 multicast address -> accept no reply) +SKIP: { + if($ENV{SKIP_IPV6}) { + skip 'Skip IPv6 tests', 3; + } + my $cmd = Test::Command->new(cmd => "fping --check-source ff02::1"); + $cmd->exit_is_num(1); + $cmd->stdout_is_eq("ff02::1 is unreachable\n"); + $cmd->stderr_is_eq(""); +} + +# fping -c N --check-source +SKIP: { + if($ENV{SKIP_IPV6}) { + skip 'Skip IPv6 tests', 3; + } + my $cmd = Test::Command->new(cmd => "fping -c1 --check-source 127.0.0.1 ff02::1"); + $cmd->exit_is_num(1); + $cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) +ff02::1 : \[0\], timed out \(NaN avg, 100% loss\) +}); + $cmd->stderr_like(qr{ +127\.0\.0\.1 : xmt/rcv/%loss = 1/1/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+ +ff02::1 : xmt/rcv/%loss = 1/0/100% +}); +} + +# fping -C N --check-source +SKIP: { + if($ENV{SKIP_IPV6}) { + skip 'Skip IPv6 tests', 3; + } + my $cmd = Test::Command->new(cmd => "fping -C1 --check-source 127.0.0.1 ff02::1"); + $cmd->exit_is_num(1); + $cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 64 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\) +ff02::1 : \[0\], timed out \(NaN avg, 100% loss\) +}); + $cmd->stderr_like(qr{ +127\.0\.0\.1 : \d\.\d+ +ff02::1 : - +}); +} + # fping -D { my $cmd = Test::Command->new(cmd => "fping -D -c 2 -p 100 127.0.0.1"); diff --git a/doc/fping.pod b/doc/fping.pod index 3fd73322..6b591d90 100644 --- a/doc/fping.pod +++ b/doc/fping.pod @@ -81,6 +81,13 @@ shows the response time in milliseconds for each of the five requests, with the C<-> indicating that no response was received to the fourth request. This option overrides B<-a> or B<-u>. +=item B<--check-source> + +Discard Echo replies that are sourced from a different address than the target +address. This avoids spurious reachability results on busy monitoring systems +where two B instances with the same lower 16 bits of the process ID may +be running at the same time. + =item B<-d>, B<--rdns> Use DNS to lookup address of ping target. This allows you to give fping diff --git a/src/fping.c b/src/fping.c index f7fe6a94..905a1f67 100644 --- a/src/fping.c +++ b/src/fping.c @@ -360,6 +360,7 @@ int timestamp_flag = 0; int timestamp_format_flag = 0; int random_data_flag = 0; int cumulative_stats_flag = 0; +int check_source_flag = 0; #if defined(DEBUG) || defined(_DEBUG) int randomly_lose_flag, trace_flag, print_per_system_flag; int lose_factor; @@ -556,6 +557,7 @@ int main(int argc, char **argv) { "version", 'v', OPTPARSE_NONE }, { "reachable", 'x', OPTPARSE_REQUIRED }, { "fast-reachable", 'X', OPTPARSE_REQUIRED }, + { "check-source", '0', OPTPARSE_NONE }, #if defined(DEBUG) || defined(_DEBUG) { NULL, 'z', OPTPARSE_REQUIRED }, #endif @@ -576,6 +578,10 @@ int main(int argc, char **argv) }else{ usage(1); } + } else if (strstr(optparse_state.optlongname, "check-source") != NULL) { + check_source_flag = 1; + } else { + usage(1); } break; case '4': @@ -2436,6 +2442,12 @@ int wait_for_reply(int64_t wait_time) dbg_printf("received [%d] from %s\n", this_count, h->host); + /* optionally require reply source equal to target address */ + if (check_source_flag && addr_cmp((struct sockaddr *)&response_addr, (struct sockaddr *)&h->saddr)) { + dbg_printf("discarding reply from wrong source address\n"); + return 1; + } + /* discard duplicates */ if (!loop_flag && h->resp_times[this_count] >= 0) { if (!per_recv_flag) { @@ -3037,6 +3049,7 @@ void usage(int is_error) 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"); + fprintf(out, " --check-source discard replies not from target address\n"); fprintf(out, "\n"); fprintf(out, "Output options:\n"); fprintf(out, " -a, --alive show targets that are alive\n");