Skip to content

Commit

Permalink
Allow user to specify default address pools for docker networks
Browse files Browse the repository at this point in the history
 This is new feature that allows  user to specify which subnetwork
 Docker contrainer should choose from when it creates bridge network.

 This libnetwork commit is to address moby PR 36054
Signed-off-by: selansen <elango.siva@docker.com>
  • Loading branch information
selansen committed Feb 22, 2018
1 parent 20dd462 commit 0ae9b6f
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 34 deletions.
9 changes: 9 additions & 0 deletions config/config.go
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/docker/libkv/store"
"github.com/docker/libnetwork/cluster"
"github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/ipamutils"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/osl"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -40,6 +41,7 @@ type DaemonCfg struct {
DriverCfg map[string]interface{}
ClusterProvider cluster.Provider
NetworkControlPlaneMTU int
DefaultAddressPool []*ipamutils.NetworkToSplit
}

// ClusterCfg represents cluster configuration
Expand Down Expand Up @@ -110,6 +112,13 @@ func OptionDefaultDriver(dd string) Option {
}
}

// OptionDefaultAddressPoolConfig function returns an option setter for default address pool
func OptionDefaultAddressPoolConfig(addressPool []*ipamutils.NetworkToSplit) Option {
return func(c *Config) {
c.Daemon.DefaultAddressPool = addressPool
}
}

// OptionDriverConfig returns an option setter for driver configuration.
func OptionDriverConfig(networkType string, config map[string]interface{}) Option {
return func(c *Config) {
Expand Down
2 changes: 1 addition & 1 deletion controller.go
Expand Up @@ -222,7 +222,7 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
}
}

