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  }