github.com/cilium/cilium@v1.16.2/pkg/maps/cidrmap/cidrmap.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package cidrmap
     5  
     6  import (
     7  	"fmt"
     8  	"net"
     9  	"path"
    10  	"unsafe"
    11  
    12  	"github.com/cilium/ebpf"
    13  	"github.com/sirupsen/logrus"
    14  
    15  	"github.com/cilium/cilium/pkg/bpf"
    16  	"github.com/cilium/cilium/pkg/logging"
    17  	"github.com/cilium/cilium/pkg/logging/logfields"
    18  )
    19  
    20  var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "map-cidr")
    21  
    22  const (
    23  	MapName    = "cilium_cidr_"
    24  	MaxEntries = 16384
    25  )
    26  
    27  // CIDRMap refers to an LPM trie map at 'path'.
    28  type CIDRMap struct {
    29  	path      string
    30  	m         *ebpf.Map
    31  	AddrSize  int // max prefix length in bytes, 4 for IPv4, 16 for IPv6
    32  	Prefixlen uint32
    33  
    34  	// PrefixIsDynamic determines whether it's valid for entries to have
    35  	// a prefix length that is not equal to the Prefixlen above
    36  	PrefixIsDynamic bool
    37  }
    38  
    39  const (
    40  	LPM_MAP_VALUE_SIZE = 1
    41  )
    42  
    43  type cidrKey struct {
    44  	Prefixlen uint32
    45  
    46  	// v4 LPM maps have 8-byte key sizes even though the 20-byte cidrKey is used
    47  	// for map ops. This hack relies on passing unsafe.Pointers to the cidrKey so
    48  	// the kernel only accesses Prefixlen (4 bytes) and the first 4 bytes of Net.
    49  	// v4 net.IPNets are packed into those 4 first bytes.
    50  	Net [16]byte
    51  }
    52  
    53  func (cm *CIDRMap) cidrKeyInit(cidr net.IPNet) (key cidrKey) {
    54  	ones, _ := cidr.Mask.Size()
    55  	key.Prefixlen = uint32(ones)
    56  	// IPv4 address can be represented by 16 byte slice in 'cidr.IP',
    57  	// in which case the address is at the end of the slice.
    58  	copy(key.Net[:], cidr.IP[len(cidr.IP)-cm.AddrSize:len(cidr.IP)])
    59  	return
    60  }
    61  
    62  func (cm *CIDRMap) keyCidrInit(key cidrKey) (cidr net.IPNet) {
    63  	cidr.Mask = net.CIDRMask(int(key.Prefixlen), cm.AddrSize*8)
    64  	cidr.IP = make(net.IP, cm.AddrSize)
    65  	copy(cidr.IP[len(cidr.IP)-cm.AddrSize:len(cidr.IP)], key.Net[:])
    66  	return
    67  }
    68  
    69  // checkPrefixlen checks whether it's valid to manipulate elements in the map
    70  // with the specified key. If it's unsupported, it returns an error.
    71  func (cm *CIDRMap) checkPrefixlen(key *cidrKey, operation string) error {
    72  	if cm.Prefixlen != 0 &&
    73  		((cm.PrefixIsDynamic && cm.Prefixlen < key.Prefixlen) ||
    74  			(!cm.PrefixIsDynamic && cm.Prefixlen != key.Prefixlen)) {
    75  		return fmt.Errorf("Unable to %s element with dynamic prefix length cm.Prefixlen=%d key.Prefixlen=%d",
    76  			operation, cm.Prefixlen, key.Prefixlen)
    77  	}
    78  	return nil
    79  }
    80  
    81  // InsertCIDR inserts an entry to 'cm' with key 'cidr'. Value is currently not
    82  // used.
    83  func (cm *CIDRMap) InsertCIDR(cidr net.IPNet) error {
    84  	key := cm.cidrKeyInit(cidr)
    85  	entry := [LPM_MAP_VALUE_SIZE]byte{}
    86  	if err := cm.checkPrefixlen(&key, "update"); err != nil {
    87  		return err
    88  	}
    89  	log.WithField(logfields.Path, cm.path).Debugf("Inserting CIDR entry %s", cidr.String())
    90  	return cm.m.Update(unsafe.Pointer(&key), unsafe.Pointer(&entry), ebpf.UpdateAny)
    91  }
    92  
    93  // DeleteCIDR deletes an entry from 'cm' with key 'cidr'.
    94  func (cm *CIDRMap) DeleteCIDR(cidr net.IPNet) error {
    95  	key := cm.cidrKeyInit(cidr)
    96  	if err := cm.checkPrefixlen(&key, "delete"); err != nil {
    97  		return err
    98  	}
    99  	log.WithField(logfields.Path, cm.path).Debugf("Removing CIDR entry %s", cidr.String())
   100  	return cm.m.Delete(unsafe.Pointer(&key))
   101  }
   102  
   103  // CIDRExists returns true if 'cidr' exists in map 'cm'
   104  func (cm *CIDRMap) CIDRExists(cidr net.IPNet) bool {
   105  	key := cm.cidrKeyInit(cidr)
   106  	var entry [LPM_MAP_VALUE_SIZE]byte
   107  	return cm.m.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&entry)) == nil
   108  }
   109  
   110  // CIDRNext returns next CIDR entry in map 'cm'
   111  func (cm *CIDRMap) CIDRNext(cidr *net.IPNet) *net.IPNet {
   112  	var key, keyNext cidrKey
   113  	if cidr != nil {
   114  		key = cm.cidrKeyInit(*cidr)
   115  	}
   116  	if err := cm.m.NextKey(unsafe.Pointer(&key), unsafe.Pointer(&keyNext)); err != nil {
   117  		return nil
   118  	}
   119  	out := cm.keyCidrInit(keyNext)
   120  	return &out
   121  }
   122  
   123  // CIDRDump walks map 'cm' and dumps all CIDR entries
   124  func (cm *CIDRMap) CIDRDump(to []string) []string {
   125  	var key, keyNext *net.IPNet
   126  	for {
   127  		keyNext = cm.CIDRNext(key)
   128  		if keyNext == nil {
   129  			return to
   130  		}
   131  		key = keyNext
   132  		to = append(to, key.String())
   133  	}
   134  }
   135  
   136  // String returns the path of the map.
   137  func (cm *CIDRMap) String() string {
   138  	if cm == nil {
   139  		return ""
   140  	}
   141  	return cm.path
   142  }
   143  
   144  // Close closes the FD of the given CIDRMap
   145  func (cm *CIDRMap) Close() error {
   146  	if cm == nil {
   147  		return nil
   148  	}
   149  	return cm.m.Close()
   150  }
   151  
   152  // OpenMapElems is the same as OpenMap only with defined maxelem as argument.
   153  func OpenMapElems(pinPath string, prefixlen int, prefixdyn bool, maxelem uint32) (*CIDRMap, error) {
   154  	mapType := ebpf.LPMTrie
   155  	prefix := 0
   156  
   157  	if !prefixdyn {
   158  		mapType = ebpf.Hash
   159  		prefix = prefixlen
   160  	}
   161  	if prefixlen <= 0 {
   162  		return nil, fmt.Errorf("prefixlen must be > 0")
   163  	}
   164  	bytes := (prefixlen-1)/8 + 1
   165  	m, err := bpf.OpenOrCreateMap(&ebpf.MapSpec{
   166  		Name:       path.Base(pinPath),
   167  		Type:       mapType,
   168  		KeySize:    uint32(unsafe.Sizeof(uint32(0)) + uintptr(bytes)),
   169  		ValueSize:  uint32(LPM_MAP_VALUE_SIZE),
   170  		MaxEntries: maxelem,
   171  		Flags:      bpf.BPF_F_NO_PREALLOC,
   172  		Pinning:    ebpf.PinByName,
   173  	}, path.Dir(pinPath))
   174  
   175  	if err != nil {
   176  		scopedLog := log.WithError(err).WithField(logfields.Path, pinPath)
   177  		scopedLog.Warning("Failed to create CIDR map")
   178  		return nil, err
   179  	}
   180  
   181  	log.WithFields(logrus.Fields{
   182  		logfields.Path: pinPath,
   183  		"fd":           m.FD(),
   184  		"LPM":          m.Type() == ebpf.LPMTrie,
   185  	}).Debug("Created CIDR map")
   186  
   187  	return &CIDRMap{
   188  		path:            pinPath,
   189  		m:               m,
   190  		AddrSize:        bytes,
   191  		Prefixlen:       uint32(prefix),
   192  		PrefixIsDynamic: prefixdyn,
   193  	}, nil
   194  }