Browse code

Merge pull request #17890 from aboch/b6

Restore deterministic IPv6 from MAC behavior on default bridge network

Tibor Vass authored on 2015/11/12 02:42:08
Showing 6 changed files
... ...
@@ -455,12 +455,25 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
455 455
 		ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4.String()
456 456
 	}
457 457
 
458
-	var ipamV6Conf *libnetwork.IpamConf
458
+	var (
459
+		ipamV6Conf     *libnetwork.IpamConf
460
+		deferIPv6Alloc bool
461
+	)
459 462
 	if config.Bridge.FixedCIDRv6 != "" {
460 463
 		_, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6)
461 464
 		if err != nil {
462 465
 			return err
463 466
 		}
467
+
468
+		// In case user has specified the daemon flag --fixed-cidr-v6 and the passed network has
469
+		// at least 48 host bits, we need to guarantee the current behavior where the containers'
470
+		// IPv6 addresses will be constructed based on the containers' interface MAC address.
471
+		// We do so by telling libnetwork to defer the IPv6 address allocation for the endpoints
472
+		// on this network until after the driver has created the endpoint and returned the
473
+		// constructed address. Libnetwork will then reserve this address with the ipam driver.
474
+		ones, _ := fCIDRv6.Mask.Size()
475
+		deferIPv6Alloc = ones <= 80
476
+
464 477
 		if ipamV6Conf == nil {
465 478
 			ipamV6Conf = &libnetwork.IpamConf{}
466 479
 		}
... ...
@@ -485,7 +498,8 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
485 485
 			netlabel.GenericData: netOption,
486 486
 			netlabel.EnableIPv6:  config.Bridge.EnableIPv6,
487 487
 		}),
488
-		libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf))
488
+		libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf),
489
+		libnetwork.NetworkOptionDeferIPv6Alloc(deferIPv6Alloc))
489 490
 	if err != nil {
490 491
 		return fmt.Errorf("Error creating default \"bridge\" network: %v", err)
491 492
 	}
... ...
@@ -22,7 +22,7 @@ clone git github.com/vdemeester/shakers 3c10293ce22b900c27acad7b28656196fcc2f73b
22 22
 clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git
23 23
 
24 24
 #get libnetwork packages
25
-clone git github.com/docker/libnetwork 5978c276ad20e104d6acd749da6ee6a8930055ae
25
+clone git github.com/docker/libnetwork e8ebc0bf6510343c88d162db08b3d855cbbe75b9
26 26
 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
27 27
 clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
28 28
 clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
... ...
@@ -377,6 +377,29 @@ func (s *DockerSuite) TestDaemonIPv6FixedCIDR(c *check.C) {
377 377
 	}
378 378
 }
379 379
 
380
+// TestDaemonIPv6FixedCIDRAndMac checks that when the daemon is started with ipv6 fixed CIDR
381
+// the running containers are given a an IPv6 address derived from the MAC address and the ipv6 fixed CIDR
382
+func (s *DockerSuite) TestDaemonIPv6FixedCIDRAndMac(c *check.C) {
383
+	err := setupV6()
384
+	c.Assert(err, checker.IsNil)
385
+
386
+	d := NewDaemon(c)
387
+
388
+	err = d.StartWithBusybox("--ipv6", "--fixed-cidr-v6='2001:db8:1::/64'")
389
+	c.Assert(err, checker.IsNil)
390
+	defer d.Stop()
391
+
392
+	out, err := d.Cmd("run", "-itd", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox")
393
+	c.Assert(err, checker.IsNil)
394
+
395
+	out, err = d.Cmd("inspect", "--format", "'{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}'", "ipv6test")
396
+	c.Assert(err, checker.IsNil)
397
+	c.Assert(strings.Trim(out, " \r\n'"), checker.Equals, "2001:db8:1::aabb:ccdd:eeff")
398
+
399
+	err = teardownV6()
400
+	c.Assert(err, checker.IsNil)
401
+}
402
+
380 403
 func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) {
381 404
 	c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level"))
382 405
 }
