diff --git a/pkg/provider/loadbalancer/iputil/prefix_tree.go b/pkg/provider/loadbalancer/iputil/prefix_tree.go deleted file mode 100644 index 2399c09ebd..0000000000 --- a/pkg/provider/loadbalancer/iputil/prefix_tree.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright 2024 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package iputil - -import ( - "net/netip" -) - -type prefixTreeNode struct { - masked bool - prefix netip.Prefix - - p *prefixTreeNode // parent node - l *prefixTreeNode // left child node - r *prefixTreeNode // right child node -} - -// pruneToRoot prunes the tree to the root. -// If a node's left and right children are both masked, -// it is masked and its children are pruned. -// This is done recursively up to the root. -func (n *prefixTreeNode) pruneToRoot() { - var node = n - for node.p != nil { - p := node.p - if p.l == nil || !p.l.masked { - break - } - if p.r == nil || !p.r.masked { - break - } - p.masked = true - p.l, p.r = nil, nil - node = p - } -} - -type prefixTree struct { - maxBits int - root *prefixTreeNode -} - -func newPrefixTreeForIPv4() *prefixTree { - return &prefixTree{ - maxBits: 32, - root: &prefixTreeNode{ - prefix: netip.MustParsePrefix("0.0.0.0/0"), - }, - } -} - -func newPrefixTreeForIPv6() *prefixTree { - return &prefixTree{ - maxBits: 128, - root: &prefixTreeNode{ - prefix: netip.MustParsePrefix("::/0"), - }, - } -} - -// Add adds a prefix to the tree. -func (t *prefixTree) Add(prefix netip.Prefix) { - var ( - n = t.root - bits = prefix.Addr().AsSlice() - ) - for i := 0; i < prefix.Bits(); i++ { - if n.masked { - break // It's already masked, the rest of the bits are irrelevant - } - - var bit = bits[i/8] >> (7 - i%8) & 1 - switch bit { - case 0: - if n.l == nil { - next, err := prefix.Addr().Prefix(i + 1) - if err != nil { - panic("unreachable: invalid prefix") - } - n.l = &prefixTreeNode{ - prefix: next, - p: n, - } - } - n = n.l - case 1: - if n.r == nil { - next, err := prefix.Addr().Prefix(i + 1) - if err != nil { - panic("unreachable: invalid prefix") - } - n.r = &prefixTreeNode{ - prefix: next, - p: n, - } - } - n = n.r - default: - panic("unreachable: unexpected bit") - } - } - - n.masked = true - n.l, n.r = nil, nil - n.pruneToRoot() -} - -// List returns all prefixes in the tree. -// Overlapping prefixes are merged. -// It will also collapse the neighboring prefixes. -// The order of the prefixes in the output is guaranteed. -// -// Example: -// - [192.168.0.0/16, 192.168.1.0/24, 192.168.0.1/32] -> [192.168.0.0/16] -// - [192.168.0.0/32, 192.168.0.1/32] -> [192.168.0.0/31] -func (t *prefixTree) List() []netip.Prefix { - var ( - rv []netip.Prefix - q = []*prefixTreeNode{t.root} - ) - - for len(q) > 0 { - n := q[len(q)-1] - q = q[:len(q)-1] - - if n.masked { - rv = append(rv, n.prefix) - continue - } - - if n.l != nil { - q = append(q, n.l) - } - if n.r != nil { - q = append(q, n.r) - } - } - - return rv -} diff --git a/pkg/provider/loadbalancer/iputil/prefix_tree_test.go b/pkg/provider/loadbalancer/iputil/prefix_tree_test.go deleted file mode 100644 index fe3f3dddf1..0000000000 --- a/pkg/provider/loadbalancer/iputil/prefix_tree_test.go +++ /dev/null @@ -1,263 +0,0 @@ -/* -Copyright 2024 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package iputil - -import ( - "math" - "net/netip" - "sort" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPrefixTreeIPv4(t *testing.T) { - tests := []struct { - Name string - Input []string - Output []string - }{ - { - "Empty", - []string{}, - nil, - }, - { - "NoOverlap", - []string{ - "192.168.0.0/16", - "10.10.0.1/32", - }, - []string{ - "192.168.0.0/16", - "10.10.0.1/32", - }, - }, - { - "Overlap", - []string{ - "192.168.0.0/16", - "192.170.0.0/16", - "10.10.0.1/32", - - "192.168.1.0/24", - "192.168.1.1/32", - }, - []string{ - "192.168.0.0/16", - "192.170.0.0/16", - "10.10.0.1/32", - }, - }, - { - "Collapse", - []string{ - "192.168.0.0/24", - "192.168.1.0/24", - "192.168.2.0/24", - "192.168.3.0/24", - "10.0.0.0/8", - "172.16.0.0/12", - "192.168.4.0/24", - "192.168.5.0/24", - }, - []string{ - "10.0.0.0/8", - "172.16.0.0/12", - "192.168.0.0/22", - "192.168.4.0/23", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.Name, func(t *testing.T) { - var tree = newPrefixTreeForIPv4() - for _, ip := range tt.Input { - p := netip.MustParsePrefix(ip) - tree.Add(p) - } - - var got []string - for _, ip := range tree.List() { - got = append(got, ip.String()) - } - - sort.Strings(got) - sort.Strings(tt.Output) - - assert.Equal(t, tt.Output, got) - }) - } -} - -func TestPrefixTreeIPv6(t *testing.T) { - tests := []struct { - Name string - Input []string - Output []string - }{ - { - "Empty", - []string{}, - nil, - }, - { - "NoOverlap", - []string{ - "2001:db8:0:1::/64", - "2001:db8:0:3::/64", - "2001:db8:0:5::/64", - }, - []string{ - "2001:db8:0:1::/64", - "2001:db8:0:3::/64", - "2001:db8:0:5::/64", - }, - }, - { - "Overlap", - []string{ - "2001:db8::/32", - "2001:db8:0:1::/64", - "2001:db8:0:3::/64", - }, - []string{ - "2001:db8::/32", - }, - }, - { - "Collapse", - []string{ - "2001:db8::/32", - "2001:db8:1::/48", - "2001:db8:2::/48", - "2001:db8:3::/48", - "2001:db8:4::/48", - "2001:db8:5::/48", - "2001:db8:6::/48", - "2001:db8:7::/48", - "2001:db8:8::/48", - "2001:db8:9::/48", - "2001:db8:a::/48", - "2001:db8:b::/48", - "2001:db8:c::/48", - "2001:db8:d::/48", - "2001:db8:e::/48", - "2001:db8:f::/48", - "2001:dbf::/32", // Noise data - "2001:dba::/32", // Noise data - }, - []string{ - "2001:db8::/32", - "2001:dbf::/32", - "2001:dba::/32", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.Name, func(t *testing.T) { - var tree = newPrefixTreeForIPv6() - for _, ip := range tt.Input { - p := netip.MustParsePrefix(ip) - tree.Add(p) - } - - var got []string - for _, ip := range tree.List() { - got = append(got, ip.String()) - } - - sort.Strings(got) - sort.Strings(tt.Output) - - assert.Equal(t, tt.Output, got) - }) - } -} - -func BenchmarkPrefixTree_Add(b *testing.B) { - b.Run("IPv4", func(b *testing.B) { - var tree = newPrefixTreeForIPv4() - for i := 0; i < b.N; i++ { - addr := netip.AddrFrom4([4]byte{ - byte(i >> 24), byte(i >> 16), byte(i >> 8), byte(i), - }) - prefix, _ := addr.Prefix(32) - - tree.Add(prefix) - } - }) - - b.Run("IPv6", func(b *testing.B) { - var tree = newPrefixTreeForIPv6() - for i := 0; i < b.N; i++ { - addr := netip.AddrFrom16([16]byte{ - 0, 0, 0, 0, - 0, 0, 0, 0, - byte(i >> 56), byte(i >> 48), byte(i >> 40), byte(i >> 32), - byte(i >> 24), byte(i >> 16), byte(i >> 8), byte(i), - }) - prefix, _ := addr.Prefix(128) - - tree.Add(prefix) - } - }) -} - -func BenchmarkPrefixTree_List(b *testing.B) { - - b.Run("IPv4", func(b *testing.B) { - b.StopTimer() - var tree = newPrefixTreeForIPv4() - for i := 0; i < math.MaxInt8; i++ { - addr := netip.AddrFrom4([4]byte{ - byte(i >> 24), byte(i >> 16), byte(i >> 8), byte(i), - }) - prefix, err := addr.Prefix(32) - assert.NoError(b, err) - - tree.Add(prefix) - } - b.StartTimer() - for i := 0; i < b.N; i++ { - tree.List() - } - }) - - b.Run("IPv6", func(b *testing.B) { - b.StopTimer() - var tree = newPrefixTreeForIPv6() - for i := 0; i < math.MaxInt8; i++ { - addr := netip.AddrFrom16([16]byte{ - 0, 0, 0, 0, - 0, 0, 0, 0, - byte(i >> 56), byte(i >> 48), byte(i >> 40), byte(i >> 32), - byte(i >> 24), byte(i >> 16), byte(i >> 8), byte(i), - }) - prefix, err := addr.Prefix(128) - assert.NoError(b, err) - - tree.Add(prefix) - } - b.StartTimer() - for i := 0; i < b.N; i++ { - tree.List() - } - }) -}