unification work

pull/89/merge
David Schweikert 8 years ago
parent 12971e5409
commit 98269fb83e

@ -1,9 +1,15 @@
Unreleased Unreleased
* (feature) Unified 'fping' and 'fping6' into one binary
* (feature) New option '-4' to force IPv4
* (feature) New option '-6' to force IPv6
* (feature) Support kernel-timestamping of received packets (#46) * (feature) Support kernel-timestamping of received packets (#46)
* (feature) Simplify restrictions: only -i >= 1 and -p >= 10 are enforced now * (feature) Simplify restrictions: only -i >= 1 and -p >= 10 are enforced now
* (bugfix) Fix option -H (ttl) for IPv6
* (bugfix) Fix option -M (don't fragment) for IPv6
* (bugfix) Fix option -O (ToS) for IPv6
* (bugfix) Fix compatibility issue with AIX (#69, @blentzgh) * (bugfix) Fix compatibility issue with AIX (#69, @blentzgh)
* (bugfix) Fix -q not suppressing some ICMP error messages (#83) * (bugfix) Fix option -q not suppressing some ICMP error messages (#83)
* (bugfix) Fix -M expecting an argument, when it shouldn't * (bugfix) Fix option -M expecting an argument, when it shouldn't
* (bugfix) Fix minor issues found by Coverity Scan * (bugfix) Fix minor issues found by Coverity Scan
2017-01-11 David Schweikert <david@schweikert.ch> 2017-01-11 David Schweikert <david@schweikert.ch>

@ -13,7 +13,7 @@ use Test::More;
# ping ::1 # ping ::1
SKIP: { SKIP: {
system("/sbin/ifconfig >&2"); #system("/sbin/ifconfig >&2");
if(system("/sbin/ifconfig | grep inet6") != 0) { if(system("/sbin/ifconfig | grep inet6") != 0) {
skip 'No IPv6 on this host', 3; skip 'No IPv6 on this host', 3;
} }

@ -11,6 +11,8 @@ $cmd1->exit_is_num(0);
$cmd1->stdout_is_eq(<<END); $cmd1->stdout_is_eq(<<END);
Usage: fping [options] [targets...] Usage: fping [options] [targets...]
-4 only use IPv4 addresses
-6 only use IPv6 addresses
-a show targets that are alive -a show targets that are alive
-A show targets by address -A show targets by address
-b n amount of ping data to send, in bytes (default 56) -b n amount of ping data to send, in bytes (default 56)

@ -24,7 +24,7 @@ END
my $cmd5 = Test::Command->new(cmd => "fping -H 300 127.0.0.1"); my $cmd5 = Test::Command->new(cmd => "fping -H 300 127.0.0.1");
$cmd5->exit_is_num(1); $cmd5->exit_is_num(1);
$cmd5->stdout_is_eq(""); $cmd5->stdout_is_eq("");
$cmd5->stderr_is_eq("ttl 300 out of range\n"); $cmd5->stderr_is_eq("fping: ttl 300 out of range\n");
# fping -a -u # fping -a -u
my $cmd6 = Test::Command->new(cmd => "fping -a -u 127.0.0.1"); my $cmd6 = Test::Command->new(cmd => "fping -a -u 127.0.0.1");

@ -1,14 +1,57 @@
#!/usr/bin/perl -w #!/usr/bin/perl -w
use Test::Command tests => 14; use Test::Command tests => 29;
use Test::More; use Test::More;
use Time::HiRes qw(gettimeofday tv_interval); use Time::HiRes qw(gettimeofday tv_interval);
# -4 only use IPv4 addresses
# -6 only use IPv6 addresses
# -a show targets that are alive # -a show targets that are alive
# -A show targets by address # -A show targets by address
# -b n amount of ping data to send, in bytes (default 56) # -b n amount of ping data to send, in bytes (default 56)
# -B f set exponential backoff factor to f # -B f set exponential backoff factor to f
# fping -4 -6
{
my $cmd = Test::Command->new(cmd => "fping -4 -6 127.0.0.1");
$cmd->exit_is_num(1);
$cmd->stdout_is_eq("");
$cmd->stderr_is_eq("fping: can't specify both -4 and -6\n");
}
# fping -4
{
my $cmd = Test::Command->new(cmd => "fping -4 127.0.0.1");
$cmd->exit_is_num(0);
$cmd->stdout_is_eq("127.0.0.1 is alive\n");
$cmd->stderr_is_eq("");
}
{
my $cmd = Test::Command->new(cmd => "fping -4 ::1");
$cmd->exit_is_num(2);
$cmd->stdout_is_eq("");
$cmd->stderr_is_eq("::1: Address family for hostname not supported\n");
}
# fping -6
SKIP: {
if(system("/sbin/ifconfig | grep inet6") != 0) {
skip 'No IPv6 on this host', 3;
}
my $cmd = Test::Command->new(cmd => "fping -6 ::1");
$cmd->exit_is_num(0);
$cmd->stdout_is_eq("::1 is alive\n");
$cmd->stderr_is_eq("");
}
{
my $cmd = Test::Command->new(cmd => "fping -6 127.0.0.1");
$cmd->exit_is_num(2);
$cmd->stdout_is_eq("");
$cmd->stderr_is_eq("127.0.0.1: Address family for hostname not supported\n");
}
# fping -a # fping -a
{ {
my $cmd = Test::Command->new(cmd => "fping -a 127.0.0.1 127.0.0.2"); my $cmd = Test::Command->new(cmd => "fping -a 127.0.0.1 127.0.0.2");

@ -66,7 +66,7 @@ $cmd->stderr_is_eq("");
my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/33"); my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/33");
$cmd->exit_is_num(1); $cmd->exit_is_num(1);
$cmd->stdout_is_eq(""); $cmd->stdout_is_eq("");
$cmd->stderr_is_eq("Error: netmask must be between 1 and 32 (is: 33)\n"); $cmd->stderr_is_eq("fping: netmask must be between 1 and 32 (is: 33)\n");
} }
# fping -H # fping -H

@ -33,9 +33,9 @@ $cmd->stdout_is_eq("google-public-dns-a.google.com (8.8.8.8) is alive\n");
$cmd->stderr_is_eq(""); $cmd->stderr_is_eq("");
} }
# fping -A -n # fping -4 -A -n
{ {
my $cmd = Test::Command->new(cmd => "fping -A -n google-public-dns-a.google.com"); my $cmd = Test::Command->new(cmd => "fping -4 -A -n google-public-dns-a.google.com");
$cmd->exit_is_num(0); $cmd->exit_is_num(0);
$cmd->stdout_is_eq("google-public-dns-a.google.com (8.8.8.8) is alive\n"); $cmd->stdout_is_eq("google-public-dns-a.google.com (8.8.8.8) is alive\n");
$cmd->stderr_is_eq(""); $cmd->stderr_is_eq("");
@ -46,7 +46,7 @@ SKIP: {
if(system("/sbin/ifconfig | grep inet6.*Scope:Global") != 0) { if(system("/sbin/ifconfig | grep inet6.*Scope:Global") != 0) {
skip 'No IPv6 on this host', 3; skip 'No IPv6 on this host', 3;
} }
my $cmd = Test::Command->new(cmd => "fping -n -A 2001:4860:4860::8888"); my $cmd = Test::Command->new(cmd => "fping -6 -n -A google-public-dns-a.google.com");
$cmd->exit_is_num(0); $cmd->exit_is_num(0);
$cmd->stdout_is_eq("google-public-dns-a.google.com (2001:4860:4860::8888) is alive\n"); $cmd->stdout_is_eq("google-public-dns-a.google.com (2001:4860:4860::8888) is alive\n");
$cmd->stderr_is_eq(""); $cmd->stderr_is_eq("");

@ -7,4 +7,4 @@ use Test::Command tests => 3;
my $cmd1 = Test::Command->new(cmd => "fping -a -g 2001:db8:120:4161::4/64"); my $cmd1 = Test::Command->new(cmd => "fping -a -g 2001:db8:120:4161::4/64");
$cmd1->exit_is_num(1); $cmd1->exit_is_num(1);
$cmd1->stdout_is_eq(""); $cmd1->stdout_is_eq("");
$cmd1->stderr_is_eq("Error: -g works only with IPv4 addresses\n"); $cmd1->stderr_is_eq("fping: -g works only with IPv4 addresses\n");

@ -7,26 +7,16 @@ AC_INIT([fping],[3.16-rc1])
dnl make ipv4 and ipv6 options dnl make ipv4 and ipv6 options
AC_ARG_ENABLE([ipv4], AC_ARG_ENABLE([ipv4],
[ --enable-ipv4 Build IPv4 capable fping], AS_HELP_STRING([--enable-ipv4], [(ignored for compatibility with previous versions)]))
[case "${enableval}" in
yes) ipv4=true ;;
no) ipv4=false ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-ipv4]) ;;
esac],[ipv4=true])
AM_CONDITIONAL([IPV4], [test x$ipv4 = xtrue])
AC_ARG_ENABLE([ipv6], AC_ARG_ENABLE([ipv6],
[ --enable-ipv6 Build IPv6 capable fping6], AS_HELP_STRING([--enable-ipv6], [Build IPv6 capable fping6]),
[case "${enableval}" in [case "${enableval}" in
yes) ipv6=true ;; yes) ipv6=true ;;
no) ipv6=false ;; no) ipv6=false ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-ipv6]) ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-ipv6]) ;;
esac],[ipv6=false]) esac],[ipv6=true])
AM_CONDITIONAL([IPV6], [test x$ipv6 = xtrue]) AM_CONDITIONAL([IPV6], [test x$ipv6 = xtrue])
if test x$ipv4 = xfalse && test x$ipv6 = xfalse; then
AC_MSG_ERROR([You must enable at least one of IPv4 and IPv6.])
fi
AC_ARG_ENABLE([timestamp], AC_ARG_ENABLE([timestamp],
AS_HELP_STRING([--disable-timestamp], [Disable kernel-based packet timestaping (SO_TIMESTAMP)])) AS_HELP_STRING([--disable-timestamp], [Disable kernel-based packet timestaping (SO_TIMESTAMP)]))

