feature/conn25: keep addrAssignments through pool reconfig
Fixes tailscale/corp#40250 Signed-off-by: Fran Bull <fran@tailscale.com>
This commit is contained in:
@@ -20,6 +20,9 @@ var errNotOurAddress = errors.New("not our address")
|
||||
// errAddrExists is returned if a returned address is already in the returned pool.
|
||||
var errAddrExists = errors.New("address already returned")
|
||||
|
||||
// errUninitializedIPPool is returned if the pool is used when it's not initialized
|
||||
var errUninitializedIPPool = errors.New("uninitialized ippool")
|
||||
|
||||
// ipSetIterator allows for round robin iteration over all the addresses within a netipx.IPSet.
|
||||
// netipx.IPSet has a Ranges call that returns the "minimum and sorted set of IP ranges that covers [the set]".
|
||||
// netipx.IPRange is "an inclusive range of IP addresses from the same address family.". So we can iterate over
|
||||
@@ -72,12 +75,23 @@ func newIPPool(ipset *netipx.IPSet) *ippool {
|
||||
}
|
||||
|
||||
type ippool struct {
|
||||
ipSet *netipx.IPSet
|
||||
// ipSet defines the addresses within the ippool, it is configured by the user.
|
||||
ipSet *netipx.IPSet
|
||||
// ipSetIterator keeps track of iteration through the ippool.
|
||||
ipSetIterator *ipSetIterator
|
||||
inUse *set.Set[netip.Addr]
|
||||
// inUse is a set of addresses that have been handed out and not yet returned.
|
||||
// Addresses in inUse won't be returned from next.
|
||||
// Addresses in inUse may no longer be in the ipSet definition of the pool bounds
|
||||
// if the ippool has been reconfigured.
|
||||
inUse *set.Set[netip.Addr]
|
||||
}
|
||||
|
||||
// next returns the next available address from within the ippool.
|
||||
// next will return errPoolExhausted if there are no more unused addresses.
|
||||
func (ipp *ippool) next() (netip.Addr, error) {
|
||||
if ipp == nil || ipp.ipSetIterator == nil {
|
||||
return netip.Addr{}, errUninitializedIPPool
|
||||
}
|
||||
a, err := ipp.ipSetIterator.next()
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
@@ -96,13 +110,33 @@ func (ipp *ippool) next() (netip.Addr, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// returnAddr puts an address back into the ippool, that address will
|
||||
// now be available to be handed out when we iterate back around to it.
|
||||
// returnAddr will return an error if the provided address is not one
|
||||
// that's currently in inUse.
|
||||
func (ipp *ippool) returnAddr(a netip.Addr) error {
|
||||
if ipp.inUse.Contains(a) {
|
||||
ipp.inUse.Delete(a)
|
||||
return nil
|
||||
}
|
||||
if !ipp.ipSet.Contains(a) {
|
||||
return errNotOurAddress
|
||||
}
|
||||
if !ipp.inUse.Contains(a) {
|
||||
return errAddrExists
|
||||
}
|
||||
ipp.inUse.Delete(a)
|
||||
return nil
|
||||
return errAddrExists
|
||||
}
|
||||
|
||||
// reconfig changes the definition of the addresses that are in the ippool
|
||||
// while keeping track of the addresses that are currently in inUse.
|
||||
func (ipp *ippool) reconfig(ipSet *netipx.IPSet) *ippool {
|
||||
if ipp != nil && ipSet != nil && ipSet.Equal(ipp.ipSet) {
|
||||
// in the common case that the definition has not changed, do nothing.
|
||||
return ipp
|
||||
}
|
||||
newPool := newIPPool(ipSet)
|
||||
if ipp != nil {
|
||||
// even if the definition of which addresses are in the pool has changed
|
||||
// we don't want to lose track of which addresses are currently in use
|
||||
newPool.inUse = ipp.inUse
|
||||
}
|
||||
return newPool
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user