diff --git a/ChangeLog b/ChangeLog index 22d313a..087ecbd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,15 @@ 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) 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 -q not suppressing some ICMP error messages (#83) - * (bugfix) Fix -M expecting an argument, when it shouldn't + * (bugfix) Fix option -q not suppressing some ICMP error messages (#83) + * (bugfix) Fix option -M expecting an argument, when it shouldn't * (bugfix) Fix minor issues found by Coverity Scan 2017-01-11 David Schweikert diff --git a/ci/test-01-basics.pl b/ci/test-01-basics.pl index c855a27..f18aa36 100755 --- a/ci/test-01-basics.pl +++ b/ci/test-01-basics.pl @@ -13,7 +13,7 @@ use Test::More; # ping ::1 SKIP: { - system("/sbin/ifconfig >&2"); + #system("/sbin/ifconfig >&2"); if(system("/sbin/ifconfig | grep inet6") != 0) { skip 'No IPv6 on this host', 3; } diff --git a/ci/test-02-help.pl b/ci/test-02-help.pl index 2c70b53..717a9b0 100755 --- a/ci/test-02-help.pl +++ b/ci/test-02-help.pl @@ -11,6 +11,8 @@ $cmd1->exit_is_num(0); $cmd1->stdout_is_eq(<new(cmd => "fping -H 300 127.0.0.1"); $cmd5->exit_is_num(1); $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 my $cmd6 = Test::Command->new(cmd => "fping -a -u 127.0.0.1"); diff --git a/ci/test-04-options-a-b.pl b/ci/test-04-options-a-b.pl index 6619597..fa716b6 100755 --- a/ci/test-04-options-a-b.pl +++ b/ci/test-04-options-a-b.pl @@ -1,14 +1,57 @@ #!/usr/bin/perl -w -use Test::Command tests => 14; +use Test::Command tests => 29; use Test::More; 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 by address # -b n amount of ping data to send, in bytes (default 56) # -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 { my $cmd = Test::Command->new(cmd => "fping -a 127.0.0.1 127.0.0.2"); diff --git a/ci/test-06-options-f-h.pl b/ci/test-06-options-f-h.pl index b5eaac5..a0e2998 100755 --- a/ci/test-06-options-f-h.pl +++ b/ci/test-06-options-f-h.pl @@ -66,7 +66,7 @@ $cmd->stderr_is_eq(""); my $cmd = Test::Command->new(cmd => "fping -g 127.0.0.2/33"); $cmd->exit_is_num(1); $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 diff --git a/ci/test-14-ping-internet-hosts.pl b/ci/test-14-ping-internet-hosts.pl index 6e31053..a71a850 100755 --- a/ci/test-14-ping-internet-hosts.pl +++ b/ci/test-14-ping-internet-hosts.pl @@ -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(""); } -# 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->stdout_is_eq("google-public-dns-a.google.com (8.8.8.8) is alive\n"); $cmd->stderr_is_eq(""); @@ -46,7 +46,7 @@ SKIP: { if(system("/sbin/ifconfig | grep inet6.*Scope:Global") != 0) { 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->stdout_is_eq("google-public-dns-a.google.com (2001:4860:4860::8888) is alive\n"); $cmd->stderr_is_eq(""); diff --git a/ci/test-issue-58.pl b/ci/test-issue-58.pl index 46c7010..db5161a 100755 --- a/ci/test-issue-58.pl +++ b/ci/test-issue-58.pl @@ -7,4 +7,4 @@ use Test::Command tests => 3; my $cmd1 = Test::Command->new(cmd => "fping -a -g 2001:db8:120:4161::4/64"); $cmd1->exit_is_num(1); $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"); diff --git a/configure.ac b/configure.ac index b6bbc49..acb1ebc 100644 --- a/configure.ac +++ b/configure.ac @@ -7,26 +7,16 @@ AC_INIT([fping],[3.16-rc1]) dnl make ipv4 and ipv6 options AC_ARG_ENABLE([ipv4], - [ --enable-ipv4 Build IPv4 capable fping], - [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]) + AS_HELP_STRING([--enable-ipv4], [(ignored for compatibility with previous versions)])) AC_ARG_ENABLE([ipv6], - [ --enable-ipv6 Build IPv6 capable fping6], + AS_HELP_STRING([--enable-ipv6], [Build IPv6 capable fping6]), [case "${enableval}" in yes) ipv6=true ;; no) ipv6=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-ipv6]) ;; - esac],[ipv6=false]) - 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 + esac],[ipv6=true]) +AM_CONDITIONAL([IPV6], [test x$ipv6 = xtrue]) AC_ARG_ENABLE([timestamp], AS_HELP_STRING([--disable-timestamp], [Disable kernel-based packet timestaping (SO_TIMESTAMP)])) diff --git a/doc/Makefile.am b/doc/Makefile.am index f452955..33e4cff 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,8 +1,6 @@ man_MANS = -if IPV4 man_MANS += fping.8 -endif if IPV6 man_MANS += fping6.8 diff --git a/doc/fping.pod b/doc/fping.pod index d38bec2..922ea93 100644 --- a/doc/fping.pod +++ b/doc/fping.pod @@ -29,6 +29,15 @@ addresses instead of IPv4. =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> Show systems that are alive. diff --git a/src/fping.c b/src/fping.c index 011f021..df5d934 100644 --- a/src/fping.c +++ b/src/fping.c @@ -236,11 +236,9 @@ char *prog; int ident; /* our pid */ int socket4 = 0; #ifndef IPV6 -int *allsocket[2] = { &socket4, NULL }; int hints_ai_family = AF_INET; #else int socket6 = 0; -int *allsocket[3] = { &socket4, &socket6, NULL }; int hints_ai_family = AF_UNSPEC; #endif @@ -381,26 +379,40 @@ int main( int argc, char **argv ) switch( c ) { 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; - // FIXME: check that -4 and -6 not used together break; 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; - // 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; case 'M': #ifdef IP_MTU_DISCOVER - { + if(socket4) { int val = IP_PMTUDISC_DO; - int **sp; - for(sp=&allsocket[0]; *sp; sp++) { - if (setsockopt(**sp, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) { - perror("setsockopt IP_MTU_DISCOVER"); - } + if (setsockopt(socket4, 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 - fprintf(stderr, "-M option not supported on this platform\n"); + fprintf(stderr, "%s, -M option not supported on this platform\n", prog); exit(1); #endif break; @@ -564,14 +576,18 @@ int main( int argc, char **argv ) case 'I': #ifdef SO_BINDTODEVICE - { - int **sp; - for(sp=&allsocket[0]; *sp; sp++) { - if (setsockopt(**sp, SOL_SOCKET, SO_BINDTODEVICE, optarg, strlen(optarg))) { - perror("binding to specific interface (SO_BINTODEVICE)"); - } + if(socket4) { + if (setsockopt(socket4, 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 printf( "%s: cant bind to a particular net interface since SO_BINDTODEVICE is not supported on your os.\n", argv[0] ); exit(3);; @@ -584,12 +600,18 @@ int main( int argc, char **argv ) case 'O': if (sscanf(optarg,"%i",&tos)){ - int **sp; - for(sp=&allsocket[0]; *sp; sp++) { - if ( setsockopt(**sp, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) { + if(socket4) { + if ( setsockopt(socket4, IPPROTO_IP, IP_TOS, &tos, sizeof(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 { usage(1); @@ -608,23 +630,28 @@ int main( int argc, char **argv ) }/* SWITCH */ }/* WHILE */ + /* if we are called 'fping6', assume '-6' */ + if(strstr(argv[0], "fping6")) { + hints_ai_family = AF_INET6; + } + /* validate various option settings */ 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); } 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); }/* IF */ 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); }/* 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) */ if(ttl > 0) { - int **sp; - for(sp=&allsocket[0]; *sp; sp++) { - if (setsockopt(**sp, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) { + if(socket4) { + if (setsockopt(socket4, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) { 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 { int opt = 1; - int **sp; - for(sp=&allsocket[0]; *sp; sp++) { - if (setsockopt(**sp, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) { + if(socket4) { + if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) { 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 @@ -937,18 +976,18 @@ void add_cidr(char *addr) addr_hints.ai_flags = AI_NUMERICHOST; ret = getaddrinfo(addr, NULL, &addr_hints, &addr_res); 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); } 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); } net_addr = ntohl(((struct sockaddr_in *) addr_res->ai_addr)->sin_addr.s_addr); /* check mask */ 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); } @@ -992,12 +1031,12 @@ void add_range(char *start, char *end) addr_hints.ai_flags = AI_NUMERICHOST; ret = getaddrinfo(start, NULL, &addr_hints, &addr_res); 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); } if(addr_res->ai_family != AF_INET) { 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); } 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; ret = getaddrinfo(end, NULL, &addr_hints, &addr_res); 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); } if(addr_res->ai_family != AF_INET) { 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); } end_long = ntohl(((struct sockaddr_in *) addr_res->ai_addr)->sin_addr.s_addr); freeaddrinfo(addr_res); 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); } @@ -2688,6 +2727,8 @@ void usage(int is_error) FILE *out = is_error ? stderr : stdout; fprintf(out, "\n" ); 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 by address\n" ); fprintf(out, " -b n amount of ping data to send, in bytes (default %d)\n", DEFAULT_PING_DATA_SIZE);