diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index cca5691a643fd..e6a6c0f05ec92 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -455,12 +455,25 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4.String() } - var ipamV6Conf *libnetwork.IpamConf + var ( + ipamV6Conf *libnetwork.IpamConf + deferIPv6Alloc bool + ) if config.Bridge.FixedCIDRv6 != "" { _, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6) if err != nil { return err } + + // In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has + // at least 48 host bits, we need to guarantee the current behavior where the containers' + // IPv6 addresses will be constructed based on the containers' interface MAC address. + // We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints + // on this network until after the driver has created the endpoint and returned the + // constructed address. Libnetwork will then reserve this address with the ipam driver. + ones, _ := fCIDRv6.Mask.Size() + deferIPv6Alloc = ones <= 80 + if ipamV6Conf == nil { ipamV6Conf = &libnetwork.IpamConf{} } @@ -485,7 +498,8 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e netlabel.GenericData: netOption, netlabel.EnableIPv6: config.Bridge.EnableIPv6, }), - libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf)) + libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf), + libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc)) if err != nil { return fmt.Errorf("Error creating default \"bridge\" network: %v", err) } diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index f8e17552336a3..21d9e6710a967 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -377,6 +377,29 @@ func (s *DockerSuite) TestDaemonIPv6FixedCIDR(c *check.C) { } } +// TestDaemonIPv6FixedCIDRAndMac checks that when the daemon is started with ipv6 fixed CIDR +// the running containers are given a an IPv6 address derived from the MAC address and the ipv6 fixed CIDR +func (s *DockerSuite) TestDaemonIPv6FixedCIDRAndMac(c *check.C) { + err := setupV6() + c.Assert(err, checker.IsNil) + + d := NewDaemon(c) + + err = d.StartWithBusybox("--ipv6", "--fixed-cidr-v6='2001:db8:1::/64'") + c.Assert(err, checker.IsNil) + defer d.Stop() + + out, err := d.Cmd("run", "-itd", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox") + c.Assert(err, checker.IsNil) + + out, err = d.Cmd("inspect", "--format", "'{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}'", "ipv6test") + c.Assert(err, checker.IsNil) + c.Assert(strings.Trim(out, " \r\n'"), checker.Equals, "2001:db8:1::aabb:ccdd:eeff") + + err = teardownV6() + c.Assert(err, checker.IsNil) +} + func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) { c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level")) }