diff --git a/ChangeLog b/ChangeLog index 8809ff4..aa657ac 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +Unreleased + * INCOMPATIBILITY WARNING: + fping and fping6 are now unified into one binary. This means that, for + example, doing 'fping www.google.com' is going to ping the IPv6 IP of + www.google.com on IPv6-enabled hosts. + + If you need exact compatibility with old versions, you can configure, + compile, and install fping twice: once for ipv4 and once for ipv6: + - ./configure --disable-ipv6; make clean install + - ./configure --disable-ipv4 --program-suffix=6; make clean install + + Or, alternatively, you could write two wrappers 'fping' and 'fping6', + that set respectively the options '-4' and '-6' when calling the original + fping binary. + + * Version 4.0 + * (feature) Unified 'fping' and 'fping6' into one binary (#80) + * (feature) --enable-ipv6 is now default + * (feature) New option '-4' to force IPv4 + * (feature) New option '-6' to force IPv6 + 2017-02-09 David Schweikert * Version 3.16 * (feature) Support kernel-timestamping of received packets (#46) diff --git a/ci/build-2-install.sh b/ci/build-2-install.sh index 2eb7ccc..247eaef 100755 --- a/ci/build-2-install.sh +++ b/ci/build-2-install.sh @@ -13,7 +13,6 @@ autoreconf -i make CFLAGS="-g -fprofile-arcs -ftest-coverage" ## setcap currently doesn't work anymore on travis-ci #sudo setcap cap_net_raw+ep src/fping -#sudo setcap cap_net_raw+ep src/fping6 ## setcap debugging: #pwd #df -k . @@ -26,6 +25,4 @@ make CFLAGS="-g -fprofile-arcs -ftest-coverage" # use setuid, since setcap is not available sudo chown root src/fping -sudo chown root src/fping6 sudo chmod u+s src/fping -sudo chmod u+s src/fping6 diff --git a/ci/prepare-linux.sh b/ci/prepare-linux.sh index 41504f1..275a56f 100755 --- a/ci/prepare-linux.sh +++ b/ci/prepare-linux.sh @@ -1,7 +1,6 @@ #!/bin/bash sudo setcap cap_net_raw+ep src/fping -sudo setcap cap_net_raw+ep src/fping6 if [[ ! $PATH =~ fping/src ]]; then echo "# WARNING: must set PATH:" diff --git a/ci/test-01-basics.pl b/ci/test-01-basics.pl index 15fc5f8..f18aa36 100755 --- a/ci/test-01-basics.pl +++ b/ci/test-01-basics.pl @@ -17,7 +17,7 @@ SKIP: { if(system("/sbin/ifconfig | grep inet6") != 0) { skip 'No IPv6 on this host', 3; } - my $cmd = Test::Command->new(cmd => "fping6 ::1"); + my $cmd = Test::Command->new(cmd => "fping ::1"); $cmd->exit_is_num(0); $cmd->stdout_is_eq("::1 is alive\n"); $cmd->stderr_is_eq(""); diff --git a/ci/test-02-help.pl b/ci/test-02-help.pl index 83446ac..d3efe26 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"); @@ -38,11 +38,11 @@ $cmd7->exit_is_num(1); $cmd7->stdout_is_eq(""); $cmd7->stderr_is_eq("fping: specify only one of c, l\n"); -# fping -b 65489 -my $cmd8 = Test::Command->new(cmd => "fping -b 65489 127.0.0.1"); +# fping -b 65509 +my $cmd8 = Test::Command->new(cmd => "fping -b 65509 127.0.0.1"); $cmd8->exit_is_num(1); $cmd8->stdout_is_eq(""); -$cmd8->stderr_is_eq("fping: data size 65489 not valid, must be lower than 65488\n"); +$cmd8->stderr_is_eq("fping: data size 65509 not valid, must be lower than 65488\n"); # fping -B 0.9 my $cmd9 = Test::Command->new(cmd => "fping -B 0.9 127.0.0.1"); diff --git a/ci/test-04-options-a-b.pl b/ci/test-04-options-a-b.pl index 2288931..7958a3c 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_like(qr{^::1:.*(not supported|not known)}); +} + +# 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_like(qr{127\.0\.0\.1:.*(not supported|not known)}); +} + # fping -a { my $cmd = Test::Command->new(cmd => "fping -a 127.0.0.1 127.0.0.2"); @@ -19,7 +62,7 @@ $cmd->stderr_is_eq(""); # fping -A { -my $cmd = Test::Command->new(cmd => "fping -A localhost"); +my $cmd = Test::Command->new(cmd => "fping -4 -A localhost"); $cmd->exit_is_num(0); $cmd->stdout_is_eq("127.0.0.1 is alive\n"); $cmd->stderr_is_eq(""); diff --git a/ci/test-05-options-c-e.pl b/ci/test-05-options-c-e.pl index ec84817..b9f9500 100755 --- a/ci/test-05-options-c-e.pl +++ b/ci/test-05-options-c-e.pl @@ -9,7 +9,7 @@ use Test::Command tests => 12; # fping -c n { -my $cmd = Test::Command->new(cmd => "fping -c 2 -p 100 localhost 127.0.0.1"); +my $cmd = Test::Command->new(cmd => "fping -4 -c 2 -p 100 localhost 127.0.0.1"); $cmd->exit_is_num(0); $cmd->stdout_like(qr{localhost : \[0\], 84 bytes, 0\.\d+ ms \(0\.\d+ avg, 0% loss\) 127\.0\.0\.1 : \[0\], 84 bytes, 0\.\d+ ms \(0.\d+ avg, 0% loss\) @@ -24,7 +24,7 @@ $cmd->stderr_like(qr{localhost : xmt/rcv/%loss = 2/2/0%, min/avg/max = 0\.\d+/0\ # fping -C n { -my $cmd = Test::Command->new(cmd => "fping -C 2 -p 100 localhost 127.0.0.1"); +my $cmd = Test::Command->new(cmd => "fping -4 -C 2 -p 100 localhost 127.0.0.1"); $cmd->exit_is_num(0); $cmd->stdout_like(qr{localhost : \[0\], 84 bytes, 0\.\d+ ms \(0\.\d+ avg, 0% loss\) 127\.0\.0\.1 : \[0\], 84 bytes, 0\.\d+ ms \(0.\d+ avg, 0% loss\) 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-09-option-r-t.pl b/ci/test-09-option-r-t.pl index 501f70e..eae162d 100755 --- a/ci/test-09-option-r-t.pl +++ b/ci/test-09-option-r-t.pl @@ -22,7 +22,7 @@ SKIP: { if(system("/sbin/ifconfig | grep inet6") != 0) { skip 'No IPv6 on this host', 3; } - my $cmd = Test::Command->new(cmd => "fping6 -q -R -c3 -p100 ::1"); + my $cmd = Test::Command->new(cmd => "fping -q -R -c3 -p100 ::1"); $cmd->exit_is_num(0); $cmd->stdout_is_eq(""); $cmd->stderr_like(qr{::1 : xmt/rcv/%loss = 3/3/0%.*}); @@ -84,12 +84,12 @@ $cmd->stdout_is_eq("127.0.0.1 is alive\n"); $cmd->stderr_is_eq(""); } -# fping6 -S +# fping -S SKIP: { if(system("/sbin/ifconfig | grep inet6") != 0) { skip 'No IPv6 on this host', 3; } - my $cmd = Test::Command->new(cmd => "fping6 -S ::1 ::1"); + my $cmd = Test::Command->new(cmd => "fping -S ::1 ::1"); $cmd->exit_is_num(0); $cmd->stdout_is_eq("::1 is alive\n"); $cmd->stderr_is_eq(""); diff --git a/ci/test-11-nopriv.pl b/ci/test-11-nopriv.pl index bd2065f..904b382 100755 --- a/ci/test-11-nopriv.pl +++ b/ci/test-11-nopriv.pl @@ -7,13 +7,11 @@ if( $^O eq 'darwin' ) { plan skip_all => 'Test irrelevant on MacOS'; exit 0; } -plan tests => 6; +plan tests => 3; # run without privileges my $fping_bin = `which fping`; chomp $fping_bin; -my $fping6_bin = `which fping6`; chomp $fping6_bin; system("cp $fping_bin /tmp/fping.copy; chmod +x /tmp/fping.copy"); -system("cp $fping6_bin /tmp/fping6.copy; chmod +x /tmp/fping6.copy"); # fping { @@ -22,11 +20,3 @@ $cmd->exit_is_num(4); $cmd->stdout_is_eq(""); $cmd->stderr_like(qr{: can't create socket \(must run as root\?\) : .*\n}); } - -# fping6 -{ -my $cmd = Test::Command->new(cmd => "/tmp/fping6.copy ::1"); -$cmd->exit_is_num(4); -$cmd->stdout_is_eq(""); -$cmd->stderr_like(qr{: can't create raw socket \(must run as root\?\) : .*\n}); -} diff --git a/ci/test-13-unknown-host.pl b/ci/test-13-unknown-host.pl index 8094c05..65c8312 100755 --- a/ci/test-13-unknown-host.pl +++ b/ci/test-13-unknown-host.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -use Test::Command tests => 6; +use Test::Command tests => 3; # fping { @@ -9,11 +9,3 @@ $cmd->exit_is_num(2); $cmd->stdout_is_eq(""); $cmd->stderr_like(qr{^nosuchname\.example\.com: .*not (known|found)}); } - -# fping6 -{ -my $cmd = Test::Command->new(cmd => "fping6 nosuchname.example.com"); -$cmd->exit_is_num(2); -$cmd->stdout_is_eq(""); -$cmd->stderr_like(qr{^nosuchname\.example\.com: .*not (known|found)}); -} diff --git a/ci/test-14-ping-internet-hosts.pl b/ci/test-14-ping-internet-hosts.pl index 4b395e1..ed6cfcd 100755 --- a/ci/test-14-ping-internet-hosts.pl +++ b/ci/test-14-ping-internet-hosts.pl @@ -9,7 +9,7 @@ if(!gethostbyname("www.google.com")) { exit 0; } -plan tests => 18; +plan tests => 21; my $re_num = qr{\d+(?:\.\d+)?}; @@ -33,35 +33,35 @@ $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(""); } -# fping6 -A -n +# fping -A -n (IPv6) SKIP: { if(system("/sbin/ifconfig | grep inet6.*Scope:Global") != 0) { skip 'No IPv6 on this host', 3; } - my $cmd = Test::Command->new(cmd => "fping6 -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(""); } # fping -m -#SKIP: { -# if(system("/sbin/ifconfig | grep inet6.*Scope:Global") != 0) { -# skip 'No IPv6 on this host', 3; -# } -# my $cmd = Test::Command->new(cmd => "fping -A -m google-public-dns-a.google.com"); -# $cmd->exit_is_num(0); -# $cmd->stdout_is_eq("2001:4860:4860::8888 is alive\n8.8.8.8 is alive\n"); -# $cmd->stderr_is_eq(""); -#} +SKIP: { + if(system("/sbin/ifconfig | grep inet6.*Scope:Global") != 0) { + skip 'No IPv6 on this host', 3; + } + my $cmd = Test::Command->new(cmd => "fping -A -m google-public-dns-a.google.com"); + $cmd->exit_is_num(0); + $cmd->stdout_is_eq("2001:4860:4860::8888 is alive\n8.8.8.8 is alive\n"); + $cmd->stderr_is_eq(""); +} # fping -n { 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 f2ea4f7..94a6db7 100644 --- a/configure.ac +++ b/configure.ac @@ -3,35 +3,38 @@ dnl Process this file with autoconf to produce a configure script. dnl Minimum Autoconf version required. AC_PREREQ(2.59) -AC_INIT([fping],[3.16]) +AC_INIT([fping],[3.16-rc2]) -dnl make ipv4 and ipv6 options +dnl --disable-ipv4 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([--disable-ipv4], [Disable support for pinging IPv4 hosts])) +AM_CONDITIONAL([IPV4], [test "x$enable_ipv4" != "xno"]) +AM_COND_IF([IPV4], [AC_DEFINE([IPV4], [1], [IPv4 enabled])]) +dnl --disable-ipv6 AC_ARG_ENABLE([ipv6], - [ --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 + AS_HELP_STRING([--disable-ipv6], [Disable support for pinging IPv6 hosts])) +AS_IF([test "x$enable_ipv6" != "xno"], [ + dnl Test if IPv6 is supported + AC_CHECK_HEADERS([netinet/icmp6.h], [have_ipv6="yes"], [], [[ + #include + ]]) +]) +dnl Can't disable both IPv4 and IPv6 +AS_IF([test "x$enable_ipv4" = "xno" -a "x$enable_ipv6" = "xno"], [ + AC_MSG_ERROR([Need to enable IPv4 or IPv6. Can't disable both!)]) +]) +dnl IPv6 required, but not supported? +AS_IF([test \( "x$enable_ipv6" = "xyes" -o "x$enable_ipv4" = "xno" \) -a "x$have_ipv6" != "xyes" ], [ + AC_MSG_ERROR([IPv6 not supported on this platform (netinet/icmp6.h header not found)]) +]) +AM_CONDITIONAL([IPV6], [test "x$have_ipv6" = "xyes"]) +AM_COND_IF([IPV6], [AC_DEFINE([IPV6], [1], [IPv6 enabled])]) AC_ARG_ENABLE([timestamp], AS_HELP_STRING([--disable-timestamp], [Disable kernel-based packet timestaping (SO_TIMESTAMP)])) AS_IF([test "x$enable_timestamp" != "xno"], [ - AC_CHECK_DECL([SO_TIMESTAMP], [AC_DEFINE(HAVE_SO_TIMESTAMP, [1], [set define])], [have_so_timestamp="no"], [#include + AC_CHECK_DECL([SO_TIMESTAMP], [AC_DEFINE(HAVE_SO_TIMESTAMP, [1], [SO_TIMESTAMP is defined])], [have_so_timestamp="no"], [#include #include ]) ]) dnl Test if --enable-timestamp is explicitely enabled and make an error if this platform doesn't support it @@ -80,7 +83,7 @@ AH_BOTTOM([ ]) dnl Checks for header files. -AC_CHECK_HEADERS(unistd.h sys/file.h stdlib.h sys/select.h) +AC_CHECK_HEADERS([unistd.h sys/file.h stdlib.h sys/select.h]) AC_CONFIG_FILES([Makefile doc/Makefile diff --git a/doc/Makefile.am b/doc/Makefile.am index f452955..450e158 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,17 +1,6 @@ -man_MANS = +man_MANS = fping.8 -if IPV4 -man_MANS += fping.8 -endif - -if IPV6 -man_MANS += fping6.8 -endif - -EXTRA_DIST = fping.8 fping6.8 fping.pod README.1992 +EXTRA_DIST = fping.8 fping.pod README.1992 fping.8: fping.pod pod2man -c "" -s 8 -r "fping" $< >$@ - -fping6.8: fping.pod - pod2man -c "" -s 8 -r "fping" -n fping6 $< >$@ diff --git a/doc/fping.pod b/doc/fping.pod index e4acfd2..0b3917f 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/Makefile.am b/src/Makefile.am index 4dcfae8..c518d34 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,18 +1,11 @@ AM_CFLAGS = -Wall -Wextra -Wno-sign-compare -prog = +sbin_PROGRAMS = fping + +fping_SOURCES = fping.c seqmap.c socket4.c fping.h options.h seqmap.h +fping_DEPENDENCIES = ../config.h -if IPV4 -prog += fping -endif if IPV6 -prog += fping6 +fping_SOURCES += socket6.c +fping_CFLAGS = $(AM_CFLAGS) -DIPV6 endif - -sbin_PROGRAMS = ${prog} - -fping_SOURCES = fping.c seqmap.c socket.c socket4.c fping.h options.h seqmap.h -fping_DEPENDENCIES = ../config.h -fping6_SOURCES = fping.c seqmap.c socket.c socket6.c fping.h options.h seqmap.h -fping6_DEPENDENCIES = ../config.h -fping6_CFLAGS = $(AM_CFLAGS) -DIPV6 diff --git a/src/fping.c b/src/fping.c index 0b958e9..f00a13e 100644 --- a/src/fping.c +++ b/src/fping.c @@ -79,15 +79,9 @@ extern "C" { #include #include -/* RS6000 hasn't getopt.h */ -#ifdef HAVE_GETOPT_H #include -#endif /* HAVE_GETOPT_H */ -/* RS6000 has sys/select.h */ -#ifdef HAVE_SYS_SELECT_H #include -#endif /* HAVE_SYS_SELECT_H */ /*** externals ***/ @@ -109,11 +103,7 @@ extern int h_errno; #define MAX_IP_PACKET 65536 /* (theoretical) max IP packet size */ #define SIZE_IP_HDR 40 -#ifndef IPV6 -#define SIZE_ICMP_HDR ICMP_MINLEN /* from ip_icmp.h */ -#else -#define SIZE_ICMP_HDR sizeof(FPING_ICMPHDR) -#endif +#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 100000 /* maximum number of hosts that -g can generate */ @@ -239,7 +229,14 @@ HOST_ENTRY* ev_last; char* prog; int ident; /* our pid */ -int s; /* socket */ +int socket4 = 0; +#ifndef IPV6 +int hints_ai_family = AF_INET; +#else +int socket6 = 0; +int hints_ai_family = AF_UNSPEC; +#endif + unsigned int debugging = 0; /* times get *100 because all times are calculated in 10 usec units, not ms */ @@ -253,11 +250,11 @@ unsigned int count = 1; unsigned int trials; unsigned int report_interval = 0; unsigned int ttl = 0; -int src_addr_present = 0; -#ifndef IPV6 +int src_addr_set = 0; struct in_addr src_addr; -#else -struct in6_addr src_addr; +#ifdef IPV6 +int src_addr6_set = 0; +struct in6_addr src_addr6; #endif /* global stats */ @@ -307,9 +304,8 @@ char* na_cat(char* name, struct in_addr ipaddr); void crash_and_burn(char* message); void errno_crash_and_burn(char* message); char* get_host_by_address(struct in_addr in); -int recvfrom_wto(int s, char* buf, int len, struct sockaddr* saddr, socklen_t* saddr_len, long timo); void remove_job(HOST_ENTRY* h); -int send_ping(int s, HOST_ENTRY* h); +int send_ping(HOST_ENTRY* h); long timeval_diff(struct timeval* a, struct timeval* b); void timeval_add(struct timeval* a, long t_10u); void usage(int); @@ -320,7 +316,6 @@ void print_netdata(void); void print_global_stats(void); void main_loop(); void finish(); -int handle_random_icmp(FPING_ICMPHDR* p, struct sockaddr* addr, socklen_t addr_len); char* sprint_tm(int t); void ev_enqueue(HOST_ENTRY* h); HOST_ENTRY* ev_dequeue(); @@ -356,7 +351,10 @@ int main(int argc, char** argv) prog = argv[0]; - s = open_ping_socket(ping_data_size); + socket4 = open_ping_socket_ipv4(ping_data_size); +#ifdef IPV6 + socket6 = open_ping_socket_ipv6(ping_data_size); +#endif if ((uid = getuid())) { /* drop privileges */ @@ -371,25 +369,48 @@ int main(int argc, char** argv) /* get command line options */ - while ((c = getopt(argc, argv, "ADMNRadeghlmnoqsuvzB:C:H:I:O:Q:S:T:b:c:f:i:p:r:t:")) != EOF) { + while ((c = getopt(argc, argv, "46ADMNRadeghlmnoqsuvzB:C:H:I:O:Q:S:T:b:c:f:i:p:r:t:")) != EOF) { switch (c) { - case 'M': -#ifdef IP_MTU_DISCOVER - { - int val = IP_PMTUDISC_DO; -#ifndef IPV6 - if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) { + 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; + 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; #else - if (setsockopt(s, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) { + fprintf(stderr, "%s: IPv6 not supported by this binary\n", prog); + exit(1); #endif - perror("setsockopt IP_MTU_DISCOVER"); + break; + case 'M': +#ifdef IP_MTU_DISCOVER + if (socket4) { + int val = IP_PMTUDISC_DO; + if (setsockopt(socket4, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val))) { + perror("setsockopt IP_MTU_DISCOVER"); + } } - } +#ifdef IPV6 + if (socket6) { + int val = IPV6_PMTUDISC_DO; + if (setsockopt(socket6, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, sizeof(val))) { + perror("setsockopt IPV6_MTU_DISCOVER"); + } + } +#endif #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; + break; case 't': if (!(timeout = (unsigned int)atoi(optarg) * 100)) @@ -533,20 +554,33 @@ int main(int argc, char** argv) break; case 'S': -#ifndef IPV6 - if (!inet_pton(AF_INET, optarg, &src_addr)) -#else - if (!inet_pton(AF_INET6, optarg, &src_addr)) + if (inet_pton(AF_INET, optarg, &src_addr)) { + src_addr_set = 1; + break; + } +#ifdef IPV6 + if (inet_pton(AF_INET6, optarg, &src_addr6)) { + src_addr6_set = 1; + break; + } #endif - usage(1); - src_addr_present = 1; + usage(1); break; case 'I': #ifdef SO_BINDTODEVICE - if (setsockopt(s, 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); @@ -560,13 +594,18 @@ int main(int argc, char** argv) case 'O': if (sscanf(optarg, "%i", &tos)) { -#ifndef IPV6 - if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) { -#else - if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))) { -#endif - perror("setting type of service octet IP_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); } @@ -580,24 +619,29 @@ int main(int argc, char** argv) fprintf(stderr, "see 'fping -h' for usage information\n"); exit(1); break; + } } + /* 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 (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); } @@ -714,21 +758,35 @@ int main(int argc, char** argv) /* set the TTL, if the -H option was set (otherwise ttl will be = 0) */ if (ttl > 0) { -#ifndef IPV6 - if (setsockopt(s, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) { -#else - if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))) { -#endif - perror("setting time to live"); + 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; - if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) { - perror("setting SO_TIMESTAMP option"); + 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 @@ -798,9 +856,15 @@ int main(int argc, char** argv) exit(num_noaddress ? 2 : 1); } - if (src_addr_present) { - socket_set_src_addr(s, src_addr); +#ifndef IPV6 + if (src_addr_set) { + socket_set_src_addr_ipv4(socket4, &src_addr); + } +#else + if (src_addr6_set) { + socket_set_src_addr_ipv6(socket6, &src_addr6); } +#endif /* allocate array to hold outstanding ping requests */ @@ -832,7 +896,10 @@ int main(int argc, char** argv) cursor = cursor->ev_next; } - init_ping_buffer(ping_data_size); + init_ping_buffer_ipv4(ping_data_size); +#ifdef IPV6 + init_ping_buffer_ipv6(ping_data_size); +#endif signal(SIGINT, finish); @@ -888,18 +955,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); } @@ -942,12 +1009,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); @@ -958,15 +1025,15 @@ 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); + end_long = ntohl(((struct sockaddr_in*)addr_res->ai_addr)->sin_addr.s_addr); freeaddrinfo(addr_res); if (end_long > start_long + MAX_GENERATE) { @@ -1005,8 +1072,7 @@ void main_loop() h = ev_dequeue(); /* Send the ping */ - /*printf("Sending ping after %d ms\n", lt/100); */ - send_ping(s, h); + send_ping(h); /* Check what needs to be done next */ if (!loop_flag && !count_flag) { @@ -1055,7 +1121,7 @@ void main_loop() } } - wait_for_reply: + wait_for_reply: /* When can we expect the next event? */ if (ev_first) { @@ -1177,7 +1243,6 @@ void finish() exit(1); exit(0); - } /************************************************************ @@ -1477,7 +1542,7 @@ void print_global_stats(void) ************************************************************/ -int send_ping(int s, HOST_ENTRY* h) +int send_ping(HOST_ENTRY* h) { int n; int myseq; @@ -1491,7 +1556,17 @@ int send_ping(int s, HOST_ENTRY* h) printf("sending [%d] to %s\n", h->num_sent, h->host); #endif /* DEBUG || _DEBUG */ - n = socket_sendto_ping(s, (struct sockaddr*)&h->saddr, h->saddr_len, myseq, ident); + if (h->saddr.ss_family == AF_INET) { + n = socket_sendto_ping_ipv4(socket4, (struct sockaddr*)&h->saddr, h->saddr_len, myseq, ident); + } +#ifdef IPV6 + else if (h->saddr.ss_family == AF_INET6) { + n = socket_sendto_ping_ipv6(socket6, (struct sockaddr*)&h->saddr, h->saddr_len, myseq, ident); + } +#endif + else { + return 0; + } if ( (n < 0) @@ -1528,17 +1603,27 @@ int send_ping(int s, HOST_ENTRY* h) return (ret); } -int wait_on_socket(int socket, struct timeval* timeout) +int socket_can_read(struct timeval* timeout) { int nfound; fd_set readset, writeset; + int socketmax; + +#ifndef IPV6 + socketmax = socket4; +#else + socketmax = socket4 > socket6 ? socket4 : socket6; +#endif select_again: FD_ZERO(&readset); FD_ZERO(&writeset); - FD_SET(s, &readset); + FD_SET(socket4, &readset); +#ifdef IPV6 + FD_SET(socket6, &readset); +#endif - nfound = select(socket + 1, &readset, &writeset, NULL, timeout); + nfound = select(socketmax + 1, &readset, &writeset, NULL, timeout); if (nfound < 0) { if (errno == EINTR) { /* interrupted system call: redo the select */ @@ -1548,14 +1633,21 @@ select_again: } } - if (nfound == 0) - return 0; - else - return 1; + if (nfound > 0) { + if (FD_ISSET(socket4, &readset)) { + return socket4; + } +#ifdef IPV6 + if (FD_ISSET(socket6, &readset)) { + return socket6; + } +#endif + } + + return 0; } -int receive_reply(int socket, - struct timeval* timeout, +int receive_packet(int socket, struct timeval* reply_timestamp, struct sockaddr* reply_src_addr, size_t reply_src_addr_len, @@ -1563,63 +1655,252 @@ int receive_reply(int socket, size_t reply_buf_len) { int recv_len; + static unsigned char msg_control[40]; + struct iovec msg_iov = { + reply_buf, + reply_buf_len + }; + struct msghdr recv_msghdr = { + reply_src_addr, + reply_src_addr_len, + &msg_iov, + 1, + &msg_control, + sizeof(msg_control), + 0 + }; + int timestamp_set = 0; + struct cmsghdr* cmsg; + + recv_len = recvmsg(socket, &recv_msghdr, 0); + if (recv_len <= 0) { + return 0; + } - /* Wait for input on the socket */ - if (timeout && !wait_on_socket(socket, timeout)) { - return 0; /* timeout */ +#if HAVE_SO_TIMESTAMP + /* ancilliary data */ + for (cmsg = CMSG_FIRSTHDR(&recv_msghdr); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) { + memcpy(reply_timestamp, CMSG_DATA(cmsg), sizeof(*reply_timestamp)); + timestamp_set = 1; + } } +#endif - /* Receive data */ - { - static unsigned char msg_control[40]; - struct iovec msg_iov = { - reply_buf, - reply_buf_len - }; - struct msghdr recv_msghdr = { - reply_src_addr, - reply_src_addr_len, - &msg_iov, - 1, - &msg_control, - sizeof(msg_control), - 0 - }; - int timestamp_set = 0; - - recv_len = recvmsg(socket, &recv_msghdr, 0); - if (recv_len <= 0) { + if (!timestamp_set) { + gettimeofday(reply_timestamp, NULL); + } + +#if defined(DEBUG) || defined(_DEBUG) + if (randomly_lose_flag) { + if ((random() & 0x07) <= lose_factor) + return 0; + } +#endif + + return recv_len; +} + +int decode_icmp_ipv4( + struct sockaddr* response_addr, + size_t response_addr_len, + char* reply_buf, + size_t reply_buf_len, + unsigned short* id, + unsigned short* seq) +{ + struct ip* ip = (struct ip*)reply_buf; + struct icmp* icp; + int hlen = 0; + +#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. + */ + hlen = (ip->ip_vhl & 0x0F) << 2; +#else + hlen = ip->ip_hl << 2; +#endif + + if (reply_buf_len < hlen + ICMP_MINLEN) { + /* too short */ + if (verbose_flag) { + char buf[INET6_ADDRSTRLEN]; + 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; + } + + icp = (struct icmp*)(reply_buf + hlen); + + if (icp->icmp_type != ICMP_ECHOREPLY) { + /* Handle other ICMP packets */ + struct icmp* sent_icmp; + SEQMAP_VALUE* seqmap_value; + char addr_ascii[INET6_ADDRSTRLEN]; + HOST_ENTRY* h; + + /* reply icmp packet (hlen + ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */ + if (reply_buf_len < hlen + ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) { + /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */ return 0; } -#if HAVE_SO_TIMESTAMP - /* ancilliary data */ - struct cmsghdr* cmsg; - for (cmsg = CMSG_FIRSTHDR(&recv_msghdr); - cmsg != NULL; - cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) { - memcpy(reply_timestamp, CMSG_DATA(cmsg), sizeof(*reply_timestamp)); - timestamp_set = 1; + sent_icmp = (struct icmp*)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip)); + + if (sent_icmp->icmp_type != ICMP_ECHO || ntohs(sent_icmp->icmp_id) != ident) { + /* not caused by us */ + return 0; + } + + seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp_seq), ¤t_time); + if (seqmap_value == NULL) { + return 0; + } + + getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST); + + switch (icp->icmp_type) { + case ICMP_UNREACH: + h = table[seqmap_value->host_nr]; + if (icp->icmp_code > ICMP_UNREACH_MAXTYPE) { + print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s", + addr_ascii, h->host); + } else { + print_warning("%s from %s for ICMP Echo sent to %s", + icmp_unreach_str[icp->icmp_code], addr_ascii, h->host); + } + + print_warning("\n"); + num_othericmprcvd++; + break; + + case ICMP_SOURCEQUENCH: + case ICMP_REDIRECT: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + h = table[seqmap_value->host_nr]; + if (icp->icmp_type <= ICMP_TYPE_STR_MAX) { + print_warning("%s from %s for ICMP Echo sent to %s", + icmp_type_str[icp->icmp_type], addr_ascii, h->host); + } else { + print_warning("ICMP %d from %s for ICMP Echo sent to %s", + icp->icmp_type, addr_ascii, h->host); } + print_warning("\n"); + num_othericmprcvd++; + break; } -#endif - if (!timestamp_set) { - gettimeofday(reply_timestamp, NULL); + return 0; + } + + *id = ntohs(icp->icmp_id); + *seq = ntohs(icp->icmp_seq); + + return 1; +} + +#ifdef IPV6 +int decode_icmp_ipv6( + struct sockaddr* response_addr, + size_t response_addr_len, + char* reply_buf, + size_t reply_buf_len, + unsigned short* id, + unsigned short* seq) +{ + struct icmp6_hdr* icp; + + if (reply_buf_len < sizeof(struct icmp6_hdr)) { + if (verbose_flag) { + char buf[INET6_ADDRSTRLEN]; + 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 */ } - return recv_len; + icp = (struct icmp6_hdr*)reply_buf; + + if (icp->icmp6_type != ICMP6_ECHO_REPLY) { + /* Handle other ICMP packets */ + struct icmp6_hdr* sent_icmp; + SEQMAP_VALUE* seqmap_value; + char addr_ascii[INET6_ADDRSTRLEN]; + HOST_ENTRY* h; + + /* reply icmp packet (ICMP_MINLEN) followed by "sent packet" (ip + icmp headers) */ + if (reply_buf_len < ICMP_MINLEN + sizeof(struct ip) + ICMP_MINLEN) { + /* discard ICMP message if we can't tell that it was caused by us (i.e. if the "sent packet" is not included). */ + return 0; + } + + sent_icmp = (struct icmp6_hdr*)(reply_buf + sizeof(struct icmp6_hdr) + sizeof(struct ip)); + + if (sent_icmp->icmp6_type != ICMP_ECHO || ntohs(sent_icmp->icmp6_id) != ident) { + /* not caused by us */ + return 0; + } + + seqmap_value = seqmap_fetch(ntohs(sent_icmp->icmp6_seq), ¤t_time); + if (seqmap_value == NULL) { + return 0; + } + + getnameinfo(response_addr, response_addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST); + + switch (icp->icmp6_type) { + case ICMP_UNREACH: + h = table[seqmap_value->host_nr]; + if (icp->icmp6_code > ICMP_UNREACH_MAXTYPE) { + print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s", + addr_ascii, h->host); + } else { + print_warning("%s from %s for ICMP Echo sent to %s", + icmp_unreach_str[icp->icmp6_code], addr_ascii, h->host); + } + + print_warning("\n"); + num_othericmprcvd++; + break; + + case ICMP_SOURCEQUENCH: + case ICMP_REDIRECT: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + h = table[seqmap_value->host_nr]; + if (icp->icmp6_type <= ICMP_TYPE_STR_MAX) { + print_warning("%s from %s for ICMP Echo sent to %s", + icmp_type_str[icp->icmp6_type], addr_ascii, h->host); + } else { + print_warning("ICMP %d from %s for ICMP Echo sent to %s", + icp->icmp6_type, addr_ascii, h->host); + } + print_warning("\n"); + num_othericmprcvd++; + break; + } + + return 0; + } + + *id = ntohs(icp->icmp6_id); + *seq = ntohs(icp->icmp6_seq); + + return 1; } +#endif int wait_for_reply(long wait_time) { int result; static char buffer[4096]; struct sockaddr_storage response_addr; - int hlen = 0; - FPING_ICMPHDR* icp; int n, avg; HOST_ENTRY* h; long this_reply; @@ -1627,95 +1908,78 @@ int wait_for_reply(long wait_time) struct timeval* sent_time; struct timeval recv_time; SEQMAP_VALUE* seqmap_value; -#ifndef IPV6 - struct ip* ip; -#endif - - /* receive packet */ - { - struct timeval to; - if (wait_time) { - if (wait_time < 100000) { - to.tv_sec = 0; - to.tv_usec = wait_time * 10; - } else { - to.tv_sec = wait_time / 100000; - to.tv_usec = (wait_time % 100000) * 10; - } - } - - result = receive_reply(s, /* socket */ - wait_time ? &to : NULL, /* timeout */ - &recv_time, /* reply_timestamp */ - (struct sockaddr*)&response_addr, /* reply_src_addr */ - sizeof(response_addr), /* reply_src_addr_len */ - buffer, /* reply_buf */ - sizeof(buffer) /* reply_buf_len */ - ); + unsigned short id; + unsigned short seq; + struct timeval to; + int s = 0; - if (result <= 0) { - return 0; + /* Wait for a socket to become ready */ + if (wait_time) { + if (wait_time < 100000) { + to.tv_sec = 0; + to.tv_usec = wait_time * 10; + } else { + to.tv_sec = wait_time / 100000; + to.tv_usec = (wait_time % 100000) * 10; } + } else { + to.tv_sec = 0; + to.tv_usec = 0; } - -#if defined(DEBUG) || defined(_DEBUG) - if (randomly_lose_flag) { - if ((random() & 0x07) <= lose_factor) - return 0; + s = socket_can_read(&to); + if (s == 0) { + return 0; /* timeout */ } -#endif -#ifndef IPV6 - ip = (struct ip*)buffer; -#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. - */ - hlen = (ip->ip_vhl & 0x0F) << 2; -#else - hlen = ip->ip_hl << 2; -#endif /* defined(__alpha__) && __STDC__ */ - if (result < hlen + ICMP_MINLEN) -#else - if (result < sizeof(FPING_ICMPHDR)) -#endif - { - if (verbose_flag) { - char buf[INET6_ADDRSTRLEN]; - 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", result, buf); - } - return (1); /* too short */ + /* Receive packet */ + result = receive_packet(s, /* socket */ + &recv_time, /* reply_timestamp */ + (struct sockaddr*)&response_addr, /* reply_src_addr */ + sizeof(response_addr), /* reply_src_addr_len */ + buffer, /* reply_buf */ + sizeof(buffer) /* reply_buf_len */ + ); + + if (result <= 0) { + return 0; } gettimeofday(¤t_time, &tz); - icp = (FPING_ICMPHDR*)(buffer + hlen); -#ifndef IPV6 - if (icp->icmp_type != ICMP_ECHOREPLY) -#else - if (icp->icmp6_type != ICMP6_ECHO_REPLY) + /* Process ICMP packet and retrieve id/seq */ + if (response_addr.ss_family == AF_INET) { + if (!decode_icmp_ipv4( + (struct sockaddr*)&response_addr, + sizeof(response_addr), + buffer, + sizeof(buffer), + &id, + &seq)) { + return 1; + } + } +#ifdef IPV6 + else if (response_addr.ss_family == AF_INET6) { + if (!decode_icmp_ipv6( + (struct sockaddr*)&response_addr, + sizeof(response_addr), + buffer, + sizeof(buffer), + &id, + &seq)) { + return 1; + } + } #endif - { - /* handle some problem */ - if (handle_random_icmp(icp, (struct sockaddr*)&response_addr, sizeof(response_addr))) - num_othericmprcvd++; + else { return 1; } -#ifndef IPV6 - if (ntohs(icp->icmp_id) != ident) -#else - if (ntohs(icp->icmp6_id) != ident) -#endif + if (id != ident) { return 1; /* packet received, but not the one we are looking for! */ + } -#ifndef IPV6 - seqmap_value = seqmap_fetch(ntohs(icp->icmp_seq), ¤t_time); -#else - seqmap_value = seqmap_fetch(ntohs(icp->icmp6_seq), ¤t_time); -#endif + seqmap_value = seqmap_fetch(seq, ¤t_time); if (seqmap_value == NULL) { return 1; } @@ -1855,107 +2119,6 @@ int wait_for_reply(long wait_time) return num_jobs; } -/************************************************************ - - Function: handle_random_icmp - -************************************************************/ - -int handle_random_icmp(FPING_ICMPHDR* p, struct sockaddr* addr, socklen_t addr_len) -{ - FPING_ICMPHDR* sent_icmp; - unsigned char* c; - HOST_ENTRY* h; - SEQMAP_VALUE* seqmap_value; - char addr_ascii[INET6_ADDRSTRLEN]; - unsigned short icmp_type; - unsigned short icmp_code; - unsigned short sent_icmp_type; - unsigned short sent_icmp_seq; - unsigned short sent_icmp_id; - - getnameinfo((struct sockaddr*)addr, addr_len, addr_ascii, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST); - - c = (unsigned char*)p; - - sent_icmp = (FPING_ICMPHDR*)(c + 28); -#ifndef IPV6 - icmp_type = p->icmp_type; - icmp_code = p->icmp_code; - sent_icmp_type = sent_icmp->icmp_type; - sent_icmp_seq = sent_icmp->icmp_seq; - sent_icmp_id = sent_icmp->icmp_id; -#else - icmp_type = p->icmp6_type; - icmp_code = p->icmp6_code; - sent_icmp_type = sent_icmp->icmp6_type; - sent_icmp_seq = sent_icmp->icmp6_seq; - sent_icmp_id = sent_icmp->icmp6_id; -#endif - - switch (icmp_type) { - case ICMP_UNREACH: - seqmap_value = seqmap_fetch(ntohs(sent_icmp_seq), ¤t_time); - - if ((sent_icmp_type == ICMP_ECHO) && (ntohs(sent_icmp_id) == ident) && (seqmap_value != NULL)) { - /* this is a response to a ping we sent */ - h = table[ntohs(sent_icmp_seq) % num_hosts]; - - if (icmp_code > ICMP_UNREACH_MAXTYPE) { - print_warning("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s", - addr_ascii, h->host); - } else { - print_warning("%s from %s for ICMP Echo sent to %s", - icmp_unreach_str[icmp_code], addr_ascii, h->host); - } - - if (inet_addr(h->host) == INADDR_NONE) - print_warning(" (%s)", addr_ascii); - - print_warning("\n"); - } - return 1; - - case ICMP_SOURCEQUENCH: - case ICMP_REDIRECT: - case ICMP_TIMXCEED: - case ICMP_PARAMPROB: - seqmap_value = seqmap_fetch(ntohs(sent_icmp_seq), ¤t_time); - - if ((sent_icmp_type == ICMP_ECHO) && (ntohs(sent_icmp_id) == ident) && (seqmap_value != NULL)) { - /* this is a response to a ping we sent */ - h = table[ntohs(sent_icmp_seq) % num_hosts]; - if (icmp_type <= ICMP_TYPE_STR_MAX) { - print_warning("%s from %s for ICMP Echo sent to %s", - icmp_type_str[icmp_type], addr_ascii, h->host); - } else { - print_warning("ICMP %d from %s for ICMP Echo sent to %s", - icmp_type, addr_ascii, h->host); - } - - if (inet_addr(h->host) == INADDR_NONE) - print_warning(" (%s)", addr_ascii); - - print_warning("\n"); - - } - - return 2; - - /* no way to tell whether any of these are sent due to our ping */ - /* or not (shouldn't be, of course), so just discard */ - case ICMP_TSTAMP: - case ICMP_TSTAMPREPLY: - case ICMP_IREQ: - case ICMP_IREQREPLY: - case ICMP_MASKREQ: - case ICMP_MASKREPLY: - default: - return 0; - - } -} - /************************************************************ Function: add_name @@ -1984,13 +2147,18 @@ void add_name(char* name) bzero(&hints, sizeof(struct addrinfo)); hints.ai_flags = 0; hints.ai_socktype = SOCK_RAW; -#ifndef IPV6 - hints.ai_family = AF_INET; - hints.ai_protocol = IPPROTO_ICMP; -#else - hints.ai_family = AF_INET6; - hints.ai_protocol = IPPROTO_ICMPV6; + hints.ai_family = hints_ai_family; + if (hints_ai_family == AF_INET) { + hints.ai_protocol = IPPROTO_ICMP; + } +#ifdef IPV6 + else if (hints_ai_family == AF_INET6) { + hints.ai_protocol = IPPROTO_ICMPV6; + } #endif + else { + hints.ai_protocol = 0; + } ret_ga = getaddrinfo(name, NULL, &hints, &res0); if (ret_ga) { if (!quiet_flag) @@ -2107,7 +2275,6 @@ void add_addr(char* name, char* host, struct sockaddr* ipaddr, socklen_t ipaddr_ i[n] = RESP_UNUSED; p->resp_times = i; - } #if defined(DEBUG) || defined(_DEBUG) @@ -2121,7 +2288,6 @@ void add_addr(char* name, char* host, struct sockaddr* ipaddr, socklen_t ipaddr_ i[n] = RESP_UNUSED; p->sent_times = i; - } #endif /* DEBUG || _DEBUG */ @@ -2301,56 +2467,6 @@ char* sprint_tm(int t) return (buf); } -/************************************************************ - Function: recvfrom_wto -************************************************************* - Description: - - receive with timeout - returns length of data read or -1 if timeout - crash_and_burn on any other errrors -************************************************************/ - -int recvfrom_wto(int s, char* buf, int len, struct sockaddr* saddr, socklen_t* saddr_len, long timo) -{ - int nfound, n; - struct timeval to; - fd_set readset, writeset; - -select_again: - if (timo < 100000) { - to.tv_sec = 0; - to.tv_usec = timo * 10; - } else { - to.tv_sec = timo / 100000; - to.tv_usec = (timo % 100000) * 10; - } - - FD_ZERO(&readset); - FD_ZERO(&writeset); - FD_SET(s, &readset); - - nfound = select(s + 1, &readset, &writeset, NULL, &to); - if (nfound < 0) { - if (errno == EINTR) { - /* interrupted system call: redo the select */ - goto select_again; - } else { - errno_crash_and_burn("select"); - } - } - - if (nfound == 0) - return -1; /* timeout */ - - /* recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len); */ - n = recvfrom(s, buf, len, 0, saddr, saddr_len); - if (n < 0) - errno_crash_and_burn("recvfrom"); - - return n; -} - /************************************************************ Function: addr_cmp @@ -2495,6 +2611,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); diff --git a/src/fping.h b/src/fping.h index 2066173..44be999 100644 --- a/src/fping.h +++ b/src/fping.h @@ -7,14 +7,6 @@ #include #include -#ifndef IPV6 -#define FPING_INADDR struct in_addr -#define FPING_ICMPHDR struct icmp -#else -#define FPING_INADDR struct in6_addr -#define FPING_ICMPHDR struct icmp6_hdr -#endif - /* fping.c */ void crash_and_burn( char *message ); void errno_crash_and_burn( char *message ); @@ -22,9 +14,15 @@ int in_cksum( unsigned short *p, int n ); int random_data_flag; /* socket.c */ -int open_ping_socket(); -void init_ping_buffer(size_t ping_data_size); -void socket_set_src_addr(int s, FPING_INADDR src_addr); -int socket_sendto_ping(int s, struct sockaddr *saddr, socklen_t saddr_len, uint16_t icmp_seq, uint16_t icmp_id); +int open_ping_socket_ipv4(); +void init_ping_buffer_ipv4(size_t ping_data_size); +void socket_set_src_addr_ipv4(int s, struct in_addr *src_addr); +int socket_sendto_ping_ipv4(int s, struct sockaddr *saddr, socklen_t saddr_len, uint16_t icmp_seq, uint16_t icmp_id); +#ifdef IPV6 +int open_ping_socket_ipv6(); +void init_ping_buffer_ipv6(size_t ping_data_size); +void socket_set_src_addr_ipv6(int s, struct in6_addr *src_addr); +int socket_sendto_ping_ipv6(int s, struct sockaddr *saddr, socklen_t saddr_len, uint16_t icmp_seq, uint16_t icmp_id); +#endif #endif diff --git a/src/socket.c b/src/socket.c deleted file mode 100644 index 1d9d542..0000000 --- a/src/socket.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * fping: fast-ping, file-ping, favorite-ping, funky-ping - * - * Ping a list of target hosts in a round robin fashion. - * A better ping overall. - * - * fping website: http://www.fping.org - * - * Current maintainer of fping: David Schweikert - * Please send suggestions and patches to: david@schweikert.ch - * - * - * Original author: Roland Schemers - * IPv6 Support: Jeroen Massar - * Improved main loop: David Schweikert - * Debian Merge, TOS settings: Tobi Oetiker - * Bugfixes, byte order & senseful seq.-numbers: Stephan Fuhrmann (stephan.fuhrmann AT 1und1.de) - * - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by Stanford University. The name of the University may not be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#include "config.h" -#include "fping.h" - -int open_ping_socket_ipv4(); -int open_ping_socket_ipv6(); -void init_ping_buffer_ipv4(size_t ping_data_size); -void init_ping_buffer_ipv6(size_t ping_data_size); -void socket_set_src_addr_ipv4(int s, FPING_INADDR src_addr); -void socket_set_src_addr_ipv6(int s, FPING_INADDR src_addr); -int socket_sendto_ping_ipv4(int s, struct sockaddr* saddr, socklen_t saddr_len, uint16_t icmp_seq_nr, uint16_t icmp_id_nr); -int socket_sendto_ping_ipv6(int s, struct sockaddr* saddr, socklen_t saddr_len, uint16_t icmp_seq_nr, uint16_t icmp_id_nr); - -int open_ping_socket() -{ -#ifndef IPV6 - return open_ping_socket_ipv4(); -#else - return open_ping_socket_ipv6(); -#endif -} - -void init_ping_buffer(size_t ping_data_size) -{ -#ifndef IPV6 - return init_ping_buffer_ipv4(ping_data_size); -#else - return init_ping_buffer_ipv6(ping_data_size); -#endif -} - -void socket_set_src_addr(int s, FPING_INADDR src_addr) -{ -#ifndef IPV6 - socket_set_src_addr_ipv4(s, src_addr); -#else - socket_set_src_addr_ipv6(s, src_addr); -#endif -} - -int socket_sendto_ping(int s, struct sockaddr* saddr, socklen_t saddr_len, uint16_t icmp_seq_nr, uint16_t icmp_id_nr) -{ -#ifndef IPV6 - return socket_sendto_ping_ipv4(s, saddr, saddr_len, icmp_seq_nr, icmp_id_nr); -#else - return socket_sendto_ping_ipv6(s, saddr, saddr_len, icmp_seq_nr, icmp_id_nr); -#endif -} diff --git a/src/socket4.c b/src/socket4.c index 28cfda4..d613059 100644 --- a/src/socket4.c +++ b/src/socket4.c @@ -44,8 +44,8 @@ #include #include -char* ping_buffer = 0; -size_t ping_pkt_size; +char* ping_buffer_ipv4 = 0; +size_t ping_pkt_size_ipv4; int open_ping_socket_ipv4() { @@ -83,18 +83,18 @@ int open_ping_socket_ipv4() void init_ping_buffer_ipv4(size_t ping_data_size) { /* allocate ping buffer */ - ping_pkt_size = ping_data_size + ICMP_MINLEN; - ping_buffer = (char*)calloc(1, ping_pkt_size); - if (!ping_buffer) + ping_pkt_size_ipv4 = ping_data_size + ICMP_MINLEN; + ping_buffer_ipv4 = (char*)calloc(1, ping_pkt_size_ipv4); + if (!ping_buffer_ipv4) crash_and_burn("can't malloc ping packet"); } -void socket_set_src_addr_ipv4(int s, FPING_INADDR src_addr) +void socket_set_src_addr_ipv4(int s, struct in_addr* src_addr) { struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; - sa.sin_addr = src_addr; + sa.sin_addr = *src_addr; if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) errno_crash_and_burn("cannot bind source address"); @@ -122,7 +122,7 @@ int socket_sendto_ping_ipv4(int s, struct sockaddr* saddr, socklen_t saddr_len, struct icmp* icp; int n; - icp = (struct icmp*)ping_buffer; + icp = (struct icmp*)ping_buffer_ipv4; icp->icmp_type = ICMP_ECHO; icp->icmp_code = 0; @@ -131,14 +131,14 @@ int socket_sendto_ping_ipv4(int s, struct sockaddr* saddr, socklen_t saddr_len, icp->icmp_id = htons(icmp_id_nr); if (random_data_flag) { - for (n = ((void*)&icp->icmp_data - (void*)icp); n < ping_pkt_size; ++n) { - ping_buffer[n] = random() & 0xFF; + for (n = ((char*)&icp->icmp_data - (char*)icp); n < ping_pkt_size_ipv4; ++n) { + ping_buffer_ipv4[n] = random() & 0xFF; } } - icp->icmp_cksum = calcsum((unsigned short*)icp, ping_pkt_size); + icp->icmp_cksum = calcsum((unsigned short*)icp, ping_pkt_size_ipv4); - n = sendto(s, icp, ping_pkt_size, 0, saddr, saddr_len); + n = sendto(s, icp, ping_pkt_size_ipv4, 0, saddr, saddr_len); return n; } diff --git a/src/socket6.c b/src/socket6.c index c32a8b5..03f2ade 100644 --- a/src/socket6.c +++ b/src/socket6.c @@ -43,8 +43,8 @@ #include -char* ping_buffer = 0; -size_t ping_pkt_size; +char* ping_buffer_ipv6 = 0; +size_t ping_pkt_size_ipv6; int open_ping_socket_ipv6() { @@ -82,18 +82,18 @@ int open_ping_socket_ipv6() void init_ping_buffer_ipv6(size_t ping_data_size) { /* allocate ping buffer */ - ping_pkt_size = ping_data_size + sizeof(struct icmp6_hdr); - ping_buffer = (char*)calloc(1, ping_pkt_size); - if (!ping_buffer) + ping_pkt_size_ipv6 = ping_data_size + sizeof(struct icmp6_hdr); + ping_buffer_ipv6 = (char*)calloc(1, ping_pkt_size_ipv6); + if (!ping_buffer_ipv6) crash_and_burn("can't malloc ping packet"); } -void socket_set_src_addr_ipv6(int s, FPING_INADDR src_addr) +void socket_set_src_addr_ipv6(int s, struct in6_addr* src_addr) { struct sockaddr_in6 sa; memset(&sa, 0, sizeof(sa)); sa.sin6_family = AF_INET6; - sa.sin6_addr = src_addr; + sa.sin6_addr = *src_addr; if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) errno_crash_and_burn("cannot bind source address"); @@ -104,21 +104,21 @@ int socket_sendto_ping_ipv6(int s, struct sockaddr* saddr, socklen_t saddr_len, struct icmp6_hdr* icp; int n; - icp = (struct icmp6_hdr*)ping_buffer; + icp = (struct icmp6_hdr*)ping_buffer_ipv6; icp->icmp6_type = ICMP6_ECHO_REQUEST; icp->icmp6_code = 0; icp->icmp6_seq = htons(icmp_seq_nr); icp->icmp6_id = htons(icmp_id_nr); if (random_data_flag) { - for (n = sizeof(struct icmp6_hdr); n < ping_pkt_size; ++n) { - ping_buffer[n] = random() & 0xFF; + for (n = sizeof(struct icmp6_hdr); n < ping_pkt_size_ipv6; ++n) { + ping_buffer_ipv6[n] = random() & 0xFF; } } icp->icmp6_cksum = 0; /* The IPv6 stack calculates the checksum for us... */ - n = sendto(s, icp, ping_pkt_size, 0, saddr, saddr_len); + n = sendto(s, icp, ping_pkt_size_ipv6, 0, saddr, saddr_len); return n; } diff --git a/src/test-c89.sh b/src/test-c89.sh index aa5f3e9..c90a148 100755 --- a/src/test-c89.sh +++ b/src/test-c89.sh @@ -1 +1,5 @@ -gcc -DHAVE_CONFIG_H -D_BSD_SOURCE -D_POSIX_SOURCE -I.. -Wall -std=c89 -pedantic -c -o fping.o fping.c +#!/bin/sh + +for f in fping.c socket4.c socket6.c seqmap.c; do + gcc -DHAVE_CONFIG_H -D_BSD_SOURCE -D_POSIX_SOURCE -I.. -Wall -std=c89 -pedantic -c -o /dev/null $f +done