From a295890c5c8ebae9cba8f01ddcee2bded7dad404 Mon Sep 17 00:00:00 2001 From: Tomasz Jezierski Date: Sun, 24 Jul 2022 23:02:53 +0200 Subject: [PATCH] net: precompute rfc6724policyTable in addrselect MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit As net package has one of the biggest init time in standard library, I have tried to improve performance by doing two things in net/addrselect.go: 1. Precompute slice with RFC rules. Currently the rules are computed and sorted in init() function. We could save the time and allocations by using prepopulated values in sorted manner. The rules haven't changed since 2015. To be extra safe we could move order validation as test case. It should slightly speed up startup of each binary with "net" package and go dns resolver. It also saves 38 allocations, ~50% of allocations in init phase of `net` module. 2. Replace internal net.IP usage with netip.Addr in `sortByRFC6724` function. It results in ~40% performance improvement on samples from tests. The only risk is the difference between net.IP and netip.Addr behaviour. Init benchmark: Init-8 1.89µs ± 2% 0.12µs ± 3% -93.79% (p=0.000 n=5+5) name old alloc/op new alloc/op delta Init-8 1.05kB ± 0% 0.38kB ± 0% ~ (zero variance) name old allocs/op new allocs/op delta Init-8 39.0 ± 0% 1.0 ± 0% ~ (zero variance) Whole sortByRFC6724 function benchmark: name old time/op new time/op delta SortByRFC6724/0-8 463ns ± 3% 303ns ± 4% -34.72% (p=0.000 n=5+5) SortByRFC6724/1-8 481ns ± 8% 306ns ± 1% -36.46% (p=0.000 n=5+5) SortByRFC6724/2-8 470ns ± 4% 307ns ± 4% -34.77% (p=0.000 n=5+5) SortByRFC6724/3-8 567ns ± 3% 367ns ± 3% -35.28% (p=0.000 n=5+5) SortByRFC6724/4-8 918ns ± 3% 560ns ± 2% -38.93% (p=0.000 n=5+5) Updates #54032 Change-Id: Ic18df1ea73805cb184c6ceb73470ca7f0b922032 Reviewed-on: https://go-review.googlesource.com/c/go/+/419356 Reviewed-by: Heschi Kreinick TryBot-Result: Gopher Robot Reviewed-by: Damien Neil Run-TryBot: Damien Neil Reviewed-by: Daniel Martí --- src/net/addrselect.go | 148 ++++++++++++++---------------- src/net/addrselect_test.go | 183 +++++++++++++++++++++++++------------ 2 files changed, 191 insertions(+), 140 deletions(-) diff --git a/src/net/addrselect.go b/src/net/addrselect.go index 59380b9486..b76183a34c 100644 --- a/src/net/addrselect.go +++ b/src/net/addrselect.go @@ -6,7 +6,10 @@ package net -import "sort" +import ( + "net/netip" + "sort" +) func sortByRFC6724(addrs []IPAddr) { if len(addrs) < 2 { @@ -15,14 +18,15 @@ func sortByRFC6724(addrs []IPAddr) { sortByRFC6724withSrcs(addrs, srcAddrs(addrs)) } -func sortByRFC6724withSrcs(addrs []IPAddr, srcs []IP) { +func sortByRFC6724withSrcs(addrs []IPAddr, srcs []netip.Addr) { if len(addrs) != len(srcs) { panic("internal error") } addrAttr := make([]ipAttr, len(addrs)) srcAttr := make([]ipAttr, len(srcs)) for i, v := range addrs { - addrAttr[i] = ipAttrOf(v.IP) + addrAttrIP, _ := netip.AddrFromSlice(v.IP) + addrAttr[i] = ipAttrOf(addrAttrIP) srcAttr[i] = ipAttrOf(srcs[i]) } sort.Stable(&byRFC6724{ @@ -36,8 +40,8 @@ func sortByRFC6724withSrcs(addrs []IPAddr, srcs []IP) { // srcsAddrs tries to UDP-connect to each address to see if it has a // route. (This doesn't send any packets). The destination port // number is irrelevant. -func srcAddrs(addrs []IPAddr) []IP { - srcs := make([]IP, len(addrs)) +func srcAddrs(addrs []IPAddr) []netip.Addr { + srcs := make([]netip.Addr, len(addrs)) dst := UDPAddr{Port: 9} for i := range addrs { dst.IP = addrs[i].IP @@ -45,7 +49,7 @@ func srcAddrs(addrs []IPAddr) []IP { c, err := DialUDP("udp", nil, &dst) if err == nil { if src, ok := c.LocalAddr().(*UDPAddr); ok { - srcs[i] = src.IP + srcs[i], _ = netip.AddrFromSlice(src.IP) } c.Close() } @@ -59,8 +63,8 @@ type ipAttr struct { Label uint8 } -func ipAttrOf(ip IP) ipAttr { - if ip == nil { +func ipAttrOf(ip netip.Addr) ipAttr { + if !ip.IsValid() { return ipAttr{} } match := rfc6724policyTable.Classify(ip) @@ -74,7 +78,7 @@ func ipAttrOf(ip IP) ipAttr { type byRFC6724 struct { addrs []IPAddr // addrs to sort addrAttr []ipAttr - srcs []IP // or nil if unreachable + srcs []netip.Addr // or not valid addr if unreachable srcAttr []ipAttr } @@ -108,13 +112,13 @@ func (s *byRFC6724) Less(i, j int) bool { // If DB is known to be unreachable or if Source(DB) is undefined, then // prefer DA. Similarly, if DA is known to be unreachable or if // Source(DA) is undefined, then prefer DB. - if SourceDA == nil && SourceDB == nil { + if !SourceDA.IsValid() && !SourceDB.IsValid() { return false // "equal" } - if SourceDB == nil { + if !SourceDB.IsValid() { return preferDA } - if SourceDA == nil { + if !SourceDA.IsValid() { return preferDB } @@ -184,7 +188,7 @@ func (s *byRFC6724) Less(i, j int) bool { return preferDB } - // Rule 9: Use longest matching prefix. + // Rule 9: Use the longest matching prefix. // When DA and DB belong to the same address family (both are IPv6 or // both are IPv4 [but see below]): If CommonPrefixLen(Source(DA), DA) > // CommonPrefixLen(Source(DB), DB), then prefer DA. Similarly, if @@ -212,7 +216,7 @@ func (s *byRFC6724) Less(i, j int) bool { } type policyTableEntry struct { - Prefix *IPNet + Prefix netip.Prefix Precedence uint8 Label uint8 } @@ -220,90 +224,75 @@ type policyTableEntry struct { type policyTable []policyTableEntry // RFC 6724 section 2.1. +// Items are sorted by the size of their Prefix.Mask.Size, var rfc6724policyTable = policyTable{ { - Prefix: mustCIDR("::1/128"), + // "::1/128" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}), 128), Precedence: 50, Label: 0, }, { - Prefix: mustCIDR("::/0"), - Precedence: 40, - Label: 1, - }, - { + // "::ffff:0:0/96" // IPv4-compatible, etc. - Prefix: mustCIDR("::ffff:0:0/96"), + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}), 96), Precedence: 35, Label: 4, }, { - // 6to4 - Prefix: mustCIDR("2002::/16"), - Precedence: 30, - Label: 2, + // "::/96" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 96), + Precedence: 1, + Label: 3, }, { + // "2001::/32" // Teredo - Prefix: mustCIDR("2001::/32"), + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x01}), 32), Precedence: 5, Label: 5, }, { - Prefix: mustCIDR("fc00::/7"), - Precedence: 3, - Label: 13, + // "2002::/16" + // 6to4 + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x20, 0x02}), 16), + Precedence: 30, + Label: 2, }, { - Prefix: mustCIDR("::/96"), + // "3ffe::/16" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0x3f, 0xfe}), 16), Precedence: 1, - Label: 3, + Label: 12, }, { - Prefix: mustCIDR("fec0::/10"), + // "fec0::/10" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfe, 0xc0}), 10), Precedence: 1, Label: 11, }, { - Prefix: mustCIDR("3ffe::/16"), - Precedence: 1, - Label: 12, + // "fc00::/7" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{0xfc}), 7), + Precedence: 3, + Label: 13, + }, + { + // "::/0" + Prefix: netip.PrefixFrom(netip.AddrFrom16([16]byte{}), 0), + Precedence: 40, + Label: 1, }, -} - -func init() { - sort.Sort(sort.Reverse(byMaskLength(rfc6724policyTable))) -} - -// byMaskLength sorts policyTableEntry by the size of their Prefix.Mask.Size, -// from smallest mask, to largest. -type byMaskLength []policyTableEntry - -func (s byMaskLength) Len() int { return len(s) } -func (s byMaskLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s byMaskLength) Less(i, j int) bool { - isize, _ := s[i].Prefix.Mask.Size() - jsize, _ := s[j].Prefix.Mask.Size() - return isize < jsize -} - -// mustCIDR calls ParseCIDR and panics on any error, or if the network -// is not IPv6. -func mustCIDR(s string) *IPNet { - ip, ipNet, err := ParseCIDR(s) - if err != nil { - panic(err.Error()) - } - if len(ip) != IPv6len { - panic("unexpected IP length") - } - return ipNet } // Classify returns the policyTableEntry of the entry with the longest // matching prefix that contains ip. // The table t must be sorted from largest mask size to smallest. -func (t policyTable) Classify(ip IP) policyTableEntry { +func (t policyTable) Classify(ip netip.Addr) policyTableEntry { + // Prefix.Contains() will not match an IPv6 prefix for an IPv4 address. + if ip.Is4() { + ip = netip.AddrFrom16(ip.As16()) + } for _, ent := range t { if ent.Prefix.Contains(ip) { return ent @@ -324,17 +313,18 @@ const ( scopeGlobal scope = 0xe ) -func classifyScope(ip IP) scope { +func classifyScope(ip netip.Addr) scope { if ip.IsLoopback() || ip.IsLinkLocalUnicast() { return scopeLinkLocal } - ipv6 := len(ip) == IPv6len && ip.To4() == nil + ipv6 := ip.Is6() && !ip.Is4In6() + ipv6AsBytes := ip.As16() if ipv6 && ip.IsMulticast() { - return scope(ip[1] & 0xf) + return scope(ipv6AsBytes[1] & 0xf) } // Site-local addresses are defined in RFC 3513 section 2.5.6 // (and deprecated in RFC 3879). - if ipv6 && ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 { + if ipv6 && ipv6AsBytes[0] == 0xfe && ipv6AsBytes[1]&0xc0 == 0xc0 { return scopeSiteLocal } return scopeGlobal @@ -350,30 +340,28 @@ func classifyScope(ip IP) scope { // If a and b are different IP versions, 0 is returned. // // See https://tools.ietf.org/html/rfc6724#section-2.2 -func commonPrefixLen(a, b IP) (cpl int) { - if a4 := a.To4(); a4 != nil { - a = a4 - } +func commonPrefixLen(a netip.Addr, b IP) (cpl int) { if b4 := b.To4(); b4 != nil { b = b4 } - if len(a) != len(b) { + aAsSlice := a.AsSlice() + if len(aAsSlice) != len(b) { return 0 } // If IPv6, only up to the prefix (first 64 bits) - if len(a) > 8 { - a = a[:8] + if len(aAsSlice) > 8 { + aAsSlice = aAsSlice[:8] b = b[:8] } - for len(a) > 0 { - if a[0] == b[0] { + for len(aAsSlice) > 0 { + if aAsSlice[0] == b[0] { cpl += 8 - a = a[1:] + aAsSlice = aAsSlice[1:] b = b[1:] continue } bits := 8 - ab, bb := a[0], b[0] + ab, bb := aAsSlice[0], b[0] for { ab >>= 1 bb >>= 1 diff --git a/src/net/addrselect_test.go b/src/net/addrselect_test.go index 2894d5d846..7e8134d754 100644 --- a/src/net/addrselect_test.go +++ b/src/net/addrselect_test.go @@ -7,6 +7,7 @@ package net import ( + "net/netip" "reflect" "testing" ) @@ -14,7 +15,7 @@ import ( func TestSortByRFC6724(t *testing.T) { tests := []struct { in []IPAddr - srcs []IP + srcs []netip.Addr want []IPAddr reverse bool // also test it starting backwards }{ @@ -26,9 +27,9 @@ func TestSortByRFC6724(t *testing.T) { {IP: ParseIP("2001:db8:1::1")}, {IP: ParseIP("198.51.100.121")}, }, - srcs: []IP{ - ParseIP("2001:db8:1::2"), - ParseIP("169.254.13.78"), + srcs: []netip.Addr{ + netip.MustParseAddr("2001:db8:1::2"), + netip.MustParseAddr("169.254.13.78"), }, want: []IPAddr{ {IP: ParseIP("2001:db8:1::1")}, @@ -43,9 +44,9 @@ func TestSortByRFC6724(t *testing.T) { {IP: ParseIP("2001:db8:1::1")}, {IP: ParseIP("198.51.100.121")}, }, - srcs: []IP{ - ParseIP("fe80::1"), - ParseIP("198.51.100.117"), + srcs: []netip.Addr{ + netip.MustParseAddr("fe80::1"), + netip.MustParseAddr("198.51.100.117"), }, want: []IPAddr{ {IP: ParseIP("198.51.100.121")}, @@ -60,9 +61,9 @@ func TestSortByRFC6724(t *testing.T) { {IP: ParseIP("2001:db8:1::1")}, {IP: ParseIP("10.1.2.3")}, }, - srcs: []IP{ - ParseIP("2001:db8:1::2"), - ParseIP("10.1.2.4"), + srcs: []netip.Addr{ + netip.MustParseAddr("2001:db8:1::2"), + netip.MustParseAddr("10.1.2.4"), }, want: []IPAddr{ {IP: ParseIP("2001:db8:1::1")}, @@ -77,9 +78,9 @@ func TestSortByRFC6724(t *testing.T) { {IP: ParseIP("2001:db8:1::1")}, {IP: ParseIP("fe80::1")}, }, - srcs: []IP{ - ParseIP("2001:db8:1::2"), - ParseIP("fe80::2"), + srcs: []netip.Addr{ + netip.MustParseAddr("2001:db8:1::2"), + netip.MustParseAddr("fe80::2"), }, want: []IPAddr{ {IP: ParseIP("fe80::1")}, @@ -99,13 +100,13 @@ func TestSortByRFC6724(t *testing.T) { {IP: ParseIP("23.23.134.56")}, {IP: ParseIP("23.21.50.150")}, }, - srcs: []IP{ - ParseIP("10.2.3.4"), - ParseIP("10.2.3.4"), - ParseIP("10.2.3.4"), - ParseIP("10.2.3.4"), - ParseIP("10.2.3.4"), - ParseIP("10.2.3.4"), + srcs: []netip.Addr{ + netip.MustParseAddr("10.2.3.4"), + netip.MustParseAddr("10.2.3.4"), + netip.MustParseAddr("10.2.3.4"), + netip.MustParseAddr("10.2.3.4"), + netip.MustParseAddr("10.2.3.4"), + netip.MustParseAddr("10.2.3.4"), }, want: []IPAddr{ {IP: ParseIP("54.83.193.112")}, @@ -121,7 +122,7 @@ func TestSortByRFC6724(t *testing.T) { for i, tt := range tests { inCopy := make([]IPAddr, len(tt.in)) copy(inCopy, tt.in) - srcCopy := make([]IP, len(tt.in)) + srcCopy := make([]netip.Addr, len(tt.in)) copy(srcCopy, tt.srcs) sortByRFC6724withSrcs(inCopy, srcCopy) if !reflect.DeepEqual(inCopy, tt.want) { @@ -145,39 +146,100 @@ func TestSortByRFC6724(t *testing.T) { } +func TestRFC6724PolicyTableOrder(t *testing.T) { + for i := 0; i < len(rfc6724policyTable)-1; i++ { + if !(rfc6724policyTable[i].Prefix.Bits() >= rfc6724policyTable[i+1].Prefix.Bits()) { + t.Errorf("rfc6724policyTable item number %d sorted in wrong order = %d bits, next item = %d bits;", i, rfc6724policyTable[i].Prefix.Bits(), rfc6724policyTable[i+1].Prefix.Bits()) + } + } +} + +func TestRFC6724PolicyTableContent(t *testing.T) { + expectedRfc6724policyTable := policyTable{ + { + Prefix: netip.MustParsePrefix("::1/128"), + Precedence: 50, + Label: 0, + }, + { + Prefix: netip.MustParsePrefix("::ffff:0:0/96"), + Precedence: 35, + Label: 4, + }, + { + Prefix: netip.MustParsePrefix("::/96"), + Precedence: 1, + Label: 3, + }, + { + Prefix: netip.MustParsePrefix("2001::/32"), + Precedence: 5, + Label: 5, + }, + { + Prefix: netip.MustParsePrefix("2002::/16"), + Precedence: 30, + Label: 2, + }, + { + Prefix: netip.MustParsePrefix("3ffe::/16"), + Precedence: 1, + Label: 12, + }, + { + Prefix: netip.MustParsePrefix("fec0::/10"), + Precedence: 1, + Label: 11, + }, + { + Prefix: netip.MustParsePrefix("fc00::/7"), + Precedence: 3, + Label: 13, + }, + { + Prefix: netip.MustParsePrefix("::/0"), + Precedence: 40, + Label: 1, + }, + } + if !reflect.DeepEqual(rfc6724policyTable, expectedRfc6724policyTable) { + t.Errorf("rfc6724policyTable has wrong contend = %v; want %v", rfc6724policyTable, expectedRfc6724policyTable) + } +} + func TestRFC6724PolicyTableClassify(t *testing.T) { tests := []struct { - ip IP + ip netip.Addr want policyTableEntry }{ { - ip: ParseIP("127.0.0.1"), + ip: netip.MustParseAddr("127.0.0.1"), want: policyTableEntry{ - Prefix: &IPNet{IP: ParseIP("::ffff:0:0"), Mask: CIDRMask(96, 128)}, + Prefix: netip.MustParsePrefix("::ffff:0:0/96"), Precedence: 35, Label: 4, }, }, { - ip: ParseIP("2601:645:8002:a500:986f:1db8:c836:bd65"), + ip: netip.MustParseAddr("2601:645:8002:a500:986f:1db8:c836:bd65"), want: policyTableEntry{ - Prefix: &IPNet{IP: ParseIP("::"), Mask: CIDRMask(0, 128)}, + Prefix: netip.MustParsePrefix("::/0"), Precedence: 40, Label: 1, }, }, { - ip: ParseIP("::1"), + ip: netip.MustParseAddr("::1"), want: policyTableEntry{ - Prefix: &IPNet{IP: ParseIP("::1"), Mask: CIDRMask(128, 128)}, + Prefix: netip.MustParsePrefix("::1/128"), Precedence: 50, Label: 0, }, }, { - ip: ParseIP("2002::ab12"), + ip: netip.MustParseAddr("2002::ab12"), want: policyTableEntry{ - Prefix: &IPNet{IP: ParseIP("2002::"), Mask: CIDRMask(16, 128)}, + Prefix: netip.MustParsePrefix("2002::/16"), Precedence: 30, Label: 2, }, @@ -193,24 +255,24 @@ func TestRFC6724PolicyTableClassify(t *testing.T) { func TestRFC6724ClassifyScope(t *testing.T) { tests := []struct { - ip IP + ip netip.Addr want scope }{ - {ParseIP("127.0.0.1"), scopeLinkLocal}, // rfc6724#section-3.2 - {ParseIP("::1"), scopeLinkLocal}, // rfc4007#section-4 - {ParseIP("169.254.1.2"), scopeLinkLocal}, // rfc6724#section-3.2 - {ParseIP("fec0::1"), scopeSiteLocal}, - {ParseIP("8.8.8.8"), scopeGlobal}, - - {ParseIP("ff02::"), scopeLinkLocal}, // IPv6 multicast - {ParseIP("ff05::"), scopeSiteLocal}, // IPv6 multicast - {ParseIP("ff04::"), scopeAdminLocal}, // IPv6 multicast - {ParseIP("ff0e::"), scopeGlobal}, // IPv6 multicast - - {IPv4(0xe0, 0, 0, 0), scopeGlobal}, // IPv4 link-local multicast as 16 bytes - {IPv4(0xe0, 2, 2, 2), scopeGlobal}, // IPv4 global multicast as 16 bytes - {IPv4(0xe0, 0, 0, 0).To4(), scopeGlobal}, // IPv4 link-local multicast as 4 bytes - {IPv4(0xe0, 2, 2, 2).To4(), scopeGlobal}, // IPv4 global multicast as 4 bytes + {netip.MustParseAddr("127.0.0.1"), scopeLinkLocal}, // rfc6724#section-3.2 + {netip.MustParseAddr("::1"), scopeLinkLocal}, // rfc4007#section-4 + {netip.MustParseAddr("169.254.1.2"), scopeLinkLocal}, // rfc6724#section-3.2 + {netip.MustParseAddr("fec0::1"), scopeSiteLocal}, + {netip.MustParseAddr("8.8.8.8"), scopeGlobal}, + + {netip.MustParseAddr("ff02::"), scopeLinkLocal}, // IPv6 multicast + {netip.MustParseAddr("ff05::"), scopeSiteLocal}, // IPv6 multicast + {netip.MustParseAddr("ff04::"), scopeAdminLocal}, // IPv6 multicast + {netip.MustParseAddr("ff0e::"), scopeGlobal}, // IPv6 multicast + + {netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xe0, 0, 0, 0}), scopeGlobal}, // IPv4 link-local multicast as 16 bytes + {netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xe0, 2, 2, 2}), scopeGlobal}, // IPv4 global multicast as 16 bytes + {netip.AddrFrom4([4]byte{0xe0, 0, 0, 0}), scopeGlobal}, // IPv4 link-local multicast as 4 bytes + {netip.AddrFrom4([4]byte{0xe0, 2, 2, 2}), scopeGlobal}, // IPv4 global multicast as 4 bytes } for i, tt := range tests { got := classifyScope(tt.ip) @@ -222,22 +284,23 @@ func TestRFC6724ClassifyScope(t *testing.T) { func TestRFC6724CommonPrefixLength(t *testing.T) { tests := []struct { - a, b IP + a netip.Addr + b IP want int }{ - {ParseIP("fe80::1"), ParseIP("fe80::2"), 64}, - {ParseIP("fe81::1"), ParseIP("fe80::2"), 15}, - {ParseIP("127.0.0.1"), ParseIP("fe80::1"), 0}, // diff size - {IPv4(1, 2, 3, 4), IP{1, 2, 3, 4}, 32}, - {IP{1, 2, 255, 255}, IP{1, 2, 0, 0}, 16}, - {IP{1, 2, 127, 255}, IP{1, 2, 0, 0}, 17}, - {IP{1, 2, 63, 255}, IP{1, 2, 0, 0}, 18}, - {IP{1, 2, 31, 255}, IP{1, 2, 0, 0}, 19}, - {IP{1, 2, 15, 255}, IP{1, 2, 0, 0}, 20}, - {IP{1, 2, 7, 255}, IP{1, 2, 0, 0}, 21}, - {IP{1, 2, 3, 255}, IP{1, 2, 0, 0}, 22}, - {IP{1, 2, 1, 255}, IP{1, 2, 0, 0}, 23}, - {IP{1, 2, 0, 255}, IP{1, 2, 0, 0}, 24}, + {netip.MustParseAddr("fe80::1"), ParseIP("fe80::2"), 64}, + {netip.MustParseAddr("fe81::1"), ParseIP("fe80::2"), 15}, + {netip.MustParseAddr("127.0.0.1"), ParseIP("fe80::1"), 0}, // diff size + {netip.AddrFrom4([4]byte{1, 2, 3, 4}), IP{1, 2, 3, 4}, 32}, + {netip.AddrFrom4([4]byte{1, 2, 255, 255}), IP{1, 2, 0, 0}, 16}, + {netip.AddrFrom4([4]byte{1, 2, 127, 255}), IP{1, 2, 0, 0}, 17}, + {netip.AddrFrom4([4]byte{1, 2, 63, 255}), IP{1, 2, 0, 0}, 18}, + {netip.AddrFrom4([4]byte{1, 2, 31, 255}), IP{1, 2, 0, 0}, 19}, + {netip.AddrFrom4([4]byte{1, 2, 15, 255}), IP{1, 2, 0, 0}, 20}, + {netip.AddrFrom4([4]byte{1, 2, 7, 255}), IP{1, 2, 0, 0}, 21}, + {netip.AddrFrom4([4]byte{1, 2, 3, 255}), IP{1, 2, 0, 0}, 22}, + {netip.AddrFrom4([4]byte{1, 2, 1, 255}), IP{1, 2, 0, 0}, 23}, + {netip.AddrFrom4([4]byte{1, 2, 0, 255}), IP{1, 2, 0, 0}, 24}, } for i, tt := range tests { got := commonPrefixLen(tt.a, tt.b) -- 2.50.0