@ -1,8 +1,6 @@
man_MANS = man_MANS =
if IPV4
man_MANS += fping.8 man_MANS += fping.8
endif
if IPV6 if IPV6
man_MANS += fping6.8 man_MANS += fping6.8

@ -29,6 +29,15 @@ addresses instead of IPv4.
=over 5 =over 5
=item B<-4>
Restrict name resolution and IPs to IPv4 addresses.
=item B<-6>
Restrict name resolution and IPs to IPv6 addresses. If the program name is
'fping6' (via a sym-link, for example), then '-6' is implicitly added.
=item B<-a> =item B<-a>
Show systems that are alive. Show systems that are alive.

@ -236,11 +236,9 @@ char *prog;
int ident; /* our pid */ int ident; /* our pid */
int socket4 = 0; int socket4 = 0;
#ifndef IPV6 #ifndef IPV6
int *allsocket[2] = { &socket4, NULL };
int hints_ai_family = AF_INET; int hints_ai_family = AF_INET;
#else #else
int socket6 = 0; int socket6 = 0;
int *allsocket[3] = { &socket4, &socket6, NULL };
int hints_ai_family = AF_UNSPEC; int hints_ai_family = AF_UNSPEC;
#endif #endif
@ -381,26 +379,40 @@ int main( int argc, char **argv )
switch( c ) switch( c )
{ {
case '4': case '4':
if(hints_ai_family != AF_UNSPEC) {
fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
exit(1);
}
hints_ai_family = AF_INET; hints_ai_family = AF_INET;
// FIXME: check that -4 and -6 not used together
break; break;
case '6': case '6':
#ifdef IPV6
if(hints_ai_family != AF_UNSPEC) {
fprintf(stderr, "%s: can't specify both -4 and -6\n", prog);
exit(1);
}
hints_ai_family = AF_INET6; hints_ai_family = AF_INET6;
// FIXME: check that -4 and -6 not used together #else
fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog);
exit(1);
#endif
break; break;
case 'M': case 'M':
#ifdef IP_MTU_DISCOVER #ifdef IP_MTU_DISCOVER
{ if(socket4) {
int val = IP_PMTUDISC_DO; int val = IP_PMTUDISC_DO;
int **sp; if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) {
for(sp=&allsocket[0]; *sp; sp++) { perror("setsockopt IP_MTU_DISCOVER");
if (setsockopt(**sp, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) { }
perror("setsockopt IP_MTU_DISCOVER"); }
} if(socket6) {
int val = IPV6_PMTUDISC_DO;
if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) {
perror("setsockopt IPV6_MTU_DISCOVER");
} }
} }
#else #else
fprintf(stderr, "-M option not supported on this platform\n"); fprintf(stderr, "%s, -M option not supported on this platform\n", prog);
exit(1); exit(1);
#endif #endif
break; break;
@ -564,14 +576,18 @@ int main( int argc, char **argv )
case 'I': case 'I':
#ifdef SO_BINDTODEVICE #ifdef SO_BINDTODEVICE
{ if(socket4) {
int **sp; if (setsockopt(socket4, SOL_SOCKET, SO_BINDTODEVICE, optarg, strlen(optarg))) {
for(sp=&allsocket[0]; *sp; sp++) { perror("binding to specific interface (SO_BINTODEVICE)");
if (setsockopt(**sp, SOL_SOCKET, SO_BINDTODEVICE, optarg, strlen(optarg))) {
perror("binding to specific interface (SO_BINTODEVICE)");
}
} }
} }
#ifdef IPV6
if(socket6) {
if (setsockopt(socket6, SOL_SOCKET, SO_BINDTODEVICE, optarg, strlen(optarg))) {
perror("binding to specific interface (SO_BINTODEVICE), IPV6");
}
}
#endif
#else #else
printf( "%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", argv[0] ); printf( "%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", argv[0] );
exit(3);; exit(3);;
@ -584,12 +600,18 @@ int main( int argc, char **argv )
case 'O': case 'O':
if (sscanf(optarg,"%i",&tos)){ if (sscanf(optarg,"%i",&tos)){
int **sp; if(socket4) {
for(sp=&allsocket[0]; *sp; sp++) { if ( setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
if ( setsockopt(**sp, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
perror("setting type of service octet IP_TOS"); perror("setting type of service octet IP_TOS");
} }
} }
#ifdef IPV6
if(socket6) {
if ( setsockopt(socket6, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) {
perror("setting type of service octet IPV6_TCLASS");
}
}
#endif
} }
else { else {
usage(1); usage(1);
@ -608,23 +630,28 @@ int main( int argc, char **argv )
}/* SWITCH */ }/* SWITCH */
}/* WHILE */ }/* WHILE */
/* if we are called 'fping6', assume '-6' */
if(strstr(argv[0], "fping6")) {
hints_ai_family = AF_INET6;
}
/* validate various option settings */ /* validate various option settings */
if (ttl > 255) { if (ttl > 255) {
fprintf(stderr, "ttl %u out of range\n", ttl); fprintf(stderr, "%s: ttl %u out of range\n", prog, ttl);
exit(1); exit(1);
} }
if( unreachable_flag && alive_flag ) if( unreachable_flag && alive_flag )
{ {
fprintf( stderr, "%s: specify only one of a, u\n", argv[0] ); fprintf( stderr, "%s: specify only one of a, u\n", prog);
exit(1); exit(1);
}/* IF */ }/* IF */
if( count_flag && loop_flag ) if( count_flag && loop_flag )
{ {
fprintf( stderr, "%s: specify only one of c, l\n", argv[0] ); fprintf( stderr, "%s: specify only one of c, l\n", prog);
exit(1); exit(1);
}/* IF */ }/* IF */
@ -740,23 +767,35 @@ int main( int argc, char **argv )
/* set the TTL, if the -H option was set (otherwise ttl will be = 0) */ /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */
if(ttl > 0) { if(ttl > 0) {
int **sp; if(socket4) {
for(sp=&allsocket[0]; *sp; sp++) { if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
if (setsockopt(**sp, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) {
perror("setting time to live"); perror("setting time to live");
} }
} }
#ifdef IPV6
if(socket6) {
if (setsockopt(socket6, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) {
perror("setting time to live");
}
}
#endif
} }
#if HAVE_SO_TIMESTAMP #if HAVE_SO_TIMESTAMP
{ {
int opt = 1; int opt = 1;
int **sp; if(socket4) {
for(sp=&allsocket[0]; *sp; sp++) { if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
if (setsockopt(**sp, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
perror("setting SO_TIMESTAMP option"); perror("setting SO_TIMESTAMP option");
} }
} }
#ifdef IPV6
if(socket6) {
if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
perror("setting SO_TIMESTAMP option (IPv6)");
}
}
#endif
} }
#endif #endif
@ -937,18 +976,18 @@ void add_cidr(char *addr)
addr_hints.ai_flags = AI_NUMERICHOST; addr_hints.ai_flags = AI_NUMERICHOST;
ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res); ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res);
if(ret) { if(ret) {
fprintf(stderr, "Error: can't parse address %s: %s\n", addr, gai_strerror(ret)); fprintf(stderr, "%s, can't parse address %s: %s\n", prog, addr, gai_strerror(ret));
exit(1); exit(1);
} }
if(addr_res->ai_family != AF_INET) { if(addr_res->ai_family != AF_INET) {
fprintf(stderr, "Error: -g works only with IPv4 addresses\n"); fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
exit(1); exit(1);
} }
net_addr = ntohl(((struct sockaddr_in *) addr_res->ai_addr)->sin_addr.s_addr); net_addr = ntohl(((struct sockaddr_in *) addr_res->ai_addr)->sin_addr.s_addr);
/* check mask */ /* check mask */
if(mask < 1 || mask > 32) { if(mask < 1 || mask > 32) {
fprintf(stderr, "Error: netmask must be between 1 and 32 (is: %s)\n", mask_str); fprintf(stderr, "%s: netmask must be between 1 and 32 (is: %s)\n", prog, mask_str);
exit(1); exit(1);
} }
@ -992,12 +1031,12 @@ void add_range(char *start, char *end)
addr_hints.ai_flags = AI_NUMERICHOST; addr_hints.ai_flags = AI_NUMERICHOST;
ret = getaddrinfo(start, NULL, &addr_hints, &addr_res); ret = getaddrinfo(start, NULL, &addr_hints, &addr_res);
if(ret) { if(ret) {
fprintf(stderr, "Error: can't parse address %s: %s\n", start, gai_strerror(ret)); fprintf(stderr, "%s: can't parse address %s: %s\n", prog, start, gai_strerror(ret));
exit(1); exit(1);
} }
if(addr_res->ai_family != AF_INET) { if(addr_res->ai_family != AF_INET) {
freeaddrinfo(addr_res); freeaddrinfo(addr_res);
fprintf(stderr, "Error: -g works only with IPv4 addresses\n"); fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
exit(1); exit(1);
} }
start_long = ntohl(((struct sockaddr_in *) addr_res->ai_addr)->sin_addr.s_addr); start_long = ntohl(((struct sockaddr_in *) addr_res->ai_addr)->sin_addr.s_addr);
@ -1008,19 +1047,19 @@ void add_range(char *start, char *end)
addr_hints.ai_flags = AI_NUMERICHOST; addr_hints.ai_flags = AI_NUMERICHOST;
ret = getaddrinfo(end, NULL, &addr_hints, &addr_res); ret = getaddrinfo(end, NULL, &addr_hints, &addr_res);
if(ret) { if(ret) {
fprintf(stderr, "Error: can't parse address %s: %s\n", end, gai_strerror(ret)); fprintf(stderr, "%s: can't parse address %s: %s\n", prog, end, gai_strerror(ret));
exit(1); exit(1);
} }
if(addr_res->ai_family != AF_INET) { if(addr_res->ai_family != AF_INET) {
freeaddrinfo(addr_res); freeaddrinfo(addr_res);
fprintf(stderr, "Error: -g works only with IPv4 addresses\n"); fprintf(stderr, "%s: -g works only with IPv4 addresses\n", prog);
exit(1); exit(1);
} }
end_long = ntohl(((struct sockaddr_in *) addr_res->ai_addr)->sin_addr.s_addr); end_long = ntohl(((struct sockaddr_in *) addr_res->ai_addr)->sin_addr.s_addr);
freeaddrinfo(addr_res); freeaddrinfo(addr_res);
if(end_long > start_long + MAX_GENERATE) { if(end_long > start_long + MAX_GENERATE) {
fprintf(stderr, "Error: -g parameter generates too many addresses\n"); fprintf(stderr, "%s: -g parameter generates too many addresses\n", prog);
exit(1); exit(1);
} }
@ -2688,6 +2727,8 @@ void usage(int is_error)
FILE *out = is_error ? stderr : stdout; FILE *out = is_error ? stderr : stdout;
fprintf(out, "\n" ); fprintf(out, "\n" );
fprintf(out, "Usage: %s [options] [targets...]\n", prog ); fprintf(out, "Usage: %s [options] [targets...]\n", prog );
fprintf(out, " -4 only use IPv4 addresses\n" );
fprintf(out, " -6 only use IPv6 addresses\n" );
fprintf(out, " -a show targets that are alive\n" ); fprintf(out, " -a show targets that are alive\n" );
fprintf(out, " -A show targets by address\n" ); fprintf(out, " -A show targets by address\n" );
fprintf(out, " -b n amount of ping data to send, in bytes (default %d)\n", DEFAULT_PING_DATA_SIZE); fprintf(out, " -b n amount of ping data to send, in bytes (default %d)\n", DEFAULT_PING_DATA_SIZE);

Loading…
Cancel
Save