浏览代码

Merge pull request #8319 from MalteJ/ipv6-ipallocator

Implementing IPv6 functionality for ipallocator
Jessie Frazelle 10 年之前
父节点
当前提交
fd19e473fc

+ 52 - 39
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())
 }

+ 194 - 11
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)
 	}

+ 0 - 15
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)
-	}
 }

+ 13 - 18
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()
-	)
-
-	for i := 0; i < len(lastIP); i++ {
-		lastIP[i] = netIP[i] | ^network.Mask[i]
+	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
 	}
-	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]
+	lastIP := make([]byte, len(netIP), len(netIP))
+
+	for i := 0; i < len(netIP); i++ {
+		lastIP[i] = netIP[i] | ^network.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)
 		}
 	}