Преглед на файлове

Merge pull request #17890 from aboch/b6

Restore deterministic IPv6 from MAC behavior on default bridge network
Tibor Vass преди 9 години
родител
ревизия
7a985cfafc

+ 16 - 2
daemon/daemon_unix.go

@@ -455,12 +455,25 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
 		ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4.String()
 		ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4.String()
 	}
 	}
 
 
-	var ipamV6Conf *libnetwork.IpamConf
+	var (
+		ipamV6Conf     *libnetwork.IpamConf
+		deferIPv6Alloc bool
+	)
 	if config.Bridge.FixedCIDRv6 != "" {
 	if config.Bridge.FixedCIDRv6 != "" {
 		_, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6)
 		_, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6)
 		if err != nil {
 		if err != nil {
 			return err
 			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 {
 		if ipamV6Conf == nil {
 			ipamV6Conf = &libnetwork.IpamConf{}
 			ipamV6Conf = &libnetwork.IpamConf{}
 		}
 		}
@@ -485,7 +498,8 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
 			netlabel.GenericData: netOption,
 			netlabel.GenericData: netOption,
 			netlabel.EnableIPv6:  config.Bridge.EnableIPv6,
 			netlabel.EnableIPv6:  config.Bridge.EnableIPv6,
 		}),
 		}),
-		libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf))
+		libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf),
+		libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc))
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("Error creating default \"bridge\" network: %v", err)
 		return fmt.Errorf("Error creating default \"bridge\" network: %v", err)
 	}
 	}

+ 1 - 1
hack/vendor.sh

@@ -22,7 +22,7 @@ clone git github.com/vdemeester/shakers 3c10293ce22b900c27acad7b28656196fcc2f73b
 clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git
 clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git
 
 
 #get libnetwork packages
 #get libnetwork packages
-clone git github.com/docker/libnetwork 5978c276ad20e104d6acd749da6ee6a8930055ae
+clone git github.com/docker/libnetwork e8ebc0bf6510343c88d162db08b3d855cbbe75b9
 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
 clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
 clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
 clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
 clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4

+ 23 - 0
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) {
 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"))
 	c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level"))
 }
 }

+ 20 - 10
vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go

