From ec73c232318a3dae803a27a6922f0af7117eec6e Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 23 Jan 2014 14:39:10 -0800 Subject: [PATCH] Refactor and fix register interface when bridge does not exist Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- network.go | 37 +-- networkdriver/ipallocator/allocator.go | 204 +++------------- networkdriver/ipallocator/allocator_test.go | 256 -------------------- networkdriver/network.go | 75 ++++++ networkdriver/network_test.go | 183 ++++++++++++++ 5 files changed, 299 insertions(+), 456 deletions(-) create mode 100644 networkdriver/network_test.go diff --git a/network.go b/network.go index 7414b348a2..3ec1f6fb73 100644 --- a/network.go +++ b/network.go @@ -2,6 +2,7 @@ package docker import ( "fmt" + "github.com/dotcloud/docker/networkdriver" "github.com/dotcloud/docker/networkdriver/ipallocator" "github.com/dotcloud/docker/pkg/iptables" "github.com/dotcloud/docker/pkg/netlink" @@ -60,13 +61,10 @@ func CreateBridgeIface(config *DaemonConfig) error { var ifaceAddr string if len(config.BridgeIp) != 0 { - _, dockerNetwork, err := net.ParseCIDR(config.BridgeIp) + _, _, err := net.ParseCIDR(config.BridgeIp) if err != nil { return err } - if err := ipallocator.RegisterNetwork(dockerNetwork, nameservers); err != nil { - return err - } ifaceAddr = config.BridgeIp } else { for _, addr := range addrs { @@ -74,12 +72,13 @@ func CreateBridgeIface(config *DaemonConfig) error { if err != nil { return err } - - if err := ipallocator.RegisterNetwork(dockerNetwork, nameservers); err == nil { - ifaceAddr = addr - break - } else { - utils.Debugf("%s: %s", addr, err) + if err := networkdriver.CheckNameserverOverlaps(nameservers, dockerNetwork); err == nil { + if err := networkdriver.CheckRouteOverlaps(dockerNetwork); err == nil { + ifaceAddr = addr + break + } else { + utils.Debugf("%s %s", addr, err) + } } } } @@ -491,21 +490,6 @@ func (manager *NetworkManager) Allocate() (*NetworkInterface, error) { return nil, err } - // TODO: @crosbymichael why are we doing this ? - /* - // avoid duplicate IP - ipNum := ipToInt(ip) - firstIP := manager.ipAllocator.network.IP.To4().Mask(manager.ipAllocator.network.Mask) - firstIPNum := ipToInt(firstIP) + 1 - - if firstIPNum == ipNum { - ip, err = manager.ipAllocator.Acquire() - if err != nil { - return nil, err - } - } - */ - iface := &NetworkInterface{ IPNet: net.IPNet{IP: *ip, Mask: manager.bridgeNetwork.Mask}, Gateway: manager.bridgeNetwork.IP, @@ -551,9 +535,6 @@ func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) { network = addr.(*net.IPNet) } else { network = addr.(*net.IPNet) - if err := ipallocator.RegisterExistingNetwork(network); err != nil { - return nil, err - } } // Configure iptables for link support diff --git a/networkdriver/ipallocator/allocator.go b/networkdriver/ipallocator/allocator.go index cca8cdb05a..09319d1332 100644 --- a/networkdriver/ipallocator/allocator.go +++ b/networkdriver/ipallocator/allocator.go @@ -3,93 +3,43 @@ package ipallocator import ( "encoding/binary" "errors" - "github.com/dotcloud/docker/pkg/netlink" + "github.com/dotcloud/docker/networkdriver" "net" "sync" ) -type networkSet map[iPNet]*iPSet - -type iPNet struct { - IP string - Mask string -} +type networkSet map[string]*iPSet var ( - ErrNetworkAlreadyAllocated = errors.New("requested network overlaps with existing network") - ErrNetworkAlreadyRegisterd = errors.New("requested network is already registered") - ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver") - ErrNoAvailableIPs = errors.New("no available ip addresses on network") - ErrIPAlreadyAllocated = errors.New("ip already allocated") - ErrNetworkNotRegistered = errors.New("network not registered") + ErrNoAvailableIPs = errors.New("no available ip addresses on network") + ErrIPAlreadyAllocated = errors.New("ip already allocated") +) +var ( lock = sync.Mutex{} allocatedIPs = networkSet{} availableIPS = networkSet{} ) -// RegisterNetwork registers a new network with the allocator -// and validates that it contains a valid ip that does not overlap -// with existing routes and nameservers -func RegisterNetwork(network *net.IPNet, nameservers []string) error { - lock.Lock() - defer lock.Unlock() - - if err := checkExistingNetworkOverlaps(network); err != nil { - return err - } - - routes, err := netlink.NetworkGetRoutes() - if err != nil { - return err - } - - if err := checkRouteOverlaps(routes, network); err != nil { - return err - } - - if err := checkNameserverOverlaps(nameservers, network); err != nil { - return err - } - return RegisterExistingNetwork(network) -} - -// RegisterExistingNetwork registers an exising network created -// for use with the allocator but does not perform any validation -func RegisterExistingNetwork(network *net.IPNet) error { - n := newIPNet(network) - - if _, exists := allocatedIPs[n]; !exists { - allocatedIPs[n] = &iPSet{} - } - if _, exists := availableIPS[n]; !exists { - availableIPS[n] = &iPSet{} - } - - return nil -} - // RequestIP requests an available ip from the given network. It // will return the next available ip if the ip provided is nil. If the // ip provided is not nil it will validate that the provided ip is available // for use or return an error -func RequestIP(network *net.IPNet, ip *net.IP) (*net.IP, error) { +func RequestIP(address *net.IPNet, ip *net.IP) (*net.IP, error) { lock.Lock() defer lock.Unlock() - if !networkExists(network) { - return nil, ErrNetworkNotRegistered - } + checkAddress(address) if ip == nil { - next, err := getNextIp(network) + next, err := getNextIp(address) if err != nil { return nil, err } return next, nil } - if err := registerIP(network, ip); err != nil { + if err := registerIP(address, ip); err != nil { return nil, err } return ip, nil @@ -97,19 +47,16 @@ func RequestIP(network *net.IPNet, ip *net.IP) (*net.IP, error) { // ReleaseIP adds the provided ip back into the pool of // available ips to be returned for use. -func ReleaseIP(network *net.IPNet, ip *net.IP) error { +func ReleaseIP(address *net.IPNet, ip *net.IP) error { lock.Lock() defer lock.Unlock() - if !networkExists(network) { - return ErrNetworkNotRegistered - } + checkAddress(address) var ( - n = newIPNet(network) - existing = allocatedIPs[n] - available = availableIPS[n] - pos = getPosition(network, ip) + existing = allocatedIPs[address.String()] + available = availableIPS[address.String()] + pos = getPosition(address, ip) ) existing.Remove(int(pos)) @@ -120,9 +67,9 @@ func ReleaseIP(network *net.IPNet, ip *net.IP) error { // convert the ip into the position in the subnet. Only // position are saved in the set -func getPosition(network *net.IPNet, ip *net.IP) int32 { +func getPosition(address *net.IPNet, ip *net.IP) int32 { var ( - first, _ = networkRange(network) + first, _ = networkdriver.NetworkRange(address) base = ipToInt(&first) i = ipToInt(ip) ) @@ -131,15 +78,14 @@ func getPosition(network *net.IPNet, ip *net.IP) int32 { // return an available ip if one is currently available. If not, // return the next available ip for the nextwork -func getNextIp(network *net.IPNet) (*net.IP, error) { +func getNextIp(address *net.IPNet) (*net.IP, error) { var ( - n = newIPNet(network) - ownIP = ipToInt(&network.IP) - available = availableIPS[n] - allocated = allocatedIPs[n] - first, _ = networkRange(network) + ownIP = ipToInt(&address.IP) + available = availableIPS[address.String()] + allocated = allocatedIPs[address.String()] + first, _ = networkdriver.NetworkRange(address) base = ipToInt(&first) - size = int(networkSize(network.Mask)) + size = int(networkdriver.NetworkSize(address.Mask)) max = int32(size - 2) // size -1 for the broadcast address, -1 for the gateway address pos = int32(available.Pop()) ) @@ -170,12 +116,11 @@ func getNextIp(network *net.IPNet) (*net.IP, error) { return nil, ErrNoAvailableIPs } -func registerIP(network *net.IPNet, ip *net.IP) error { +func registerIP(address *net.IPNet, ip *net.IP) error { var ( - n = newIPNet(network) - existing = allocatedIPs[n] - available = availableIPS[n] - pos = getPosition(network, ip) + existing = allocatedIPs[address.String()] + available = availableIPS[address.String()] + pos = getPosition(address, ip) ) if existing.Exists(int(pos)) { @@ -186,68 +131,6 @@ func registerIP(network *net.IPNet, ip *net.IP) error { return nil } -func checkRouteOverlaps(networks []netlink.Route, toCheck *net.IPNet) error { - for _, network := range networks { - if network.IPNet != nil && networkOverlaps(toCheck, network.IPNet) { - return ErrNetworkAlreadyAllocated - } - } - return nil -} - -// Detects overlap between one IPNet and another -func networkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { - if firstIP, _ := networkRange(netX); netY.Contains(firstIP) { - return true - } - if firstIP, _ := networkRange(netY); netX.Contains(firstIP) { - return true - } - return false -} - -func checkExistingNetworkOverlaps(network *net.IPNet) error { - for existing := range allocatedIPs { - if newIPNet(network) == existing { - return ErrNetworkAlreadyRegisterd - } - - ex := newNetIPNet(existing) - if networkOverlaps(network, ex) { - return ErrNetworkAlreadyAllocated - } - } - return nil -} - -// Calculates the first and last IP addresses in an IPNet -func networkRange(network *net.IPNet) (net.IP, net.IP) { - var ( - netIP = network.IP.To4() - firstIP = netIP.Mask(network.Mask) - lastIP = net.IPv4(0, 0, 0, 0).To4() - ) - - for i := 0; i < len(lastIP); i++ { - lastIP[i] = netIP[i] | ^network.Mask[i] - } - return firstIP, lastIP -} - -func newIPNet(network *net.IPNet) iPNet { - return iPNet{ - IP: string(network.IP), - Mask: string(network.Mask), - } -} - -func newNetIPNet(network iPNet) *net.IPNet { - return &net.IPNet{ - IP: []byte(network.IP), - Mask: []byte(network.Mask), - } -} - // Converts a 4 bytes IP into a 32 bit integer func ipToInt(ip *net.IP) int32 { return int32(binary.BigEndian.Uint32(ip.To4())) @@ -261,33 +144,10 @@ func intToIP(n int32) *net.IP { return &ip } -// Given a netmask, calculates the number of available hosts -func networkSize(mask net.IPMask) int32 { - m := net.IPv4Mask(0, 0, 0, 0) - for i := 0; i < net.IPv4len; i++ { - m[i] = ^mask[i] +func checkAddress(address *net.IPNet) { + key := address.String() + if _, exists := allocatedIPs[key]; !exists { + allocatedIPs[key] = &iPSet{} + availableIPS[key] = &iPSet{} } - - return int32(binary.BigEndian.Uint32(m)) + 1 -} - -func checkNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error { - if len(nameservers) > 0 { - for _, ns := range nameservers { - _, nsNetwork, err := net.ParseCIDR(ns) - if err != nil { - return err - } - if networkOverlaps(toCheck, nsNetwork) { - return ErrNetworkOverlapsWithNameservers - } - } - } - return nil -} - -func networkExists(network *net.IPNet) bool { - n := newIPNet(network) - _, exists := allocatedIPs[n] - return exists } diff --git a/networkdriver/ipallocator/allocator_test.go b/networkdriver/ipallocator/allocator_test.go index f574dfda70..871f143521 100644 --- a/networkdriver/ipallocator/allocator_test.go +++ b/networkdriver/ipallocator/allocator_test.go @@ -2,7 +2,6 @@ package ipallocator import ( "fmt" - "github.com/dotcloud/docker/pkg/netlink" "net" "testing" ) @@ -12,64 +11,6 @@ func reset() { availableIPS = networkSet{} } -func TestRegisterNetwork(t *testing.T) { - defer reset() - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - - if err := RegisterNetwork(network, nil); err != nil { - t.Fatal(err) - } - - n := newIPNet(network) - if _, exists := allocatedIPs[n]; !exists { - t.Fatal("IPNet should exist in allocated IPs") - } - - if _, exists := availableIPS[n]; !exists { - t.Fatal("IPNet should exist in available IPs") - } -} - -func TestRegisterTwoNetworks(t *testing.T) { - defer reset() - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - - if err := RegisterNetwork(network, nil); err != nil { - t.Fatal(err) - } - - network2 := &net.IPNet{ - IP: []byte{10, 1, 42, 1}, - Mask: []byte{255, 255, 255, 0}, - } - - if err := RegisterNetwork(network2, nil); err != nil { - t.Fatal(err) - } -} - -func TestRegisterNetworkThatExists(t *testing.T) { - defer reset() - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - - if err := RegisterNetwork(network, nil); err != nil { - t.Fatal(err) - } - - if err := RegisterNetwork(network, nil); err != ErrNetworkAlreadyRegisterd { - t.Fatalf("Expected error of %s got %s", ErrNetworkAlreadyRegisterd, err) - } -} - func TestRequestNewIps(t *testing.T) { defer reset() network := &net.IPNet{ @@ -77,10 +18,6 @@ func TestRequestNewIps(t *testing.T) { Mask: []byte{255, 255, 255, 0}, } - if err := RegisterNetwork(network, nil); err != nil { - t.Fatal(err) - } - for i := 2; i < 10; i++ { ip, err := RequestIP(network, nil) if err != nil { @@ -100,10 +37,6 @@ func TestReleaseIp(t *testing.T) { Mask: []byte{255, 255, 255, 0}, } - if err := RegisterNetwork(network, nil); err != nil { - t.Fatal(err) - } - ip, err := RequestIP(network, nil) if err != nil { t.Fatal(err) @@ -121,10 +54,6 @@ func TestGetReleasedIp(t *testing.T) { Mask: []byte{255, 255, 255, 0}, } - if err := RegisterNetwork(network, nil); err != nil { - t.Fatal(err) - } - ip, err := RequestIP(network, nil) if err != nil { t.Fatal(err) @@ -152,10 +81,6 @@ func TestRequesetSpecificIp(t *testing.T) { Mask: []byte{255, 255, 255, 0}, } - if err := RegisterNetwork(network, nil); err != nil { - t.Fatal(err) - } - ip := net.ParseIP("192.168.1.5") if _, err := RequestIP(network, &ip); err != nil { @@ -163,113 +88,6 @@ func TestRequesetSpecificIp(t *testing.T) { } } -func TestNonOverlapingNameservers(t *testing.T) { - defer reset() - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - nameservers := []string{ - "127.0.0.1/32", - } - - if err := RegisterNetwork(network, nameservers); err != nil { - t.Fatal(err) - } -} - -func TestOverlapingNameservers(t *testing.T) { - defer reset() - network := &net.IPNet{ - IP: []byte{192, 168, 0, 1}, - Mask: []byte{255, 255, 255, 0}, - } - nameservers := []string{ - "192.168.0.1/32", - } - - if err := RegisterNetwork(network, nameservers); err != ErrNetworkOverlapsWithNameservers { - t.Fatalf("Expectecd error of %s got %s", ErrNetworkOverlapsWithNameservers, err) - } -} - -func TestNetworkRange(t *testing.T) { - // Simple class C test - _, network, _ := net.ParseCIDR("192.168.0.1/24") - first, last := networkRange(network) - if !first.Equal(net.ParseIP("192.168.0.0")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("192.168.0.255")) { - t.Error(last.String()) - } - if size := networkSize(network.Mask); size != 256 { - t.Error(size) - } - - // Class A test - _, network, _ = net.ParseCIDR("10.0.0.1/8") - first, last = networkRange(network) - if !first.Equal(net.ParseIP("10.0.0.0")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("10.255.255.255")) { - t.Error(last.String()) - } - if size := networkSize(network.Mask); size != 16777216 { - t.Error(size) - } - - // Class A, random IP address - _, network, _ = net.ParseCIDR("10.1.2.3/8") - first, last = networkRange(network) - if !first.Equal(net.ParseIP("10.0.0.0")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("10.255.255.255")) { - t.Error(last.String()) - } - - // 32bit mask - _, network, _ = net.ParseCIDR("10.1.2.3/32") - first, last = networkRange(network) - if !first.Equal(net.ParseIP("10.1.2.3")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("10.1.2.3")) { - t.Error(last.String()) - } - if size := networkSize(network.Mask); size != 1 { - t.Error(size) - } - - // 31bit mask - _, network, _ = net.ParseCIDR("10.1.2.3/31") - first, last = networkRange(network) - if !first.Equal(net.ParseIP("10.1.2.2")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("10.1.2.3")) { - t.Error(last.String()) - } - if size := networkSize(network.Mask); size != 2 { - t.Error(size) - } - - // 26bit mask - _, network, _ = net.ParseCIDR("10.1.2.3/26") - first, last = networkRange(network) - if !first.Equal(net.ParseIP("10.1.2.0")) { - t.Error(first.String()) - } - if !last.Equal(net.ParseIP("10.1.2.63")) { - t.Error(last.String()) - } - if size := networkSize(network.Mask); size != 64 { - t.Error(size) - } -} - func TestConversion(t *testing.T) { ip := net.ParseIP("127.0.0.1") i := ipToInt(&ip) @@ -293,9 +111,6 @@ func TestIPAllocator(t *testing.T) { gwIP, n, _ := net.ParseCIDR("127.0.0.1/29") network := &net.IPNet{IP: gwIP, Mask: n.Mask} - if err := RegisterNetwork(network, nil); err != nil { - t.Fatal(err) - } // Pool after initialisation (f = free, u = used) // 2(f) - 3(f) - 4(f) - 5(f) - 6(f) // ↑ @@ -403,74 +218,3 @@ func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) { t.Fatalf("Expected IP %s, got %s", ip1, ip2) } } - -func AssertOverlap(CIDRx string, CIDRy string, t *testing.T) { - _, netX, _ := net.ParseCIDR(CIDRx) - _, netY, _ := net.ParseCIDR(CIDRy) - if !networkOverlaps(netX, netY) { - t.Errorf("%v and %v should overlap", netX, netY) - } -} - -func AssertNoOverlap(CIDRx string, CIDRy string, t *testing.T) { - _, netX, _ := net.ParseCIDR(CIDRx) - _, netY, _ := net.ParseCIDR(CIDRy) - if networkOverlaps(netX, netY) { - t.Errorf("%v and %v should not overlap", netX, netY) - } -} - -func TestNetworkOverlaps(t *testing.T) { - //netY starts at same IP and ends within netX - AssertOverlap("172.16.0.1/24", "172.16.0.1/25", t) - //netY starts within netX and ends at same IP - AssertOverlap("172.16.0.1/24", "172.16.0.128/25", t) - //netY starts and ends within netX - AssertOverlap("172.16.0.1/24", "172.16.0.64/25", t) - //netY starts at same IP and ends outside of netX - AssertOverlap("172.16.0.1/24", "172.16.0.1/23", t) - //netY starts before and ends at same IP of netX - AssertOverlap("172.16.1.1/24", "172.16.0.1/23", t) - //netY starts before and ends outside of netX - AssertOverlap("172.16.1.1/24", "172.16.0.1/23", t) - //netY starts and ends before netX - AssertNoOverlap("172.16.1.1/25", "172.16.0.1/24", t) - //netX starts and ends before netY - AssertNoOverlap("172.16.1.1/25", "172.16.2.1/24", t) -} - -func TestCheckRouteOverlaps(t *testing.T) { - routesData := []string{"10.0.2.0/32", "10.0.3.0/24", "10.0.42.0/24", "172.16.42.0/24", "192.168.142.0/24"} - - routes := []netlink.Route{} - for _, addr := range routesData { - _, netX, _ := net.ParseCIDR(addr) - routes = append(routes, netlink.Route{IPNet: netX}) - } - - _, netX, _ := net.ParseCIDR("172.16.0.1/24") - if err := checkRouteOverlaps(routes, netX); err != nil { - t.Fatal(err) - } - - _, netX, _ = net.ParseCIDR("10.0.2.0/24") - if err := checkRouteOverlaps(routes, netX); err == nil { - t.Fatalf("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't") - } -} - -func TestCheckNameserverOverlaps(t *testing.T) { - nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"} - - _, netX, _ := net.ParseCIDR("10.0.2.3/32") - - if err := checkNameserverOverlaps(nameservers, netX); err == nil { - t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX) - } - - _, netX, _ = net.ParseCIDR("192.168.102.2/32") - - if err := checkNameserverOverlaps(nameservers, netX); err != nil { - t.Fatalf("%s should not overlap %v but it does", netX, nameservers) - } -} diff --git a/networkdriver/network.go b/networkdriver/network.go index e74734d55f..cce91d7a55 100644 --- a/networkdriver/network.go +++ b/networkdriver/network.go @@ -1 +1,76 @@ package networkdriver + +import ( + "encoding/binary" + "errors" + "github.com/dotcloud/docker/pkg/netlink" + "net" +) + +var ( + ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver") + ErrNetworkOverlaps = errors.New("requested network overlaps with existing network") +) + +func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error { + if len(nameservers) > 0 { + for _, ns := range nameservers { + _, nsNetwork, err := net.ParseCIDR(ns) + if err != nil { + return err + } + if NetworkOverlaps(toCheck, nsNetwork) { + return ErrNetworkOverlapsWithNameservers + } + } + } + return nil +} + +func CheckRouteOverlaps(toCheck *net.IPNet) error { + networks, err := netlink.NetworkGetRoutes() + if err != nil { + return err + } + + for _, network := range networks { + if network.IPNet != nil && NetworkOverlaps(toCheck, network.IPNet) { + return ErrNetworkOverlaps + } + } + return nil +} + +// Detects overlap between one IPNet and another +func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { + if firstIP, _ := NetworkRange(netX); netY.Contains(firstIP) { + return true + } + if firstIP, _ := NetworkRange(netY); netX.Contains(firstIP) { + return true + } + return false +} + +// Calculates the first and last IP addresses in an IPNet +func NetworkRange(network *net.IPNet) (net.IP, net.IP) { + var ( + netIP = network.IP.To4() + firstIP = netIP.Mask(network.Mask) + lastIP = net.IPv4(0, 0, 0, 0).To4() + ) + + for i := 0; i < len(lastIP); i++ { + lastIP[i] = netIP[i] | ^network.Mask[i] + } + return firstIP, lastIP +} + +// Given a netmask, calculates the number of available hosts +func NetworkSize(mask net.IPMask) int32 { + m := net.IPv4Mask(0, 0, 0, 0) + for i := 0; i < net.IPv4len; i++ { + m[i] = ^mask[i] + } + return int32(binary.BigEndian.Uint32(m)) + 1 +} diff --git a/networkdriver/network_test.go b/networkdriver/network_test.go new file mode 100644 index 0000000000..8905dad053 --- /dev/null +++ b/networkdriver/network_test.go @@ -0,0 +1,183 @@ +package networkdriver + +import ( + "github.com/dotcloud/docker/pkg/netlink" + "net" + "testing" +) + +func TestNonOverlapingNameservers(t *testing.T) { + network := &net.IPNet{ + IP: []byte{192, 168, 0, 1}, + Mask: []byte{255, 255, 255, 0}, + } + nameservers := []string{ + "127.0.0.1/32", + } + + if err := CheckNameserverOverlaps(nameservers, network); err != nil { + t.Fatal(err) + } +} + +func TestOverlapingNameservers(t *testing.T) { + network := &net.IPNet{ + IP: []byte{192, 168, 0, 1}, + Mask: []byte{255, 255, 255, 0}, + } + nameservers := []string{ + "192.168.0.1/32", + } + + if err := CheckNameserverOverlaps(nameservers, network); err == nil { + t.Fatalf("Expected error %s got %s", ErrNetworkOverlapsWithNameservers, err) + } +} + +func TestCheckRouteOverlaps(t *testing.T) { + routesData := []string{"10.0.2.0/32", "10.0.3.0/24", "10.0.42.0/24", "172.16.42.0/24", "192.168.142.0/24"} + + routes := []netlink.Route{} + for _, addr := range routesData { + _, netX, _ := net.ParseCIDR(addr) + routes = append(routes, netlink.Route{IPNet: netX}) + } + + _, netX, _ := net.ParseCIDR("172.16.0.1/24") + if err := CheckRouteOverlaps(netX); err != nil { + t.Fatal(err) + } + + _, netX, _ = net.ParseCIDR("10.0.2.0/24") + if err := CheckRouteOverlaps(netX); err == nil { + t.Fatalf("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't") + } +} + +func TestCheckNameserverOverlaps(t *testing.T) { + nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"} + + _, netX, _ := net.ParseCIDR("10.0.2.3/32") + + if err := CheckNameserverOverlaps(nameservers, netX); err == nil { + t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX) + } + + _, netX, _ = net.ParseCIDR("192.168.102.2/32") + + if err := CheckNameserverOverlaps(nameservers, netX); err != nil { + t.Fatalf("%s should not overlap %v but it does", netX, nameservers) + } +} + +func AssertOverlap(CIDRx string, CIDRy string, t *testing.T) { + _, netX, _ := net.ParseCIDR(CIDRx) + _, netY, _ := net.ParseCIDR(CIDRy) + if !NetworkOverlaps(netX, netY) { + t.Errorf("%v and %v should overlap", netX, netY) + } +} + +func AssertNoOverlap(CIDRx string, CIDRy string, t *testing.T) { + _, netX, _ := net.ParseCIDR(CIDRx) + _, netY, _ := net.ParseCIDR(CIDRy) + if NetworkOverlaps(netX, netY) { + t.Errorf("%v and %v should not overlap", netX, netY) + } +} + +func TestNetworkOverlaps(t *testing.T) { + //netY starts at same IP and ends within netX + AssertOverlap("172.16.0.1/24", "172.16.0.1/25", t) + //netY starts within netX and ends at same IP + AssertOverlap("172.16.0.1/24", "172.16.0.128/25", t) + //netY starts and ends within netX + AssertOverlap("172.16.0.1/24", "172.16.0.64/25", t) + //netY starts at same IP and ends outside of netX + AssertOverlap("172.16.0.1/24", "172.16.0.1/23", t) + //netY starts before and ends at same IP of netX + AssertOverlap("172.16.1.1/24", "172.16.0.1/23", t) + //netY starts before and ends outside of netX + AssertOverlap("172.16.1.1/24", "172.16.0.1/23", t) + //netY starts and ends before netX + AssertNoOverlap("172.16.1.1/25", "172.16.0.1/24", t) + //netX starts and ends before netY + AssertNoOverlap("172.16.1.1/25", "172.16.2.1/24", t) +} + +func TestNetworkRange(t *testing.T) { + // Simple class C test + _, network, _ := net.ParseCIDR("192.168.0.1/24") + first, last := NetworkRange(network) + if !first.Equal(net.ParseIP("192.168.0.0")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("192.168.0.255")) { + t.Error(last.String()) + } + if size := NetworkSize(network.Mask); size != 256 { + t.Error(size) + } + + // Class A test + _, network, _ = net.ParseCIDR("10.0.0.1/8") + first, last = NetworkRange(network) + if !first.Equal(net.ParseIP("10.0.0.0")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("10.255.255.255")) { + t.Error(last.String()) + } + if size := NetworkSize(network.Mask); size != 16777216 { + t.Error(size) + } + + // Class A, random IP address + _, network, _ = net.ParseCIDR("10.1.2.3/8") + first, last = NetworkRange(network) + if !first.Equal(net.ParseIP("10.0.0.0")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("10.255.255.255")) { + t.Error(last.String()) + } + + // 32bit mask + _, network, _ = net.ParseCIDR("10.1.2.3/32") + first, last = NetworkRange(network) + if !first.Equal(net.ParseIP("10.1.2.3")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("10.1.2.3")) { + t.Error(last.String()) + } + if size := NetworkSize(network.Mask); size != 1 { + t.Error(size) + } + + // 31bit mask + _, network, _ = net.ParseCIDR("10.1.2.3/31") + first, last = NetworkRange(network) + if !first.Equal(net.ParseIP("10.1.2.2")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("10.1.2.3")) { + t.Error(last.String()) + } + if size := NetworkSize(network.Mask); size != 2 { + t.Error(size) + } + + // 26bit mask + _, network, _ = net.ParseCIDR("10.1.2.3/26") + first, last = NetworkRange(network) + if !first.Equal(net.ParseIP("10.1.2.0")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("10.1.2.63")) { + t.Error(last.String()) + } + if size := NetworkSize(network.Mask); size != 64 { + t.Error(size) + } +}