diff --git a/libnetwork/drivers.go b/libnetwork/drivers.go index 1a4b348303..566d330ff4 100644 --- a/libnetwork/drivers.go +++ b/libnetwork/drivers.go @@ -9,6 +9,7 @@ import ( "github.com/docker/libnetwork/netlabel" builtinIpam "github.com/docker/libnetwork/ipams/builtin" + nullIpam "github.com/docker/libnetwork/ipams/null" remoteIpam "github.com/docker/libnetwork/ipams/remote" ) @@ -73,6 +74,7 @@ func initIpams(ic ipamapi.Callback, lDs, gDs interface{}) error { for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){ builtinIpam.Init, remoteIpam.Init, + nullIpam.Init, } { if err := fn(ic, lDs, gDs); err != nil { return err diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index 8324d61fbf..eb61d206a5 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -543,6 +543,9 @@ func (d *driver) getNetworks() []*bridgeNetwork { // Create a new network using bridge plugin func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error { + if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" { + return types.BadRequestErrorf("ipv4 pool is empty") + } // Sanity checks d.Lock() if _, ok := d.networks[id]; ok { diff --git a/libnetwork/drivers/overlay/ov_network.go b/libnetwork/drivers/overlay/ov_network.go index f0b9b2b1f5..306806a8b8 100644 --- a/libnetwork/drivers/overlay/ov_network.go +++ b/libnetwork/drivers/overlay/ov_network.go @@ -63,6 +63,9 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat if id == "" { return fmt.Errorf("invalid network id") } + if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" { + return types.BadRequestErrorf("ipv4 pool is empty") + } // Since we perform lazy configuration make sure we try // configuring the driver when we enter CreateNetwork diff --git a/libnetwork/ipamapi/contract.go b/libnetwork/ipamapi/contract.go index eda461120e..513e482349 100644 --- a/libnetwork/ipamapi/contract.go +++ b/libnetwork/ipamapi/contract.go @@ -15,6 +15,8 @@ import ( const ( // DefaultIPAM is the name of the built-in default ipam driver DefaultIPAM = "default" + // NullIPAM is the name of the built-in null ipam driver + NullIPAM = "null" // PluginEndpointType represents the Endpoint Type used by Plugin system PluginEndpointType = "IpamDriver" // RequestAddressType represents the Address Type used when requesting an address diff --git a/libnetwork/ipams/null/null.go b/libnetwork/ipams/null/null.go new file mode 100644 index 0000000000..60119a36ab --- /dev/null +++ b/libnetwork/ipams/null/null.go @@ -0,0 +1,71 @@ +// Package null implements the null ipam driver. Null ipam driver satisfies ipamapi contract, +// but does not effectively reserve/allocate any address pool or address +package null + +import ( + "fmt" + "net" + + "github.com/docker/libnetwork/discoverapi" + "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/types" +) + +var ( + defaultAS = "null" + defaultPool, _ = types.ParseCIDR("0.0.0.0/0") + defaultPoolID = fmt.Sprintf("%s/%s", defaultAS, defaultPool.String()) +) + +type allocator struct{} + +func (a *allocator) GetDefaultAddressSpaces() (string, string, error) { + return defaultAS, defaultAS, nil +} + +func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { + if addressSpace != defaultAS { + return "", nil, nil, types.BadRequestErrorf("unknown address space: %s", addressSpace) + } + if pool != "" { + return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle specific address pool requests") + } + if subPool != "" { + return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle specific address subpool requests") + } + if v6 { + return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle IPv6 address pool pool requests") + } + return defaultPoolID, defaultPool, nil, nil +} + +func (a *allocator) ReleasePool(poolID string) error { + return nil +} + +func (a *allocator) RequestAddress(poolID string, ip net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) { + if poolID != defaultPoolID { + return nil, nil, types.BadRequestErrorf("unknown pool id: %s", poolID) + } + return nil, nil, nil +} + +func (a *allocator) ReleaseAddress(poolID string, ip net.IP) error { + if poolID != defaultPoolID { + return types.BadRequestErrorf("unknown pool id: %s", poolID) + } + return nil +} + +func (a *allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +func (a *allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +// Init registers a remote ipam when its plugin is activated +func Init(ic ipamapi.Callback, l, g interface{}) error { + return ic.RegisterIpamDriver(ipamapi.NullIPAM, &allocator{}) +} diff --git a/libnetwork/ipams/null/null_test.go b/libnetwork/ipams/null/null_test.go new file mode 100644 index 0000000000..d56135e9a5 --- /dev/null +++ b/libnetwork/ipams/null/null_test.go @@ -0,0 +1,61 @@ +package null + +import ( + "testing" + + _ "github.com/docker/libnetwork/testutils" + "github.com/docker/libnetwork/types" +) + +func TestPoolRequest(t *testing.T) { + a := allocator{} + + pid, pool, _, err := a.RequestPool(defaultAS, "", "", nil, false) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(defaultPool, pool) { + t.Fatalf("Unexpected pool returned. Expected %v. Got: %v", defaultPool, pool) + } + if pid != defaultPoolID { + t.Fatalf("Unexpected pool id returned. Expected: %s. Got: %s", defaultPoolID, pid) + } + + _, _, _, err = a.RequestPool("default", "", "", nil, false) + if err == nil { + t.Fatalf("Unexpected success") + } + + _, _, _, err = a.RequestPool(defaultAS, "192.168.0.0/16", "", nil, false) + if err == nil { + t.Fatalf("Unexpected success") + } + + _, _, _, err = a.RequestPool(defaultAS, "", "192.168.0.0/24", nil, false) + if err == nil { + t.Fatalf("Unexpected success") + } + + _, _, _, err = a.RequestPool(defaultAS, "", "", nil, true) + if err == nil { + t.Fatalf("Unexpected success") + } +} + +func TestOtherRequests(t *testing.T) { + a := allocator{} + + ip, _, err := a.RequestAddress(defaultPoolID, nil, nil) + if err != nil { + t.Fatal(err) + } + if ip != nil { + t.Fatalf("Unexpected address returned: %v", ip) + } + + _, _, err = a.RequestAddress("anypid", nil, nil) + if err == nil { + t.Fatalf("Unexpected success") + } + +} diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index 44348dc1cc..44b636f10d 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -2356,3 +2356,10 @@ func TestParallel2(t *testing.T) { func TestParallel3(t *testing.T) { runParallelTests(t, 3) } + +func TestNullIpam(t *testing.T) { + _, err := controller.NewNetwork(bridgeNetType, "testnetworkinternal", libnetwork.NetworkOptionIpam(ipamapi.NullIPAM, "", nil, nil, nil)) + if err == nil || err.Error() != "ipv4 pool is empty" { + t.Fatal("bridge network should complain empty pool") + } +}