1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

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 <aboch@docker.com>
This commit is contained in:
Alessandro Boch 2015-10-05 14:53:25 -07:00
parent f2f536032b
commit af3eb25d44
17 changed files with 334 additions and 329 deletions

View file

@ -5,6 +5,7 @@ package bitseq
import ( import (
"encoding/binary" "encoding/binary"
"encoding/json"
"fmt" "fmt"
"sync" "sync"
@ -392,6 +393,38 @@ func (h *Handle) String() string {
h.app, h.id, h.dbIndex, h.bits, h.unselected, h.head.toString()) 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 // getFirstAvailable looks for the first unset bit in passed mask starting from start
func getFirstAvailable(head *sequence, start uint32) (uint32, uint32, error) { func getFirstAvailable(head *sequence, start uint32) (uint32, uint32, error) {
// Find sequence which contains the start bit // Find sequence which contains the start bit

View file

@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/types" "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 // Value marshals the data to be stored in the KV store
func (h *Handle) Value() []byte { func (h *Handle) Value() []byte {
b, err := h.ToByteArray() b, err := json.Marshal(h)
if err != nil { if err != nil {
log.Warnf("Failed to serialize Handle: %v", err) return nil
b = []byte{}
} }
jv, err := json.Marshal(b) return b
if err != nil {
log.Warnf("Failed to json encode bitseq handler byte array: %v", err)
return []byte{}
}
return jv
} }
// SetValue unmarshals the data from the KV store // SetValue unmarshals the data from the KV store
func (h *Handle) SetValue(value []byte) error { func (h *Handle) SetValue(value []byte) error {
var b []byte return json.Unmarshal(value, h)
if err := json.Unmarshal(value, &b); err != nil {
return err
}
return h.FromByteArray(b)
} }
// Index returns the latest DB Index as seen by this object // Index returns the latest DB Index as seen by this object
@ -77,7 +65,6 @@ func (h *Handle) New() datastore.KVObject {
return &Handle{ return &Handle{
app: h.app, app: h.app,
id: h.id,
store: h.store, store: h.store,
} }
} }

View file

@ -29,8 +29,10 @@ import (
"github.com/docker/libnetwork/config" "github.com/docker/libnetwork/config"
"github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/ipamutils"
"github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options" "github.com/docker/libnetwork/options"
"github.com/docker/libnetwork/types"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
@ -187,7 +189,12 @@ func createDefaultNetwork(c libnetwork.NetworkController) {
} }
createOptions = append(createOptions, createOptions = append(createOptions,
libnetwork.NetworkOptionGeneric(genericOption), 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...) _, err := c.NewNetwork(d, nw, createOptions...)
if err != nil { if err != nil {
@ -405,3 +412,15 @@ func encodeData(data interface{}) (*bytes.Buffer, error) {
} }
return params, nil 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
}

View file

@ -56,15 +56,17 @@ type configuration struct {
// networkConfiguration for network specific configuration // networkConfiguration for network specific configuration
type networkConfiguration struct { type networkConfiguration struct {
BridgeName string BridgeName string
AddressIPv4 *net.IPNet
EnableIPv6 bool EnableIPv6 bool
EnableIPMasquerade bool EnableIPMasquerade bool
EnableICC bool EnableICC bool
Mtu int Mtu int
DefaultGatewayIPv4 net.IP
DefaultGatewayIPv6 net.IP
DefaultBindingIP net.IP DefaultBindingIP net.IP
DefaultBridge bool 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 // 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 return nil
} }
// Conflicts check if two NetworkConfiguration objects overlap // Conflicts check if two NetworkConfiguration objects overlap
func (c *networkConfiguration) Conflicts(o *networkConfiguration) bool { func (c *networkConfiguration) Conflicts(o *networkConfiguration) error {
if o == nil { if o == nil {
return false return fmt.Errorf("same configuration")
} }
// Also empty, becasue only one network with empty name is allowed // Also empty, becasue only one network with empty name is allowed
if c.BridgeName == o.BridgeName { if c.BridgeName == o.BridgeName {
return true return fmt.Errorf("networks have same name")
} }
// They must be in different subnets // They must be in different subnets
if (c.AddressIPv4 != nil && o.AddressIPv4 != nil) && if (c.AddressIPv4 != nil && o.AddressIPv4 != nil) &&
(c.AddressIPv4.Contains(o.AddressIPv4.IP) || o.AddressIPv4.Contains(c.AddressIPv4.IP)) { (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. // 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) c.AddressIPv4 = types.GetIPNetCopy(ipamV4Data[0].Gateway)
} }
if c.EnableIPv6 && len(ipamV6Data) == 0 { if gw, ok := ipamV4Data[0].AuxAddresses[DefaultGatewayV4AuxKey]; ok {
return types.BadRequestErrorf("bridge network %s requires ipv6 configuration", id)
}
gw, ok := ipamV4Data[0].AuxAddresses[DefaultGatewayV4AuxKey]
if ok {
c.DefaultGatewayIPv4 = gw.IP c.DefaultGatewayIPv4 = gw.IP
} }
gw, ok = ipamV4Data[0].AuxAddresses[DefaultGatewayV6AuxKey] if len(ipamV6Data) > 0 {
if ok { if ipamV6Data[0].Gateway != nil {
c.DefaultGatewayIPv6 = gw.IP c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
}
if gw, ok := ipamV6Data[0].AuxAddresses[DefaultGatewayV6AuxKey]; ok {
c.DefaultGatewayIPv6 = gw.IP
}
} }
return nil 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 // 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 { 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() { if i.bridgeIPv6 != nil && i.bridgeIPv6.IP != nil && !i.bridgeIPv6.IP.IsLinkLocalUnicast() {
return i.bridgeIPv6 return i.bridgeIPv6
} }
@ -551,8 +568,9 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat
nw.Lock() nw.Lock()
nwConfig := nw.config nwConfig := nw.config
nw.Unlock() nw.Unlock()
if nwConfig.Conflicts(config) { if err := nwConfig.Conflicts(config); err != nil {
return types.ForbiddenErrorf("conflicts with network %s (%s)", nw.id, nw.config.BridgeName) 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. // Even if a bridge exists try to setup IPv4.
bridgeSetup.queueStep(setupBridgeIPv4) bridgeSetup.queueStep(setupBridgeIPv4)
enableIPv6Forwarding := false enableIPv6Forwarding := d.config.EnableIPForwarding && config.AddressIPv6 != nil
if d.config.EnableIPForwarding {
enableIPv6Forwarding = true
}
// Conditionally queue setup steps depending on configuration values. // Conditionally queue setup steps depending on configuration values.
for _, step := range []struct { 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 { func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
var (
ipv6Addr *net.IPNet
err error
)
defer osl.InitOSContext()() defer osl.InitOSContext()()
if ifInfo == nil { 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. // Down the interface before configuring mac address.
if err = netlink.LinkSetDown(sbox); err != nil { 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. // Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP.
mac := ifInfo.MacAddress() if endpoint.macAddress == nil {
if mac == nil { endpoint.macAddress = electMacAddress(epConfig, endpoint.addr.IP)
mac = electMacAddress(epConfig, ipv4Addr.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 { if err != nil {
return fmt.Errorf("could not set mac address for container interface %s: %v", containerIfName, err) 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 // Up the host interface after finishing all netlink configuration
if err = netlink.LinkSetUp(host); err != nil { if err = netlink.LinkSetUp(host); err != nil {
return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err) return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err)
} }
ipv6Addr = ifInfo.AddressIPv6() if endpoint.addrv6 == nil && config.EnableIPv6 {
// Create the sandbox side pipe interface var ip6 net.IP
endpoint.srcName = containerIfName network := n.bridge.bridgeIPv6
endpoint.addr = ipv4Addr 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 = &net.IPNet{IP: ip6, Mask: network.Mask}
endpoint.addrv6 = ipv6Addr if err := ifInfo.SetIPAddress(endpoint.addrv6); err != nil {
return err
}
} }
// Program any required port mapping and store them in the endpoint // 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 return err
} }
if ifInfo.MacAddress() == nil {
err = ifInfo.SetMacAddress(endpoint.macAddress)
if err != nil {
return err
}
}
return nil return nil
} }

View file

@ -8,14 +8,27 @@ import (
"testing" "testing"
"github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/ipamutils"
"github.com/docker/libnetwork/iptables" "github.com/docker/libnetwork/iptables"
"github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/testutils" "github.com/docker/libnetwork/testutils"
"github.com/docker/libnetwork/types" "github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink" "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) { func TestCreateFullOptions(t *testing.T) {
defer testutils.SetupTestOSContext(t)() defer testutils.SetupTestOSContext(t)()
d := newDriver() d := newDriver()
@ -27,17 +40,14 @@ func TestCreateFullOptions(t *testing.T) {
// Test this scenario: Default gw address does not belong to // Test this scenario: Default gw address does not belong to
// container network and it's greater than bridge address // container network and it's greater than bridge address
cip, cnw, _ := net.ParseCIDR("172.16.122.0/24") cnw, _ := types.ParseCIDR("172.16.122.0/24")
cnw.IP = cip bnw, _ := types.ParseCIDR("172.16.0.0/24")
ip, nw, _ := net.ParseCIDR("172.16.0.10/16") br, _ := types.ParseCIDR("172.16.0.1/16")
nw.IP = ip defgw, _ := types.ParseCIDR("172.16.0.100/16")
gw := net.ParseIP("172.16.0.1")
netConfig := &networkConfiguration{ netConfig := &networkConfiguration{
BridgeName: DefaultBridgeName, BridgeName: DefaultBridgeName,
AddressIPv4: nw, EnableIPv6: true,
DefaultGatewayIPv4: gw,
EnableIPv6: true,
} }
genericOption := make(map[string]interface{}) genericOption := make(map[string]interface{})
genericOption[netlabel.GenericData] = config genericOption[netlabel.GenericData] = config
@ -49,14 +59,21 @@ func TestCreateFullOptions(t *testing.T) {
netOption := make(map[string]interface{}) netOption := make(map[string]interface{})
netOption[netlabel.GenericData] = netConfig 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 { if err != nil {
t.Fatalf("Failed to create bridge: %v", err) t.Fatalf("Failed to create bridge: %v", err)
} }
// Verify the IP address allocated for the endpoint belongs to the container network // Verify the IP address allocated for the endpoint belongs to the container network
epOptions := make(map[string]interface{}) epOptions := make(map[string]interface{})
te := &testEndpoint{iface: &testInterface{}} te := newTestEndpoint(cnw, 10)
err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions) err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions)
if err != nil { if err != nil {
t.Fatalf("Failed to create an endpoint : %s", err.Error()) 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 := make(map[string]interface{})
genericOption[netlabel.GenericData] = netconfig 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) t.Fatalf("Failed to create bridge: %v", err)
} }
} }
@ -92,11 +109,11 @@ func TestCreate(t *testing.T) {
genericOption := make(map[string]interface{}) genericOption := make(map[string]interface{})
genericOption[netlabel.GenericData] = netconfig 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) 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 { if err == nil {
t.Fatalf("Expected bridge driver to refuse creation of second network with default name") 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 := make(map[string]interface{})
genericOption[netlabel.GenericData] = netconfig 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") t.Fatal("Bridge creation was expected to fail")
} }
} }
@ -147,19 +164,19 @@ func TestCreateMultipleNetworks(t *testing.T) {
config1 := &networkConfiguration{BridgeName: "net_test_1"} config1 := &networkConfiguration{BridgeName: "net_test_1"}
genericOption = make(map[string]interface{}) genericOption = make(map[string]interface{})
genericOption[netlabel.GenericData] = config1 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) t.Fatalf("Failed to create bridge: %v", err)
} }
config2 := &networkConfiguration{BridgeName: "net_test_2"} config2 := &networkConfiguration{BridgeName: "net_test_2"}
genericOption[netlabel.GenericData] = config2 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) t.Fatalf("Failed to create bridge: %v", err)
} }
config3 := &networkConfiguration{BridgeName: "net_test_3"} config3 := &networkConfiguration{BridgeName: "net_test_3"}
genericOption[netlabel.GenericData] = config3 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) t.Fatalf("Failed to create bridge: %v", err)
} }
@ -168,7 +185,7 @@ func TestCreateMultipleNetworks(t *testing.T) {
config4 := &networkConfiguration{BridgeName: "net_test_4"} config4 := &networkConfiguration{BridgeName: "net_test_4"}
genericOption[netlabel.GenericData] = config4 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) t.Fatalf("Failed to create bridge: %v", err)
} }
@ -221,6 +238,12 @@ type testEndpoint struct {
routes []types.StaticRoute 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 { func (te *testEndpoint) Interface() driverapi.InterfaceInfo {
if te.iface != nil { if te.iface != nil {
return te.iface return te.iface
@ -329,7 +352,8 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
genericOption = make(map[string]interface{}) genericOption = make(map[string]interface{})
genericOption[netlabel.GenericData] = netconfig genericOption[netlabel.GenericData] = netconfig
err := d.CreateNetwork("net1", genericOption, nil, nil) ipdList := getIPv4Data(t)
err := d.CreateNetwork("net1", genericOption, ipdList, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create bridge: %v", err) 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 := make(map[string]interface{})
epOptions[netlabel.PortMap] = portMappings epOptions[netlabel.PortMap] = portMappings
te := &testEndpoint{iface: &testInterface{}} te := newTestEndpoint(ipdList[0].Pool, 11)
err = d.CreateEndpoint("net1", "ep1", te.Interface(), epOptions) err = d.CreateEndpoint("net1", "ep1", te.Interface(), epOptions)
if err != nil { if err != nil {
t.Fatalf("Failed to create an endpoint : %s", err.Error()) 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 := make(map[string]interface{})
netOptions[netlabel.GenericData] = netconfig netOptions[netlabel.GenericData] = netconfig
err := d.CreateNetwork("net1", netOptions, nil, nil) ipdList := getIPv4Data(t)
err := d.CreateNetwork("net1", netOptions, ipdList, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create bridge: %v", err) t.Fatalf("Failed to create bridge: %v", err)
} }
@ -398,7 +423,7 @@ func TestCreateLinkWithOptions(t *testing.T) {
epOptions := make(map[string]interface{}) epOptions := make(map[string]interface{})
epOptions[netlabel.MacAddress] = mac epOptions[netlabel.MacAddress] = mac
te := &testEndpoint{iface: &testInterface{}} te := newTestEndpoint(ipdList[0].Pool, 11)
err = d.CreateEndpoint("net1", "ep", te.Interface(), epOptions) err = d.CreateEndpoint("net1", "ep", te.Interface(), epOptions)
if err != nil { if err != nil {
t.Fatalf("Failed to create an endpoint: %s", err.Error()) 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 = make(map[string]interface{})
genericOption[netlabel.GenericData] = netconfig genericOption[netlabel.GenericData] = netconfig
err := d.CreateNetwork("net1", genericOption, nil, nil) ipdList := getIPv4Data(t)
err := d.CreateNetwork("net1", genericOption, ipdList, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create bridge: %v", err) t.Fatalf("Failed to create bridge: %v", err)
} }
@ -467,7 +493,7 @@ func TestLinkContainers(t *testing.T) {
epOptions := make(map[string]interface{}) epOptions := make(map[string]interface{})
epOptions[netlabel.ExposedPorts] = exposedPorts epOptions[netlabel.ExposedPorts] = exposedPorts
te1 := &testEndpoint{iface: &testInterface{}} te1 := newTestEndpoint(ipdList[0].Pool, 11)
err = d.CreateEndpoint("net1", "ep1", te1.Interface(), epOptions) err = d.CreateEndpoint("net1", "ep1", te1.Interface(), epOptions)
if err != nil { if err != nil {
t.Fatalf("Failed to create an endpoint : %s", err.Error()) 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") 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) err = d.CreateEndpoint("net1", "ep2", te2.Interface(), nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create an endpoint : %s", err.Error()) t.Fatalf("Failed to create an endpoint : %s", err.Error())
@ -581,6 +607,14 @@ func TestValidateConfig(t *testing.T) {
// Bridge network // Bridge network
_, network, _ := net.ParseCIDR("172.28.0.0/16") _, network, _ := net.ParseCIDR("172.28.0.0/16")
c = networkConfiguration{
AddressIPv4: network,
}
err = c.Validate()
if err != nil {
t.Fatal(err)
}
// Test v4 gw // Test v4 gw
c.DefaultGatewayIPv4 = net.ParseIP("172.27.30.234") c.DefaultGatewayIPv4 = net.ParseIP("172.27.30.234")
@ -596,9 +630,10 @@ func TestValidateConfig(t *testing.T) {
} }
// Test v6 gw // Test v6 gw
_, containerSubnet, _ = net.ParseCIDR("2001:1234:ae:b004::/64") _, v6nw, _ := net.ParseCIDR("2001:1234:ae:b004::/64")
c = networkConfiguration{ c = networkConfiguration{
EnableIPv6: true, EnableIPv6: true,
AddressIPv6: v6nw,
DefaultGatewayIPv6: net.ParseIP("2001:1234:ac:b004::bad:a55"), DefaultGatewayIPv6: net.ParseIP("2001:1234:ac:b004::bad:a55"),
} }
err = c.Validate() err = c.Validate()
@ -611,6 +646,18 @@ func TestValidateConfig(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Unexpected validation error on v6 default gateway") 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) { func TestSetDefaultGw(t *testing.T) {
@ -623,24 +670,15 @@ func TestSetDefaultGw(t *testing.T) {
_, subnetv6, _ := net.ParseCIDR("2001:db8:ea9:9abc:b0c4::/80") _, subnetv6, _ := net.ParseCIDR("2001:db8:ea9:9abc:b0c4::/80")
var nw *net.IPNet ipdList := getIPv4Data(t)
for _, n := range bridgeNetworks { gw4 := types.GetIPCopy(ipdList[0].Pool.IP).To4()
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()
gw4[3] = 254 gw4[3] = 254
gw6 := net.ParseIP("2001:db8:ea9:9abc:b0c4::254") gw6 := net.ParseIP("2001:db8:ea9:9abc:b0c4::254")
config := &networkConfiguration{ config := &networkConfiguration{
BridgeName: DefaultBridgeName, BridgeName: DefaultBridgeName,
EnableIPv6: true, EnableIPv6: true,
AddressIPv6: subnetv6,
DefaultGatewayIPv4: gw4, DefaultGatewayIPv4: gw4,
DefaultGatewayIPv6: gw6, DefaultGatewayIPv6: gw6,
} }
@ -648,12 +686,12 @@ func TestSetDefaultGw(t *testing.T) {
genericOption := make(map[string]interface{}) genericOption := make(map[string]interface{})
genericOption[netlabel.GenericData] = config genericOption[netlabel.GenericData] = config
err := d.CreateNetwork("dummy", genericOption, nil, nil) err := d.CreateNetwork("dummy", genericOption, ipdList, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create bridge: %v", err) 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) err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create endpoint: %v", err) t.Fatalf("Failed to create endpoint: %v", err)

View file

@ -26,12 +26,13 @@ func TestLinkCreate(t *testing.T) {
genericOption := make(map[string]interface{}) genericOption := make(map[string]interface{})
genericOption[netlabel.GenericData] = config genericOption[netlabel.GenericData] = config
err := d.CreateNetwork("dummy", genericOption, nil, nil) ipdList := getIPv4Data(t)
err := d.CreateNetwork("dummy", genericOption, ipdList, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create bridge: %v", err) 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) err = d.CreateEndpoint("dummy", "", te.Interface(), nil)
if err != nil { if err != nil {
if _, ok := err.(InvalidEndpointIDError); !ok { 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 // TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName
// then we could check the MTU on hostLnk as well. // 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) err = d.CreateEndpoint("dummy", "ep", te1.Interface(), nil)
if err == nil { if err == nil {
t.Fatalf("Failed to detect duplicate endpoint id on same network") 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 := make(map[string]interface{})
genericOption[netlabel.GenericData] = config genericOption[netlabel.GenericData] = config
err := d.CreateNetwork("dummy", genericOption, nil, nil) ipdList := getIPv4Data(t)
err := d.CreateNetwork("dummy", genericOption, ipdList, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create bridge: %v", err) 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) err = d.CreateEndpoint("dummy", "ep", te1.Interface(), nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create a link: %s", err.Error()) 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) err = d.CreateEndpoint("dummy", "ep", te2.Interface(), nil)
if err != nil { if err != nil {
if _, ok := err.(driverapi.ErrEndpointExists); !ok { if _, ok := err.(driverapi.ErrEndpointExists); !ok {
@ -152,12 +154,12 @@ func TestLinkCreateNoEnableIPv6(t *testing.T) {
genericOption := make(map[string]interface{}) genericOption := make(map[string]interface{})
genericOption[netlabel.GenericData] = config genericOption[netlabel.GenericData] = config
err := d.CreateNetwork("dummy", genericOption, nil, nil) ipdList := getIPv4Data(t)
err := d.CreateNetwork("dummy", genericOption, ipdList, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create bridge: %v", err) t.Fatalf("Failed to create bridge: %v", err)
} }
te := newTestEndpoint(ipdList[0].Pool, 30)
te := &testEndpoint{iface: &testInterface{}}
err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil) err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create a link: %s", err.Error()) 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 := make(map[string]interface{})
genericOption[netlabel.GenericData] = config genericOption[netlabel.GenericData] = config
err := d.CreateNetwork("dummy", genericOption, nil, nil) ipdList := getIPv4Data(t)
err := d.CreateNetwork("dummy", genericOption, ipdList, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create bridge: %v", err) 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) err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create a link: %s", err.Error()) t.Fatalf("Failed to create a link: %s", err.Error())

View file

@ -44,12 +44,13 @@ func TestPortMappingConfig(t *testing.T) {
netOptions := make(map[string]interface{}) netOptions := make(map[string]interface{})
netOptions[netlabel.GenericData] = netConfig netOptions[netlabel.GenericData] = netConfig
err := d.CreateNetwork("dummy", netOptions, nil, nil) ipdList := getIPv4Data(t)
err := d.CreateNetwork("dummy", netOptions, ipdList, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to create bridge: %v", err) 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) err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions)
if err != nil { if err != nil {
t.Fatalf("Failed to create the endpoint: %s", err.Error()) t.Fatalf("Failed to create the endpoint: %s", err.Error())

View file

@ -22,7 +22,7 @@ const (
//Gets the IP version in use ( [ipv4], [ipv6] or [ipv4 and ipv6] ) //Gets the IP version in use ( [ipv4], [ipv6] or [ipv4 and ipv6] )
func getIPVersion(config *networkConfiguration) ipVersion { func getIPVersion(config *networkConfiguration) ipVersion {
ipVersion := ipv4 ipVersion := ipv4
if config.EnableIPv6 { if config.AddressIPv6 != nil || config.EnableIPv6 {
ipVersion |= ipv6 ipVersion |= ipv6
} }
return ipVersion return ipVersion

View file

@ -3,101 +3,36 @@ package bridge
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"path/filepath" "path/filepath"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink" "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 { func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error {
addrv4, _, err := i.addresses() addrv4, _, err := i.addresses()
if err != nil { 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 !types.CompareIPNet(addrv4.IPNet, config.AddressIPv4) {
if addrv4.IPNet != nil { if addrv4.IPNet != nil {
// Make sure to store bridge network and default gateway before getting out. if err := netlink.AddrDel(i.Link, &addrv4); err != nil {
i.bridgeIPv4 = addrv4.IPNet return fmt.Errorf("failed to remove current ip address from bridge: %v", err)
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
} }
} }
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 { func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error {

View file

@ -4,7 +4,6 @@ import (
"net" "net"
"testing" "testing"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/testutils" "github.com/docker/libnetwork/testutils"
"github.com/vishvananda/netlink" "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) { func TestSetupGatewayIPv4(t *testing.T) {
defer testutils.SetupTestOSContext(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) 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())
}
}
}

View file

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net" "net"
"os"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
@ -57,12 +58,35 @@ func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error {
i.bridgeIPv6 = bridgeIPv6 i.bridgeIPv6 = bridgeIPv6
i.gatewayIPv6 = i.bridgeIPv6.IP 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 return nil
} }
func setupGatewayIPv6(config *networkConfiguration, i *bridgeInterface) error { 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 // Store requested default gateway
i.gatewayIPv6 = config.DefaultGatewayIPv6 i.gatewayIPv6 = config.DefaultGatewayIPv6
return nil return nil
} }

View file

@ -55,6 +55,7 @@ func TestSetupGatewayIPv6(t *testing.T) {
config := &networkConfiguration{ config := &networkConfiguration{
BridgeName: DefaultBridgeName, BridgeName: DefaultBridgeName,
AddressIPv6: nw,
DefaultGatewayIPv6: gw} DefaultGatewayIPv6: gw}
br := &bridgeInterface{} br := &bridgeInterface{}

View file

@ -622,10 +622,14 @@ func (ep *endpoint) assignAddress() error {
ipam ipamapi.Ipam ipam ipamapi.Ipam
err error err error
) )
n := ep.getNetwork() n := ep.getNetwork()
if n.Type() == "host" || n.Type() == "null" { if n.Type() == "host" || n.Type() == "null" {
return nil 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) ipam, err = n.getController().getIpamDriver(n.ipamType)
if err != nil { if err != nil {
return err return err
@ -683,6 +687,9 @@ func (ep *endpoint) releaseAddress() {
if n.Type() == "host" || n.Type() == "null" { if n.Type() == "host" || n.Type() == "null" {
return return
} }
log.Debugf("Releasing addresses for endpoint %s's interface on network %s", ep.Name(), n.Name())
ipam, err := n.getController().getIpamDriver(n.ipamType) ipam, err := n.getController().getIpamDriver(n.ipamType)
if err != nil { 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) log.Warnf("Failed to retrieve ipam driver to release interface address on delete of endpoint %s (%s): %v", ep.Name(), ep.ID(), err)

View file

@ -102,8 +102,9 @@ func (a *Allocator) updateBitMasks(aSpace *addrSpace) error {
aSpace.Lock() aSpace.Lock()
for k, v := range aSpace.subnets { for k, v := range aSpace.subnets {
if v.Range == nil { if v.Range == nil {
inserterList = append(inserterList, kk := k
func() error { return a.insertBitMask(k, v.Pool) }) vv := v
inserterList = append(inserterList, func() error { return a.insertBitMask(kk, vv.Pool) })
} }
} }
aSpace.Unlock() aSpace.Unlock()
@ -127,6 +128,7 @@ func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
// RequestPool returns an address pool along with its unique id. // 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) { 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) k, nw, aw, ipr, err := a.parsePoolRequest(addressSpace, pool, subPool, v6)
if err != nil { if err != nil {
return "", nil, nil, ipamapi.ErrInvalidPool return "", nil, nil, ipamapi.ErrInvalidPool
@ -160,6 +162,7 @@ retry:
// ReleasePool releases the address pool identified by the passed id // ReleasePool releases the address pool identified by the passed id
func (a *Allocator) ReleasePool(poolID string) error { func (a *Allocator) ReleasePool(poolID string) error {
log.Debugf("ReleasePool(%s)", poolID)
k := SubnetKey{} k := SubnetKey{}
if err := k.FromString(poolID); err != nil { if err := k.FromString(poolID); err != nil {
return types.BadRequestErrorf("invalid pool id: %s", poolID) 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 // 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) { 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{} k := SubnetKey{}
if err := k.FromString(poolID); err != nil { if err := k.FromString(poolID); err != nil {
return nil, nil, types.BadRequestErrorf("invalid pool id: %s", poolID) 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 // ReleaseAddress releases the address from the specified pool ID
func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error { func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
log.Debugf("ReleaseAddress(%s, %v)", poolID, address)
k := SubnetKey{} k := SubnetKey{}
if err := k.FromString(poolID); err != nil { if err := k.FromString(poolID); err != nil {
return types.BadRequestErrorf("invalid pool id: %s", poolID) return types.BadRequestErrorf("invalid pool id: %s", poolID)

View file

@ -13,7 +13,7 @@ import (
"github.com/docker/libnetwork/bitseq" "github.com/docker/libnetwork/bitseq"
"github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/ipamapi" "github.com/docker/libnetwork/ipamapi"
"github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/ipamutils"
_ "github.com/docker/libnetwork/testutils" _ "github.com/docker/libnetwork/testutils"
"github.com/docker/libnetwork/types" "github.com/docker/libnetwork/types"
) )
@ -461,12 +461,7 @@ func TestPredefinedPool(t *testing.T) {
t.Fatalf("Expected failure for non default addr space") t.Fatalf("Expected failure for non default addr space")
} }
i, available, err := getFirstAvailablePool(a, localAddressSpace, 2) exp, err := ipamutils.FindAvailableNetwork(a.predefined[localAddressSpace])
if err != nil {
t.Skip(err)
}
pid, _, _, err := a.RequestPool(localAddressSpace, available.String(), "", nil, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -475,8 +470,24 @@ func TestPredefinedPool(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if nw != a.predefined[localAddressSpace][i+1] { if !types.CompareIPNet(nw, exp) {
t.Fatalf("Unexpected default network returned: %s", nw) 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 { if err := a.ReleasePool(pid); err != nil {
@ -487,25 +498,11 @@ func TestPredefinedPool(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if nw != a.predefined[localAddressSpace][i] { if !types.CompareIPNet(nw, exp) {
t.Fatalf("Unexpected default network returned: %s", nw) 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) { func TestAdjustAndCheckSubnet(t *testing.T) {
_, sub6, _ := net.ParseCIDR("1003:1:2:300::/63") _, sub6, _ := net.ParseCIDR("1003:1:2:300::/63")
_, err := adjustAndCheckSubnetSize(sub6) _, err := adjustAndCheckSubnetSize(sub6)

View file

@ -23,6 +23,7 @@ import (
"github.com/docker/libnetwork/config" "github.com/docker/libnetwork/config"
"github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/ipamapi"
"github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options" "github.com/docker/libnetwork/options"
"github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/osl"
@ -82,14 +83,10 @@ func createController() error {
return nil return nil
} }
func createTestNetwork(networkType, networkName string, netOption options.Generic) (libnetwork.Network, error) { func createTestNetwork(networkType, networkName string, netOption options.Generic, ipamV4Configs, ipamV6Configs []*libnetwork.IpamConf) (libnetwork.Network, error) {
network, err := controller.NewNetwork(networkType, networkName, return controller.NewNetwork(networkType, networkName,
libnetwork.NetworkOptionGeneric(netOption)) libnetwork.NetworkOptionGeneric(netOption),
if err != nil { libnetwork.NetworkOptionIpam(ipamapi.DefaultIPAM, "", ipamV4Configs, ipamV6Configs))
return nil, err
}
return network, nil
} }
func getEmptyGenericOption() map[string]interface{} { func getEmptyGenericOption() map[string]interface{} {
@ -117,7 +114,7 @@ func TestNull(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
network, err := createTestNetwork("null", "testnull", options.Generic{}) network, err := createTestNetwork("null", "testnull", options.Generic{}, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -270,24 +267,18 @@ func TestBridge(t *testing.T) {
defer testutils.SetupTestOSContext(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{ netOption := options.Generic{
netlabel.GenericData: options.Generic{ netlabel.GenericData: options.Generic{
"BridgeName": "testnetwork", "BridgeName": "testnetwork",
"AddressIPv4": subnet,
"EnableIPv6": true, "EnableIPv6": true,
"EnableICC": true, "EnableICC": true,
"EnableIPMasquerade": 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -327,7 +318,7 @@ func TestUnknownDriver(t *testing.T) {
defer testutils.SetupTestOSContext(t)() defer testutils.SetupTestOSContext(t)()
} }
_, err := createTestNetwork("unknowndriver", "testnetwork", options.Generic{}) _, err := createTestNetwork("unknowndriver", "testnetwork", options.Generic{}, nil, nil)
if err == nil { if err == nil {
t.Fatal("Expected to fail. But instead succeeded") 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 { if err == nil {
t.Fatal("Expected to fail. But instead succeeded") t.Fatal("Expected to fail. But instead succeeded")
} }
@ -370,7 +361,7 @@ func TestNetworkName(t *testing.T) {
} }
networkName := "testnetwork" networkName := "testnetwork"
n, err := createTestNetwork(bridgeNetType, networkName, netOption) n, err := createTestNetwork(bridgeNetType, networkName, netOption, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) 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 { if err != nil {
t.Fatal(err) 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -449,7 +440,7 @@ func TestDeleteNetworkWithActiveEndpoints(t *testing.T) {
netlabel.GenericData: netOption, netlabel.GenericData: netOption,
} }
network, err := createTestNetwork(bridgeNetType, "testnetwork", option) network, err := createTestNetwork(bridgeNetType, "testnetwork", option, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -490,7 +481,7 @@ func TestUnknownNetwork(t *testing.T) {
netlabel.GenericData: netOption, netlabel.GenericData: netOption,
} }
network, err := createTestNetwork(bridgeNetType, "testnetwork", option) network, err := createTestNetwork(bridgeNetType, "testnetwork", option, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -515,20 +506,15 @@ func TestUnknownEndpoint(t *testing.T) {
defer testutils.SetupTestOSContext(t)() defer testutils.SetupTestOSContext(t)()
} }
subnet, err := types.ParseCIDR("192.168.100.1/24")
if err != nil {
t.Fatal(err)
}
netOption := options.Generic{ netOption := options.Generic{
"BridgeName": "testnetwork", "BridgeName": "testnetwork",
"AddressIPv4": subnet,
} }
option := options.Generic{ option := options.Generic{
netlabel.GenericData: netOption, 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 { if err != nil {
t.Fatal(err) 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 { if err != nil {
t.Fatal(err) 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -697,7 +683,7 @@ func TestDuplicateEndpoint(t *testing.T) {
"BridgeName": "testnetwork", "BridgeName": "testnetwork",
}, },
} }
n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption) n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -747,7 +733,7 @@ func TestControllerQuery(t *testing.T) {
"BridgeName": "network1", "BridgeName": "network1",
}, },
} }
net1, err := createTestNetwork(bridgeNetType, "network1", netOption) net1, err := createTestNetwork(bridgeNetType, "network1", netOption, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -763,7 +749,7 @@ func TestControllerQuery(t *testing.T) {
"BridgeName": "network2", "BridgeName": "network2",
}, },
} }
net2, err := createTestNetwork(bridgeNetType, "network2", netOption) net2, err := createTestNetwork(bridgeNetType, "network2", netOption, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -849,7 +835,7 @@ func TestNetworkQuery(t *testing.T) {
"BridgeName": "network1", "BridgeName": "network1",
}, },
} }
net1, err := createTestNetwork(bridgeNetType, "network1", netOption) net1, err := createTestNetwork(bridgeNetType, "network1", netOption, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -969,7 +955,7 @@ func TestEndpointJoin(t *testing.T) {
netlabel.GenericData: options.Generic{ netlabel.GenericData: options.Generic{
"BridgeName": "testnetwork1", "BridgeName": "testnetwork1",
}, },
}) }, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1078,7 +1064,7 @@ func TestEndpointJoin(t *testing.T) {
netlabel.GenericData: options.Generic{ netlabel.GenericData: options.Generic{
"BridgeName": "testnetwork2", "BridgeName": "testnetwork2",
}, },
}) }, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1169,7 +1155,7 @@ func externalKeyTest(t *testing.T, reexec bool) {
netlabel.GenericData: options.Generic{ netlabel.GenericData: options.Generic{
"BridgeName": "testnetwork", "BridgeName": "testnetwork",
}, },
}) }, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1318,7 +1304,7 @@ func TestEndpointDeleteWithActiveContainer(t *testing.T) {
netlabel.GenericData: options.Generic{ netlabel.GenericData: options.Generic{
"BridgeName": "testnetwork", "BridgeName": "testnetwork",
}, },
}) }, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1381,7 +1367,7 @@ func TestEndpointMultipleJoins(t *testing.T) {
netlabel.GenericData: options.Generic{ netlabel.GenericData: options.Generic{
"BridgeName": "testmultiple", "BridgeName": "testmultiple",
}, },
}) }, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1452,7 +1438,7 @@ func TestLeaveAll(t *testing.T) {
netlabel.GenericData: options.Generic{ netlabel.GenericData: options.Generic{
"BridgeName": "testnetwork", "BridgeName": "testnetwork",
}, },
}) }, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1505,7 +1491,7 @@ func TestontainerInvalidLeave(t *testing.T) {
netlabel.GenericData: options.Generic{ netlabel.GenericData: options.Generic{
"BridgeName": "testnetwork", "BridgeName": "testnetwork",
}, },
}) }, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1571,7 +1557,7 @@ func TestEndpointUpdateParent(t *testing.T) {
netlabel.GenericData: options.Generic{ netlabel.GenericData: options.Generic{
"BridgeName": "testnetwork", "BridgeName": "testnetwork",
}, },
}) }, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1655,8 +1641,9 @@ func TestEnableIPv6(t *testing.T) {
"BridgeName": "testnetwork", "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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1814,7 +1801,7 @@ func TestResolvConf(t *testing.T) {
"BridgeName": "testnetwork", "BridgeName": "testnetwork",
}, },
} }
n, err := createTestNetwork("bridge", "testnetwork", netOption) n, err := createTestNetwork("bridge", "testnetwork", netOption, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -2084,7 +2071,7 @@ func createGlobalInstance(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
net2, err := createTestNetwork("bridge", "network2", netOption) net2, err := createTestNetwork("bridge", "network2", netOption, nil, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -852,7 +852,7 @@ func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error {
*infoList = make([]*IpamInfo, len(*cfgList)) *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 { for i, cfg := range *cfgList {
if err = cfg.Validate(); err != nil { if err = cfg.Validate(); err != nil {