- Add v4_ip_start and v4_ip_end columns to live_routes table - Calculate IPv4 CIDR ranges as 32-bit integers for fast lookups - Update PrefixHandler to populate IPv4 range fields - Add GetASInfoForIP method with optimized IPv4 queries - Add comprehensive tests for IP conversion functions
302 lines
6.3 KiB
Go
302 lines
6.3 KiB
Go
package database
|
|
|
|
import (
|
|
"net"
|
|
"testing"
|
|
)
|
|
|
|
func TestIPToUint32(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
ip string
|
|
expected uint32
|
|
}{
|
|
{
|
|
name: "Simple IP",
|
|
ip: "192.168.1.1",
|
|
expected: 3232235777, // 192<<24 + 168<<16 + 1<<8 + 1
|
|
},
|
|
{
|
|
name: "Minimum IP",
|
|
ip: "0.0.0.0",
|
|
expected: 0,
|
|
},
|
|
{
|
|
name: "Maximum IP",
|
|
ip: "255.255.255.255",
|
|
expected: 4294967295,
|
|
},
|
|
{
|
|
name: "10.0.0.0",
|
|
ip: "10.0.0.0",
|
|
expected: 167772160,
|
|
},
|
|
{
|
|
name: "172.16.0.0",
|
|
ip: "172.16.0.0",
|
|
expected: 2886729728,
|
|
},
|
|
{
|
|
name: "8.8.8.8",
|
|
ip: "8.8.8.8",
|
|
expected: 134744072,
|
|
},
|
|
{
|
|
name: "1.2.3.4",
|
|
ip: "1.2.3.4",
|
|
expected: 16909060,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ip := net.ParseIP(tt.ip)
|
|
if ip == nil {
|
|
t.Fatalf("Failed to parse IP: %s", tt.ip)
|
|
}
|
|
|
|
result := ipToUint32(ip)
|
|
if result != tt.expected {
|
|
t.Errorf("ipToUint32(%s) = %d, want %d", tt.ip, result, tt.expected)
|
|
}
|
|
|
|
// Test with IPv4-mapped IPv6 address
|
|
ip6 := net.ParseIP(tt.ip).To16()
|
|
if ip6 != nil {
|
|
result6 := ipToUint32(ip6)
|
|
if result6 != tt.expected {
|
|
t.Errorf("ipToUint32(%s as IPv6) = %d, want %d", tt.ip, result6, tt.expected)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculateIPv4Range(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
cidr string
|
|
wantStart uint32
|
|
wantEnd uint32
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "Single IP /32",
|
|
cidr: "192.168.1.1/32",
|
|
wantStart: 3232235777,
|
|
wantEnd: 3232235777,
|
|
},
|
|
{
|
|
name: "Class C /24",
|
|
cidr: "192.168.1.0/24",
|
|
wantStart: 3232235776, // 192.168.1.0
|
|
wantEnd: 3232236031, // 192.168.1.255
|
|
},
|
|
{
|
|
name: "Class B /16",
|
|
cidr: "192.168.0.0/16",
|
|
wantStart: 3232235520, // 192.168.0.0
|
|
wantEnd: 3232301055, // 192.168.255.255
|
|
},
|
|
{
|
|
name: "Class A /8",
|
|
cidr: "10.0.0.0/8",
|
|
wantStart: 167772160, // 10.0.0.0
|
|
wantEnd: 184549375, // 10.255.255.255
|
|
},
|
|
{
|
|
name: "Entire IPv4 space /0",
|
|
cidr: "0.0.0.0/0",
|
|
wantStart: 0,
|
|
wantEnd: 4294967295,
|
|
},
|
|
{
|
|
name: "Small subnet /30",
|
|
cidr: "192.168.1.0/30",
|
|
wantStart: 3232235776, // 192.168.1.0
|
|
wantEnd: 3232235779, // 192.168.1.3
|
|
},
|
|
{
|
|
name: "Medium subnet /20",
|
|
cidr: "172.16.0.0/20",
|
|
wantStart: 2886729728, // 172.16.0.0
|
|
wantEnd: 2886733823, // 172.16.15.255
|
|
},
|
|
{
|
|
name: "Private range 172.16/12",
|
|
cidr: "172.16.0.0/12",
|
|
wantStart: 2886729728, // 172.16.0.0
|
|
wantEnd: 2887778303, // 172.31.255.255
|
|
},
|
|
{
|
|
name: "Google DNS /29",
|
|
cidr: "8.8.8.8/29",
|
|
wantStart: 134744072, // 8.8.8.8 (network is actually 8.8.8.8 with /29)
|
|
wantEnd: 134744079, // 8.8.8.15
|
|
},
|
|
{
|
|
name: "Non-zero host bits",
|
|
cidr: "192.168.1.5/24",
|
|
wantStart: 3232235776, // 192.168.1.0 (network address)
|
|
wantEnd: 3232236031, // 192.168.1.255
|
|
},
|
|
{
|
|
name: "Invalid CIDR",
|
|
cidr: "192.168.1.1/33",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Invalid IP",
|
|
cidr: "256.256.256.256/24",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "IPv6 CIDR",
|
|
cidr: "2001:db8::/32",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Empty CIDR",
|
|
cidr: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "Missing mask",
|
|
cidr: "192.168.1.1",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
start, end, err := CalculateIPv4Range(tt.cidr)
|
|
|
|
if tt.wantErr {
|
|
if err == nil {
|
|
t.Errorf("CalculateIPv4Range(%s) expected error, got nil", tt.cidr)
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("CalculateIPv4Range(%s) unexpected error: %v", tt.cidr, err)
|
|
return
|
|
}
|
|
|
|
if start != tt.wantStart {
|
|
t.Errorf("CalculateIPv4Range(%s) start = %d, want %d", tt.cidr, start, tt.wantStart)
|
|
}
|
|
|
|
if end != tt.wantEnd {
|
|
t.Errorf("CalculateIPv4Range(%s) end = %d, want %d", tt.cidr, end, tt.wantEnd)
|
|
}
|
|
|
|
// Verify that start <= end
|
|
if start > end {
|
|
t.Errorf("CalculateIPv4Range(%s) start (%d) > end (%d)", tt.cidr, start, end)
|
|
}
|
|
|
|
// Verify the range size matches the CIDR mask
|
|
if !tt.wantErr && tt.cidr != "" {
|
|
_, ipNet, _ := net.ParseCIDR(tt.cidr)
|
|
if ipNet != nil {
|
|
ones, bits := ipNet.Mask.Size()
|
|
expectedSize := uint32(1) << uint(bits-ones)
|
|
actualSize := end - start + 1
|
|
if actualSize != expectedSize {
|
|
t.Errorf("CalculateIPv4Range(%s) range size = %d, want %d", tt.cidr, actualSize, expectedSize)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPv4RangeIntegration(t *testing.T) {
|
|
// Test that our functions work correctly together
|
|
tests := []struct {
|
|
name string
|
|
cidr string
|
|
testIPs []string
|
|
shouldContain []bool
|
|
}{
|
|
{
|
|
name: "192.168.1.0/24",
|
|
cidr: "192.168.1.0/24",
|
|
testIPs: []string{
|
|
"192.168.1.0",
|
|
"192.168.1.1",
|
|
"192.168.1.255",
|
|
"192.168.0.255",
|
|
"192.168.2.0",
|
|
},
|
|
shouldContain: []bool{true, true, true, false, false},
|
|
},
|
|
{
|
|
name: "10.0.0.0/8",
|
|
cidr: "10.0.0.0/8",
|
|
testIPs: []string{
|
|
"10.0.0.0",
|
|
"10.255.255.255",
|
|
"10.1.2.3",
|
|
"9.255.255.255",
|
|
"11.0.0.0",
|
|
},
|
|
shouldContain: []bool{true, true, true, false, false},
|
|
},
|
|
{
|
|
name: "172.16.0.0/12",
|
|
cidr: "172.16.0.0/12",
|
|
testIPs: []string{
|
|
"172.16.0.0",
|
|
"172.31.255.255",
|
|
"172.20.1.1",
|
|
"172.15.255.255",
|
|
"172.32.0.0",
|
|
},
|
|
shouldContain: []bool{true, true, true, false, false},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
start, end, err := CalculateIPv4Range(tt.cidr)
|
|
if err != nil {
|
|
t.Fatalf("Failed to calculate range for %s: %v", tt.cidr, err)
|
|
}
|
|
|
|
for i, testIP := range tt.testIPs {
|
|
ip := net.ParseIP(testIP)
|
|
if ip == nil {
|
|
t.Fatalf("Failed to parse test IP: %s", testIP)
|
|
}
|
|
|
|
ipUint := ipToUint32(ip)
|
|
contained := ipUint >= start && ipUint <= end
|
|
|
|
if contained != tt.shouldContain[i] {
|
|
t.Errorf("IP %s in range %s: got %v, want %v", testIP, tt.cidr, contained, tt.shouldContain[i])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkIPToUint32(b *testing.B) {
|
|
ip := net.ParseIP("192.168.1.1")
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ipToUint32(ip)
|
|
}
|
|
}
|
|
|
|
func BenchmarkCalculateIPv4Range(b *testing.B) {
|
|
cidr := "192.168.0.0/16"
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_, _, _ = CalculateIPv4Range(cidr)
|
|
}
|
|
}
|