A surprising discovery on converting IPv6 addresses: we no longer prefer getaddrinfo()

Yesterday, we were contacted by PowerDNS user James Baer who noted strange crashes in PowerDNS (on Linux) upon adding thousands and thousands of IP addresses to his system. Notably, PowerDNS did not even use any of those thousands of addresses, but it still crashed. As James noted, this should not even be possible.

Quick investigation by PowerDNS contributors Aki Tuomi and Imre Gergely (thanks!) showed that the crashes emanated from a call to getaddrinfo() to convert an IPv6 address. Now, here over at PowerDNS, we are big fans of IPv6, to the point that we even presented at RIPE66, “Implementing Full IPv6 Support: More than Binding to an AF_INET6 Socket“. In this presentation, we noted “getaddrinfo() is _the only way_ to convert presentation format IPv4 and IPv6 addresses. Ignore anything else”.

In this post, we sadly have to recant this statement. In fact, we’d like to replace it with ‘getaddrinfo() considered slow and potentially dangerous in 2014’.

So, what did we find this morning? We were expecting our calls to getaddrinfo() to be a quick string conversion of things like ‘::1’ to the binary representation ‘1’. On reading the backtraces however, we found that getaddrinfo() was talking to the kernel, and enumerating all local IP addresses (!). I was actually stunned. We also found that Akamai had noted that this was crashing their code when using 64000 IP addresses. Because talking to the kernel this way is expensive, getaddrinfo() maintains a local cache for one second. This however adds the need for locking and memory management, which it predictably gets wrong.

And lo, bug reports about getaddrinfo() not being thread safe, slow, or crash prone abound. We’ve found issues reported for Linux, Solaris, FreeBSD and OSX.

Now, it should be noted that getaddrinfo()  offers us quite a few things. It can go out on the wire and do DNS queries. It can parse gai.conf to find how we want to sort our addresses, and which address families we prefer. These days, it can even perform Internationalized Domain Name (IDN) conversions! Also, getaddrinfo() is tasked with making sure ‘local IP addresses’ get returned in preference to remote ones (‘Rule 7’). These are indeed things that may require kernel communication.

However, no such complication is required when wanting to convert “::1” into 1. And in fact, using the flags, we can tell getaddrinfo() not to do anything complicated (by omitting AI_ADDRCONFIG). Sadly, the vast majority of getaddrinfo() implementations currently in production get it wrong. This is probably an artifact of the huge mission statement that has been heaped on this function.

So, as of 2014, we consider getaddrinfo() to be avoided for IPv6 address conversions under any except the least demanding, single threaded, applications. Within PowerDNS, we have reverted to using inet_pton() whenever we can get away with it – which is almost always, except in the case of scoped addresses.

Over time, the most common C library implementations may get their act together, or they may not. But for now, to convert from representation format, inet_pton() it is.

8 comments

  1. David Malone

    Are any of the FreeBSD bugs current? I had a quick look at the links you posted, and it looks like the FreeBSD non-thread-safeness might date from 2004, or before. If they are current, I could have a look at getting them fixed.

      • David Malone

        Thanks Ben – If Tony has had a look, then it’s probably pretty sensible. Care is always advised! Some of the AI_ADDRCONFIG questions are interesting, it might be worth thrashing them out at some point.

  2. Rich Felker

    Can you clarify whether you were using AI_ADDRCONFIG? I agree the behavior you observed is undesirable, but it’s at least somewhat understandable if you were, since you would have essentially been asking getaddrinfo to “convert ::1, but only if doing so yields an address in a currently-configured address family”. If there are places where glibc is performing expensive operations that it doesn’t need to, for the request it was given, please open bug reports on the glibc bug tracker. The new maintainers are receptive to such bug reports, but as this is an ugly component not many people are familiar with, it might take a while to get fixed.

    In musl libc we don’t have this problem, partly because AI_ADDRCONFIG isn’t even supported yet. I mention it though because the resolver overhaul, which will include adding support for AI_ADDRCONFIG, is the next item on our development roadmap and I’d be interested in having your feedback on behaviors that are currently suboptimal or which turn up bugs/regressions during the overhaul.

  3. Pingback: Authoritative Server 3.4.0 Release Candidate 1 | PowerDNS Blog
  4. Pingback: PowerDNS Authoritative Server 3.4.0 released | PowerDNS Blog
  5. Pingback: Flagword.net » TIL gethostbyname*() and gethostbyaddr*() functions are obsolete

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s