Restore deterministic IPv6 from MAC behavior on default bridge network
| ... | ... |
@@ -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 |
} |