Package: iputils-ping
Version: 3:20121221-4sjm1
Severity: normal
Tags: patch
Hi there,
The fix for bug #109709 uses the cached result of a DNS lookup
unconditionally, so that when I am pinging 1.2.3.4 and that host goes
away, the "Destination Host Unreachable" message appears to come from the
host being reported as unreachable.
Reproducing this problem is fairly simple. Start with:
$ ping 8.8.8.8
While that is running, add an iptables rule to block that host:
# iptables -I OUTPUT -p icmp -d 8.8.8.8 -j REJECT
You will see that you get error messages which appear to come from
8.8.8.8 rather than your local host. This also happens with ping6, and
you can reproduce the problem similarly with an ip6tables rule.
I have attached a patch which fixes this problem for me in both ping and
ping6, by treating the DNS cache as invalid if the remote host address
has changed since the last time pr_addr was called.
I'm not sure if memcpy/memcmp is the "correct" way to handle a
struct in6_addr, but it seems to work fine for this purpose. I'm more
than willing to provide a revised patch if you know of a better way.
Let me know if you need any more information.
Thanks!
Steven.
-- System Information:
Debian Release: jessie/sid
APT prefers unstable
APT policy: (800, 'unstable'), (700, 'experimental')
Architecture: amd64 (x86_64)
Foreign Architectures: i386
Kernel: Linux 3.11-2-amd64 (SMP w/8 CPU cores)
Locale: LANG=en_AU.UTF-8, LC_CTYPE=en_AU.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Versions of packages iputils-ping depends on:
ii libc6 2.17-97
ii libcap2 1:2.22-1.2
ii libgnutls-openssl27 2.12.23-8
Versions of packages iputils-ping recommends:
ii libcap2-bin 1:2.22-1.2
iputils-ping suggests no packages.
-- no debconf information
Treat DNS lookup caches as invalid when the IP address being looked up
changes.
--- a/ping.c
+++ b/ping.c
@@ -1244,10 +1244,15 @@
{
struct hostent *hp;
static char buf[4096] = "";
+ static __u32 last_addr = 0;
+ static __u8 last_addr_set = 0;
- if (*buf)
+ if (last_addr_set && addr == last_addr && *buf)
return(buf);
+ last_addr = addr;
+ last_addr_set = 1;
+
in_pr_addr = !setjmp(pr_addr_jmp);
if (exiting || (options & F_NUMERIC) ||
--- a/ping6.c
+++ b/ping6.c
@@ -1796,6 +1796,8 @@
{
static struct hostent *hp = NULL;
static char *s;
+ static struct in6_addr last_addr;
+ static __u8 last_addr_set = 0;
#ifdef USE_IDN
free(s);
@@ -1803,11 +1805,15 @@
in_pr_addr = !setjmp(pr_addr_jmp);
- if (!hp && !(exiting || options&F_NUMERIC))
+ if (!(last_addr_set && memcmp(addr, &last_addr, sizeof(struct in6_addr)) && hp) &&
+ !(exiting || options&F_NUMERIC))
hp = gethostbyaddr((__u8*)addr, sizeof(struct in6_addr), AF_INET6);
in_pr_addr = 0;
+ memcpy(&last_addr, addr, sizeof(struct in6_addr));
+ last_addr_set = 1;
+
if (!hp
#ifdef USE_IDN
|| idna_to_unicode_lzlz(hp->h_name, &s, 0) != IDNA_SUCCESS