2

While troubleshooting an occasional network "stall" on my home internet, I came across this technical tip from Dell. They suggest using ping -n to avoid a stall caused by DNS resolution. This got me thinking, what does the -n switch actually do? It seems to me that DNS resolution is required if you ping a DNS name, but not required if you ping an IP.

From the man page:
-n Numeric output only. No attempt will be made to lookup symbolic names for host addresses.

If I ping an IP address like ping 8.8.8.8 how is there any DNS lookup here? Does ping -n 8.8.8.8 do something different?

2

If I ping an IP address like ping 8.8.8.8 how is there any DNS lookup here? Does ping -n 8.8.8.8 do something different?

It may depend on the implementation. If ping without -n includes dns.google in its output, ping -n probably doesn't, this is the difference. But it's possible that neither output includes the string.


In my Ubuntu 18.04.4 LTS man 8 ping reads:

ping is part of iputils package and the latest versions are available in source form at http://www.skbuff.net/iputils/iputils-current.tar.bz2.

This exact address seems not to work, still http://www.skbuff.net/iputils provides other links, including the one to SourceForge. I have downloaded iputils-s20151218 and read the code.

-n is handled in ping_common.c:

case 'n':
        options |= F_NUMERIC;
        break;

Then the option (options & F_NUMERIC) is important in ping.c:

