Skip to content

IPv6 addresses not available for a brief period after container start #18871

@mpalmer

Description

@mpalmer

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions