nss-dns4only / libc / disable AAAA lookups
Have you ever noticed how some applications can do AAAA DNS record lookups even though the host has no IPv6 connectivity? That means double DNS lookups for zero profit. Why is that? And how can you disable it?
Problem
To make a long story short, a common combination of circumstances can cause useless gratuitous AAAA lookups:
- Applications that are IPv6 ready (or applications that don't care);
- on hosts using libc;
- where IPv6 is enabled (
net.ipv6.conf.all.disable_ipv6=0
) — even though there is no IPv6 router in the network.
Where is that?
Well, with all the microservices around nowadays, it's likely that's the case for your nearest Kubernetes node. And it's also likely there are one or more jobs doing DNS queries every second or more.
Now, I'm all for the adoption of IPv6. But to be realistic, there are simply many hosts that are not directly connected to IPv6 yet. And even if they are, it's still a shame that every lookup is done in twofold.
Details
The problem is in the getaddrinfo
libc
call. Most applications that connect somewhere, e.g. curl, call
getaddrinfo()
with the "unspecified" (AF_UNSPEC
) address family;
i.e. both an IPv4 and an IPv6 response is okay.
In the background, getaddrinfo()
will do two DNS record lookups over
UDP: one for an (IPv4) A record, and one for an (IPv6) AAAA record. It
collects the responses and returns both to the calling application. The
application will then try to connect over IPv6 and IPv4 sequentially,
depending on which records were returned.
If you're lucky, your host has no IPv6 address at all (not even a
link-local one) and your application passes the AI_ADDRCONFIG
parameter (or gets it by default from GNU libc). Then you might only
get a single lookup. But in all other cases, you get two lookups. And
you always have to wait for the slowest of the two responses.
Solutions
So, can you disable IPv6 lookups for these cases?
Some applications will have an option, like curl has the -4
option.
But adding that everywhere is tedious. It would be nice to disable A or
AAAA lookups on a host
entirely
(see also a RedHat libc bug
report). However,
as you can see in the RFE above, no work appears to have been done in
GNU libc since that report was filed in 2016.
Are there any other workarounds then?
I'm glad you asked! Because this issue has other side
effects
than just increased load and latency, I made a workaround in the form of
a nsswitch.conf
callback:
nss-dns4only
It works by intercepting the getaddrinfo()
calls, and turning them
into AF_INET
lookups:
ret = _nss_dns_gethostbyname3_r(
lookupname, AF_INET, &result, buffer2, buflen, errnop, herrnop,
ttlp, NULL);
Try it out. You can download the source or fetch a pre-built Debian package from the nss-dns4only releases page. Installation is described in the README.
(Note that it does not work with libc versions that have no NSS system, like musl libc.)
Disclaimer: Again, I do not wish to hinder the adoption of IPv6 in any way. If there is demand, I'll gladly help create the inverse: an IPv6-only version that only does AAAA lookups.