github.com/datadog/cilium@v1.6.12/pkg/counter/prefixes.go (about)

     1  // Copyright 2018 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package counter
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  
    21  	"github.com/cilium/cilium/pkg/lock"
    22  )
    23  
    24  // PrefixLengthCounter tracks references to prefix lengths, limited by the
    25  // maxUniquePrefixes count. Neither of the IPv4 or IPv6 counters nested within
    26  // may contain more keys than the specified maximum number of unique prefixes.
    27  type PrefixLengthCounter struct {
    28  	lock.RWMutex
    29  
    30  	v4 IntCounter
    31  	v6 IntCounter
    32  
    33  	maxUniquePrefixes4 int
    34  	maxUniquePrefixes6 int
    35  }
    36  
    37  // NewPrefixLengthCounter returns a new PrefixLengthCounter which limits
    38  // insertions to the specified maximum number of unique prefix lengths.
    39  func NewPrefixLengthCounter(maxUniquePrefixes6, maxUniquePrefixes4 int) *PrefixLengthCounter {
    40  	return &PrefixLengthCounter{
    41  		v4:                 make(IntCounter),
    42  		v6:                 make(IntCounter),
    43  		maxUniquePrefixes4: maxUniquePrefixes4,
    44  		maxUniquePrefixes6: maxUniquePrefixes6,
    45  	}
    46  }
    47  
    48  // checkLimits checks whether the specified new count of prefixes would exceed
    49  // the specified limit on the maximum number of unique keys, and returns an
    50  // error if it would exceed the limit.
    51  func checkLimits(current, newCount, max int) error {
    52  	if newCount > max {
    53  		return fmt.Errorf("adding specified prefixes would result in too many prefix lengths (current: %d, result: %d, max: %d)",
    54  			current, newCount, max)
    55  	}
    56  	return nil
    57  }
    58  
    59  // Add increments references to prefix lengths for the specified IPNets to the
    60  // counter. If the maximum number of unique prefix lengths would be exceeded,
    61  // returns an error.
    62  //
    63  // Returns true if adding these prefixes results in an increase in the total
    64  // number of unique prefix lengths in the counter.
    65  func (p *PrefixLengthCounter) Add(prefixes []*net.IPNet) (bool, error) {
    66  	p.Lock()
    67  	defer p.Unlock()
    68  
    69  	// Assemble a map of references that need to be added
    70  	newV4Counter := p.v4.DeepCopy()
    71  	newV6Counter := p.v6.DeepCopy()
    72  	newV4Prefixes := false
    73  	newV6Prefixes := false
    74  	for _, prefix := range prefixes {
    75  		ones, bits := prefix.Mask.Size()
    76  
    77  		switch bits {
    78  		case net.IPv4len * 8:
    79  			if newV4Counter.Add(ones) {
    80  				newV4Prefixes = true
    81  			}
    82  		case net.IPv6len * 8:
    83  			if newV6Counter.Add(ones) {
    84  				newV6Prefixes = true
    85  			}
    86  		default:
    87  			return false, fmt.Errorf("unsupported IPAddr bitlength %d", bits)
    88  		}
    89  	}
    90  
    91  	// Check if they can be added given the limit in place
    92  	if newV4Prefixes {
    93  		if err := checkLimits(len(p.v4), len(newV4Counter), p.maxUniquePrefixes4); err != nil {
    94  			return false, err
    95  		}
    96  	}
    97  	if newV6Prefixes {
    98  		if err := checkLimits(len(p.v6), len(newV6Counter), p.maxUniquePrefixes6); err != nil {
    99  			return false, err
   100  		}
   101  	}
   102  
   103  	// Set and return whether anything changed
   104  	p.v4 = newV4Counter
   105  	p.v6 = newV6Counter
   106  	return newV4Prefixes || newV6Prefixes, nil
   107  }
   108  
   109  // Delete reduces references to prefix lengths in the the specified IPNets from
   110  // the counter. Returns true if removing references to these prefix lengths
   111  // would result in a decrese in the total number of unique prefix lengths in
   112  // the counter.
   113  func (p *PrefixLengthCounter) Delete(prefixes []*net.IPNet) (changed bool) {
   114  	p.Lock()
   115  	defer p.Unlock()
   116  
   117  	for _, prefix := range prefixes {
   118  		ones, bits := prefix.Mask.Size()
   119  		switch bits {
   120  		case net.IPv4len * 8:
   121  			if p.v4.Delete(ones) {
   122  				changed = true
   123  			}
   124  		case net.IPv6len * 8:
   125  			if p.v6.Delete(ones) {
   126  				changed = true
   127  			}
   128  		}
   129  	}
   130  
   131  	return changed
   132  }
   133  
   134  // ToBPFData converts the counter into a set of prefix lengths that the BPF
   135  // datapath can use for LPM lookup.
   136  func (p *PrefixLengthCounter) ToBPFData() (s6, s4 []int) {
   137  	p.RLock()
   138  	defer p.RUnlock()
   139  
   140  	return p.v6.ToBPFData(), p.v4.ToBPFData()
   141  }