How to listen on a socket in the modern IPv6-enabled world

February 24, 2010

Suppose that you are a fancy modern server program, and you want to listen for both IPv4 and (if available) IPv6 connections. As it happens this is a bit more intricate than you might expect, and it varies between systems.

To start with:

  • use getaddrinfo() with a NULL host, the port you want, AF_UNSPEC as the address family, and AI_PASSIVE and AI_ADDRCONFIG in the flags.

  • if the results don't contain any IPv6 sockets, you have an IPv4 only system. You can skip the rest of this and just create and bind an IPv4 socket. (You probably want to set SO_REUSEADDR on the newly created socket; pretty much everyone does this.)

Otherwise, you have a choice of two options, because you are on a system that seems to support both IPv4 and IPv6. You either need to create two listening sockets, one for IPv4 only and one for IPv6 only, or you need to create an IPv6 listening socket that is also guaranteed to get IPv4 traffic. Simply binding an IPv6 socket is not enough to insure that you get IPv4 traffic; many systems do not default to dual binding sockets.

(Since there are systems that don't support dual binding sockets under any circumstances, such as OpenBSD, using two separate sockets is more portable.)

To be able to create separate IPv6 and IPv4 sockets, you need to explicitly turn on the IPV6_V6ONLY socket option on your IPv6 socket before you attempt to bind it to the wildcard listening address. To create an IPv6 socket that will also get IPv4 connections, you need to explicitly turn off IPV6_V6ONLY on your IPv6 socket, probably before you bind() it to the listening address.

(I believe but cannot completely confirm that all current IPv6 enabled Unix systems have IPV6_V6ONLY; certainly Linux, OpenBSD, NetBSD, FreeBSD, Mac OS X, AIX, and Solaris 10 all do, although OpenBSD doesn't let you turn it off. Solaris 8 doesn't have it, but if you still have a Solaris 8 system you may have other problems.)

Note that you should care about this issue even if your software is Linux specific. While Linux defaults to having dual binding on (which lets you just bind an IPv6 socket and get IPv4 for free), this can be turned off on individual systems and Debian is apparently going to default to having it turned off in their next release.

Written on 24 February 2010.
« What Linux's getaddrinfo() is doing with IPv6 (on some versions)
Reading the Oracle tea leaves for Solaris »

Page tools: View Source, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Wed Feb 24 01:04:26 2010
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.