github.com/cilium/cilium@v1.16.2/operator/pkg/lbipam/range_store.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package lbipam 5 6 import ( 7 "fmt" 8 "net/netip" 9 "slices" 10 11 "github.com/cilium/cilium/pkg/ipalloc" 12 cilium_api_v2alpha1 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" 13 ) 14 15 type rangesStore struct { 16 ranges []*LBRange 17 poolToRanges map[string][]*LBRange 18 sharingKeyToServiceViewIPs map[string][]*ServiceViewIP 19 } 20 21 func newRangesStore() rangesStore { 22 return rangesStore{ 23 poolToRanges: make(map[string][]*LBRange), 24 sharingKeyToServiceViewIPs: make(map[string][]*ServiceViewIP), 25 } 26 } 27 28 func (rs *rangesStore) Delete(lbRange *LBRange) { 29 idx := slices.Index(rs.ranges, lbRange) 30 if idx != -1 { 31 rs.ranges = slices.Delete(rs.ranges, idx, idx+1) 32 } 33 34 poolRanges := rs.poolToRanges[lbRange.originPool] 35 36 idx = slices.Index(poolRanges, lbRange) 37 if idx != -1 { 38 poolRanges = slices.Delete(poolRanges, idx, idx+1) 39 } 40 41 if len(poolRanges) > 0 { 42 rs.poolToRanges[lbRange.originPool] = poolRanges 43 } else { 44 delete(rs.poolToRanges, lbRange.originPool) 45 } 46 } 47 48 func (rs *rangesStore) Add(lbRange *LBRange) { 49 rs.ranges = append(rs.ranges, lbRange) 50 poolRanges := rs.poolToRanges[lbRange.originPool] 51 poolRanges = append(poolRanges, lbRange) 52 rs.poolToRanges[lbRange.originPool] = poolRanges 53 } 54 55 func (rs *rangesStore) GetRangesForPool(name string) ([]*LBRange, bool) { 56 ranges, found := rs.poolToRanges[name] 57 return ranges, found 58 } 59 60 func (rs *rangesStore) GetServiceViewIPsForSharingKey(sk string) ([]*ServiceViewIP, bool) { 61 serviceViewIPs, found := rs.sharingKeyToServiceViewIPs[sk] 62 return serviceViewIPs, found 63 } 64 65 func (rs *rangesStore) AddServiceViewIPForSharingKey(sk string, svip *ServiceViewIP) { 66 serviceViewIPs, found := rs.sharingKeyToServiceViewIPs[sk] 67 if !found { 68 serviceViewIPs = make([]*ServiceViewIP, 0) 69 serviceViewIPs = append(serviceViewIPs, svip) 70 } else { 71 for _, serviceViewIP := range serviceViewIPs { 72 if *serviceViewIP == *svip { 73 return 74 } 75 } 76 serviceViewIPs = append(serviceViewIPs, svip) 77 } 78 rs.sharingKeyToServiceViewIPs[sk] = serviceViewIPs 79 } 80 81 func (rs *rangesStore) DeleteServiceViewIPForSharingKey(sk string, svip *ServiceViewIP) { 82 serviceViewIPs, found := rs.sharingKeyToServiceViewIPs[sk] 83 if !found { 84 return 85 } 86 for i, serviceViewIP := range serviceViewIPs { 87 if *serviceViewIP == *svip { 88 serviceViewIPs = slices.Delete(serviceViewIPs, i, i+1) 89 break 90 } 91 } 92 if len(serviceViewIPs) > 0 { 93 rs.sharingKeyToServiceViewIPs[sk] = serviceViewIPs 94 } else { 95 delete(rs.sharingKeyToServiceViewIPs, sk) 96 } 97 } 98 99 type LBRange struct { 100 // the actual data of which ips have been allocated or not and to what services 101 alloc ipalloc.Allocator[[]*ServiceView] 102 // If true, the LB range has been disabled via the CRD and thus no IPs should be allocated from this range 103 externallyDisabled bool 104 // If true, the LB range has been disabled by us, because it conflicts with other ranges for example. 105 // This range should not be used for allocation. 106 internallyDisabled bool 107 // The name of the pool that originated this LB range 108 originPool string 109 } 110 111 func NewLBRange(from, to netip.Addr, pool *cilium_api_v2alpha1.CiliumLoadBalancerIPPool) (*LBRange, error) { 112 alloc, err := ipalloc.NewHashAllocator[[]*ServiceView](from, to, 0) 113 if err != nil { 114 return nil, fmt.Errorf("new cidr range: %w", err) 115 } 116 117 return &LBRange{ 118 alloc: alloc, 119 internallyDisabled: false, 120 externallyDisabled: pool.Spec.Disabled, 121 originPool: pool.GetName(), 122 }, err 123 } 124 125 func (lr *LBRange) Disabled() bool { 126 return lr.internallyDisabled || lr.externallyDisabled 127 } 128 129 func (lr *LBRange) String() string { 130 from, to := lr.alloc.Range() 131 used, available := lr.alloc.Stats() 132 return fmt.Sprintf( 133 "%s - %s (free: %s, used: %d, intDis: %v, extDis: %v) - origin %s", 134 from, 135 to, 136 available, 137 used, 138 lr.internallyDisabled, 139 lr.externallyDisabled, 140 lr.originPool, 141 ) 142 } 143 144 func (lr *LBRange) Equal(other *LBRange) bool { 145 lrFrom, lrTo := lr.alloc.Range() 146 otherFrom, otherTo := other.alloc.Range() 147 return lrFrom == otherFrom && lrTo == otherTo 148 } 149 150 func (lr *LBRange) EqualCIDR(from, to netip.Addr) bool { 151 lrFrom, lrTo := lr.alloc.Range() 152 return lrFrom == from && lrTo == to 153 } 154 155 func ipNetStr(rng *LBRange) string { 156 from, to := rng.alloc.Range() 157 return fmt.Sprintf("%s - %s", from, to) 158 } 159 160 // areRangesInternallyConflicting checks if any of the ranges within the same list conflict with each other. 161 func areRangesInternallyConflicting(ranges []*LBRange) (conflicting bool, rangeA, rangeB *LBRange) { 162 for i, outer := range ranges { 163 for ii, inner := range ranges { 164 if i == ii { 165 continue 166 } 167 168 if !rangesIntersect(outer, inner) { 169 continue 170 } 171 172 return true, outer, inner 173 } 174 } 175 176 return false, nil, nil 177 } 178 179 func areRangesConflicting(outerRanges, innerRanges []*LBRange) (conflicting bool, targetRange, conflictingRange *LBRange) { 180 for _, outerRange := range outerRanges { 181 for _, innerRange := range innerRanges { 182 // no intersection, no conflict 183 if !rangesIntersect(outerRange, innerRange) { 184 continue 185 } 186 187 return true, outerRange, innerRange 188 } 189 } 190 191 return false, nil, nil 192 } 193 194 func rangesIntersect(one, two *LBRange) bool { 195 oneFrom, oneTo := one.alloc.Range() 196 twoFrom, twoTo := two.alloc.Range() 197 return intersect(oneFrom, oneTo, twoFrom, twoTo) 198 } 199 200 func intersect(from1, to1, from2, to2 netip.Addr) bool { 201 return from1.Compare(to2) <= 0 && from2.Compare(to1) <= 0 202 }