Go's network package and IPv6 (and my ideal version thereof)
As the result of making a couple of chainsaw passes at really fixing my issues from yesterday, I've now got a better idea of what Go's network package is doing with IPv6 and what I think I want it to do.
Go's net package has a very Plan 9
(and Research Unix) approach to networking APIs, where it makes no
particular attemtp to expose some version of the standard sockets
API but instead goes its own, higher level way. As part of this it
has three flavours of each sort of IP-based networking; for TCP,
for example, these are
and the generic
tcp (cf the description of
The question is what Unix socket type you use for each setting; IPv4, IPv6 with dual binding turned off, or IPv6 with dual binding turned on. How I think it would ideally work is this:
- the *4 flavours use
AF_INET(IPv4) sockets. (This is the current behavior and works fine.)
- the *6 flavours use
AF_INET6(IPv6) sockets with dual binding off. This is consistent with the net package's documentation and is the only way to allow you to listen separately for IPv6 and IPv4 versions of the same port (including cooperating with non-Go programs that are listening on the IPv4 version of a port).
- the generic flavours use IPv6 sockets with dual binding on (because that is the 'allows anything' option), with two exceptions. They use IPv4 sockets if either the system doesn't have IPv6 enabled at all or the connection involves only IPv4 addresses (because I think that the socket type used should match the actual wire level protocol that will get used when we know what it is).
The one wrinkle with getting to my ideal situation is that the current
Go self tests explicitly make connections to IPv4 addresses using
networking. This is intrinsically unportable, as there are Unixes where
you cannot enable dual binding (OpenBSD is the one I know of, although
Go has not been ported to it). If you ignore that, you can make the code
turn dual binding on for this case when you detect it.
(Sufficiently perverse tests will defeat this.)
Right now what Go does for the non *4 flavours on an IPv6-enabled system is to use IPv6 with the system default dual binding settings; this works okay on a machine that defaults to dual binding being on, but very badly on a machine with it defaulting to off. The Go people seem to want to turn dual binding on all the time when using IPv6 sockets in order to fix this; I maintain that this is the wrong decision, and besides it's not completely portable (cf OpenBSD).
I have a hacked up version of the Go net package that is most of the way to my ideal situation, but I think I'm missing a corner case or two. It turned out to be relatively easy to do but also somewhat ugly.
(As a language, I am half enthused and half unenthused about Go for reasons that don't fit within the margins of this entry.)