diff --git a/daemon/networkdriver/ipallocator/allocator.go b/daemon/networkdriver/ipallocator/allocator.go index a1aaabbdfe..d5c644b23c 100644 --- a/daemon/networkdriver/ipallocator/allocator.go +++ b/daemon/networkdriver/ipallocator/allocator.go @@ -1,31 +1,38 @@ package ipallocator import ( - "encoding/binary" "errors" + "math/big" "net" "sync" "github.com/docker/docker/daemon/networkdriver" + "github.com/docker/docker/pkg/log" ) // allocatedMap is thread-unsafe set of allocated IP type allocatedMap struct { - p map[uint32]struct{} - last uint32 - begin uint32 - end uint32 + p map[string]struct{} + last *big.Int + begin *big.Int + end *big.Int } func newAllocatedMap(network *net.IPNet) *allocatedMap { firstIP, lastIP := networkdriver.NetworkRange(network) - begin := ipToInt(firstIP) + 2 - end := ipToInt(lastIP) - 1 + begin := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1)) + end := big.NewInt(0).Sub(ipToBigInt(lastIP), big.NewInt(1)) + + // if IPv4 network, then allocation range starts at begin + 1 because begin is bridge IP + if len(firstIP) == 4 { + begin = begin.Add(begin, big.NewInt(1)) + } + return &allocatedMap{ - p: make(map[uint32]struct{}), + p: make(map[string]struct{}), begin: begin, end: end, - last: begin - 1, // so first allocated will be begin + last: big.NewInt(0).Sub(begin, big.NewInt(1)), // so first allocated will be begin } } @@ -56,13 +63,16 @@ func RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error { } n := newAllocatedMap(network) beginIP, endIP := networkdriver.NetworkRange(subnet) - begin, end := ipToInt(beginIP)+1, ipToInt(endIP)-1 - if !(begin >= n.begin && end <= n.end && begin < end) { + begin := big.NewInt(0).Add(ipToBigInt(beginIP), big.NewInt(1)) + end := big.NewInt(0).Sub(ipToBigInt(endIP), big.NewInt(1)) + + // Check that subnet is within network + if !(begin.Cmp(n.begin) >= 0 && end.Cmp(n.end) <= 0 && begin.Cmp(end) == -1) { return ErrBadSubnet } - n.begin = begin - n.end = end - n.last = begin - 1 + n.begin.Set(begin) + n.end.Set(end) + n.last.Sub(begin, big.NewInt(1)) allocatedIPs[key] = n return nil } @@ -93,28 +103,25 @@ func ReleaseIP(network *net.IPNet, ip net.IP) error { lock.Lock() defer lock.Unlock() if allocated, exists := allocatedIPs[network.String()]; exists { - pos := ipToInt(ip) - delete(allocated.p, pos) + delete(allocated.p, ip.String()) } return nil } func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) { - pos := ipToInt(ip) - - // Verify that the IP address has not been already allocated. - if _, ok := allocated.p[pos]; ok { + if _, ok := allocated.p[ip.String()]; ok { return nil, ErrIPAlreadyAllocated } + pos := ipToBigInt(ip) // Verify that the IP address is within our network range. - if pos < allocated.begin || pos > allocated.end { + if pos.Cmp(allocated.begin) == -1 || pos.Cmp(allocated.end) == 1 { return nil, ErrIPOutOfRange } // Register the IP. - allocated.p[pos] = struct{}{} - allocated.last = pos + allocated.p[ip.String()] = struct{}{} + allocated.last.Set(pos) return ip, nil } @@ -122,29 +129,35 @@ func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) { // return an available ip if one is currently available. If not, // return the next available ip for the nextwork func (allocated *allocatedMap) getNextIP() (net.IP, error) { - for pos := allocated.last + 1; pos != allocated.last; pos++ { - if pos > allocated.end { - pos = allocated.begin + for pos := big.NewInt(0).Add(allocated.last, big.NewInt(1)); pos.Cmp(allocated.last) != 0; pos.Add(pos, big.NewInt(1)) { + if pos.Cmp(allocated.end) == 1 { + pos.Set(allocated.begin) } - if _, ok := allocated.p[pos]; ok { + if _, ok := allocated.p[bigIntToIP(pos).String()]; ok { continue } - allocated.p[pos] = struct{}{} - allocated.last = pos - return intToIP(pos), nil + allocated.p[bigIntToIP(pos).String()] = struct{}{} + allocated.last.Set(pos) + return bigIntToIP(pos), nil } return nil, ErrNoAvailableIPs } -// Converts a 4 bytes IP into a 32 bit integer -func ipToInt(ip net.IP) uint32 { - return binary.BigEndian.Uint32(ip.To4()) +// Converts a 4 bytes IP into a 128 bit integer +func ipToBigInt(ip net.IP) *big.Int { + x := big.NewInt(0) + if ip4 := ip.To4(); ip4 != nil { + return x.SetBytes(ip4) + } + if ip6 := ip.To16(); ip6 != nil { + return x.SetBytes(ip6) + } + + log.Errorf("ipToBigInt: Wrong IP length! %s", ip) + return nil } -// Converts 32 bit integer into a 4 bytes IP address -func intToIP(n uint32) net.IP { - b := make([]byte, 4) - binary.BigEndian.PutUint32(b, n) - ip := net.IP(b) - return ip +// Converts 128 bit integer into a 4 bytes IP address +func bigIntToIP(v *big.Int) net.IP { + return net.IP(v.Bytes()) } diff --git a/daemon/networkdriver/ipallocator/allocator_test.go b/daemon/networkdriver/ipallocator/allocator_test.go index 056c13b647..c4ce40cd0a 100644 --- a/daemon/networkdriver/ipallocator/allocator_test.go +++ b/daemon/networkdriver/ipallocator/allocator_test.go @@ -2,6 +2,7 @@ package ipallocator import ( "fmt" + "math/big" "net" "testing" ) @@ -10,6 +11,46 @@ func reset() { allocatedIPs = networkSet{} } +func TestConversion(t *testing.T) { + ip := net.ParseIP("127.0.0.1") + i := ipToBigInt(ip) + if i.Cmp(big.NewInt(0x7f000001)) != 0 { + t.Fatal("incorrect conversion") + } + conv := bigIntToIP(i) + if !ip.Equal(conv) { + t.Error(conv.String()) + } +} + +func TestConversionIPv6(t *testing.T) { + ip := net.ParseIP("2a00:1450::1") + ip2 := net.ParseIP("2a00:1450::2") + ip3 := net.ParseIP("2a00:1450::1:1") + i := ipToBigInt(ip) + val, success := big.NewInt(0).SetString("2a001450000000000000000000000001", 16) + if !success { + t.Fatal("Hex-String to BigInt conversion failed.") + } + if i.Cmp(val) != 0 { + t.Fatal("incorrent conversion") + } + + conv := bigIntToIP(i) + conv2 := bigIntToIP(big.NewInt(0).Add(i, big.NewInt(1))) + conv3 := bigIntToIP(big.NewInt(0).Add(i, big.NewInt(0x10000))) + + if !ip.Equal(conv) { + t.Error("2a00:1450::1 should be equal to " + conv.String()) + } + if !ip2.Equal(conv2) { + t.Error("2a00:1450::2 should be equal to " + conv2.String()) + } + if !ip3.Equal(conv3) { + t.Error("2a00:1450::1:1 should be equal to " + conv3.String()) + } +} + func TestRequestNewIps(t *testing.T) { defer reset() network := &net.IPNet{ @@ -19,6 +60,7 @@ func TestRequestNewIps(t *testing.T) { var ip net.IP var err error + for i := 2; i < 10; i++ { ip, err = RequestIP(network, nil) if err != nil { @@ -29,7 +71,39 @@ func TestRequestNewIps(t *testing.T) { t.Fatalf("Expected ip %s got %s", expected, ip.String()) } } - value := intToIP(ipToInt(ip) + 1).String() + value := bigIntToIP(big.NewInt(0).Add(ipToBigInt(ip), big.NewInt(1))).String() + if err := ReleaseIP(network, ip); err != nil { + t.Fatal(err) + } + ip, err = RequestIP(network, nil) + if err != nil { + t.Fatal(err) + } + if ip.String() != value { + t.Fatalf("Expected to receive the next ip %s got %s", value, ip.String()) + } +} + +func TestRequestNewIpV6(t *testing.T) { + defer reset() + network := &net.IPNet{ + IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask + } + + var ip net.IP + var err error + for i := 1; i < 10; i++ { + ip, err = RequestIP(network, nil) + if err != nil { + t.Fatal(err) + } + + if expected := fmt.Sprintf("2a00:1450::%d", i); ip.String() != expected { + t.Fatalf("Expected ip %s got %s", expected, ip.String()) + } + } + value := bigIntToIP(big.NewInt(0).Add(ipToBigInt(ip), big.NewInt(1))).String() if err := ReleaseIP(network, ip); err != nil { t.Fatal(err) } @@ -59,6 +133,23 @@ func TestReleaseIp(t *testing.T) { } } +func TestReleaseIpV6(t *testing.T) { + defer reset() + network := &net.IPNet{ + IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask + } + + ip, err := RequestIP(network, nil) + if err != nil { + t.Fatal(err) + } + + if err := ReleaseIP(network, ip); err != nil { + t.Fatal(err) + } +} + func TestGetReleasedIp(t *testing.T) { defer reset() network := &net.IPNet{ @@ -97,6 +188,44 @@ func TestGetReleasedIp(t *testing.T) { } } +func TestGetReleasedIpV6(t *testing.T) { + defer reset() + network := &net.IPNet{ + IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0}, + } + + ip, err := RequestIP(network, nil) + if err != nil { + t.Fatal(err) + } + + value := ip.String() + if err := ReleaseIP(network, ip); err != nil { + t.Fatal(err) + } + + for i := 0; i < 253; i++ { + _, err = RequestIP(network, nil) + if err != nil { + t.Fatal(err) + } + err = ReleaseIP(network, ip) + if err != nil { + t.Fatal(err) + } + } + + ip, err = RequestIP(network, nil) + if err != nil { + t.Fatal(err) + } + + if ip.String() != value { + t.Fatalf("Expected to receive same ip %s got %s", value, ip.String()) + } +} + func TestRequestSpecificIp(t *testing.T) { defer reset() network := &net.IPNet{ @@ -122,15 +251,28 @@ func TestRequestSpecificIp(t *testing.T) { } } -func TestConversion(t *testing.T) { - ip := net.ParseIP("127.0.0.1") - i := ipToInt(ip) - if i == 0 { - t.Fatal("converted to zero") +func TestRequestSpecificIpV6(t *testing.T) { + defer reset() + network := &net.IPNet{ + IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask } - conv := intToIP(i) - if !ip.Equal(conv) { - t.Error(conv.String()) + + ip := net.ParseIP("2a00:1450::5") + + // Request a "good" IP. + if _, err := RequestIP(network, ip); err != nil { + t.Fatal(err) + } + + // Request the same IP again. + if _, err := RequestIP(network, ip); err != ErrIPAlreadyAllocated { + t.Fatalf("Got the same IP twice: %#v", err) + } + + // Request an out of range IP. + if _, err := RequestIP(network, net.ParseIP("2a00:1500::1")); err != ErrIPOutOfRange { + t.Fatalf("Got an out of range IP: %#v", err) } } @@ -144,6 +286,7 @@ func TestIPAllocator(t *testing.T) { } gwIP, n, _ := net.ParseCIDR("127.0.0.1/29") + network := &net.IPNet{IP: gwIP, Mask: n.Mask} // Pool after initialisation (f = free, u = used) // 2(f) - 3(f) - 4(f) - 5(f) - 6(f) @@ -237,13 +380,13 @@ func TestAllocateFirstIP(t *testing.T) { } firstIP := network.IP.To4().Mask(network.Mask) - first := ipToInt(firstIP) + 1 + first := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1)) ip, err := RequestIP(network, nil) if err != nil { t.Fatal(err) } - allocated := ipToInt(ip) + allocated := ipToBigInt(ip) if allocated == first { t.Fatalf("allocated ip should not equal first ip: %d == %d", first, allocated) @@ -301,11 +444,24 @@ func TestAllocateDifferentSubnets(t *testing.T) { IP: []byte{127, 0, 0, 1}, Mask: []byte{255, 255, 255, 0}, } + network3 := &net.IPNet{ + IP: []byte{0x2a, 0x00, 0x14, 0x50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask + } + network4 := &net.IPNet{ + IP: []byte{0x2a, 0x00, 0x16, 0x32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + Mask: []byte{255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0}, // /64 netmask + } expectedIPs := []net.IP{ 0: net.IPv4(192, 168, 0, 2), 1: net.IPv4(192, 168, 0, 3), 2: net.IPv4(127, 0, 0, 2), 3: net.IPv4(127, 0, 0, 3), + 4: net.ParseIP("2a00:1450::1"), + 5: net.ParseIP("2a00:1450::2"), + 6: net.ParseIP("2a00:1450::3"), + 7: net.ParseIP("2a00:1632::1"), + 8: net.ParseIP("2a00:1632::2"), } ip11, err := RequestIP(network1, nil) @@ -324,11 +480,37 @@ func TestAllocateDifferentSubnets(t *testing.T) { if err != nil { t.Fatal(err) } + ip31, err := RequestIP(network3, nil) + if err != nil { + t.Fatal(err) + } + ip32, err := RequestIP(network3, nil) + if err != nil { + t.Fatal(err) + } + ip33, err := RequestIP(network3, nil) + if err != nil { + t.Fatal(err) + } + ip41, err := RequestIP(network4, nil) + if err != nil { + t.Fatal(err) + } + ip42, err := RequestIP(network4, nil) + if err != nil { + t.Fatal(err) + } assertIPEquals(t, expectedIPs[0], ip11) assertIPEquals(t, expectedIPs[1], ip12) assertIPEquals(t, expectedIPs[2], ip21) assertIPEquals(t, expectedIPs[3], ip22) + assertIPEquals(t, expectedIPs[4], ip31) + assertIPEquals(t, expectedIPs[5], ip32) + assertIPEquals(t, expectedIPs[6], ip33) + assertIPEquals(t, expectedIPs[7], ip41) + assertIPEquals(t, expectedIPs[8], ip42) } + func TestRegisterBadTwice(t *testing.T) { defer reset() network := &net.IPNet{ @@ -378,6 +560,7 @@ func TestAllocateFromRange(t *testing.T) { IP: []byte{192, 168, 0, 8}, Mask: []byte{255, 255, 255, 248}, } + if err := RegisterSubnet(network, subnet); err != nil { t.Fatal(err) } diff --git a/daemon/networkdriver/network_test.go b/daemon/networkdriver/network_test.go index d655cb30e4..1a6336b5de 100644 --- a/daemon/networkdriver/network_test.go +++ b/daemon/networkdriver/network_test.go @@ -122,9 +122,6 @@ func TestNetworkRange(t *testing.T) { 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") @@ -135,9 +132,6 @@ func TestNetworkRange(t *testing.T) { 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") @@ -158,9 +152,6 @@ func TestNetworkRange(t *testing.T) { 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") @@ -171,9 +162,6 @@ func TestNetworkRange(t *testing.T) { 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") @@ -184,7 +172,4 @@ func TestNetworkRange(t *testing.T) { if !last.Equal(net.ParseIP("10.1.2.63")) { t.Error(last.String()) } - if size := NetworkSize(network.Mask); size != 64 { - t.Error(size) - } } diff --git a/daemon/networkdriver/utils.go b/daemon/networkdriver/utils.go index 410d6010c4..07d95445a0 100644 --- a/daemon/networkdriver/utils.go +++ b/daemon/networkdriver/utils.go @@ -1,7 +1,6 @@ package networkdriver import ( - "encoding/binary" "errors" "fmt" "net" @@ -56,25 +55,21 @@ func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { // 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() - ) + var netIP net.IP + if network.IP.To4() != nil { + netIP = network.IP.To4() + } else if network.IP.To16() != nil { + netIP = network.IP.To16() + } else { + return nil, nil + } - for i := 0; i < len(lastIP); i++ { + lastIP := make([]byte, len(netIP), len(netIP)) + + for i := 0; i < len(netIP); 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 + return netIP.Mask(network.Mask), net.IP(lastIP) } // Return the IPv4 address of a network interface @@ -90,7 +85,7 @@ func GetIfaceAddr(name string) (net.Addr, error) { var addrs4 []net.Addr for _, addr := range addrs { ip := (addr.(*net.IPNet)).IP - if ip4 := ip.To4(); len(ip4) == net.IPv4len { + if ip4 := ip.To4(); ip4 != nil { addrs4 = append(addrs4, addr) } }