... ...
@@ -450,6 +450,8 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
450 450
 	}
451 451
 
452 452
 	if len(ipamV6Data) > 0 {
453
+		c.AddressIPv6 = ipamV6Data[0].Pool
454
+
453 455
 		if ipamV6Data[0].Gateway != nil {
454 456
 			c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
455 457
 		}
... ...
@@ -738,7 +740,9 @@ func (d *driver) DeleteNetwork(nid string) error {
738 738
 
739 739
 	// We only delete the bridge when it's not the default bridge. This is keep the backward compatible behavior.
740 740
 	if !config.DefaultBridge {
741
-		err = netlink.LinkDel(n.bridge.Link)
741
+		if err := netlink.LinkDel(n.bridge.Link); err != nil {
742
+			logrus.Warnf("Failed to remove bridge interface %s on network %s delete: %v", config.BridgeName, nid, err)
743
+		}
742 744
 	}
743 745
 
744 746
 	return d.storeDelete(config)
... ...
@@ -959,13 +963,20 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
959 959
 	if endpoint.addrv6 == nil && config.EnableIPv6 {
960 960
 		var ip6 net.IP
961 961
 		network := n.bridge.bridgeIPv6
962
+		if config.AddressIPv6 != nil {
963
+			network = config.AddressIPv6
964
+		}
965
+
962 966
 		ones, _ := network.Mask.Size()
963
-		if ones <= 80 {
964
-			ip6 = make(net.IP, len(network.IP))
965
-			copy(ip6, network.IP)
966
-			for i, h := range endpoint.macAddress {
967
-				ip6[i+10] = h
968
-			}
967
+		if ones > 80 {
968
+			err = types.ForbiddenErrorf("Cannot self generate an IPv6 address on network %v: At least 48 host bits are needed.", network)
969
+			return err
970
+		}
971
+
972
+		ip6 = make(net.IP, len(network.IP))
973
+		copy(ip6, network.IP)
974
+		for i, h := range endpoint.macAddress {
975
+			ip6[i+10] = h
969 976
 		}
970 977
 
971 978
 		endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
... ...
@@ -1037,9 +1048,8 @@ func (d *driver) DeleteEndpoint(nid, eid string) error {
1037 1037
 	// Remove port mappings. Do not stop endpoint delete on unmap failure
1038 1038
 	n.releasePorts(ep)
1039 1039
 
1040
-	// Try removal of link. Discard error: link pair might have
1041
-	// already been deleted by sandbox delete. Make sure defer
1042
-	// does not see this error either.
1040
+	// Try removal of link. Discard error: it is a best effort.
1041
+	// Also make sure defer does not see this error either.
1043 1042
 	if link, err := netlink.LinkByName(ep.srcName); err == nil {
1044 1043
 		netlink.LinkDel(link)
1045 1044
 	}
... ...
@@ -737,7 +737,7 @@ func (ep *endpoint) DataScope() string {
737 737
 	return ep.getNetwork().DataScope()
738 738
 }
739 739
 
740
-func (ep *endpoint) assignAddress() error {
740
+func (ep *endpoint) assignAddress(assignIPv4, assignIPv6 bool) error {
741 741
 	var (
742 742
 		ipam ipamapi.Ipam
743 743
 		err  error
... ...
@@ -754,11 +754,18 @@ func (ep *endpoint) assignAddress() error {
754 754
 	if err != nil {
755 755
 		return err
756 756
 	}
757
-	err = ep.assignAddressVersion(4, ipam)
758
-	if err != nil {
759
-		return err
757
+
758
+	if assignIPv4 {
759
+		if err = ep.assignAddressVersion(4, ipam); err != nil {
760
+			return err
761
+		}
760 762
 	}
761
-	return ep.assignAddressVersion(6, ipam)
763
+
764
+	if assignIPv6 {
765
+		err = ep.assignAddressVersion(6, ipam)
766
+	}
767
+
768
+	return err
762 769
 }
763 770
 
764 771
 func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
... ...
@@ -787,7 +794,11 @@ func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
787 787
 	}
788 788
 
789 789
 	for _, d := range ipInfo {
790
-		addr, _, err := ipam.RequestAddress(d.PoolID, nil, nil)
790
+		var prefIP net.IP
791
+		if *address != nil {
792
+			prefIP = (*address).IP
793
+		}
794
+		addr, _, err := ipam.RequestAddress(d.PoolID, prefIP, nil)
791 795
 		if err == nil {
792 796
 			ep.Lock()
793 797
 			*address = addr
... ...
@@ -152,6 +152,7 @@ type network struct {
152 152
 	ipamV4Info   []*IpamInfo
153 153
 	ipamV6Info   []*IpamInfo
154 154
 	enableIPv6   bool
155
+	postIPv6     bool
155 156
 	epCnt        *endpointCnt
156 157
 	generic      options.Generic
157 158
 	dbIndex      uint64
... ...
@@ -298,6 +299,7 @@ func (n *network) CopyTo(o datastore.KVObject) error {
298 298
 	dstN.ipamType = n.ipamType
299 299
 	dstN.enableIPv6 = n.enableIPv6
300 300
 	dstN.persist = n.persist
301
+	dstN.postIPv6 = n.postIPv6
301 302
 	dstN.dbIndex = n.dbIndex
302 303
 	dstN.dbExists = n.dbExists
303 304
 	dstN.drvOnce = n.drvOnce
... ...
@@ -358,6 +360,7 @@ func (n *network) MarshalJSON() ([]byte, error) {
358 358
 		netMap["generic"] = n.generic
359 359
 	}
360 360
 	netMap["persist"] = n.persist
361
+	netMap["postIPv6"] = n.postIPv6
361 362
 	if len(n.ipamV4Config) > 0 {
362 363
 		ics, err := json.Marshal(n.ipamV4Config)
363 364
 		if err != nil {
... ...
@@ -418,6 +421,9 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
418 418
 	if v, ok := netMap["persist"]; ok {
419 419
 		n.persist = v.(bool)
420 420
 	}
421
+	if v, ok := netMap["postIPv6"]; ok {
422
+		n.postIPv6 = v.(bool)
423
+	}
421 424
 	if v, ok := netMap["ipamType"]; ok {
422 425
 		n.ipamType = v.(string)
423 426
 	} else {
... ...
@@ -505,6 +511,16 @@ func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
505 505
 	}
506 506
 }
507 507
 
508
+// NetworkOptionDeferIPv6Alloc instructs the network to defer the IPV6 address allocation until after the endpoint has been created
509
+// It is being provided to support the specific docker daemon flags where user can deterministically assign an IPv6 address
510
+// to a container as combination of fixed-cidr-v6 + mac-address
511
+// TODO: Remove this option setter once we support endpoint ipam options
512
+func NetworkOptionDeferIPv6Alloc(enable bool) NetworkOption {
513
+	return func(n *network) {
514
+		n.postIPv6 = enable
515
+	}
516
+}
517
+
508 518
 func (n *network) processOptions(options ...NetworkOption) {
509 519
 	for _, opt := range options {
510 520
 		if opt != nil {
... ...
@@ -655,7 +671,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
655 655
 
656 656
 	ep.processOptions(options...)
657 657
 
658
-	if err = ep.assignAddress(); err != nil {
658
+	if err = ep.assignAddress(true, !n.postIPv6); err != nil {
659 659
 		return nil, err
660 660
 	}
661 661
 	defer func() {
... ...
@@ -675,6 +691,10 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
675 675
 		}
676 676
 	}()
677 677
 
678
+	if err = ep.assignAddress(false, n.postIPv6); err != nil {
679
+		return nil, err
680
+	}
681
+
678 682
 	if err = n.getController().updateToStore(ep); err != nil {
679 683
 		return nil, err
680 684
 	}