pr_addr(__u32 addr)
{
        …

        if (exiting || (options & F_NUMERIC) ||
            !(hp = gethostbyaddr((char *)&addr, 4, AF_INET)))
                sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&addr));
        else {
                …

To get to gethostbyaddr, both exiting and options & F_NUMERIC must be false (because a || b does will not evaluate b if a is true). The latter depends on whether -n was or wasn't used.

gethostbyaddr is the "attempt to lookup symbolic names for host addresses". See man 3 gethostbyaddr. This is the same library call getent hosts uses when you provide an IP address (see man 1 getent).

See the difference:

$ getent hosts 8.8.8.8
8.8.8.8         dns.google
$ ping -c 1 dns.google
…
64 bytes from dns.google (8.8.8.8): icmp_seq=1 ttl=120 time=10.6 ms
…
$ ping -n -c 1 dns.google
…
64 bytes from 8.8.8.8: icmp_seq=1 ttl=120 time=11.3 ms
…
$

It may seem ping without -n just uses the string the user provided; but no:

$ getent hosts poczta.wp.pl
193.17.41.99    poczta.wp.pl
$ getent hosts 193.17.41.99
193.17.41.99    poczta.o2.pl
$ ping -c 1 poczta.wp.pl
…
64 bytes from poczta.o2.pl (193.17.41.99): icmp_seq=1 ttl=60 time=9.71 ms
…
$

Here poczta.wp.pl was resolved to 193.17.41.99 and then 193.17.41.99 was resolved to poczta.o2.pl (note o2 instead of wp). Usage of -n would suppress the latter step.

For some addresses this happens:

$ getent hosts superuser.com
151.101.1.69    superuser.com
151.101.193.69  superuser.com
151.101.65.69   superuser.com
151.101.129.69  superuser.com
$ getent hosts 151.101.1.69 151.101.193.69 151.101.65.69 151.101.129.69        
$        # the output was empty
$ ping -c 1 superuser.com
…
64 bytes from 151.101.1.69 (151.101.1.69): icmp_seq=1 ttl=58 time=37.1 ms
…
$ ping -n -c 1 superuser.com
…
64 bytes from 151.101.1.69: icmp_seq=1 ttl=58 time=36.8 ms
…
$

But if I provide a numeric address then there will be no difference:

$ getent hosts 8.8.8.8
8.8.8.8         dns.google
$ ping -c 1 8.8.8.8
…
64 bytes from 8.8.8.8: icmp_seq=1 ttl=120 time=10.8 ms
…
$ ping -n -c 1 8.8.8.8
…
64 bytes from 8.8.8.8: icmp_seq=1 ttl=120 time=8.91 ms
…
$ 

It's because of this fragment from ping.c:

if (inet_aton(target, &whereto.sin_addr) == 1) {
        hostname = target;
         if (argc == 1)
                 options |= F_NUMERIC;
} else

inet_aton converts from the IPv4 numbers-and-dots notation into binary form. It returns 1 on success. If the last argument given to ping can be converted, the fragment evaluates options |= F_NUMERIC as if -n was used.

I actually compiled ping from the source in two versions: the original and with if … options |= F_NUMERIC; commented out. The modified version behaves like this:

$ ./ping -c 1 8.8.8.8
…
64 bytes from dns.google (8.8.8.8): icmp_seq=1 ttl=120 time=9.67 ms
…
$ ./ping -n -c 1 8.8.8.8
…
64 bytes from 8.8.8.8: icmp_seq=1 ttl=120 time=8.69 ms
…
$ 

Now I can explicitly answer the question:

Does ping -n 8.8.8.8 do something different [than ping 8.8.8.8]?

No. The address in the IPv4 numbers-and-dots notation makes ping (from iputils on Linux) work as if -n was used.


If I ping an IP address like ping 8.8.8.8 how is there any DNS lookup here?

I created a separate network namespace (to make sure as little traffic as possible interferes) with a veth pair, then used wireshark there. (If you want to replicate my results and need help with the procedure, see this answer, example 2).

With the original ping:

  • ping -n -c 1 8.8.8.8 generates ICMP echo request and receives ICMP echo reply. No DNS involved.
  • ping -c 1 8.8.8.8 does the same (no surprise here, explained above).

This means there is no DNS lookup. Below are some tests for comparison.

With my modified ping:

  • ping -n -c 1 8.8.8.8 behaves like the original.
  • ping -c 1 8.8.8.8 queries the DNS server after ICMP and receives a response. This is to get dns.google.

Again with the original ping:

  • ping -n -c 1 dns.google queries the DNS server and receives a response before ICMP. This is to translate dns.google to 8.8.8.8 or 8.8.4.4.
  • ping -c 1 dns.google queries the DNS sever and receives a response before ICMP (to translate to 8.8.8.8 or to 8.8.4.4) and separately after ICMP (to translate back).
| improve this answer | |
1

From the man page:

-n Numeric output only. No attempt will be made to lookup symbolic names for host addresses

so the difference is in the output:

>ping  whitehouse.gov
PING whitehouse.gov(g2a02-26f0-0082-02b2-0000-0000-0000-2add.deploy.static.akamaitechnologies.com (2a02:26f0:82:2b2::2add)) 56 data bytes
64 bytes from g2a02-26f0-0082-02b2-0000-0000-0000-2add.deploy.static.akamaitechnologies.com (2a02:26f0:82:2b2::2add): icmp_seq=1 ttl=52 time=7.38 ms
64 bytes from g2a02-26f0-0082-02b2-0000-0000-0000-2add.deploy.static.akamaitechnologies.com (2a02:26f0:82:2b2::2add): icmp_seq=2 ttl=52 time=6.91 ms
64 bytes from g2a02-26f0-0082-02b2-0000-0000-0000-2add.deploy.static.akamaitechnologies.com (2a02:26f0:82:2b2::2add): icmp_seq=3 ttl=52 time=21.6 ms
64 bytes from g2a02-26f0-0082-02b2-0000-0000-0000-2add.deploy.static.akamaitechnologies.com (2a02:26f0:82:2b2::2add): icmp_seq=4 ttl=52 time=7.60 ms
>ping  -n whitehouse.gov
PING whitehouse.gov(2a02:26f0:82:280::2add) 56 data bytes
64 bytes from 2a02:26f0:82:280::2add: icmp_seq=1 ttl=52 time=10.0 ms
64 bytes from 2a02:26f0:82:280::2add: icmp_seq=2 ttl=52 time=5.63 ms
64 bytes from 2a02:26f0:82:280::2add: icmp_seq=3 ttl=52 time=10.4 ms
64 bytes from 2a02:26f0:82:280::2add: icmp_seq=4 ttl=52 time=12.5 ms
| improve this answer | |
  • The condition (emphasis mine): "if I ping an IP address like ping 8.8.8.8 …". The questions: "how is there any DNS lookup here?", "does ping -n 8.8.8.8 do something different?". While everything in your answer is true, the condition is ignored and the questions are not answered. – Kamil Maciorowski Sep 2 at 23:46
  • 1
    I believe the Dell support is confused about the purpose of -n. As my example shows, there are reverse DNS calls in addition to the initial call. Historically, you could ping the broadcast address of your LAN, and get answers from several machines. – xenoid Sep 3 at 0:32
  • @xenoid so you're saying there is no difference when you ping an IP (not a DNS name)? – Elliott B Sep 3 at 3:17
  • 1
    I'm saying that -n doesn't apply to the name resolution of the <destination> parameter of the ping command but only to the name resolution of the address(es) that answer the ping. – xenoid Sep 3 at 6:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.