From f2f536032b5b26140e289a4654e140740256408f Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Sun, 4 Oct 2015 19:08:31 -0700 Subject: [PATCH 1/2] Phase-1 bridge driver changes to support IPAM Signed-off-by: Madhu Venugopal --- libnetwork/drivers/bridge/bridge.go | 225 +++++------------- libnetwork/drivers/bridge/bridge_test.go | 43 ---- .../bridge/setup_bridgenetfiltering.go | 2 +- .../drivers/bridge/setup_fixedcidrv4.go | 19 -- .../drivers/bridge/setup_fixedcidrv4_test.go | 62 ----- .../drivers/bridge/setup_fixedcidrv6.go | 27 --- .../drivers/bridge/setup_fixedcidrv6_test.go | 44 ---- libnetwork/drivers/bridge/setup_ipv4.go | 21 -- libnetwork/drivers/bridge/setup_ipv6.go | 11 - libnetwork/drivers/bridge/setup_ipv6_test.go | 1 - libnetwork/endpoint.go | 4 +- libnetwork/libnetwork_test.go | 20 +- libnetwork/network.go | 6 +- 13 files changed, 61 insertions(+), 424 deletions(-) delete mode 100644 libnetwork/drivers/bridge/setup_fixedcidrv4.go delete mode 100644 libnetwork/drivers/bridge/setup_fixedcidrv4_test.go delete mode 100644 libnetwork/drivers/bridge/setup_fixedcidrv6.go delete mode 100644 libnetwork/drivers/bridge/setup_fixedcidrv6_test.go diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index edcd7250ce..72f58a5f7c 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -35,6 +35,13 @@ const ( maxAllocatePortAttempts = 10 ) +const ( + // DefaultGatewayV4AuxKey represents the default-gateway configured by the user + DefaultGatewayV4AuxKey = "DefaultGatewayIPv4" + // DefaultGatewayV6AuxKey represents the ipv6 default-gateway configured by the user + DefaultGatewayV6AuxKey = "DefaultGatewayIPv6" +) + var ( ipAllocator *ipallocator.IPAllocator ) @@ -50,8 +57,6 @@ type configuration struct { type networkConfiguration struct { BridgeName string AddressIPv4 *net.IPNet - FixedCIDR *net.IPNet - FixedCIDRv6 *net.IPNet EnableIPv6 bool EnableIPMasquerade bool EnableICC bool @@ -148,19 +153,6 @@ func (c *networkConfiguration) Validate() error { // If bridge v4 subnet is specified if c.AddressIPv4 != nil { - // If Container restricted subnet is specified, it must be a subset of bridge subnet - if c.FixedCIDR != nil { - // Check Network address - if !c.AddressIPv4.Contains(c.FixedCIDR.IP) { - return &ErrInvalidContainerSubnet{} - } - // Check it is effectively a subset - brNetLen, _ := c.AddressIPv4.Mask.Size() - cnNetLen, _ := c.FixedCIDR.Mask.Size() - if brNetLen > cnNetLen { - return &ErrInvalidContainerSubnet{} - } - } // If default gw is specified, it must be part of bridge subnet if c.DefaultGatewayIPv4 != nil { if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) { @@ -169,13 +161,6 @@ func (c *networkConfiguration) Validate() error { } } - // If default v6 gw is specified, FixedCIDRv6 must be specified and gw must belong to FixedCIDRv6 subnet - if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil { - if c.FixedCIDRv6 == nil || !c.FixedCIDRv6.Contains(c.DefaultGatewayIPv6) { - return &ErrInvalidGateway{} - } - } - return nil } @@ -259,65 +244,6 @@ func (c *networkConfiguration) fromMap(data map[string]interface{}) error { } } - if i, ok := data["AddressIPv4"]; ok && i != nil { - if s, ok := i.(string); ok { - if ip, nw, e := net.ParseCIDR(s); e == nil { - nw.IP = ip - c.AddressIPv4 = nw - } else { - return types.BadRequestErrorf("failed to parse AddressIPv4 value") - } - } else { - return types.BadRequestErrorf("invalid type for AddressIPv4 value") - } - } - - if i, ok := data["FixedCIDR"]; ok && i != nil { - if s, ok := i.(string); ok { - if ip, nw, e := net.ParseCIDR(s); e == nil { - nw.IP = ip - c.FixedCIDR = nw - } else { - return types.BadRequestErrorf("failed to parse FixedCIDR value") - } - } else { - return types.BadRequestErrorf("invalid type for FixedCIDR value") - } - } - - if i, ok := data["FixedCIDRv6"]; ok && i != nil { - if s, ok := i.(string); ok { - if ip, nw, e := net.ParseCIDR(s); e == nil { - nw.IP = ip - c.FixedCIDRv6 = nw - } else { - return types.BadRequestErrorf("failed to parse FixedCIDRv6 value") - } - } else { - return types.BadRequestErrorf("invalid type for FixedCIDRv6 value") - } - } - - if i, ok := data["DefaultGatewayIPv4"]; ok && i != nil { - if s, ok := i.(string); ok { - if c.DefaultGatewayIPv4 = net.ParseIP(s); c.DefaultGatewayIPv4 == nil { - return types.BadRequestErrorf("failed to parse DefaultGatewayIPv4 value") - } - } else { - return types.BadRequestErrorf("invalid type for DefaultGatewayIPv4 value") - } - } - - if i, ok := data["DefaultGatewayIPv6"]; ok && i != nil { - if s, ok := i.(string); ok { - if c.DefaultGatewayIPv6 = net.ParseIP(s); c.DefaultGatewayIPv6 == nil { - return types.BadRequestErrorf("failed to parse DefaultGatewayIPv6 value") - } - } else { - return types.BadRequestErrorf("invalid type for DefaultGatewayIPv6 value") - } - } - if i, ok := data["DefaultBindingIP"]; ok && i != nil { if s, ok := i.(string); ok { if c.DefaultBindingIP = net.ParseIP(s); c.DefaultBindingIP == nil { @@ -327,6 +253,7 @@ func (c *networkConfiguration) fromMap(data map[string]interface{}) error { return types.BadRequestErrorf("invalid type for DefaultBindingIP value") } } + return nil } @@ -516,6 +443,36 @@ func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error) return config, err } +func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error { + if len(ipamV4Data) > 1 || len(ipamV6Data) > 1 { + return types.ForbiddenErrorf("bridge driver doesnt support multiple subnets") + } + + if len(ipamV4Data) == 0 { + return types.BadRequestErrorf("bridge network %s requires ipv4 configuration", id) + } + + if ipamV4Data[0].Gateway != nil { + c.AddressIPv4 = types.GetIPNetCopy(ipamV4Data[0].Gateway) + } + + if c.EnableIPv6 && len(ipamV6Data) == 0 { + return types.BadRequestErrorf("bridge network %s requires ipv6 configuration", id) + } + + gw, ok := ipamV4Data[0].AuxAddresses[DefaultGatewayV4AuxKey] + if ok { + c.DefaultGatewayIPv4 = gw.IP + } + + gw, ok = ipamV4Data[0].AuxAddresses[DefaultGatewayV6AuxKey] + if ok { + c.DefaultGatewayIPv6 = gw.IP + } + + return nil +} + func parseNetworkOptions(id string, option options.Generic) (*networkConfiguration, error) { var err error config := &networkConfiguration{} @@ -545,10 +502,6 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati // Returns the non link-local IPv6 subnet for the containers attached to this bridge if found, nil otherwise func getV6Network(config *networkConfiguration, i *bridgeInterface) *net.IPNet { - if config.FixedCIDRv6 != nil { - return config.FixedCIDRv6 - } - if i.bridgeIPv6 != nil && i.bridgeIPv6.IP != nil && !i.bridgeIPv6.IP.IsLinkLocalUnicast() { return i.bridgeIPv6 } @@ -587,6 +540,12 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat if err != nil { return err } + + err = config.processIPAM(id, ipV4Data, ipV6Data) + if err != nil { + return err + } + networkList := d.getNetworks() for _, nw := range networkList { nw.Lock() @@ -655,7 +614,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat bridgeSetup.queueStep(setupBridgeIPv4) enableIPv6Forwarding := false - if d.config.EnableIPForwarding && config.FixedCIDRv6 != nil { + if d.config.EnableIPForwarding { enableIPv6Forwarding = true } @@ -674,14 +633,6 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat // the case of a previously existing device. {bridgeAlreadyExists, setupVerifyAndReconcile}, - // Setup the bridge to allocate containers IPv4 addresses in the - // specified subnet. - {config.FixedCIDR != nil, setupFixedCIDRv4}, - - // Setup the bridge to allocate containers global IPv6 addresses in the - // specified subnet. - {config.FixedCIDRv6 != nil, setupFixedCIDRv6}, - // Enable IPv6 Forwarding {enableIPv6Forwarding, setupIPv6Forwarding}, @@ -712,8 +663,6 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat } } - // Block bridge IP from being allocated. - bridgeSetup.queueStep(allocateBridgeIP) // Apply the prepared list of steps, and abort at the first error. bridgeSetup.queueStep(setupDeviceUp) if err = bridgeSetup.apply(); err != nil { @@ -790,21 +739,7 @@ func (d *driver) DeleteNetwork(nid string) error { } // Programming - err = netlink.LinkDel(n.bridge.Link) - - // Release ip addresses (ignore errors) - if config.FixedCIDR == nil || config.FixedCIDR.Contains(config.DefaultGatewayIPv4) { - if e := ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, n.bridge.gatewayIPv4); e != nil { - logrus.Warnf("Failed to release default gateway address %s: %v", n.bridge.gatewayIPv4.String(), e) - } - } - if config.FixedCIDR == nil || config.FixedCIDR.Contains(n.bridge.bridgeIPv4.IP) { - if e := ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, n.bridge.bridgeIPv4.IP); e != nil { - logrus.Warnf("Failed to release bridge IP %s: %v", n.bridge.bridgeIPv4.IP.String(), e) - } - } - - return err + return netlink.LinkDel(n.bridge.Link) } func addToBridge(ifaceName, bridgeName string) error { @@ -996,12 +931,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } } - // v4 address for the sandbox side pipe interface - ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil) - if err != nil { - return err - } - ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask} + ipv4Addr := ifInfo.Address() // Down the interface before configuring mac address. if err = netlink.LinkSetDown(sbox); err != nil { @@ -1009,7 +939,10 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } // Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP. - mac := electMacAddress(epConfig, ip4) + mac := ifInfo.MacAddress() + if mac == nil { + mac = electMacAddress(epConfig, ipv4Addr.IP) + } err = netlink.LinkSetHardwareAddr(sbox, mac) if err != nil { return fmt.Errorf("could not set mac address for container interface %s: %v", containerIfName, err) @@ -1021,33 +954,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err) } - // v6 address for the sandbox side pipe interface - ipv6Addr = &net.IPNet{} - if config.EnableIPv6 { - var ip6 net.IP - - network := n.bridge.bridgeIPv6 - if config.FixedCIDRv6 != nil { - network = config.FixedCIDRv6 - } - - ones, _ := network.Mask.Size() - if ones <= 80 { - ip6 = make(net.IP, len(network.IP)) - copy(ip6, network.IP) - for i, h := range mac { - ip6[i+10] = h - } - } - - ip6, err := ipAllocator.RequestIP(network, ip6) - if err != nil { - return err - } - - ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask} - } - + ipv6Addr = ifInfo.AddressIPv6() // Create the sandbox side pipe interface endpoint.srcName = containerIfName endpoint.addr = ipv4Addr @@ -1062,16 +969,8 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, return err } - err = ifInfo.SetMacAddress(endpoint.macAddress) - if err != nil { - return err - } - err = ifInfo.SetIPAddress(ipv4Addr) - if err != nil { - return err - } - if config.EnableIPv6 { - err = ifInfo.SetIPAddress(ipv6Addr) + if ifInfo.MacAddress() == nil { + err = ifInfo.SetMacAddress(endpoint.macAddress) if err != nil { return err } @@ -1140,22 +1039,6 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { return err } - n.Lock() - config := n.config - n.Unlock() - - // Release the v6 address allocated to this endpoint's sandbox interface - if config.EnableIPv6 { - network := n.bridge.bridgeIPv6 - if config.FixedCIDRv6 != nil { - network = config.FixedCIDRv6 - } - err := ipAllocator.ReleaseIP(network, ep.addrv6.IP) - if err != nil { - return err - } - } - // 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. diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index 36c6f6338c..8cf577ce7c 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -36,11 +36,9 @@ func TestCreateFullOptions(t *testing.T) { netConfig := &networkConfiguration{ BridgeName: DefaultBridgeName, AddressIPv4: nw, - FixedCIDR: cnw, DefaultGatewayIPv4: gw, EnableIPv6: true, } - _, netConfig.FixedCIDRv6, _ = net.ParseCIDR("2001:db8::/48") genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = config @@ -584,39 +582,6 @@ func TestValidateConfig(t *testing.T) { // Bridge network _, network, _ := net.ParseCIDR("172.28.0.0/16") - // Test FixedCIDR - _, containerSubnet, _ := net.ParseCIDR("172.27.0.0/16") - c = networkConfiguration{ - AddressIPv4: network, - FixedCIDR: containerSubnet, - } - - err = c.Validate() - if err == nil { - t.Fatalf("Failed to detect invalid FixedCIDR network") - } - - _, containerSubnet, _ = net.ParseCIDR("172.28.0.0/16") - c.FixedCIDR = containerSubnet - err = c.Validate() - if err != nil { - t.Fatalf("Unexpected validation error on FixedCIDR network") - } - - _, containerSubnet, _ = net.ParseCIDR("172.28.0.0/15") - c.FixedCIDR = containerSubnet - err = c.Validate() - if err == nil { - t.Fatalf("Failed to detect invalid FixedCIDR network") - } - - _, containerSubnet, _ = net.ParseCIDR("172.28.0.0/17") - c.FixedCIDR = containerSubnet - err = c.Validate() - if err != nil { - t.Fatalf("Unexpected validation error on FixedCIDR network") - } - // Test v4 gw c.DefaultGatewayIPv4 = net.ParseIP("172.27.30.234") err = c.Validate() @@ -634,7 +599,6 @@ func TestValidateConfig(t *testing.T) { _, containerSubnet, _ = net.ParseCIDR("2001:1234:ae:b004::/64") c = networkConfiguration{ EnableIPv6: true, - FixedCIDRv6: containerSubnet, DefaultGatewayIPv6: net.ParseIP("2001:1234:ac:b004::bad:a55"), } err = c.Validate() @@ -647,12 +611,6 @@ func TestValidateConfig(t *testing.T) { if err != nil { t.Fatalf("Unexpected validation error on v6 default gateway") } - - c.FixedCIDRv6 = nil - err = c.Validate() - if err == nil { - t.Fatalf("Failed to detect invalid v6 default gateway") - } } func TestSetDefaultGw(t *testing.T) { @@ -683,7 +641,6 @@ func TestSetDefaultGw(t *testing.T) { config := &networkConfiguration{ BridgeName: DefaultBridgeName, EnableIPv6: true, - FixedCIDRv6: subnetv6, DefaultGatewayIPv4: gw4, DefaultGatewayIPv6: gw6, } diff --git a/libnetwork/drivers/bridge/setup_bridgenetfiltering.go b/libnetwork/drivers/bridge/setup_bridgenetfiltering.go index e7a5f4b077..b17fe98914 100644 --- a/libnetwork/drivers/bridge/setup_bridgenetfiltering.go +++ b/libnetwork/drivers/bridge/setup_bridgenetfiltering.go @@ -22,7 +22,7 @@ const ( //Gets the IP version in use ( [ipv4], [ipv6] or [ipv4 and ipv6] ) func getIPVersion(config *networkConfiguration) ipVersion { ipVersion := ipv4 - if config.FixedCIDRv6 != nil || config.EnableIPv6 { + if config.EnableIPv6 { ipVersion |= ipv6 } return ipVersion diff --git a/libnetwork/drivers/bridge/setup_fixedcidrv4.go b/libnetwork/drivers/bridge/setup_fixedcidrv4.go deleted file mode 100644 index 0702053430..0000000000 --- a/libnetwork/drivers/bridge/setup_fixedcidrv4.go +++ /dev/null @@ -1,19 +0,0 @@ -package bridge - -import ( - log "github.com/Sirupsen/logrus" -) - -func setupFixedCIDRv4(config *networkConfiguration, i *bridgeInterface) error { - addrv4, _, err := i.addresses() - if err != nil { - return err - } - - log.Debugf("Using IPv4 subnet: %v", config.FixedCIDR) - if err := ipAllocator.RegisterSubnet(addrv4.IPNet, config.FixedCIDR); err != nil { - return &FixedCIDRv4Error{Subnet: config.FixedCIDR, Net: addrv4.IPNet, Err: err} - } - - return nil -} diff --git a/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go b/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go deleted file mode 100644 index 9a3f01f53b..0000000000 --- a/libnetwork/drivers/bridge/setup_fixedcidrv4_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package bridge - -import ( - "net" - "testing" - - "github.com/docker/libnetwork/testutils" -) - -func TestSetupFixedCIDRv4(t *testing.T) { - defer testutils.SetupTestOSContext(t)() - - config := &networkConfiguration{ - BridgeName: DefaultBridgeName, - AddressIPv4: &net.IPNet{IP: net.ParseIP("192.168.1.1"), Mask: net.CIDRMask(16, 32)}, - FixedCIDR: &net.IPNet{IP: net.ParseIP("192.168.2.0"), Mask: net.CIDRMask(24, 32)}} - br := &bridgeInterface{} - - if err := setupDevice(config, br); err != nil { - t.Fatalf("Bridge creation failed: %v", err) - } - if err := setupBridgeIPv4(config, br); err != nil { - t.Fatalf("Assign IPv4 to bridge failed: %v", err) - } - - if err := setupFixedCIDRv4(config, br); err != nil { - t.Fatalf("Failed to setup bridge FixedCIDRv4: %v", err) - } - - if ip, err := ipAllocator.RequestIP(config.FixedCIDR, nil); err != nil { - t.Fatalf("Failed to request IP to allocator: %v", err) - } else if expected := "192.168.2.1"; ip.String() != expected { - t.Fatalf("Expected allocated IP %s, got %s", expected, ip) - } -} - -func TestSetupBadFixedCIDRv4(t *testing.T) { - defer testutils.SetupTestOSContext(t)() - - config := &networkConfiguration{ - BridgeName: DefaultBridgeName, - AddressIPv4: &net.IPNet{IP: net.ParseIP("192.168.1.1"), Mask: net.CIDRMask(24, 32)}, - FixedCIDR: &net.IPNet{IP: net.ParseIP("192.168.2.0"), Mask: net.CIDRMask(24, 32)}} - br := &bridgeInterface{} - - if err := setupDevice(config, br); err != nil { - t.Fatalf("Bridge creation failed: %v", err) - } - if err := setupBridgeIPv4(config, br); err != nil { - t.Fatalf("Assign IPv4 to bridge failed: %v", err) - } - - err := setupFixedCIDRv4(config, br) - if err == nil { - t.Fatal("Setup bridge FixedCIDRv4 should have failed") - } - - if _, ok := err.(*FixedCIDRv4Error); !ok { - t.Fatalf("Did not fail with expected error. Actual error: %v", err) - } - -} diff --git a/libnetwork/drivers/bridge/setup_fixedcidrv6.go b/libnetwork/drivers/bridge/setup_fixedcidrv6.go deleted file mode 100644 index b2a949be5b..0000000000 --- a/libnetwork/drivers/bridge/setup_fixedcidrv6.go +++ /dev/null @@ -1,27 +0,0 @@ -package bridge - -import ( - "os" - - log "github.com/Sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -func setupFixedCIDRv6(config *networkConfiguration, i *bridgeInterface) error { - log.Debugf("Using IPv6 subnet: %v", config.FixedCIDRv6) - if err := ipAllocator.RegisterSubnet(config.FixedCIDRv6, config.FixedCIDRv6); err != nil { - return &FixedCIDRv6Error{Net: config.FixedCIDRv6, Err: err} - } - - // Setting route to global IPv6 subnet - log.Debugf("Adding route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName) - err := netlink.RouteAdd(&netlink.Route{ - Scope: netlink.SCOPE_UNIVERSE, - LinkIndex: i.Link.Attrs().Index, - Dst: config.FixedCIDRv6, - }) - if err != nil && !os.IsExist(err) { - log.Errorf("Could not add route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName) - } - return nil -} diff --git a/libnetwork/drivers/bridge/setup_fixedcidrv6_test.go b/libnetwork/drivers/bridge/setup_fixedcidrv6_test.go deleted file mode 100644 index 5a46f1b0f2..0000000000 --- a/libnetwork/drivers/bridge/setup_fixedcidrv6_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package bridge - -import ( - "net" - "testing" - - "github.com/docker/libnetwork/testutils" -) - -func TestSetupFixedCIDRv6(t *testing.T) { - defer testutils.SetupTestOSContext(t)() - - config := &networkConfiguration{} - br := newInterface(config) - - _, config.FixedCIDRv6, _ = net.ParseCIDR("2002:db8::/48") - if err := setupDevice(config, br); err != nil { - t.Fatalf("Bridge creation failed: %v", err) - } - if err := setupBridgeIPv4(config, br); err != nil { - t.Fatalf("Assign IPv4 to bridge failed: %v", err) - } - - if err := setupBridgeIPv6(config, br); err != nil { - t.Fatalf("Assign IPv4 to bridge failed: %v", err) - } - - if err := setupFixedCIDRv6(config, br); err != nil { - t.Fatalf("Failed to setup bridge FixedCIDRv6: %v", err) - } - - var ip net.IP - if ip, err := ipAllocator.RequestIP(config.FixedCIDRv6, nil); err != nil { - t.Fatalf("Failed to request IP to allocator: %v", err) - } else if expected := "2002:db8::1"; ip.String() != expected { - t.Fatalf("Expected allocated IP %s, got %s", expected, ip) - } - - if err := ipAllocator.ReleaseIP(config.FixedCIDRv6, ip); err != nil { - t.Fatalf("Failed to release IP from allocator: %v", err) - } else if _, err := ipAllocator.RequestIP(config.FixedCIDRv6, ip); err != nil { - t.Fatalf("Failed to request a released IP: %v", err) - } -} diff --git a/libnetwork/drivers/bridge/setup_ipv4.go b/libnetwork/drivers/bridge/setup_ipv4.go index e63c18df21..6e955d6fb5 100644 --- a/libnetwork/drivers/bridge/setup_ipv4.go +++ b/libnetwork/drivers/bridge/setup_ipv4.go @@ -74,18 +74,6 @@ func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error { return nil } -func allocateBridgeIP(config *networkConfiguration, i *bridgeInterface) error { - // Because of the way ipallocator manages the container address space, - // reserve bridge address only if it belongs to the container network - // (if defined), no need otherwise - if config.FixedCIDR == nil || config.FixedCIDR.Contains(i.bridgeIPv4.IP) { - if _, err := ipAllocator.RequestIP(i.bridgeIPv4, i.bridgeIPv4.IP); err != nil { - return fmt.Errorf("failed to reserve bridge IP %s: %v", i.bridgeIPv4.IP.String(), err) - } - } - return nil -} - func electBridgeIPv4(config *networkConfiguration) (*net.IPNet, error) { // Use the requested IPv4 CIDR when available. if config.AddressIPv4 != nil { @@ -117,15 +105,6 @@ func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error { return &ErrInvalidGateway{} } - // Because of the way ipallocator manages the container address space, - // reserve default gw address only if it belongs to the container network - // (if defined), no need otherwise - if config.FixedCIDR == nil || config.FixedCIDR.Contains(config.DefaultGatewayIPv4) { - if _, err := ipAllocator.RequestIP(i.bridgeIPv4, config.DefaultGatewayIPv4); err != nil { - return fmt.Errorf("failed to reserve default gateway %s: %v", config.DefaultGatewayIPv4.String(), err) - } - } - // Store requested default gateway i.gatewayIPv4 = config.DefaultGatewayIPv4 diff --git a/libnetwork/drivers/bridge/setup_ipv6.go b/libnetwork/drivers/bridge/setup_ipv6.go index da3c9db9c3..f84a5da809 100644 --- a/libnetwork/drivers/bridge/setup_ipv6.go +++ b/libnetwork/drivers/bridge/setup_ipv6.go @@ -61,19 +61,8 @@ func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error { } func setupGatewayIPv6(config *networkConfiguration, i *bridgeInterface) error { - if config.FixedCIDRv6 == nil { - return &ErrInvalidContainerSubnet{} - } - if !config.FixedCIDRv6.Contains(config.DefaultGatewayIPv6) { - return &ErrInvalidGateway{} - } - if _, err := ipAllocator.RequestIP(config.FixedCIDRv6, config.DefaultGatewayIPv6); err != nil { - return err - } - // Store requested default gateway i.gatewayIPv6 = config.DefaultGatewayIPv6 - return nil } diff --git a/libnetwork/drivers/bridge/setup_ipv6_test.go b/libnetwork/drivers/bridge/setup_ipv6_test.go index fa264e4aac..a237b42e05 100644 --- a/libnetwork/drivers/bridge/setup_ipv6_test.go +++ b/libnetwork/drivers/bridge/setup_ipv6_test.go @@ -55,7 +55,6 @@ func TestSetupGatewayIPv6(t *testing.T) { config := &networkConfiguration{ BridgeName: DefaultBridgeName, - FixedCIDRv6: nw, DefaultGatewayIPv6: gw} br := &bridgeInterface{} diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 684df12c34..2aa946a153 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -623,7 +623,7 @@ func (ep *endpoint) assignAddress() error { err error ) n := ep.getNetwork() - if n.Type() == "host" || n.Type() == "null" || n.Type() == "bridge" { + if n.Type() == "host" || n.Type() == "null" { return nil } ipam, err = n.getController().getIpamDriver(n.ipamType) @@ -680,7 +680,7 @@ func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error { func (ep *endpoint) releaseAddress() { n := ep.getNetwork() - if n.Type() == "host" || n.Type() == "null" || n.Type() == "bridge" { + if n.Type() == "host" || n.Type() == "null" { return } ipam, err := n.getController().getIpamDriver(n.ipamType) diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index 8e40c05f5d..47613bd6d0 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -275,24 +275,12 @@ func TestBridge(t *testing.T) { t.Fatal(err) } - cidr, err := types.ParseCIDR("192.168.100.2/28") - if err != nil { - t.Fatal(err) - } - - cidrv6, err := types.ParseCIDR("fe90::1/96") - if err != nil { - t.Fatal(err) - } - log.Debug("Adding a bridge") netOption := options.Generic{ netlabel.GenericData: options.Generic{ "BridgeName": "testnetwork", "AddressIPv4": subnet, - "FixedCIDR": cidr, - "FixedCIDRv6": cidrv6, "EnableIPv6": true, "EnableICC": true, "EnableIPMasquerade": true, @@ -1661,16 +1649,10 @@ func TestEnableIPv6(t *testing.T) { } }() - cidrv6, err := types.ParseCIDR("fe80::1/64") - if err != nil { - t.Fatal(err) - } - netOption := options.Generic{ netlabel.EnableIPv6: true, netlabel.GenericData: options.Generic{ - "BridgeName": "testnetwork", - "FixedCIDRv6": cidrv6, + "BridgeName": "testnetwork", }, } diff --git a/libnetwork/network.go b/libnetwork/network.go index 486f40380e..647cac5d07 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -796,7 +796,7 @@ func (n *network) getController() *controller { func (n *network) ipamAllocate() error { // For now also exclude bridge from using new ipam - if n.Type() == "host" || n.Type() == "null" || n.Type() == "bridge" { + if n.Type() == "host" || n.Type() == "null" { return nil } @@ -913,8 +913,8 @@ func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error { } func (n *network) ipamRelease() { - // For now also exclude bridge from using new ipam - if n.Type() == "host" || n.Type() == "null" || n.Type() == "bridge" { + // For now exclude host and null + if n.Type() == "host" || n.Type() == "null" { return } ipam, err := n.getController().getIpamDriver(n.ipamType) From af3eb25d447af92a24ff4dfd6f736d2c33e5e3a1 Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Mon, 5 Oct 2015 14:53:25 -0700 Subject: [PATCH 2/2] Phase-2 bridge driver changes to support IPAM - Set bridge ipv4 address when bridge is present - IPv6 changes for bridge - Convert unit tests to the new model Signed-off-by: Alessandro Boch --- libnetwork/bitseq/sequence.go | 33 +++++ libnetwork/bitseq/store.go | 21 +-- libnetwork/cmd/dnet/dnet.go | 21 ++- libnetwork/drivers/bridge/bridge.go | 111 +++++++++------- libnetwork/drivers/bridge/bridge_test.go | 122 ++++++++++++------ libnetwork/drivers/bridge/network_test.go | 25 ++-- .../drivers/bridge/port_mapping_test.go | 5 +- .../bridge/setup_bridgenetfiltering.go | 2 +- libnetwork/drivers/bridge/setup_ipv4.go | 95 +++----------- libnetwork/drivers/bridge/setup_ipv4_test.go | 49 ------- libnetwork/drivers/bridge/setup_ipv6.go | 24 ++++ libnetwork/drivers/bridge/setup_ipv6_test.go | 1 + libnetwork/endpoint.go | 7 + libnetwork/ipam/allocator.go | 9 +- libnetwork/ipam/allocator_test.go | 47 ++++--- libnetwork/libnetwork_test.go | 89 ++++++------- libnetwork/network.go | 2 +- 17 files changed, 334 insertions(+), 329 deletions(-) diff --git a/libnetwork/bitseq/sequence.go b/libnetwork/bitseq/sequence.go index 20c50c91ed..0ad173a0fe 100644 --- a/libnetwork/bitseq/sequence.go +++ b/libnetwork/bitseq/sequence.go @@ -5,6 +5,7 @@ package bitseq import ( "encoding/binary" + "encoding/json" "fmt" "sync" @@ -392,6 +393,38 @@ func (h *Handle) String() string { h.app, h.id, h.dbIndex, h.bits, h.unselected, h.head.toString()) } +// MarshalJSON encodes Handle into json message +func (h *Handle) MarshalJSON() ([]byte, error) { + m := map[string]interface{}{ + "id": h.id, + } + + b, err := h.ToByteArray() + if err != nil { + return nil, err + } + m["sequence"] = b + return json.Marshal(m) +} + +// UnmarshalJSON decodes json message into Handle +func (h *Handle) UnmarshalJSON(data []byte) error { + var ( + m map[string]interface{} + b []byte + err error + ) + if err = json.Unmarshal(data, &m); err != nil { + return err + } + h.id = m["id"].(string) + bi, _ := json.Marshal(m["sequence"]) + if err := json.Unmarshal(bi, &b); err != nil { + return err + } + return h.FromByteArray(b) +} + // getFirstAvailable looks for the first unset bit in passed mask starting from start func getFirstAvailable(head *sequence, start uint32) (uint32, uint32, error) { // Find sequence which contains the start bit diff --git a/libnetwork/bitseq/store.go b/libnetwork/bitseq/store.go index ef7fe33400..df50331227 100644 --- a/libnetwork/bitseq/store.go +++ b/libnetwork/bitseq/store.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" - log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/types" ) @@ -25,27 +24,16 @@ func (h *Handle) KeyPrefix() []string { // Value marshals the data to be stored in the KV store func (h *Handle) Value() []byte { - b, err := h.ToByteArray() + b, err := json.Marshal(h) if err != nil { - log.Warnf("Failed to serialize Handle: %v", err) - b = []byte{} + return nil } - jv, err := json.Marshal(b) - if err != nil { - log.Warnf("Failed to json encode bitseq handler byte array: %v", err) - return []byte{} - } - return jv + return b } // SetValue unmarshals the data from the KV store func (h *Handle) SetValue(value []byte) error { - var b []byte - if err := json.Unmarshal(value, &b); err != nil { - return err - } - - return h.FromByteArray(b) + return json.Unmarshal(value, h) } // Index returns the latest DB Index as seen by this object @@ -77,7 +65,6 @@ func (h *Handle) New() datastore.KVObject { return &Handle{ app: h.app, - id: h.id, store: h.store, } } diff --git a/libnetwork/cmd/dnet/dnet.go b/libnetwork/cmd/dnet/dnet.go index 7bac7c7881..17a1fa7bea 100644 --- a/libnetwork/cmd/dnet/dnet.go +++ b/libnetwork/cmd/dnet/dnet.go @@ -29,8 +29,10 @@ import ( "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/ipamutils" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/options" + "github.com/docker/libnetwork/types" "github.com/gorilla/mux" ) @@ -187,7 +189,12 @@ func createDefaultNetwork(c libnetwork.NetworkController) { } createOptions = append(createOptions, libnetwork.NetworkOptionGeneric(genericOption), - libnetwork.NetworkOptionPersist(false)) + ipamOption(nw)) + } + + if _, err := c.NetworkByName(nw); err == nil { + logrus.Debugf("Default network %s already present", nw) + return } _, err := c.NewNetwork(d, nw, createOptions...) if err != nil { @@ -405,3 +412,15 @@ func encodeData(data interface{}) (*bytes.Buffer, error) { } return params, nil } + +func ipamOption(bridgeName string) libnetwork.NetworkOption { + if nw, _, err := ipamutils.ElectInterfaceAddresses(bridgeName); err == nil { + ipamV4Conf := &libnetwork.IpamConf{PreferredPool: nw.String()} + hip, _ := types.GetHostPartIP(nw.IP, nw.Mask) + if hip.IsGlobalUnicast() { + ipamV4Conf.Gateway = nw.IP.String() + } + return libnetwork.NetworkOptionIpam("default", "", []*libnetwork.IpamConf{ipamV4Conf}, nil) + } + return nil +} diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index 72f58a5f7c..040de05738 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -56,15 +56,17 @@ type configuration struct { // networkConfiguration for network specific configuration type networkConfiguration struct { BridgeName string - AddressIPv4 *net.IPNet EnableIPv6 bool EnableIPMasquerade bool EnableICC bool Mtu int - DefaultGatewayIPv4 net.IP - DefaultGatewayIPv6 net.IP DefaultBindingIP net.IP DefaultBridge bool + // Internal fields set after ipam data parsing + AddressIPv4 *net.IPNet + AddressIPv6 *net.IPNet + DefaultGatewayIPv4 net.IP + DefaultGatewayIPv6 net.IP } // endpointConfiguration represents the user specified configuration for the sandbox endpoint @@ -161,27 +163,39 @@ func (c *networkConfiguration) Validate() error { } } + // If default v6 gw is specified, AddressIPv6 must be specified and gw must belong to AddressIPv6 subnet + if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil { + if c.AddressIPv6 == nil || !c.AddressIPv6.Contains(c.DefaultGatewayIPv6) { + return &ErrInvalidGateway{} + } + } return nil } // Conflicts check if two NetworkConfiguration objects overlap -func (c *networkConfiguration) Conflicts(o *networkConfiguration) bool { +func (c *networkConfiguration) Conflicts(o *networkConfiguration) error { if o == nil { - return false + return fmt.Errorf("same configuration") } // Also empty, becasue only one network with empty name is allowed if c.BridgeName == o.BridgeName { - return true + return fmt.Errorf("networks have same name") } // They must be in different subnets if (c.AddressIPv4 != nil && o.AddressIPv4 != nil) && (c.AddressIPv4.Contains(o.AddressIPv4.IP) || o.AddressIPv4.Contains(c.AddressIPv4.IP)) { - return true + return fmt.Errorf("networks have overlapping IPv4") } - return false + // They must be in different v6 subnets + if (c.AddressIPv6 != nil && o.AddressIPv6 != nil) && + (c.AddressIPv6.Contains(o.AddressIPv6.IP) || o.AddressIPv6.Contains(c.AddressIPv6.IP)) { + return fmt.Errorf("networks have overlapping IPv6") + } + + return nil } // fromMap retrieve the configuration data from the map form. @@ -456,18 +470,18 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d c.AddressIPv4 = types.GetIPNetCopy(ipamV4Data[0].Gateway) } - if c.EnableIPv6 && len(ipamV6Data) == 0 { - return types.BadRequestErrorf("bridge network %s requires ipv6 configuration", id) - } - - gw, ok := ipamV4Data[0].AuxAddresses[DefaultGatewayV4AuxKey] - if ok { + if gw, ok := ipamV4Data[0].AuxAddresses[DefaultGatewayV4AuxKey]; ok { c.DefaultGatewayIPv4 = gw.IP } - gw, ok = ipamV4Data[0].AuxAddresses[DefaultGatewayV6AuxKey] - if ok { - c.DefaultGatewayIPv6 = gw.IP + if len(ipamV6Data) > 0 { + if ipamV6Data[0].Gateway != nil { + c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway) + } + + if gw, ok := ipamV6Data[0].AuxAddresses[DefaultGatewayV6AuxKey]; ok { + c.DefaultGatewayIPv6 = gw.IP + } } return nil @@ -502,6 +516,9 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati // Returns the non link-local IPv6 subnet for the containers attached to this bridge if found, nil otherwise func getV6Network(config *networkConfiguration, i *bridgeInterface) *net.IPNet { + if config.AddressIPv6 != nil { + return config.AddressIPv6 + } if i.bridgeIPv6 != nil && i.bridgeIPv6.IP != nil && !i.bridgeIPv6.IP.IsLinkLocalUnicast() { return i.bridgeIPv6 } @@ -551,8 +568,9 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat nw.Lock() nwConfig := nw.config nw.Unlock() - if nwConfig.Conflicts(config) { - return types.ForbiddenErrorf("conflicts with network %s (%s)", nw.id, nw.config.BridgeName) + if err := nwConfig.Conflicts(config); err != nil { + return types.ForbiddenErrorf("cannot create network %s (%s): conflicts with network %s (%s): %s", + nwConfig.BridgeName, id, nw.id, nw.config.BridgeName, err.Error()) } } @@ -613,10 +631,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat // Even if a bridge exists try to setup IPv4. bridgeSetup.queueStep(setupBridgeIPv4) - enableIPv6Forwarding := false - if d.config.EnableIPForwarding { - enableIPv6Forwarding = true - } + enableIPv6Forwarding := d.config.EnableIPForwarding && config.AddressIPv6 != nil // Conditionally queue setup steps depending on configuration values. for _, step := range []struct { @@ -797,11 +812,6 @@ func setHairpinMode(link netlink.Link, enable bool) error { } func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { - var ( - ipv6Addr *net.IPNet - err error - ) - defer osl.InitOSContext()() if ifInfo == nil { @@ -931,7 +941,11 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } } - ipv4Addr := ifInfo.Address() + // Create the sandbox side pipe interface + endpoint.srcName = containerIfName + endpoint.macAddress = ifInfo.MacAddress() + endpoint.addr = ifInfo.Address() + endpoint.addrv6 = ifInfo.AddressIPv6() // Down the interface before configuring mac address. if err = netlink.LinkSetDown(sbox); err != nil { @@ -939,28 +953,38 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } // Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP. - mac := ifInfo.MacAddress() - if mac == nil { - mac = electMacAddress(epConfig, ipv4Addr.IP) + if endpoint.macAddress == nil { + endpoint.macAddress = electMacAddress(epConfig, endpoint.addr.IP) + if err := ifInfo.SetMacAddress(endpoint.macAddress); err != nil { + return err + } } - err = netlink.LinkSetHardwareAddr(sbox, mac) + err = netlink.LinkSetHardwareAddr(sbox, endpoint.macAddress) if err != nil { return fmt.Errorf("could not set mac address for container interface %s: %v", containerIfName, err) } - endpoint.macAddress = mac // Up the host interface after finishing all netlink configuration if err = netlink.LinkSetUp(host); err != nil { return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err) } - ipv6Addr = ifInfo.AddressIPv6() - // Create the sandbox side pipe interface - endpoint.srcName = containerIfName - endpoint.addr = ipv4Addr + if endpoint.addrv6 == nil && config.EnableIPv6 { + var ip6 net.IP + network := n.bridge.bridgeIPv6 + 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 config.EnableIPv6 { - endpoint.addrv6 = ipv6Addr + endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask} + if err := ifInfo.SetIPAddress(endpoint.addrv6); err != nil { + return err + } } // Program any required port mapping and store them in the endpoint @@ -969,13 +993,6 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, return err } - if ifInfo.MacAddress() == nil { - err = ifInfo.SetMacAddress(endpoint.macAddress) - if err != nil { - return err - } - } - return nil } diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index 8cf577ce7c..40ce65de49 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -8,14 +8,27 @@ import ( "testing" "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/ipamutils" "github.com/docker/libnetwork/iptables" "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/testutils" "github.com/docker/libnetwork/types" "github.com/vishvananda/netlink" ) +func getIPv4Data(t *testing.T) []driverapi.IPAMData { + ipd := driverapi.IPAMData{AddressSpace: "full"} + nw, _, err := ipamutils.ElectInterfaceAddresses("") + if err != nil { + t.Fatal(err) + } + ipd.Pool = nw + // Set network gateway to X.X.X.1 + ipd.Gateway = types.GetIPNetCopy(nw) + ipd.Gateway.IP[len(ipd.Gateway.IP)-1] = 1 + return []driverapi.IPAMData{ipd} +} + func TestCreateFullOptions(t *testing.T) { defer testutils.SetupTestOSContext(t)() d := newDriver() @@ -27,17 +40,14 @@ func TestCreateFullOptions(t *testing.T) { // Test this scenario: Default gw address does not belong to // container network and it's greater than bridge address - cip, cnw, _ := net.ParseCIDR("172.16.122.0/24") - cnw.IP = cip - ip, nw, _ := net.ParseCIDR("172.16.0.10/16") - nw.IP = ip - gw := net.ParseIP("172.16.0.1") + cnw, _ := types.ParseCIDR("172.16.122.0/24") + bnw, _ := types.ParseCIDR("172.16.0.0/24") + br, _ := types.ParseCIDR("172.16.0.1/16") + defgw, _ := types.ParseCIDR("172.16.0.100/16") netConfig := &networkConfiguration{ - BridgeName: DefaultBridgeName, - AddressIPv4: nw, - DefaultGatewayIPv4: gw, - EnableIPv6: true, + BridgeName: DefaultBridgeName, + EnableIPv6: true, } genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = config @@ -49,14 +59,21 @@ func TestCreateFullOptions(t *testing.T) { netOption := make(map[string]interface{}) netOption[netlabel.GenericData] = netConfig - err := d.CreateNetwork("dummy", netOption, nil, nil) + ipdList := []driverapi.IPAMData{ + driverapi.IPAMData{ + Pool: bnw, + Gateway: br, + AuxAddresses: map[string]*net.IPNet{DefaultGatewayV4AuxKey: defgw}, + }, + } + err := d.CreateNetwork("dummy", netOption, ipdList, nil) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } // Verify the IP address allocated for the endpoint belongs to the container network epOptions := make(map[string]interface{}) - te := &testEndpoint{iface: &testInterface{}} + te := newTestEndpoint(cnw, 10) err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions) if err != nil { t.Fatalf("Failed to create an endpoint : %s", err.Error()) @@ -75,7 +92,7 @@ func TestCreateNoConfig(t *testing.T) { genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = netconfig - if err := d.CreateNetwork("dummy", genericOption, nil, nil); err != nil { + if err := d.CreateNetwork("dummy", genericOption, getIPv4Data(t), nil); err != nil { t.Fatalf("Failed to create bridge: %v", err) } } @@ -92,11 +109,11 @@ func TestCreate(t *testing.T) { genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = netconfig - if err := d.CreateNetwork("dummy", genericOption, nil, nil); err != nil { + if err := d.CreateNetwork("dummy", genericOption, getIPv4Data(t), nil); err != nil { t.Fatalf("Failed to create bridge: %v", err) } - err := d.CreateNetwork("dummy", genericOption, nil, nil) + err := d.CreateNetwork("dummy", genericOption, getIPv4Data(t), nil) if err == nil { t.Fatalf("Expected bridge driver to refuse creation of second network with default name") } @@ -125,7 +142,7 @@ func TestCreateFail(t *testing.T) { genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = netconfig - if err := d.CreateNetwork("dummy", genericOption, nil, nil); err == nil { + if err := d.CreateNetwork("dummy", genericOption, getIPv4Data(t), nil); err == nil { t.Fatal("Bridge creation was expected to fail") } } @@ -147,19 +164,19 @@ func TestCreateMultipleNetworks(t *testing.T) { config1 := &networkConfiguration{BridgeName: "net_test_1"} genericOption = make(map[string]interface{}) genericOption[netlabel.GenericData] = config1 - if err := d.CreateNetwork("1", genericOption, nil, nil); err != nil { + if err := d.CreateNetwork("1", genericOption, getIPv4Data(t), nil); err != nil { t.Fatalf("Failed to create bridge: %v", err) } config2 := &networkConfiguration{BridgeName: "net_test_2"} genericOption[netlabel.GenericData] = config2 - if err := d.CreateNetwork("2", genericOption, nil, nil); err != nil { + if err := d.CreateNetwork("2", genericOption, getIPv4Data(t), nil); err != nil { t.Fatalf("Failed to create bridge: %v", err) } config3 := &networkConfiguration{BridgeName: "net_test_3"} genericOption[netlabel.GenericData] = config3 - if err := d.CreateNetwork("3", genericOption, nil, nil); err != nil { + if err := d.CreateNetwork("3", genericOption, getIPv4Data(t), nil); err != nil { t.Fatalf("Failed to create bridge: %v", err) } @@ -168,7 +185,7 @@ func TestCreateMultipleNetworks(t *testing.T) { config4 := &networkConfiguration{BridgeName: "net_test_4"} genericOption[netlabel.GenericData] = config4 - if err := d.CreateNetwork("4", genericOption, nil, nil); err != nil { + if err := d.CreateNetwork("4", genericOption, getIPv4Data(t), nil); err != nil { t.Fatalf("Failed to create bridge: %v", err) } @@ -221,6 +238,12 @@ type testEndpoint struct { routes []types.StaticRoute } +func newTestEndpoint(nw *net.IPNet, ordinal byte) *testEndpoint { + addr := types.GetIPNetCopy(nw) + addr.IP[len(addr.IP)-1] = ordinal + return &testEndpoint{iface: &testInterface{addr: addr}} +} + func (te *testEndpoint) Interface() driverapi.InterfaceInfo { if te.iface != nil { return te.iface @@ -329,7 +352,8 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) { genericOption = make(map[string]interface{}) genericOption[netlabel.GenericData] = netconfig - err := d.CreateNetwork("net1", genericOption, nil, nil) + ipdList := getIPv4Data(t) + err := d.CreateNetwork("net1", genericOption, ipdList, nil) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } @@ -338,7 +362,7 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) { epOptions := make(map[string]interface{}) epOptions[netlabel.PortMap] = portMappings - te := &testEndpoint{iface: &testInterface{}} + te := newTestEndpoint(ipdList[0].Pool, 11) err = d.CreateEndpoint("net1", "ep1", te.Interface(), epOptions) if err != nil { t.Fatalf("Failed to create an endpoint : %s", err.Error()) @@ -389,7 +413,8 @@ func TestCreateLinkWithOptions(t *testing.T) { netOptions := make(map[string]interface{}) netOptions[netlabel.GenericData] = netconfig - err := d.CreateNetwork("net1", netOptions, nil, nil) + ipdList := getIPv4Data(t) + err := d.CreateNetwork("net1", netOptions, ipdList, nil) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } @@ -398,7 +423,7 @@ func TestCreateLinkWithOptions(t *testing.T) { epOptions := make(map[string]interface{}) epOptions[netlabel.MacAddress] = mac - te := &testEndpoint{iface: &testInterface{}} + te := newTestEndpoint(ipdList[0].Pool, 11) err = d.CreateEndpoint("net1", "ep", te.Interface(), epOptions) if err != nil { t.Fatalf("Failed to create an endpoint: %s", err.Error()) @@ -458,7 +483,8 @@ func TestLinkContainers(t *testing.T) { genericOption = make(map[string]interface{}) genericOption[netlabel.GenericData] = netconfig - err := d.CreateNetwork("net1", genericOption, nil, nil) + ipdList := getIPv4Data(t) + err := d.CreateNetwork("net1", genericOption, ipdList, nil) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } @@ -467,7 +493,7 @@ func TestLinkContainers(t *testing.T) { epOptions := make(map[string]interface{}) epOptions[netlabel.ExposedPorts] = exposedPorts - te1 := &testEndpoint{iface: &testInterface{}} + te1 := newTestEndpoint(ipdList[0].Pool, 11) err = d.CreateEndpoint("net1", "ep1", te1.Interface(), epOptions) if err != nil { t.Fatalf("Failed to create an endpoint : %s", err.Error()) @@ -478,7 +504,7 @@ func TestLinkContainers(t *testing.T) { t.Fatalf("No Ipv4 address assigned to the endpoint: ep1") } - te2 := &testEndpoint{iface: &testInterface{}} + te2 := newTestEndpoint(ipdList[0].Pool, 22) err = d.CreateEndpoint("net1", "ep2", te2.Interface(), nil) if err != nil { t.Fatalf("Failed to create an endpoint : %s", err.Error()) @@ -581,6 +607,14 @@ func TestValidateConfig(t *testing.T) { // Bridge network _, network, _ := net.ParseCIDR("172.28.0.0/16") + c = networkConfiguration{ + AddressIPv4: network, + } + + err = c.Validate() + if err != nil { + t.Fatal(err) + } // Test v4 gw c.DefaultGatewayIPv4 = net.ParseIP("172.27.30.234") @@ -596,9 +630,10 @@ func TestValidateConfig(t *testing.T) { } // Test v6 gw - _, containerSubnet, _ = net.ParseCIDR("2001:1234:ae:b004::/64") + _, v6nw, _ := net.ParseCIDR("2001:1234:ae:b004::/64") c = networkConfiguration{ EnableIPv6: true, + AddressIPv6: v6nw, DefaultGatewayIPv6: net.ParseIP("2001:1234:ac:b004::bad:a55"), } err = c.Validate() @@ -611,6 +646,18 @@ func TestValidateConfig(t *testing.T) { if err != nil { t.Fatalf("Unexpected validation error on v6 default gateway") } + + c.AddressIPv6 = nil + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid v6 default gateway") + } + + c.AddressIPv6 = nil + err = c.Validate() + if err == nil { + t.Fatalf("Failed to detect invalid v6 default gateway") + } } func TestSetDefaultGw(t *testing.T) { @@ -623,24 +670,15 @@ func TestSetDefaultGw(t *testing.T) { _, subnetv6, _ := net.ParseCIDR("2001:db8:ea9:9abc:b0c4::/80") - var nw *net.IPNet - for _, n := range bridgeNetworks { - if err := netutils.CheckRouteOverlaps(n); err == nil { - nw = n - break - } - } - if nw == nil { - t.Skipf("Skip as no more automatic networks available") - } - - gw4 := types.GetIPCopy(nw.IP).To4() + ipdList := getIPv4Data(t) + gw4 := types.GetIPCopy(ipdList[0].Pool.IP).To4() gw4[3] = 254 gw6 := net.ParseIP("2001:db8:ea9:9abc:b0c4::254") config := &networkConfiguration{ BridgeName: DefaultBridgeName, EnableIPv6: true, + AddressIPv6: subnetv6, DefaultGatewayIPv4: gw4, DefaultGatewayIPv6: gw6, } @@ -648,12 +686,12 @@ func TestSetDefaultGw(t *testing.T) { genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = config - err := d.CreateNetwork("dummy", genericOption, nil, nil) + err := d.CreateNetwork("dummy", genericOption, ipdList, nil) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - te := &testEndpoint{iface: &testInterface{}} + te := newTestEndpoint(ipdList[0].Pool, 10) err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil) if err != nil { t.Fatalf("Failed to create endpoint: %v", err) diff --git a/libnetwork/drivers/bridge/network_test.go b/libnetwork/drivers/bridge/network_test.go index ee94e13d6b..ab6be19c16 100644 --- a/libnetwork/drivers/bridge/network_test.go +++ b/libnetwork/drivers/bridge/network_test.go @@ -26,12 +26,13 @@ func TestLinkCreate(t *testing.T) { genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = config - err := d.CreateNetwork("dummy", genericOption, nil, nil) + ipdList := getIPv4Data(t) + err := d.CreateNetwork("dummy", genericOption, ipdList, nil) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - te := &testEndpoint{iface: &testInterface{}} + te := newTestEndpoint(ipdList[0].Pool, 10) err = d.CreateEndpoint("dummy", "", te.Interface(), nil) if err != nil { if _, ok := err.(InvalidEndpointIDError); !ok { @@ -63,7 +64,7 @@ func TestLinkCreate(t *testing.T) { // TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName // then we could check the MTU on hostLnk as well. - te1 := &testEndpoint{iface: &testInterface{}} + te1 := newTestEndpoint(ipdList[0].Pool, 11) err = d.CreateEndpoint("dummy", "ep", te1.Interface(), nil) if err == nil { t.Fatalf("Failed to detect duplicate endpoint id on same network") @@ -117,18 +118,19 @@ func TestLinkCreateTwo(t *testing.T) { genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = config - err := d.CreateNetwork("dummy", genericOption, nil, nil) + ipdList := getIPv4Data(t) + err := d.CreateNetwork("dummy", genericOption, ipdList, nil) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - te1 := &testEndpoint{iface: &testInterface{}} + te1 := newTestEndpoint(ipdList[0].Pool, 11) err = d.CreateEndpoint("dummy", "ep", te1.Interface(), nil) if err != nil { t.Fatalf("Failed to create a link: %s", err.Error()) } - te2 := &testEndpoint{iface: &testInterface{}} + te2 := newTestEndpoint(ipdList[0].Pool, 12) err = d.CreateEndpoint("dummy", "ep", te2.Interface(), nil) if err != nil { if _, ok := err.(driverapi.ErrEndpointExists); !ok { @@ -152,12 +154,12 @@ func TestLinkCreateNoEnableIPv6(t *testing.T) { genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = config - err := d.CreateNetwork("dummy", genericOption, nil, nil) + ipdList := getIPv4Data(t) + err := d.CreateNetwork("dummy", genericOption, ipdList, nil) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - - te := &testEndpoint{iface: &testInterface{}} + te := newTestEndpoint(ipdList[0].Pool, 30) err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil) if err != nil { t.Fatalf("Failed to create a link: %s", err.Error()) @@ -187,12 +189,13 @@ func TestLinkDelete(t *testing.T) { genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = config - err := d.CreateNetwork("dummy", genericOption, nil, nil) + ipdList := getIPv4Data(t) + err := d.CreateNetwork("dummy", genericOption, ipdList, nil) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - te := &testEndpoint{iface: &testInterface{}} + te := newTestEndpoint(ipdList[0].Pool, 30) err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil) if err != nil { t.Fatalf("Failed to create a link: %s", err.Error()) diff --git a/libnetwork/drivers/bridge/port_mapping_test.go b/libnetwork/drivers/bridge/port_mapping_test.go index 9fb03a7b92..8aa2bc0228 100644 --- a/libnetwork/drivers/bridge/port_mapping_test.go +++ b/libnetwork/drivers/bridge/port_mapping_test.go @@ -44,12 +44,13 @@ func TestPortMappingConfig(t *testing.T) { netOptions := make(map[string]interface{}) netOptions[netlabel.GenericData] = netConfig - err := d.CreateNetwork("dummy", netOptions, nil, nil) + ipdList := getIPv4Data(t) + err := d.CreateNetwork("dummy", netOptions, ipdList, nil) if err != nil { t.Fatalf("Failed to create bridge: %v", err) } - te := &testEndpoint{iface: &testInterface{}} + te := newTestEndpoint(ipdList[0].Pool, 11) err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions) if err != nil { t.Fatalf("Failed to create the endpoint: %s", err.Error()) diff --git a/libnetwork/drivers/bridge/setup_bridgenetfiltering.go b/libnetwork/drivers/bridge/setup_bridgenetfiltering.go index b17fe98914..f7a536adb0 100644 --- a/libnetwork/drivers/bridge/setup_bridgenetfiltering.go +++ b/libnetwork/drivers/bridge/setup_bridgenetfiltering.go @@ -22,7 +22,7 @@ const ( //Gets the IP version in use ( [ipv4], [ipv6] or [ipv4 and ipv6] ) func getIPVersion(config *networkConfiguration) ipVersion { ipVersion := ipv4 - if config.EnableIPv6 { + if config.AddressIPv6 != nil || config.EnableIPv6 { ipVersion |= ipv6 } return ipVersion diff --git a/libnetwork/drivers/bridge/setup_ipv4.go b/libnetwork/drivers/bridge/setup_ipv4.go index 6e955d6fb5..db913909c1 100644 --- a/libnetwork/drivers/bridge/setup_ipv4.go +++ b/libnetwork/drivers/bridge/setup_ipv4.go @@ -3,101 +3,36 @@ package bridge import ( "fmt" "io/ioutil" - "net" "path/filepath" log "github.com/Sirupsen/logrus" - "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/types" "github.com/vishvananda/netlink" ) -var bridgeNetworks []*net.IPNet - -func init() { - // Here we don't follow the convention of using the 1st IP of the range for the gateway. - // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. - // In theory this shouldn't matter - in practice there's bound to be a few scripts relying - // on the internal addressing or other stupid things like that. - // They shouldn't, but hey, let's not break them unless we really have to. - // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23 - - // 172.[17-31].42.1/16 - mask := []byte{255, 255, 0, 0} - for i := 17; i < 32; i++ { - bridgeNetworks = append(bridgeNetworks, &net.IPNet{IP: []byte{172, byte(i), 42, 1}, Mask: mask}) - } - // 10.[0-255].42.1/16 - for i := 0; i < 256; i++ { - bridgeNetworks = append(bridgeNetworks, &net.IPNet{IP: []byte{10, byte(i), 42, 1}, Mask: mask}) - } - // 192.168.[42-44].1/24 - mask24 := []byte{255, 255, 255, 0} - for i := 42; i < 45; i++ { - bridgeNetworks = append(bridgeNetworks, &net.IPNet{IP: []byte{192, 168, byte(i), 1}, Mask: mask24}) - } -} - func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error { addrv4, _, err := i.addresses() if err != nil { - return err + return fmt.Errorf("failed to retrieve bridge interface addresses: %v", err) } - // Check if we have an IP address already on the bridge. - if addrv4.IPNet != nil { - // Make sure to store bridge network and default gateway before getting out. - i.bridgeIPv4 = addrv4.IPNet - i.gatewayIPv4 = addrv4.IPNet.IP - return nil - } - - // Do not try to configure IPv4 on a non-default bridge unless you are - // specifically asked to do so. - if config.BridgeName != DefaultBridgeName && config.DefaultBridge { - return NonDefaultBridgeNeedsIPError(config.BridgeName) - } - - bridgeIPv4, err := electBridgeIPv4(config) - if err != nil { - return err - } - - log.Debugf("Creating bridge interface %s with network %s", config.BridgeName, bridgeIPv4) - if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv4}); err != nil { - return &IPv4AddrAddError{IP: bridgeIPv4, Err: err} - } - - // Store bridge network and default gateway - i.bridgeIPv4 = bridgeIPv4 - i.gatewayIPv4 = i.bridgeIPv4.IP - - return nil -} - -func electBridgeIPv4(config *networkConfiguration) (*net.IPNet, error) { - // Use the requested IPv4 CIDR when available. - if config.AddressIPv4 != nil { - return config.AddressIPv4, nil - } - - // We don't check for an error here, because we don't really care if we - // can't read /etc/resolv.conf. So instead we skip the append if resolvConf - // is nil. It either doesn't exist, or we can't read it for some reason. - nameservers := []string{} - if resolvConf, _ := readResolvConf(); resolvConf != nil { - nameservers = append(nameservers, getNameserversAsCIDR(resolvConf)...) - } - - // Try to automatically elect appropriate bridge IPv4 settings. - for _, n := range bridgeNetworks { - if err := netutils.CheckNameserverOverlaps(nameservers, n); err == nil { - if err := netutils.CheckRouteOverlaps(n); err == nil { - return n, nil + if !types.CompareIPNet(addrv4.IPNet, config.AddressIPv4) { + if addrv4.IPNet != nil { + if err := netlink.AddrDel(i.Link, &addrv4); err != nil { + return fmt.Errorf("failed to remove current ip address from bridge: %v", err) } } + log.Debugf("Assigning address to bridge interface %s: %s", config.BridgeName, config.AddressIPv4) + if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil { + return &IPv4AddrAddError{IP: config.AddressIPv4, Err: err} + } } - return nil, IPv4AddrRangeError(config.BridgeName) + // Store bridge network and default gateway + i.bridgeIPv4 = config.AddressIPv4 + i.gatewayIPv4 = config.AddressIPv4.IP + + return nil } func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error { diff --git a/libnetwork/drivers/bridge/setup_ipv4_test.go b/libnetwork/drivers/bridge/setup_ipv4_test.go index 35fb4b49c8..785de6ee45 100644 --- a/libnetwork/drivers/bridge/setup_ipv4_test.go +++ b/libnetwork/drivers/bridge/setup_ipv4_test.go @@ -4,7 +4,6 @@ import ( "net" "testing" - "github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/testutils" "github.com/vishvananda/netlink" ) @@ -52,43 +51,6 @@ func TestSetupBridgeIPv4Fixed(t *testing.T) { } } -func TestSetupBridgeIPv4Auto(t *testing.T) { - defer testutils.SetupTestOSContext(t)() - - var toBeChosen *net.IPNet - for _, n := range bridgeNetworks { - if err := netutils.CheckRouteOverlaps(n); err == nil { - toBeChosen = n - break - } - } - if toBeChosen == nil { - t.Skipf("Skip as no more automatic networks available") - } - - config, br := setupTestInterface(t) - if err := setupBridgeIPv4(config, br); err != nil { - t.Fatalf("Failed to setup bridge IPv4: %v", err) - } - - addrsv4, err := netlink.AddrList(br.Link, netlink.FAMILY_V4) - if err != nil { - t.Fatalf("Failed to list device IPv4 addresses: %v", err) - } - - var found bool - for _, addr := range addrsv4 { - if toBeChosen.String() == addr.IPNet.String() { - found = true - break - } - } - - if !found { - t.Fatalf("Bridge device does not have the automatic IPv4 address %s", toBeChosen.String()) - } -} - func TestSetupGatewayIPv4(t *testing.T) { defer testutils.SetupTestOSContext(t)() @@ -110,14 +72,3 @@ func TestSetupGatewayIPv4(t *testing.T) { t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv4) } } - -func TestCheckPreallocatedBridgeNetworks(t *testing.T) { - // Just make sure the bridge networks are created the way we want (172.17.x.x/16) - for i := 0; i < len(bridgeNetworks); i++ { - fb := bridgeNetworks[i].IP[0] - ones, _ := bridgeNetworks[i].Mask.Size() - if ((fb == 172 || fb == 10) && ones != 16) || (fb == 192 && ones != 24) { - t.Fatalf("Wrong mask for preallocated bridge network: %s", bridgeNetworks[i].String()) - } - } -} diff --git a/libnetwork/drivers/bridge/setup_ipv6.go b/libnetwork/drivers/bridge/setup_ipv6.go index f84a5da809..5ce53dd879 100644 --- a/libnetwork/drivers/bridge/setup_ipv6.go +++ b/libnetwork/drivers/bridge/setup_ipv6.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "net" + "os" "github.com/Sirupsen/logrus" "github.com/vishvananda/netlink" @@ -57,12 +58,35 @@ func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error { i.bridgeIPv6 = bridgeIPv6 i.gatewayIPv6 = i.bridgeIPv6.IP + if config.AddressIPv6 == nil { + return nil + } + + // Setting route to global IPv6 subnet + logrus.Debugf("Adding route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName) + err = netlink.RouteAdd(&netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + LinkIndex: i.Link.Attrs().Index, + Dst: config.AddressIPv6, + }) + if err != nil && !os.IsExist(err) { + logrus.Errorf("Could not add route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName) + } + return nil } func setupGatewayIPv6(config *networkConfiguration, i *bridgeInterface) error { + if config.AddressIPv6 == nil { + return &ErrInvalidContainerSubnet{} + } + if !config.AddressIPv6.Contains(config.DefaultGatewayIPv6) { + return &ErrInvalidGateway{} + } + // Store requested default gateway i.gatewayIPv6 = config.DefaultGatewayIPv6 + return nil } diff --git a/libnetwork/drivers/bridge/setup_ipv6_test.go b/libnetwork/drivers/bridge/setup_ipv6_test.go index a237b42e05..8ab9225859 100644 --- a/libnetwork/drivers/bridge/setup_ipv6_test.go +++ b/libnetwork/drivers/bridge/setup_ipv6_test.go @@ -55,6 +55,7 @@ func TestSetupGatewayIPv6(t *testing.T) { config := &networkConfiguration{ BridgeName: DefaultBridgeName, + AddressIPv6: nw, DefaultGatewayIPv6: gw} br := &bridgeInterface{} diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 2aa946a153..cfbab0edc7 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -622,10 +622,14 @@ func (ep *endpoint) assignAddress() error { ipam ipamapi.Ipam err error ) + n := ep.getNetwork() if n.Type() == "host" || n.Type() == "null" { return nil } + + log.Debugf("Assigning addresses for endpoint %s's interface on network %s", ep.Name(), n.Name()) + ipam, err = n.getController().getIpamDriver(n.ipamType) if err != nil { return err @@ -683,6 +687,9 @@ func (ep *endpoint) releaseAddress() { if n.Type() == "host" || n.Type() == "null" { return } + + log.Debugf("Releasing addresses for endpoint %s's interface on network %s", ep.Name(), n.Name()) + ipam, err := n.getController().getIpamDriver(n.ipamType) if err != nil { log.Warnf("Failed to retrieve ipam driver to release interface address on delete of endpoint %s (%s): %v", ep.Name(), ep.ID(), err) diff --git a/libnetwork/ipam/allocator.go b/libnetwork/ipam/allocator.go index 61a5cdc398..7c3815c3d1 100644 --- a/libnetwork/ipam/allocator.go +++ b/libnetwork/ipam/allocator.go @@ -102,8 +102,9 @@ func (a *Allocator) updateBitMasks(aSpace *addrSpace) error { aSpace.Lock() for k, v := range aSpace.subnets { if v.Range == nil { - inserterList = append(inserterList, - func() error { return a.insertBitMask(k, v.Pool) }) + kk := k + vv := v + inserterList = append(inserterList, func() error { return a.insertBitMask(kk, vv.Pool) }) } } aSpace.Unlock() @@ -127,6 +128,7 @@ func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) { // RequestPool returns an address pool along with its unique id. func (a *Allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { + log.Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, pool, subPool, options, v6) k, nw, aw, ipr, err := a.parsePoolRequest(addressSpace, pool, subPool, v6) if err != nil { return "", nil, nil, ipamapi.ErrInvalidPool @@ -160,6 +162,7 @@ retry: // ReleasePool releases the address pool identified by the passed id func (a *Allocator) ReleasePool(poolID string) error { + log.Debugf("ReleasePool(%s)", poolID) k := SubnetKey{} if err := k.FromString(poolID); err != nil { return types.BadRequestErrorf("invalid pool id: %s", poolID) @@ -343,6 +346,7 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) // RequestAddress returns an address from the specified pool ID func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) { + log.Debugf("RequestAddress(%s, %v, %v)", poolID, prefAddress, opts) k := SubnetKey{} if err := k.FromString(poolID); err != nil { return nil, nil, types.BadRequestErrorf("invalid pool id: %s", poolID) @@ -391,6 +395,7 @@ func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s // ReleaseAddress releases the address from the specified pool ID func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error { + log.Debugf("ReleaseAddress(%s, %v)", poolID, address) k := SubnetKey{} if err := k.FromString(poolID); err != nil { return types.BadRequestErrorf("invalid pool id: %s", poolID) diff --git a/libnetwork/ipam/allocator_test.go b/libnetwork/ipam/allocator_test.go index bf5132ee1d..df3b01f3b6 100644 --- a/libnetwork/ipam/allocator_test.go +++ b/libnetwork/ipam/allocator_test.go @@ -13,7 +13,7 @@ import ( "github.com/docker/libnetwork/bitseq" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/ipamutils" _ "github.com/docker/libnetwork/testutils" "github.com/docker/libnetwork/types" ) @@ -461,12 +461,7 @@ func TestPredefinedPool(t *testing.T) { t.Fatalf("Expected failure for non default addr space") } - i, available, err := getFirstAvailablePool(a, localAddressSpace, 2) - if err != nil { - t.Skip(err) - } - - pid, _, _, err := a.RequestPool(localAddressSpace, available.String(), "", nil, false) + exp, err := ipamutils.FindAvailableNetwork(a.predefined[localAddressSpace]) if err != nil { t.Fatal(err) } @@ -475,8 +470,24 @@ func TestPredefinedPool(t *testing.T) { if err != nil { t.Fatal(err) } - if nw != a.predefined[localAddressSpace][i+1] { - t.Fatalf("Unexpected default network returned: %s", nw) + if !types.CompareIPNet(nw, exp) { + t.Fatalf("Unexpected default network returned: %s. Expected: %s", nw, exp) + } + + pid, nw, _, err := a.RequestPool(localAddressSpace, exp.String(), "", nil, false) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(nw, exp) { + t.Fatalf("Unexpected default network returned: %s. Expected: %s", nw, exp) + } + + nw2, err := a.getPredefinedPool(localAddressSpace, false) + if err != nil { + t.Fatal(err) + } + if types.CompareIPNet(nw, nw2) { + t.Fatalf("Unexpected default network returned: %s = %s", nw2, nw) } if err := a.ReleasePool(pid); err != nil { @@ -487,25 +498,11 @@ func TestPredefinedPool(t *testing.T) { if err != nil { t.Fatal(err) } - if nw != a.predefined[localAddressSpace][i] { - t.Fatalf("Unexpected default network returned: %s", nw) + if !types.CompareIPNet(nw, exp) { + t.Fatalf("Unexpected default network returned: %s. Expected %s", nw, exp) } } -func getFirstAvailablePool(a *Allocator, as string, atLeast int) (int, *net.IPNet, error) { - i := 0 - for i < len(a.predefined[as])-1 { - if err := netutils.CheckRouteOverlaps(a.predefined[as][i]); err == nil { - break - } - i++ - } - if i > len(a.predefined[as])-1-atLeast { - return 0, nil, fmt.Errorf("Not enough non-overlapping networks to run the test") - } - return i, a.predefined[as][i], nil -} - func TestAdjustAndCheckSubnet(t *testing.T) { _, sub6, _ := net.ParseCIDR("1003:1:2:300::/63") _, err := adjustAndCheckSubnetSize(sub6) diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index 47613bd6d0..2d6a3e4772 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -23,6 +23,7 @@ import ( "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/ipamapi" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/options" "github.com/docker/libnetwork/osl" @@ -82,14 +83,10 @@ func createController() error { return nil } -func createTestNetwork(networkType, networkName string, netOption options.Generic) (libnetwork.Network, error) { - network, err := controller.NewNetwork(networkType, networkName, - libnetwork.NetworkOptionGeneric(netOption)) - if err != nil { - return nil, err - } - - return network, nil +func createTestNetwork(networkType, networkName string, netOption options.Generic, ipamV4Configs, ipamV6Configs []*libnetwork.IpamConf) (libnetwork.Network, error) { + return controller.NewNetwork(networkType, networkName, + libnetwork.NetworkOptionGeneric(netOption), + libnetwork.NetworkOptionIpam(ipamapi.DefaultIPAM, "", ipamV4Configs, ipamV6Configs)) } func getEmptyGenericOption() map[string]interface{} { @@ -117,7 +114,7 @@ func TestNull(t *testing.T) { t.Fatal(err) } - network, err := createTestNetwork("null", "testnull", options.Generic{}) + network, err := createTestNetwork("null", "testnull", options.Generic{}, nil, nil) if err != nil { t.Fatal(err) } @@ -184,7 +181,7 @@ func TestHost(t *testing.T) { } }() - network, err := createTestNetwork("host", "testhost", options.Generic{}) + network, err := createTestNetwork("host", "testhost", options.Generic{}, nil, nil) if err != nil { t.Fatal(err) } @@ -270,24 +267,18 @@ func TestBridge(t *testing.T) { defer testutils.SetupTestOSContext(t)() } - subnet, err := types.ParseCIDR("192.168.100.1/24") - if err != nil { - t.Fatal(err) - } - - log.Debug("Adding a bridge") - netOption := options.Generic{ netlabel.GenericData: options.Generic{ "BridgeName": "testnetwork", - "AddressIPv4": subnet, "EnableIPv6": true, "EnableICC": true, "EnableIPMasquerade": true, }, } + ipamV4ConfList := []*libnetwork.IpamConf{&libnetwork.IpamConf{PreferredPool: "192.168.100.0/24", Gateway: "192.168.100.1"}} + ipamV6ConfList := []*libnetwork.IpamConf{&libnetwork.IpamConf{PreferredPool: "fe90::/98", Gateway: "fe90::22"}} - network, err := createTestNetwork(bridgeNetType, "testnetwork", netOption) + network, err := createTestNetwork(bridgeNetType, "testnetwork", netOption, ipamV4ConfList, ipamV6ConfList) if err != nil { t.Fatal(err) } @@ -327,7 +318,7 @@ func TestUnknownDriver(t *testing.T) { defer testutils.SetupTestOSContext(t)() } - _, err := createTestNetwork("unknowndriver", "testnetwork", options.Generic{}) + _, err := createTestNetwork("unknowndriver", "testnetwork", options.Generic{}, nil, nil) if err == nil { t.Fatal("Expected to fail. But instead succeeded") } @@ -360,7 +351,7 @@ func TestNetworkName(t *testing.T) { }, } - _, err := createTestNetwork(bridgeNetType, "", netOption) + _, err := createTestNetwork(bridgeNetType, "", netOption, nil, nil) if err == nil { t.Fatal("Expected to fail. But instead succeeded") } @@ -370,7 +361,7 @@ func TestNetworkName(t *testing.T) { } networkName := "testnetwork" - n, err := createTestNetwork(bridgeNetType, networkName, netOption) + n, err := createTestNetwork(bridgeNetType, networkName, netOption, nil, nil) if err != nil { t.Fatal(err) } @@ -396,7 +387,7 @@ func TestNetworkType(t *testing.T) { }, } - n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption) + n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption, nil, nil) if err != nil { t.Fatal(err) } @@ -422,7 +413,7 @@ func TestNetworkID(t *testing.T) { }, } - n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption) + n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption, nil, nil) if err != nil { t.Fatal(err) } @@ -449,7 +440,7 @@ func TestDeleteNetworkWithActiveEndpoints(t *testing.T) { netlabel.GenericData: netOption, } - network, err := createTestNetwork(bridgeNetType, "testnetwork", option) + network, err := createTestNetwork(bridgeNetType, "testnetwork", option, nil, nil) if err != nil { t.Fatal(err) } @@ -490,7 +481,7 @@ func TestUnknownNetwork(t *testing.T) { netlabel.GenericData: netOption, } - network, err := createTestNetwork(bridgeNetType, "testnetwork", option) + network, err := createTestNetwork(bridgeNetType, "testnetwork", option, nil, nil) if err != nil { t.Fatal(err) } @@ -515,20 +506,15 @@ func TestUnknownEndpoint(t *testing.T) { defer testutils.SetupTestOSContext(t)() } - subnet, err := types.ParseCIDR("192.168.100.1/24") - if err != nil { - t.Fatal(err) - } - netOption := options.Generic{ - "BridgeName": "testnetwork", - "AddressIPv4": subnet, + "BridgeName": "testnetwork", } option := options.Generic{ netlabel.GenericData: netOption, } + ipamV4ConfList := []*libnetwork.IpamConf{&libnetwork.IpamConf{PreferredPool: "192.168.100.0/24"}} - network, err := createTestNetwork(bridgeNetType, "testnetwork", option) + network, err := createTestNetwork(bridgeNetType, "testnetwork", option, ipamV4ConfList, nil) if err != nil { t.Fatal(err) } @@ -569,7 +555,7 @@ func TestNetworkEndpointsWalkers(t *testing.T) { }, } - net1, err := createTestNetwork(bridgeNetType, "network1", netOption) + net1, err := createTestNetwork(bridgeNetType, "network1", netOption, nil, nil) if err != nil { t.Fatal(err) } @@ -641,7 +627,7 @@ func TestNetworkEndpointsWalkers(t *testing.T) { }, } - net2, err := createTestNetwork(bridgeNetType, "network2", netOption) + net2, err := createTestNetwork(bridgeNetType, "network2", netOption, nil, nil) if err != nil { t.Fatal(err) } @@ -697,7 +683,7 @@ func TestDuplicateEndpoint(t *testing.T) { "BridgeName": "testnetwork", }, } - n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption) + n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption, nil, nil) if err != nil { t.Fatal(err) } @@ -747,7 +733,7 @@ func TestControllerQuery(t *testing.T) { "BridgeName": "network1", }, } - net1, err := createTestNetwork(bridgeNetType, "network1", netOption) + net1, err := createTestNetwork(bridgeNetType, "network1", netOption, nil, nil) if err != nil { t.Fatal(err) } @@ -763,7 +749,7 @@ func TestControllerQuery(t *testing.T) { "BridgeName": "network2", }, } - net2, err := createTestNetwork(bridgeNetType, "network2", netOption) + net2, err := createTestNetwork(bridgeNetType, "network2", netOption, nil, nil) if err != nil { t.Fatal(err) } @@ -849,7 +835,7 @@ func TestNetworkQuery(t *testing.T) { "BridgeName": "network1", }, } - net1, err := createTestNetwork(bridgeNetType, "network1", netOption) + net1, err := createTestNetwork(bridgeNetType, "network1", netOption, nil, nil) if err != nil { t.Fatal(err) } @@ -969,7 +955,7 @@ func TestEndpointJoin(t *testing.T) { netlabel.GenericData: options.Generic{ "BridgeName": "testnetwork1", }, - }) + }, nil, nil) if err != nil { t.Fatal(err) } @@ -1078,7 +1064,7 @@ func TestEndpointJoin(t *testing.T) { netlabel.GenericData: options.Generic{ "BridgeName": "testnetwork2", }, - }) + }, nil, nil) if err != nil { t.Fatal(err) } @@ -1169,7 +1155,7 @@ func externalKeyTest(t *testing.T, reexec bool) { netlabel.GenericData: options.Generic{ "BridgeName": "testnetwork", }, - }) + }, nil, nil) if err != nil { t.Fatal(err) } @@ -1318,7 +1304,7 @@ func TestEndpointDeleteWithActiveContainer(t *testing.T) { netlabel.GenericData: options.Generic{ "BridgeName": "testnetwork", }, - }) + }, nil, nil) if err != nil { t.Fatal(err) } @@ -1381,7 +1367,7 @@ func TestEndpointMultipleJoins(t *testing.T) { netlabel.GenericData: options.Generic{ "BridgeName": "testmultiple", }, - }) + }, nil, nil) if err != nil { t.Fatal(err) } @@ -1452,7 +1438,7 @@ func TestLeaveAll(t *testing.T) { netlabel.GenericData: options.Generic{ "BridgeName": "testnetwork", }, - }) + }, nil, nil) if err != nil { t.Fatal(err) } @@ -1505,7 +1491,7 @@ func TestontainerInvalidLeave(t *testing.T) { netlabel.GenericData: options.Generic{ "BridgeName": "testnetwork", }, - }) + }, nil, nil) if err != nil { t.Fatal(err) } @@ -1571,7 +1557,7 @@ func TestEndpointUpdateParent(t *testing.T) { netlabel.GenericData: options.Generic{ "BridgeName": "testnetwork", }, - }) + }, nil, nil) if err != nil { t.Fatal(err) } @@ -1655,8 +1641,9 @@ func TestEnableIPv6(t *testing.T) { "BridgeName": "testnetwork", }, } + ipamV6ConfList := []*libnetwork.IpamConf{&libnetwork.IpamConf{PreferredPool: "fe80::/98"}} - n, err := createTestNetwork("bridge", "testnetwork", netOption) + n, err := createTestNetwork("bridge", "testnetwork", netOption, nil, ipamV6ConfList) if err != nil { t.Fatal(err) } @@ -1814,7 +1801,7 @@ func TestResolvConf(t *testing.T) { "BridgeName": "testnetwork", }, } - n, err := createTestNetwork("bridge", "testnetwork", netOption) + n, err := createTestNetwork("bridge", "testnetwork", netOption, nil, nil) if err != nil { t.Fatal(err) } @@ -2084,7 +2071,7 @@ func createGlobalInstance(t *testing.T) { t.Fatal(err) } - net2, err := createTestNetwork("bridge", "network2", netOption) + net2, err := createTestNetwork("bridge", "network2", netOption, nil, nil) if err != nil { t.Fatal(err) } diff --git a/libnetwork/network.go b/libnetwork/network.go index 647cac5d07..4aca2b934a 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -852,7 +852,7 @@ func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error { *infoList = make([]*IpamInfo, len(*cfgList)) - log.Debugf("allocating IPv%d pools for network %s (%s)", ipVer, n.Name(), n.ID()) + log.Debugf("Allocating IPv%d pools for network %s (%s)", ipVer, n.Name(), n.ID()) for i, cfg := range *cfgList { if err = cfg.Validate(); err != nil {