diff --git a/CHANGELOG.md b/CHANGELOG.md index f4241893..38cc36da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Next - New option --print-reply-dst to show the destination address of the received reply packet (#461, thanks @gsnw-sebast) - New option --oiface for outgoing interface (#463, thanks @gsnw-sebast) +- Add IPv6 support to --print-reply-dst (#462, thanks @gsnw-sebast) ## Bugfixes and other changes diff --git a/ci/test-11-unpriv.pl b/ci/test-11-unpriv.pl index 728a99b9..5dcf1280 100755 --- a/ci/test-11-unpriv.pl +++ b/ci/test-11-unpriv.pl @@ -40,7 +40,7 @@ sub get_ping_gid_range { } sub test_unprivileged_works { - plan tests => 21; + plan tests => 24; { my $cmd = Test::Command->new(cmd => "$fping_copy 127.0.0.1"); @@ -54,6 +54,12 @@ sub test_unprivileged_works { $cmd->stdout_like(qr{127\.0\.0\.1 is alive \(Reply-DST unknown\)\n}); $cmd->stderr_is_eq(""); } + { + my $cmd = Test::Command->new(cmd => "$fping_copy --print-reply-dst ::1"); + $cmd->exit_is_num(0); + $cmd->stdout_like(qr{::1 is alive \(Reply-DST ::1\)\n}); + $cmd->stderr_is_eq(""); + } { my $cmd = Test::Command->new(cmd => "$fping_copy --print-tos 127.0.0.1"); $cmd->exit_is_num(0); diff --git a/ci/test-16-json-output.pl b/ci/test-16-json-output.pl index a9db4db7..75f632b1 100644 --- a/ci/test-16-json-output.pl +++ b/ci/test-16-json-output.pl @@ -1,6 +1,6 @@ #!/usr/bin/perl -w -use Test::Command tests => 66; +use Test::Command tests => 69; use Test::More; # fping -J -c 2 127.0.0.1 @@ -98,12 +98,23 @@ $cmd->stderr_is_eq(""); } -# fping -J -c 1 --print-srcaddr 127.0.0.1 +# fping -J -c 1 --print-reply-dst 127.0.0.1 +{ my $cmd = Test::Command->new(cmd => "fping -J -c 1 --print-reply-dst 127.0.0.1"); $cmd->exit_is_num(0); $cmd->stdout_like(qr/^\{"resp":\s\{"host":\s"127\.0\.0\.1",\s"seq":\s0,\s"size":\s\d+,\s"rtt":\s\d+\.\d+,\s"replyDst":\s"127\.0\.0\.1"\}\} \{"summary":\s\{"host":\s"127\.0\.0\.1",\s"xmt":\s\d+,\s"rcv":\s\d+,\s"loss":\s\d+,\s"rttMin":\s\d+\.\d+,\s"rttAvg":\s\d+\.\d+,\s"rttMax":\s\d+\.\d+\}\}\n?$/); $cmd->stderr_is_eq(""); +} + +# fping -J -c 1 --print-reply-dst ::1 +{ +my $cmd = Test::Command->new(cmd => "fping -J -c 1 --print-reply-dst ::1"); +$cmd->exit_is_num(0); +$cmd->stdout_like(qr/^\{"resp":\s\{"host":\s"::1",\s"seq":\s0,\s"size":\s\d+,\s"rtt":\s\d+\.\d+,\s"replyDst":\s"::1"\}\} +\{"summary":\s\{"host":\s"::1",\s"xmt":\s\d+,\s"rcv":\s\d+,\s"loss":\s\d+,\s"rttMin":\s\d+\.\d+,\s"rttAvg":\s\d+\.\d+,\s"rttMax":\s\d+\.\d+\}\}\n?$/); +$cmd->stderr_is_eq(""); +} # fping -J -c 1 -q 127.0.0.1 { diff --git a/doc/fping.pod b/doc/fping.pod index 2b3f0750..1bd21c7e 100644 --- a/doc/fping.pod +++ b/doc/fping.pod @@ -261,7 +261,7 @@ Set source address. =item B<--print-reply-dst> -Displays the destination address of the received reply packet. (IPv6 is currently not supported). +Displays the destination address of the received reply packet. Note this reflects the address as seen on the reply packet, not necessarily the address the request was sent from. It is only printed for hosts that reply (not for timeouts), and NAT between fping and the target can make it differ from the request's source or the reply's real origin. diff --git a/src/fping.c b/src/fping.c index ba58e779..ae90abc6 100644 --- a/src/fping.c +++ b/src/fping.c @@ -607,6 +607,13 @@ int main(int argc, char **argv) #endif } else if (strstr(optparse_state.optlongname, "print-reply-dst") != NULL) { opt_print_reply_dst_on = 1; +#if defined(IPV6) && defined(IPV6_RECVPKTINFO) + if (socket6 >= 0) { + if (setsockopt(socket6, IPPROTO_IPV6, IPV6_RECVPKTINFO, &sock_opt_on, sizeof(sock_opt_on))) { + perror("setsockopt IPV6_RECVPKTINFO"); + } + } +#endif } else if (strstr(optparse_state.optlongname, "seqmap-timeout") != NULL) { opt_seqmap_timeout = strtod_strict(optparse_state.optarg) * 1000000; } else if (strstr(optparse_state.optlongname, "oiface") != NULL) { @@ -2077,7 +2084,13 @@ int receive_packet(int64_t wait_time, char *reply_buf, size_t reply_buf_len, int *ip_header_tos, - int *ip_header_ttl) + int *ip_header_ttl, +#ifdef IPV6 + struct in6_addr *recv_dst_addr_ipv6 +#else + void *recv_dst_addr_ipv6 +#endif + ) { struct timeval to; int s = 0; @@ -2161,6 +2174,11 @@ int receive_packet(int64_t wait_time, if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) { memcpy(ip_header_ttl, CMSG_DATA(cmsg), sizeof(*ip_header_ttl)); } + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); + if (recv_dst_addr_ipv6) + memcpy(recv_dst_addr_ipv6, &pktinfo->ipi6_addr, sizeof(*recv_dst_addr_ipv6)); + } #endif } } @@ -2384,7 +2402,8 @@ int decode_icmp_ipv6( size_t reply_buf_len, unsigned short *id, unsigned short *seq, - IP_HEADER_RESULT *ip_header_res) + IP_HEADER_RESULT *ip_header_res, + struct in6_addr *local_addr) { struct icmp6_hdr *icp; @@ -2494,8 +2513,10 @@ int decode_icmp_ipv6( *seq = ntohs(icp->icmp6_seq); if (opt_print_reply_dst_on) { - strncpy(ip_header_res->reply_dst_addr, "not supported", sizeof(ip_header_res->reply_dst_addr) - 1); - ip_header_res->reply_dst_addr[sizeof(ip_header_res->reply_dst_addr) - 1] = '\0'; + if (local_addr == NULL || IN6_IS_ADDR_UNSPECIFIED(local_addr) || inet_ntop(AF_INET6, local_addr, ip_header_res->reply_dst_addr, sizeof(ip_header_res->reply_dst_addr)) == NULL) { + strncpy(ip_header_res->reply_dst_addr, "unknown", sizeof(ip_header_res->reply_dst_addr) - 1); + ip_header_res->reply_dst_addr[sizeof(ip_header_res->reply_dst_addr) - 1] = '\0'; + } } return 1; @@ -2517,6 +2538,11 @@ int wait_for_reply(int64_t wait_time) unsigned short seq; IP_HEADER_RESULT ip_header_res = default_ip_header_result(); +#ifdef IPV6 + struct in6_addr recv_dst_addr_ipv6; + memset(&recv_dst_addr_ipv6, 0, sizeof(recv_dst_addr_ipv6)); +#endif + /* Receive packet */ result = receive_packet(wait_time, /* max. wait time, in ns */ &recv_time, /* reply_timestamp */ @@ -2525,7 +2551,12 @@ int wait_for_reply(int64_t wait_time) buffer, /* reply_buf */ sizeof(buffer), /* reply_buf_len */ &ip_header_res.tos, /* TOS resp. TC byte */ - &ip_header_res.ttl /* TTL resp. hop limit */ + &ip_header_res.ttl, /* TTL resp. hop limit */ +#ifdef IPV6 + &recv_dst_addr_ipv6 +#else + NULL +#endif ); if (result <= 0) { @@ -2567,7 +2598,8 @@ int wait_for_reply(int64_t wait_time) sizeof(buffer), &id, &seq, - &ip_header_res)) { + &ip_header_res, + &recv_dst_addr_ipv6)) { return 1; } if (id != ident6) { @@ -3165,6 +3197,6 @@ void usage(int is_error) fprintf(out, " -X, --fast-reachable=N exits true immediately when N hosts are found\n"); fprintf(out, " --print-tos show received TOS value\n"); fprintf(out, " --print-ttl show IP TTL value\n"); - fprintf(out, " --print-reply-dst show the destination address of the received reply packet (IPv6 is currently not supported).\n"); + fprintf(out, " --print-reply-dst show the destination address of the received reply packet\n"); exit(is_error); }