bind(2) failed on Linux?

I’ll start this off with an example. How many of you running a Linux variant have seen the following in your logs?

sshd[6238]: Server listening on :: port 22.
sshd[6238]: error: Bind to port 22 on 0.0.0.0

When I first discovered this I had a couple theories about why this might be happening, but all I really knew for sure was that I couldn’t reproduce the problem on an OpenBSD or FreeBSD system. Soon enough, I came across numerous blog and list posts with sledgehammer answers such as “disable IPv6 support in the kernel!” I didn’t know what was going on at the time, but I certainly wasn’t satisfied by this answer. I went looking for behavior specific to Linux and came across this excerpt from Linux’s ipv6(7):

IPv4 connections can be handled with the v6 API by using the v4-mapped-on-v6 address type; thus a program only needs only to support this API type to support both protocols. This is handled transparently by the address handling functions in libc.

IPv4 and IPv6 share the local port space. When you get an IPv4 connection or packet to a IPv6 socket its source address will be mapped to v6 and it will be mapped to v6.

That’s more like the information I’m looking for. From what I’ve gathered, this v4-mapped addressing behavior is supposed to make it simpler for applications to be configured during the IPv4 to IPv6 transition (you only need to specify one Listen directive in Apache, for example).

I personally dislike the idea of the v4-mapped-on-v6 address type. Put simply, when I create an INET6 socket, I expect that I’m going to do communication over that protocol. I wouldn’t expect to do IPX communication on an INET socket, why should I expect to do INET communication on an INET6 socket? Furthermore, if I have an application like sshd and I give it the -6 parameter, my expectation is that it will no longer use IPv4 for communication. It will, however, do IPv4 communication via v4-mapped addresses on certain systems and this may cause problems for administrators who believe their system is only listening for certain types of communication when in reality it is listening for others as well. I believe if you’re going to write software that listens for both IPv4 and IPv6 communication, you should be writing AF-independent software, and not have to rely on something that may make your life more difficult in the future. You’re going to have to touch the code anyway to update it (if you even have to), so you might as well get in there and do it right. The downside due to the v4-mapped behavior being the default is that those who wish to write portable AF-independent code (at least using the method linked above, if there is a better way I’d like to know about it) will need to explicitly turn on the socket option IPV6_V6ONLY if the system supports it.

If you don’t want this v4-mapped behavior at all in Linux, the following should turn it off globally, but will reset on the next reboot unless you make it permanent (see your distribution documentation for details):

net.ipv6.bindv6only = 1

In any case, I prefer the default behavior of the BSDs who, at the time of this writing, have disabled (by default) the v4-mapped addressing. You can always turn it on using the inverse of the socket option method described above.

I’ve submitted a patch to the Portable OpenSSH project. I fully expect to get flamed to dust because of something I’ve overlooked, but as far as I can tell setting the socket option in OpenSSH as the patch does should bring Portable OpenSSH on systems using v4-mapped addresses more in line with the behavior of OpenSSH on OpenBSD.

Update: The patch has been accepted by the Portable OpenSSH project.

Leave a Reply