github.com/cilium/cilium@v1.16.2/pkg/counter/prefixes.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package counter 5 6 import ( 7 "fmt" 8 "net" 9 "net/netip" 10 11 "github.com/cilium/cilium/pkg/lock" 12 ) 13 14 // PrefixLengthCounter tracks references to prefix lengths, limited by the 15 // maxUniquePrefixes count. Neither of the IPv4 or IPv6 counters nested within 16 // may contain more keys than the specified maximum number of unique prefixes. 17 type PrefixLengthCounter struct { 18 lock.RWMutex 19 20 v4 IntCounter 21 v6 IntCounter 22 23 maxUniquePrefixes4 int 24 maxUniquePrefixes6 int 25 } 26 27 // NewPrefixLengthCounter returns a new PrefixLengthCounter which limits 28 // insertions to the specified maximum number of unique prefix lengths. 29 func NewPrefixLengthCounter(maxUniquePrefixes6, maxUniquePrefixes4 int) *PrefixLengthCounter { 30 return &PrefixLengthCounter{ 31 v4: make(IntCounter), 32 v6: make(IntCounter), 33 maxUniquePrefixes4: maxUniquePrefixes4, 34 maxUniquePrefixes6: maxUniquePrefixes6, 35 } 36 } 37 38 func createIPNet(ones, bits int) netip.Prefix { 39 var addr netip.Addr 40 switch bits { 41 case net.IPv4len * 8: 42 addr = netip.IPv4Unspecified() 43 case net.IPv6len * 8: 44 addr = netip.IPv6Unspecified() 45 default: 46 // fall through to default library error 47 } 48 return netip.PrefixFrom(addr, ones) 49 } 50 51 // DefaultPrefixLengthCounter creates a default prefix length counter that 52 // already counts the minimum and maximum prefix lengths for IP hosts and 53 // default routes (ie, /32 and /0). As with NewPrefixLengthCounter, insertions 54 // are limited to the specified maximum number of unique prefix lengths. 55 func DefaultPrefixLengthCounter() *PrefixLengthCounter { 56 maxIPv4 := net.IPv4len*8 + 1 57 maxIPv6 := net.IPv6len*8 + 1 58 counter := NewPrefixLengthCounter(maxIPv6, maxIPv4) 59 60 defaultPrefixes := []netip.Prefix{ 61 // IPv4 62 createIPNet(0, net.IPv4len*8), // world 63 createIPNet(net.IPv4len*8, net.IPv4len*8), // hosts 64 65 // IPv6 66 createIPNet(0, net.IPv6len*8), // world 67 createIPNet(net.IPv6len*8, net.IPv6len*8), // hosts 68 } 69 if _, err := counter.Add(defaultPrefixes); err != nil { 70 panic(fmt.Errorf("Failed to create default prefix lengths: %w", err)) 71 } 72 73 return counter 74 } 75 76 // checkLimits checks whether the specified new count of prefixes would exceed 77 // the specified limit on the maximum number of unique keys, and returns an 78 // error if it would exceed the limit. 79 func checkLimits(current, newCount, max int) error { 80 if newCount > max { 81 return fmt.Errorf("adding specified prefixes would result in too many prefix lengths (current: %d, result: %d, max: %d)", 82 current, newCount, max) 83 } 84 return nil 85 } 86 87 // Add increments references to prefix lengths for the specified IPNets to the 88 // counter. If the maximum number of unique prefix lengths would be exceeded, 89 // returns an error. 90 // 91 // Returns true if adding these prefixes results in an increase in the total 92 // number of unique prefix lengths in the counter. 93 func (p *PrefixLengthCounter) Add(prefixes []netip.Prefix) (bool, error) { 94 p.Lock() 95 defer p.Unlock() 96 97 // Assemble a map of references that need to be added 98 newV4Counter := p.v4.DeepCopy() 99 newV6Counter := p.v6.DeepCopy() 100 newV4Prefixes := false 101 newV6Prefixes := false 102 for _, prefix := range prefixes { 103 ones := prefix.Bits() 104 bits := prefix.Addr().BitLen() 105 106 switch bits { 107 case net.IPv4len * 8: 108 if newV4Counter.Add(ones) { 109 newV4Prefixes = true 110 } 111 case net.IPv6len * 8: 112 if newV6Counter.Add(ones) { 113 newV6Prefixes = true 114 } 115 default: 116 return false, fmt.Errorf("unsupported IPAddr bitlength %d", bits) 117 } 118 } 119 120 // Check if they can be added given the limit in place 121 if newV4Prefixes { 122 if err := checkLimits(len(p.v4), len(newV4Counter), p.maxUniquePrefixes4); err != nil { 123 return false, err 124 } 125 } 126 if newV6Prefixes { 127 if err := checkLimits(len(p.v6), len(newV6Counter), p.maxUniquePrefixes6); err != nil { 128 return false, err 129 } 130 } 131 132 // Set and return whether anything changed 133 p.v4 = newV4Counter 134 p.v6 = newV6Counter 135 return newV4Prefixes || newV6Prefixes, nil 136 } 137 138 // Delete reduces references to prefix lengths in the specified IPNets from 139 // the counter. Returns true if removing references to these prefix lengths 140 // would result in a decrese in the total number of unique prefix lengths in 141 // the counter. 142 func (p *PrefixLengthCounter) Delete(prefixes []netip.Prefix) (changed bool) { 143 p.Lock() 144 defer p.Unlock() 145 146 for _, prefix := range prefixes { 147 ones := prefix.Bits() 148 bits := prefix.Addr().BitLen() 149 switch bits { 150 case net.IPv4len * 8: 151 if p.v4.Delete(ones) { 152 changed = true 153 } 154 case net.IPv6len * 8: 155 if p.v6.Delete(ones) { 156 changed = true 157 } 158 } 159 } 160 161 return changed 162 } 163 164 // ToBPFData converts the counter into a set of prefix lengths that the BPF 165 // datapath can use for LPM lookup. 166 func (p *PrefixLengthCounter) ToBPFData() (s6, s4 []int) { 167 p.RLock() 168 defer p.RUnlock() 169 170 return p.v6.ToBPFData(), p.v4.ToBPFData() 171 }