diff --git a/net/ip_test.go b/net/ip_test.go index 03f78ec9e8154b5292e5eaf824f74af020f7355f..4720985f825ba242b1c78cf75773499d55bdd9bf 100644 --- a/net/ip_test.go +++ b/net/ip_test.go @@ -312,6 +312,12 @@ func TestBitAtPosition(t *testing.T) { position uint8 expected bool }{ + { + name: "IPv4: all ones -> 0", + input: IPv4FromOctets(255, 255, 255, 255), + position: 1, + expected: true, + }, { name: "IPv4: Bit 8 from 1.0.0.0 -> 0", input: IPv4FromOctets(10, 0, 0, 0), @@ -348,6 +354,12 @@ func TestBitAtPosition(t *testing.T) { position: 115, expected: false, }, + { + name: "IPv6: all ones -> 1", + input: IPv6FromBlocks(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF), + position: 1, + expected: true, + }, } for _, test := range tests { diff --git a/net/prefix.go b/net/prefix.go index c40420247dfbfb11e8e05c01bae2fa1ed7d5e183..f75c2004abf3194de0c6f430afa5d29fc0579a7a 100644 --- a/net/prefix.go +++ b/net/prefix.go @@ -89,7 +89,7 @@ func (pfx Prefix) GetSupernet(x Prefix) Prefix { return pfx.supernetIPv4(x) } - panic("No IPv6 support yet!") + return pfx.supernetIPv6(x) } func (pfx Prefix) supernetIPv4(x Prefix) Prefix { @@ -109,6 +109,37 @@ func (pfx Prefix) supernetIPv4(x Prefix) Prefix { } } +func (pfx Prefix) supernetIPv6(x Prefix) Prefix { + maxPfxLen := min(pfx.pfxlen, x.pfxlen) + + a := pfx.addr.BitAtPosition(1) + b := x.addr.BitAtPosition(1) + pfxLen := uint8(0) + mask := uint64(0) + for a == b && pfxLen < maxPfxLen { + a = pfx.addr.BitAtPosition(pfxLen + 2) + b = x.addr.BitAtPosition(pfxLen + 2) + pfxLen++ + + if pfxLen == 64 { + mask = 0 + } + + m := pfxLen % 64 + mask = mask + uint64(1)<<(64-m) + } + + if pfxLen == 0 { + return NewPfx(IPv6(0, 0), pfxLen) + } + + if pfxLen > 64 { + return NewPfx(IPv6(pfx.addr.higher, pfx.addr.lower&mask), pfxLen) + } + + return NewPfx(IPv6(pfx.addr.higher&mask, 0), pfxLen) +} + func min(a uint8, b uint8) uint8 { if a < b { return a diff --git a/net/prefix_test.go b/net/prefix_test.go index d886a49e4ae1ed49fede133df33ac4d90331dd16..4428a4e84181f2f20ce5862aca531a23fcff0487 100644 --- a/net/prefix_test.go +++ b/net/prefix_test.go @@ -63,7 +63,7 @@ func TestGetSupernet(t *testing.T) { expected Prefix }{ { - name: "Test 1", + name: "Supernet of 10.0.0.0 and 11.100.123.0 -> 10.0.0.0/7", a: Prefix{ addr: IPv4FromOctets(10, 0, 0, 0), pfxlen: 8, @@ -78,7 +78,7 @@ func TestGetSupernet(t *testing.T) { }, }, { - name: "Test 2", + name: "Supernet of 10.0.0.0 and 192.168.0.0 -> 0.0.0.0/0", a: Prefix{ addr: IPv4FromOctets(10, 0, 0, 0), pfxlen: 8, @@ -88,15 +88,64 @@ func TestGetSupernet(t *testing.T) { pfxlen: 24, }, expected: Prefix{ - addr: IPv4(0), // 0.0.0.0/0 + addr: IPv4(0), + pfxlen: 0, + }, + }, + { + name: "Supernet of 2001:678:1e0:100:23::/64 and 2001:678:1e0:1ff::/64 -> 2001:678:1e0:100::/56", + a: Prefix{ + addr: IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0x100, 0x23, 0, 0, 0), + pfxlen: 64, + }, + b: Prefix{ + addr: IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0x1ff, 0, 0, 0, 0), + pfxlen: 64, + }, + expected: Prefix{ + addr: IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0x100, 0, 0, 0, 0), + pfxlen: 56, + }, + }, + { + name: "Supernet of 2001:678:1e0::/128 and 2001:678:1e0::1/128 -> 2001:678:1e0:100::/127", + a: Prefix{ + addr: IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0), + pfxlen: 128, + }, + b: Prefix{ + addr: IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 1), + pfxlen: 128, + }, + expected: Prefix{ + addr: IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0), + pfxlen: 127, + }, + }, + { + name: "Supernet of all ones and all zeros -> ::/0", + a: Prefix{ + addr: IPv6FromBlocks(0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF), + pfxlen: 128, + }, + b: Prefix{ + addr: IPv6(0, 0), + pfxlen: 128, + }, + expected: Prefix{ + addr: IPv6FromBlocks(0, 0, 0, 0, 0, 0, 0, 0), pfxlen: 0, }, }, } + t.Parallel() + for _, test := range tests { - s := test.a.GetSupernet(test.b) - assert.Equal(t, s, test.expected) + t.Run(test.name, func(t *testing.T) { + s := test.a.GetSupernet(test.b) + assert.Equal(t, test.expected, s) + }) } }