if err = initIPAMDrivers(drvRegistry, nil, c.getStore(datastore.GlobalScope)); err != nil {
if err = initIPAMDrivers(drvRegistry, nil, c.getStore(datastore.GlobalScope), c.cfg.Daemon.DefaultAddressPool); err != nil {
return nil, err
}

Expand Down
2 changes: 1 addition & 1 deletion drivers/bridge/bridge_test.go
Expand Up @@ -21,7 +21,7 @@ import (
)

func init() {
ipamutils.InitNetworks()
ipamutils.InitNetworks(nil)
}

func TestEndpointMarshalling(t *testing.T) {
Expand Down
4 changes: 3 additions & 1 deletion drivers_ipam.go
Expand Up @@ -6,9 +6,11 @@ import (
builtinIpam "github.com/docker/libnetwork/ipams/builtin"
nullIpam "github.com/docker/libnetwork/ipams/null"
remoteIpam "github.com/docker/libnetwork/ipams/remote"
"github.com/docker/libnetwork/ipamutils"
)

func initIPAMDrivers(r *drvregistry.DrvRegistry, lDs, gDs interface{}) error {
func initIPAMDrivers(r *drvregistry.DrvRegistry, lDs, gDs interface{}, addressPool []*ipamutils.NetworkToSplit) error {
builtinIpam.SetDefaultIPAddressPool(addressPool)
for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){
builtinIpam.Init,
remoteIpam.Init,
Expand Down
2 changes: 1 addition & 1 deletion ipam/allocator_test.go
Expand Up @@ -51,7 +51,7 @@ func randomLocalStore() (datastore.DataStore, error) {
}

func getAllocator() (*Allocator, error) {
ipamutils.InitNetworks()
ipamutils.InitNetworks(nil)
ds, err := randomLocalStore()
if err != nil {
return nil, err
Expand Down
17 changes: 16 additions & 1 deletion ipams/builtin/builtin_unix.go
Expand Up @@ -11,6 +11,11 @@ import (
"github.com/docker/libnetwork/ipamutils"
)

var (
// defaultAddressPool Stores user configured subnet list
defaultAddressPool []*ipamutils.NetworkToSplit
)

// Init registers the built-in ipam service with libnetwork
func Init(ic ipamapi.Callback, l, g interface{}) error {
var (
Expand All @@ -30,7 +35,7 @@ func Init(ic ipamapi.Callback, l, g interface{}) error {
}
}

ipamutils.InitNetworks()
ipamutils.InitNetworks(GetDefaultIPAddressPool())

a, err := ipam.NewAllocator(localDs, globalDs)
if err != nil {
Expand All @@ -41,3 +46,13 @@ func Init(ic ipamapi.Callback, l, g interface{}) error {

return ic.RegisterIpamDriverWithCapabilities(ipamapi.DefaultIPAM, a, cps)
}

// SetDefaultIPAddressPool stores default address pool.
func SetDefaultIPAddressPool(addressPool []*ipamutils.NetworkToSplit) {
defaultAddressPool = addressPool
}

// GetDefaultIPAddressPool returns default address pool.
func GetDefaultIPAddressPool() []*ipamutils.NetworkToSplit {
return defaultAddressPool
}
17 changes: 16 additions & 1 deletion ipams/builtin/builtin_windows.go
Expand Up @@ -13,6 +13,11 @@ import (
windowsipam "github.com/docker/libnetwork/ipams/windowsipam"
)

var (
// defaultAddressPool Stores user configured subnet list
defaultAddressPool []*ipamutils.NetworkToSplit
)

// InitDockerDefault registers the built-in ipam service with libnetwork
func InitDockerDefault(ic ipamapi.Callback, l, g interface{}) error {
var (
Expand All @@ -32,7 +37,7 @@ func InitDockerDefault(ic ipamapi.Callback, l, g interface{}) error {
}
}

ipamutils.InitNetworks()
ipamutils.InitNetworks(nil)

a, err := ipam.NewAllocator(localDs, globalDs)
if err != nil {
Expand All @@ -55,3 +60,13 @@ func Init(ic ipamapi.Callback, l, g interface{}) error {

return initFunc(ic, l, g)
}

// SetDefaultIPAddressPool stores default address pool .
func SetDefaultIPAddressPool(addressPool []*ipamutils.NetworkToSplit) {
defaultAddressPool = addressPool
}

// GetDefaultIPAddressPool returns default address pool .
func GetDefaultIPAddressPool() []*ipamutils.NetworkToSplit {
return defaultAddressPool
}
90 changes: 68 additions & 22 deletions ipamutils/utils.go
Expand Up @@ -2,8 +2,11 @@
package ipamutils

import (
"fmt"
"net"
"sync"

"github.com/sirupsen/logrus"
)

var (
Expand All @@ -13,38 +16,81 @@ var (
// PredefinedGranularNetworks contains a list of 64K IPv4 private networks with host size 8
// (10.x.x.x/24) which do not overlap with the networks in `PredefinedBroadNetworks`
PredefinedGranularNetworks []*net.IPNet
initNetworksOnce sync.Once

initNetworksOnce sync.Once
defaultBroadNetwork = []*NetworkToSplit{{"172.17.0.0/16", 16}, {"172.18.0.0/16", 16}, {"172.19.0.0/16", 16},
{"172.20.0.0/14", 16}, {"172.24.0.0/14", 16}, {"172.28.0.0/14", 16},
{"192.168.0.0/16", 20}}
defaultGranularNetwork = []*NetworkToSplit{{"10.0.0.0/8", 24}}
)

// InitNetworks initializes the pre-defined networks used by the built-in IP allocator
func InitNetworks() {
// NetworkToSplit represent a network that has to be split in chunks with mask length Size.
// Each subnet in the set is derived from the Base pool. Base is to be passed
// in CIDR format.
// Example: a Base "10.10.0.0/16 with Size 24 will define the set of 256
// 10.10.[0-255].0/24 address pools
type NetworkToSplit struct {
Base string `json:"base"`
Size int `json:"size"`
}

// InitNetworks initializes the broad network pool and the granular network pool
func InitNetworks(defaultAddressPool []*NetworkToSplit) {
initNetworksOnce.Do(func() {
PredefinedBroadNetworks = initBroadPredefinedNetworks()
PredefinedGranularNetworks = initGranularPredefinedNetworks()
// error ingnored should never fail
PredefinedGranularNetworks, _ = splitNetworks(defaultGranularNetwork)
if defaultAddressPool == nil {
defaultAddressPool = defaultBroadNetwork
}
var err error
if PredefinedBroadNetworks, err = splitNetworks(defaultAddressPool); err != nil {
logrus.WithError(err).Error("InitAddressPools failed to initialize the default address pool")
}
})
}

func initBroadPredefinedNetworks() []*net.IPNet {
pl := make([]*net.IPNet, 0, 31)
mask := []byte{255, 255, 0, 0}
for i := 17; i < 32; i++ {
pl = append(pl, &net.IPNet{IP: []byte{172, byte(i), 0, 0}, Mask: mask})
// splitNetworks takes a slice of networks, split them accordingly and returns them
func splitNetworks(list []*NetworkToSplit) ([]*net.IPNet, error) {
localPools := make([]*net.IPNet, 0, len(list))

for _, p := range list {
_, b, err := net.ParseCIDR(p.Base)
if err != nil {
return nil, fmt.Errorf("invalid base pool %q: %v", p.Base, err)
}
ones, _ := b.Mask.Size()
if p.Size <= 0 || p.Size < ones {
return nil, fmt.Errorf("invalid pools size: %d", p.Size)
}
localPools = append(localPools, splitNetwork(p.Size, b)...)
}
mask20 := []byte{255, 255, 240, 0}
for i := 0; i < 16; i++ {
pl = append(pl, &net.IPNet{IP: []byte{192, 168, byte(i << 4), 0}, Mask: mask20})
return localPools, nil
}

func splitNetwork(size int, base *net.IPNet) []*net.IPNet {
one, bits := base.Mask.Size()
mask := net.CIDRMask(size, bits)
n := 1 << uint(size-one)
s := uint(bits - size)
list := make([]*net.IPNet, 0, n)

for i := 0; i < n; i++ {
ip := copyIP(base.IP)
addIntToIP(ip, uint(i<<s))
list = append(list, &net.IPNet{IP: ip, Mask: mask})
}
return pl
return list
}

func initGranularPredefinedNetworks() []*net.IPNet {
pl := make([]*net.IPNet, 0, 256*256)
mask := []byte{255, 255, 255, 0}
for i := 0; i < 256; i++ {
for j := 0; j < 256; j++ {
pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), byte(j), 0}, Mask: mask})
}
func copyIP(from net.IP) net.IP {
ip := make([]byte, len(from))
copy(ip, from)
return ip
}

func addIntToIP(array net.IP, ordinal uint) {
for i := len(array) - 1; i >= 0; i-- {
array[i] |= (byte)(ordinal & 0xff)
ordinal >>= 8
}
return pl
}
70 changes: 67 additions & 3 deletions ipamutils/utils_test.go
@@ -1,16 +1,40 @@
package ipamutils

import (
"net"
"sync"
"testing"

_ "github.com/docker/libnetwork/testutils"
"github.com/stretchr/testify/assert"
)

func init() {
InitNetworks()
func initBroadPredefinedNetworks() []*net.IPNet {
pl := make([]*net.IPNet, 0, 31)
mask := []byte{255, 255, 0, 0}
for i := 17; i < 32; i++ {
pl = append(pl, &net.IPNet{IP: []byte{172, byte(i), 0, 0}, Mask: mask})
}
mask20 := []byte{255, 255, 240, 0}
for i := 0; i < 16; i++ {
pl = append(pl, &net.IPNet{IP: []byte{192, 168, byte(i << 4), 0}, Mask: mask20})
}
return pl
}

func initGranularPredefinedNetworks() []*net.IPNet {
pl := make([]*net.IPNet, 0, 256*256)
mask := []byte{255, 255, 255, 0}
for i := 0; i < 256; i++ {
for j := 0; j < 256; j++ {
pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), byte(j), 0}, Mask: mask})
}
}
return pl
}

func TestGranularPredefined(t *testing.T) {
func TestDefaultNetwork(t *testing.T) {
InitNetworks(nil)
for _, nw := range PredefinedGranularNetworks {
if ones, bits := nw.Mask.Size(); bits != 32 || ones != 24 {
t.Fatalf("Unexpected size for network in granular list: %v", nw)
Expand All @@ -23,4 +47,44 @@ func TestGranularPredefined(t *testing.T) {
}
}

originalBroadNets := initBroadPredefinedNetworks()
m := make(map[string]bool)
for _, v := range originalBroadNets {
m[v.String()] = true
}
for _, nw := range PredefinedBroadNetworks {
_, ok := m[nw.String()]
assert.True(t, ok)
delete(m, nw.String())
}

assert.Len(t, m, 0)

originalGranularNets := initGranularPredefinedNetworks()

m = make(map[string]bool)
for _, v := range originalGranularNets {
m[v.String()] = true
}
for _, nw := range PredefinedGranularNetworks {
_, ok := m[nw.String()]
assert.True(t, ok)
delete(m, nw.String())
}

assert.Len(t, m, 0)
}

func TestInitAddressPools(t *testing.T) {
initNetworksOnce = sync.Once{}
InitNetworks([]*NetworkToSplit{{"172.80.0.0/16", 24}, {"172.90.0.0/16", 24}})

// Check for Random IPAddresses in PredefinedBroadNetworks ex: first , last and middle
assert.Len(t, PredefinedBroadNetworks, 512, "Failed to find PredefinedBroadNetworks")
assert.Equal(t, PredefinedBroadNetworks[0].String(), "172.80.0.0/24")
assert.Equal(t, PredefinedBroadNetworks[127].String(), "172.80.127.0/24")
assert.Equal(t, PredefinedBroadNetworks[255].String(), "172.80.255.0/24")
assert.Equal(t, PredefinedBroadNetworks[256].String(), "172.90.0.0/24")
assert.Equal(t, PredefinedBroadNetworks[383].String(), "172.90.127.0/24")
assert.Equal(t, PredefinedBroadNetworks[511].String(), "172.90.255.0/24")
}
6 changes: 3 additions & 3 deletions netutils/utils_test.go
Expand Up @@ -212,7 +212,7 @@ func TestUtilGenerateRandomMAC(t *testing.T) {

func TestNetworkRequest(t *testing.T) {
defer testutils.SetupTestOSContext(t)()
ipamutils.InitNetworks()
ipamutils.InitNetworks(nil)

nw, err := FindAvailableNetwork(ipamutils.PredefinedBroadNetworks)
if err != nil {
Expand Down Expand Up @@ -266,7 +266,7 @@ func TestNetworkRequest(t *testing.T) {

func TestElectInterfaceAddressMultipleAddresses(t *testing.T) {
defer testutils.SetupTestOSContext(t)()
ipamutils.InitNetworks()
ipamutils.InitNetworks(nil)

nws := []string{"172.101.202.254/16", "172.102.202.254/16"}
createInterface(t, "test", nws...)
Expand Down Expand Up @@ -303,7 +303,7 @@ func TestElectInterfaceAddressMultipleAddresses(t *testing.T) {

func TestElectInterfaceAddress(t *testing.T) {
defer testutils.SetupTestOSContext(t)()
ipamutils.InitNetworks()
ipamutils.InitNetworks(nil)

nws := "172.101.202.254/16"
createInterface(t, "test", nws)
Expand Down

0 comments on commit 0ae9b6f

Please sign in to comment.