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  }