@@ -450,6 +450,8 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
 	}
 	}
 
 
 	if len(ipamV6Data) > 0 {
 	if len(ipamV6Data) > 0 {
+		c.AddressIPv6 = ipamV6Data[0].Pool
+
 		if ipamV6Data[0].Gateway != nil {
 		if ipamV6Data[0].Gateway != nil {
 			c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
 			c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
 		}
 		}
@@ -738,7 +740,9 @@ func (d *driver) DeleteNetwork(nid string) error {
 
 
 	// We only delete the bridge when it's not the default bridge. This is keep the backward compatible behavior.
 	// We only delete the bridge when it's not the default bridge. This is keep the backward compatible behavior.
 	if !config.DefaultBridge {
 	if !config.DefaultBridge {
-		err = netlink.LinkDel(n.bridge.Link)
+		if err := netlink.LinkDel(n.bridge.Link); err != nil {
+			logrus.Warnf("Failed to remove bridge interface %s on network %s delete: %v", config.BridgeName, nid, err)
+		}
 	}
 	}
 
 
 	return d.storeDelete(config)
 	return d.storeDelete(config)
@@ -959,13 +963,20 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
 	if endpoint.addrv6 == nil && config.EnableIPv6 {
 	if endpoint.addrv6 == nil && config.EnableIPv6 {
 		var ip6 net.IP
 		var ip6 net.IP
 		network := n.bridge.bridgeIPv6
 		network := n.bridge.bridgeIPv6
+		if config.AddressIPv6 != nil {
+			network = config.AddressIPv6
+		}
+
 		ones, _ := network.Mask.Size()
 		ones, _ := network.Mask.Size()
-		if ones <= 80 {
-			ip6 = make(net.IP, len(network.IP))
-			copy(ip6, network.IP)
-			for i, h := range endpoint.macAddress {
-				ip6[i+10] = h
-			}
+		if ones > 80 {
+			err = types.ForbiddenErrorf("Cannot self generate an IPv6 address on network %v: At least 48 host bits are needed.", network)
+			return err
+		}
+
+		ip6 = make(net.IP, len(network.IP))
+		copy(ip6, network.IP)
+		for i, h := range endpoint.macAddress {
+			ip6[i+10] = h
 		}
 		}
 
 
 		endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
 		endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
@@ -1037,9 +1048,8 @@ func (d *driver) DeleteEndpoint(nid, eid string) error {
 	// Remove port mappings. Do not stop endpoint delete on unmap failure
 	// Remove port mappings. Do not stop endpoint delete on unmap failure
 	n.releasePorts(ep)
 	n.releasePorts(ep)
 
 
-	// Try removal of link. Discard error: link pair might have
-	// already been deleted by sandbox delete. Make sure defer
-	// does not see this error either.
+	// Try removal of link. Discard error: it is a best effort.
+	// Also make sure defer does not see this error either.
 	if link, err := netlink.LinkByName(ep.srcName); err == nil {
 	if link, err := netlink.LinkByName(ep.srcName); err == nil {
 		netlink.LinkDel(link)
 		netlink.LinkDel(link)
 	}
 	}

+ 17 - 6
vendor/src/github.com/docker/libnetwork/endpoint.go

@@ -737,7 +737,7 @@ func (ep *endpoint) DataScope() string {
 	return ep.getNetwork().DataScope()
 	return ep.getNetwork().DataScope()
 }
 }
 
 
-func (ep *endpoint) assignAddress() error {
+func (ep *endpoint) assignAddress(assignIPv4, assignIPv6 bool) error {
 	var (
 	var (
 		ipam ipamapi.Ipam
 		ipam ipamapi.Ipam
 		err  error
 		err  error
@@ -754,11 +754,18 @@ func (ep *endpoint) assignAddress() error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	err = ep.assignAddressVersion(4, ipam)
-	if err != nil {
-		return err
+
+	if assignIPv4 {
+		if err = ep.assignAddressVersion(4, ipam); err != nil {
+			return err
+		}
 	}
 	}
-	return ep.assignAddressVersion(6, ipam)
+
+	if assignIPv6 {
+		err = ep.assignAddressVersion(6, ipam)
+	}
+
+	return err
 }
 }
 
 
 func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
 func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
@@ -787,7 +794,11 @@ func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
 	}
 	}
 
 
 	for _, d := range ipInfo {
 	for _, d := range ipInfo {
-		addr, _, err := ipam.RequestAddress(d.PoolID, nil, nil)
+		var prefIP net.IP
+		if *address != nil {
+			prefIP = (*address).IP
+		}
+		addr, _, err := ipam.RequestAddress(d.PoolID, prefIP, nil)
 		if err == nil {
 		if err == nil {
 			ep.Lock()
 			ep.Lock()
 			*address = addr
 			*address = addr

+ 21 - 1
vendor/src/github.com/docker/libnetwork/network.go

@@ -152,6 +152,7 @@ type network struct {
 	ipamV4Info   []*IpamInfo
 	ipamV4Info   []*IpamInfo
 	ipamV6Info   []*IpamInfo
 	ipamV6Info   []*IpamInfo
 	enableIPv6   bool
 	enableIPv6   bool
+	postIPv6     bool
 	epCnt        *endpointCnt
 	epCnt        *endpointCnt
 	generic      options.Generic
 	generic      options.Generic
 	dbIndex      uint64
 	dbIndex      uint64
@@ -298,6 +299,7 @@ func (n *network) CopyTo(o datastore.KVObject) error {
 	dstN.ipamType = n.ipamType
 	dstN.ipamType = n.ipamType
 	dstN.enableIPv6 = n.enableIPv6
 	dstN.enableIPv6 = n.enableIPv6
 	dstN.persist = n.persist
 	dstN.persist = n.persist
+	dstN.postIPv6 = n.postIPv6
 	dstN.dbIndex = n.dbIndex
 	dstN.dbIndex = n.dbIndex
 	dstN.dbExists = n.dbExists
 	dstN.dbExists = n.dbExists
 	dstN.drvOnce = n.drvOnce
 	dstN.drvOnce = n.drvOnce
@@ -358,6 +360,7 @@ func (n *network) MarshalJSON() ([]byte, error) {
 		netMap["generic"] = n.generic
 		netMap["generic"] = n.generic
 	}
 	}
 	netMap["persist"] = n.persist
 	netMap["persist"] = n.persist
+	netMap["postIPv6"] = n.postIPv6
 	if len(n.ipamV4Config) > 0 {
 	if len(n.ipamV4Config) > 0 {
 		ics, err := json.Marshal(n.ipamV4Config)
 		ics, err := json.Marshal(n.ipamV4Config)
 		if err != nil {
 		if err != nil {
@@ -418,6 +421,9 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
 	if v, ok := netMap["persist"]; ok {
 	if v, ok := netMap["persist"]; ok {
 		n.persist = v.(bool)
 		n.persist = v.(bool)
 	}
 	}
+	if v, ok := netMap["postIPv6"]; ok {
+		n.postIPv6 = v.(bool)
+	}
 	if v, ok := netMap["ipamType"]; ok {
 	if v, ok := netMap["ipamType"]; ok {
 		n.ipamType = v.(string)
 		n.ipamType = v.(string)
 	} else {
 	} else {
@@ -505,6 +511,16 @@ func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
 	}
 	}
 }
 }
 
 
+// NetworkOptionDeferIPv6Alloc instructs the network to defer the IPV6 address allocation until after the endpoint has been created
+// It is being provided to support the specific docker daemon flags where user can deterministically assign an IPv6 address
+// to a container as combination of fixed-cidr-v6 + mac-address
+// TODO: Remove this option setter once we support endpoint ipam options
+func NetworkOptionDeferIPv6Alloc(enable bool) NetworkOption {
+	return func(n *network) {
+		n.postIPv6 = enable
+	}
+}
+
 func (n *network) processOptions(options ...NetworkOption) {
 func (n *network) processOptions(options ...NetworkOption) {
 	for _, opt := range options {
 	for _, opt := range options {
 		if opt != nil {
 		if opt != nil {
@@ -655,7 +671,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
 
 
 	ep.processOptions(options...)
 	ep.processOptions(options...)
 
 
-	if err = ep.assignAddress(); err != nil {
+	if err = ep.assignAddress(true, !n.postIPv6); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	defer func() {
 	defer func() {
@@ -675,6 +691,10 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
 		}
 		}
 	}()
 	}()
 
 
+	if err = ep.assignAddress(false, n.postIPv6); err != nil {
+		return nil, err
+	}
+
 	if err = n.getController().updateToStore(ep); err != nil {
 	if err = n.getController().updateToStore(ep); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}