When using IPv6 addresses on a Docker container, there is a brief(ish) period during which the address assigned to the container is not available, either to bind to or as a source address. The symptoms I saw were that attempts to connect to external IPv6 addresses from within the container would fail, and the network traffic had a source address of ::1, but there are any number of other symptoms that could occur. Debugging this is made somewhat more difficult by the fact that, by the time you docker exec -it into the container, everything seems to work quite nicely.
As an example, here is some output from a startup script which basically does ip ad sh eth0; sleep 5; ip ad sh eth0 immediately after starting:
113: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.4/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 2001db8::4/64 scope global tentative
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:4/64 scope link tentative
valid_lft forever preferred_lft forever
Pausing to remember that things are not always as they seem...
113: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.4/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 2001:db8::4/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:4/64 scope link
valid_lft forever preferred_lft forever
Note that before the sleep, the GUA address (2001:db8::4) is marked as being "tentative", whereas afterwards it is OK. Take careful note, you will see this material again... (grin)
The root cause of the problem is IPv6's duplicate address detection logic: a brief period when the kernel is trying to work out if someone else might have the same address, by sending some neighbour solicitation packets (IPv6-speak for ARP) and seeing if anyone replies. When nobody does after a brief pause, the address is marked as good to use, and we're away.
However, until that "tentative" flag comes down, nothing can bind to the address, and -- more importantly in my case -- it will never be selected as the source address for an outgoing connection.
There are two possible solutions I can think of. Both will require changes to the vendored netlink library, I think, because it lacks the necessary plumbing already to implement either of them.
The first is to make the (not unreasonable) assumption that, since Docker controls (or should control) allocation of all addresses within the netblock that has been given to it, we can skip the duplicate address detection phase, and just immediately mark the address as available. For that, the netlink library needs to be taught how to set the nodad flag when adding an IPv6 address.
The other way is to play it safe, and let duplicate address detection do its thing, and poll until the new address is properly available. For that, the Addr struct needs to be extended to hold the tentative flag, and the AddrList function needs to be modified to fill that flag out. Then, you loop on AddrList after AddrAdd, until the tentative flag goes away.
When using IPv6 addresses on a Docker container, there is a brief(ish) period during which the address assigned to the container is not available, either to bind to or as a source address. The symptoms I saw were that attempts to connect to external IPv6 addresses from within the container would fail, and the network traffic had a source address of
::1, but there are any number of other symptoms that could occur. Debugging this is made somewhat more difficult by the fact that, by the time youdocker exec -itinto the container, everything seems to work quite nicely.As an example, here is some output from a startup script which basically does
ip ad sh eth0; sleep 5; ip ad sh eth0immediately after starting:Note that before the sleep, the GUA address (
2001:db8::4) is marked as being "tentative", whereas afterwards it is OK. Take careful note, you will see this material again... (grin)The root cause of the problem is IPv6's duplicate address detection logic: a brief period when the kernel is trying to work out if someone else might have the same address, by sending some neighbour solicitation packets (IPv6-speak for ARP) and seeing if anyone replies. When nobody does after a brief pause, the address is marked as good to use, and we're away.
However, until that "tentative" flag comes down, nothing can bind to the address, and -- more importantly in my case -- it will never be selected as the source address for an outgoing connection.
There are two possible solutions I can think of. Both will require changes to the vendored
netlinklibrary, I think, because it lacks the necessary plumbing already to implement either of them.The first is to make the (not unreasonable) assumption that, since Docker controls (or should control) allocation of all addresses within the netblock that has been given to it, we can skip the duplicate address detection phase, and just immediately mark the address as available. For that, the
netlinklibrary needs to be taught how to set thenodadflag when adding an IPv6 address.The other way is to play it safe, and let duplicate address detection do its thing, and poll until the new address is properly available. For that, the
Addrstruct needs to be extended to hold thetentativeflag, and theAddrListfunction needs to be modified to fill that flag out. Then, you loop onAddrListafterAddrAdd, until thetentativeflag goes away.