Merge pull request #8319 from MalteJ/ipv6-ipallocator

Implementing IPv6 functionality for ipallocator
This commit is contained in:
Jessie Frazelle 2014-10-29 09:20:52 -07:00
Родитель 7d538542ca f5f4d2d9cb
Коммит fd19e473fc
4 изменённых файлов: 259 добавлений и 83 удалений

Просмотреть файл

@ -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())
}

Просмотреть файл

@ -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)
}

Просмотреть файл

@ -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)
}
}

Просмотреть файл

@ -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)
}
}