package ipallocator import ( "fmt" "math/big" "net" "testing" ) 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) { a := New() network := &net.IPNet{ IP: []byte{192, 168, 0, 1}, Mask: []byte{255, 255, 255, 0}, } var ip net.IP var err error for i := 1; i < 10; i++ { ip, err = a.RequestIP(network, nil) if err != nil { t.Fatal(err) } if expected := fmt.Sprintf("192.168.0.%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 := a.ReleaseIP(network, ip); err != nil { t.Fatal(err) } ip, err = a.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) { a := New() 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 = a.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 := a.ReleaseIP(network, ip); err != nil { t.Fatal(err) } ip, err = a.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 TestReleaseIp(t *testing.T) { a := New() network := &net.IPNet{ IP: []byte{192, 168, 0, 1}, Mask: []byte{255, 255, 255, 0}, } ip, err := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } if err := a.ReleaseIP(network, ip); err != nil { t.Fatal(err) } } func TestReleaseIpV6(t *testing.T) { a := New() 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 := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } if err := a.ReleaseIP(network, ip); err != nil { t.Fatal(err) } } func TestGetReleasedIp(t *testing.T) { a := New() network := &net.IPNet{ IP: []byte{192, 168, 0, 1}, Mask: []byte{255, 255, 255, 0}, } ip, err := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } value := ip.String() if err := a.ReleaseIP(network, ip); err != nil { t.Fatal(err) } for i := 0; i < 253; i++ { _, err = a.RequestIP(network, nil) if err != nil { t.Fatal(err) } err = a.ReleaseIP(network, ip) if err != nil { t.Fatal(err) } } ip, err = a.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 TestGetReleasedIpV6(t *testing.T) { a := New() 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 := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } value := ip.String() if err := a.ReleaseIP(network, ip); err != nil { t.Fatal(err) } for i := 0; i < 253; i++ { _, err = a.RequestIP(network, nil) if err != nil { t.Fatal(err) } err = a.ReleaseIP(network, ip) if err != nil { t.Fatal(err) } } ip, err = a.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) { a := New() network := &net.IPNet{ IP: []byte{192, 168, 0, 1}, Mask: []byte{255, 255, 255, 224}, } ip := net.ParseIP("192.168.0.5") // Request a "good" IP. if _, err := a.RequestIP(network, ip); err != nil { t.Fatal(err) } // Request the same IP again. if _, err := a.RequestIP(network, ip); err != ErrIPAlreadyAllocated { t.Fatalf("Got the same IP twice: %#v", err) } // Request an out of range IP. if _, err := a.RequestIP(network, net.ParseIP("192.168.0.42")); err != ErrIPOutOfRange { t.Fatalf("Got an out of range IP: %#v", err) } } func TestRequestSpecificIpV6(t *testing.T) { a := New() 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 := net.ParseIP("2a00:1450::5") // Request a "good" IP. if _, err := a.RequestIP(network, ip); err != nil { t.Fatal(err) } // Request the same IP again. if _, err := a.RequestIP(network, ip); err != ErrIPAlreadyAllocated { t.Fatalf("Got the same IP twice: %#v", err) } // Request an out of range IP. if _, err := a.RequestIP(network, net.ParseIP("2a00:1500::1")); err != ErrIPOutOfRange { t.Fatalf("Got an out of range IP: %#v", err) } } func TestIPAllocator(t *testing.T) { a := New() expectedIPs := []net.IP{ 0: net.IPv4(127, 0, 0, 1), 1: net.IPv4(127, 0, 0, 2), 2: net.IPv4(127, 0, 0, 3), 3: net.IPv4(127, 0, 0, 4), 4: net.IPv4(127, 0, 0, 5), 5: net.IPv4(127, 0, 0, 6), } gwIP, n, _ := net.ParseCIDR("127.0.0.1/29") network := &net.IPNet{IP: gwIP, Mask: n.Mask} // Pool after initialisation (f = free, u = used) // 1(f) - 2(f) - 3(f) - 4(f) - 5(f) - 6(f) // ↑ // Check that we get 6 IPs, from 127.0.0.1–127.0.0.6, in that // order. for i := 0; i < 6; i++ { ip, err := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } assertIPEquals(t, expectedIPs[i], ip) } // Before loop begin // 1(f) - 2(f) - 3(f) - 4(f) - 5(f) - 6(f) // ↑ // After i = 0 // 1(u) - 2(f) - 3(f) - 4(f) - 5(f) - 6(f) // ↑ // After i = 1 // 1(u) - 2(u) - 3(f) - 4(f) - 5(f) - 6(f) // ↑ // After i = 2 // 1(u) - 2(u) - 3(u) - 4(f) - 5(f) - 6(f) // ↑ // After i = 3 // 1(u) - 2(u) - 3(u) - 4(u) - 5(f) - 6(f) // ↑ // After i = 4 // 1(u) - 2(u) - 3(u) - 4(u) - 5(u) - 6(f) // ↑ // After i = 5 // 1(u) - 2(u) - 3(u) - 4(u) - 5(u) - 6(u) // ↑ // Check that there are no more IPs ip, err := a.RequestIP(network, nil) if err == nil { t.Fatalf("There shouldn't be any IP addresses at this point, got %s\n", ip) } // Release some IPs in non-sequential order if err := a.ReleaseIP(network, expectedIPs[3]); err != nil { t.Fatal(err) } // 1(u) - 2(u) - 3(u) - 4(f) - 5(u) - 6(u) // ↑ if err := a.ReleaseIP(network, expectedIPs[2]); err != nil { t.Fatal(err) } // 1(u) - 2(u) - 3(f) - 4(f) - 5(u) - 6(u) // ↑ if err := a.ReleaseIP(network, expectedIPs[4]); err != nil { t.Fatal(err) } // 1(u) - 2(u) - 3(f) - 4(f) - 5(f) - 6(u) // ↑ // Make sure that IPs are reused in sequential order, starting // with the first released IP newIPs := make([]net.IP, 3) for i := 0; i < 3; i++ { ip, err := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } newIPs[i] = ip } assertIPEquals(t, expectedIPs[2], newIPs[0]) assertIPEquals(t, expectedIPs[3], newIPs[1]) assertIPEquals(t, expectedIPs[4], newIPs[2]) _, err = a.RequestIP(network, nil) if err == nil { t.Fatal("There shouldn't be any IP addresses at this point") } } func TestAllocateFirstIP(t *testing.T) { a := New() network := &net.IPNet{ IP: []byte{192, 168, 0, 0}, Mask: []byte{255, 255, 255, 0}, } firstIP := network.IP.To4().Mask(network.Mask) first := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1)) ip, err := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } allocated := ipToBigInt(ip) if allocated == first { t.Fatalf("allocated ip should not equal first ip: %d == %d", first, allocated) } } func TestAllocateAllIps(t *testing.T) { a := New() network := &net.IPNet{ IP: []byte{192, 168, 0, 1}, Mask: []byte{255, 255, 255, 0}, } var ( current, first net.IP err error isFirst = true ) for err == nil { current, err = a.RequestIP(network, nil) if isFirst { first = current isFirst = false } } if err != ErrNoAvailableIPs { t.Fatal(err) } if _, err := a.RequestIP(network, nil); err != ErrNoAvailableIPs { t.Fatal(err) } if err := a.ReleaseIP(network, first); err != nil { t.Fatal(err) } again, err := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } assertIPEquals(t, first, again) // ensure that alloc.last == alloc.begin won't result in dead loop if _, err := a.RequestIP(network, nil); err != ErrNoAvailableIPs { t.Fatal(err) } // Test by making alloc.last the only free ip and ensure we get it back // #1. first of the range, (alloc.last == ipToInt(first) already) if err := a.ReleaseIP(network, first); err != nil { t.Fatal(err) } ret, err := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } assertIPEquals(t, first, ret) // #2. last of the range, note that current is the last one last := net.IPv4(192, 168, 0, 254) setLastTo(t, a, network, last) ret, err = a.RequestIP(network, nil) if err != nil { t.Fatal(err) } assertIPEquals(t, last, ret) // #3. middle of the range mid := net.IPv4(192, 168, 0, 7) setLastTo(t, a, network, mid) ret, err = a.RequestIP(network, nil) if err != nil { t.Fatal(err) } assertIPEquals(t, mid, ret) } // make sure the pool is full when calling setLastTo. // we don't cheat here func setLastTo(t *testing.T, a *IPAllocator, network *net.IPNet, ip net.IP) { if err := a.ReleaseIP(network, ip); err != nil { t.Fatal(err) } ret, err := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } assertIPEquals(t, ip, ret) if err := a.ReleaseIP(network, ip); err != nil { t.Fatal(err) } } func TestAllocateDifferentSubnets(t *testing.T) { a := New() network1 := &net.IPNet{ IP: []byte{192, 168, 0, 1}, Mask: []byte{255, 255, 255, 0}, } network2 := &net.IPNet{ 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, 1), 1: net.IPv4(192, 168, 0, 2), 2: net.IPv4(127, 0, 0, 1), 3: net.IPv4(127, 0, 0, 2), 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 := a.RequestIP(network1, nil) if err != nil { t.Fatal(err) } ip12, err := a.RequestIP(network1, nil) if err != nil { t.Fatal(err) } ip21, err := a.RequestIP(network2, nil) if err != nil { t.Fatal(err) } ip22, err := a.RequestIP(network2, nil) if err != nil { t.Fatal(err) } ip31, err := a.RequestIP(network3, nil) if err != nil { t.Fatal(err) } ip32, err := a.RequestIP(network3, nil) if err != nil { t.Fatal(err) } ip33, err := a.RequestIP(network3, nil) if err != nil { t.Fatal(err) } ip41, err := a.RequestIP(network4, nil) if err != nil { t.Fatal(err) } ip42, err := a.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) { a := New() network := &net.IPNet{ IP: []byte{192, 168, 1, 1}, Mask: []byte{255, 255, 255, 0}, } subnet := &net.IPNet{ IP: []byte{192, 168, 1, 8}, Mask: []byte{255, 255, 255, 248}, } if err := a.RegisterSubnet(network, subnet); err != nil { t.Fatal(err) } subnet = &net.IPNet{ IP: []byte{192, 168, 1, 16}, Mask: []byte{255, 255, 255, 248}, } if err := a.RegisterSubnet(network, subnet); err != ErrNetworkAlreadyRegistered { t.Fatalf("Expecteded ErrNetworkAlreadyRegistered error, got %v", err) } } func TestRegisterBadRange(t *testing.T) { a := New() network := &net.IPNet{ IP: []byte{192, 168, 1, 1}, Mask: []byte{255, 255, 255, 0}, } subnet := &net.IPNet{ IP: []byte{192, 168, 1, 1}, Mask: []byte{255, 255, 0, 0}, } if err := a.RegisterSubnet(network, subnet); err != ErrBadSubnet { t.Fatalf("Expected ErrBadSubnet error, got %v", err) } } func TestAllocateFromRange(t *testing.T) { a := New() network := &net.IPNet{ IP: []byte{192, 168, 0, 1}, Mask: []byte{255, 255, 255, 0}, } // 192.168.1.9 - 192.168.1.14 subnet := &net.IPNet{ IP: []byte{192, 168, 0, 8}, Mask: []byte{255, 255, 255, 248}, } if err := a.RegisterSubnet(network, subnet); err != nil { t.Fatal(err) } expectedIPs := []net.IP{ 0: net.IPv4(192, 168, 0, 9), 1: net.IPv4(192, 168, 0, 10), 2: net.IPv4(192, 168, 0, 11), 3: net.IPv4(192, 168, 0, 12), 4: net.IPv4(192, 168, 0, 13), 5: net.IPv4(192, 168, 0, 14), } for _, ip := range expectedIPs { rip, err := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } assertIPEquals(t, ip, rip) } if _, err := a.RequestIP(network, nil); err != ErrNoAvailableIPs { t.Fatalf("Expected ErrNoAvailableIPs error, got %v", err) } for _, ip := range expectedIPs { a.ReleaseIP(network, ip) rip, err := a.RequestIP(network, nil) if err != nil { t.Fatal(err) } assertIPEquals(t, ip, rip) } } func assertIPEquals(t *testing.T, ip1, ip2 net.IP) { if !ip1.Equal(ip2) { t.Fatalf("Expected IP %s, got %s", ip1, ip2) } } func BenchmarkRequestIP(b *testing.B) { network := &net.IPNet{ IP: []byte{192, 168, 0, 1}, Mask: []byte{255, 255, 255, 0}, } b.ResetTimer() for i := 0; i < b.N; i++ { a := New() for j := 0; j < 253; j++ { _, err := a.RequestIP(network, nil) if err != nil { b.Fatal(err) } } } }