github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/maps/cidrmap/cidrmap.go (about)

     1  // Copyright 2016-2017 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 cidrmap
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"unsafe"
    21  
    22  	"github.com/cilium/cilium/pkg/bpf"
    23  	"github.com/cilium/cilium/pkg/logging"
    24  	"github.com/cilium/cilium/pkg/logging/logfields"
    25  
    26  	"github.com/sirupsen/logrus"
    27  )
    28  
    29  var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "map-cidr")
    30  
    31  const (
    32  	MapName    = "cilium_cidr_"
    33  	MaxEntries = 16384
    34  )
    35  
    36  // CIDRMap refers to an LPM trie map at 'path'.
    37  type CIDRMap struct {
    38  	path      string
    39  	Fd        int
    40  	AddrSize  int // max prefix length in bytes, 4 for IPv4, 16 for IPv6
    41  	Prefixlen uint32
    42  
    43  	// PrefixIsDynamic determines whether it's valid for entries to have
    44  	// a prefix length that is not equal to the Prefixlen above
    45  	PrefixIsDynamic bool
    46  }
    47  
    48  const (
    49  	LPM_MAP_VALUE_SIZE = 1
    50  )
    51  
    52  type cidrKey struct {
    53  	Prefixlen uint32
    54  	Net       [16]byte
    55  }
    56  
    57  func (cm *CIDRMap) cidrKeyInit(cidr net.IPNet) (key cidrKey) {
    58  	ones, _ := cidr.Mask.Size()
    59  	key.Prefixlen = uint32(ones)
    60  	// IPv4 address can be represented by 16 byte slice in 'cidr.IP',
    61  	// in which case the address is at the end of the slice.
    62  	copy(key.Net[:], cidr.IP[len(cidr.IP)-cm.AddrSize:len(cidr.IP)])
    63  	return
    64  }
    65  
    66  func (cm *CIDRMap) keyCidrInit(key cidrKey) (cidr net.IPNet) {
    67  	cidr.Mask = net.CIDRMask(int(key.Prefixlen), cm.AddrSize*8)
    68  	cidr.IP = make(net.IP, cm.AddrSize)
    69  	copy(cidr.IP[len(cidr.IP)-cm.AddrSize:len(cidr.IP)], key.Net[:])
    70  	return
    71  }
    72  
    73  // checkPrefixlen checks whether it's valid to manipulate elements in the map
    74  // with the specified key. If it's unsupported, it returns an error.
    75  func (cm *CIDRMap) checkPrefixlen(key *cidrKey, operation string) error {
    76  	if cm.Prefixlen != 0 &&
    77  		((cm.PrefixIsDynamic && cm.Prefixlen < key.Prefixlen) ||
    78  			(!cm.PrefixIsDynamic && cm.Prefixlen != key.Prefixlen)) {
    79  		return fmt.Errorf("Unable to %s element with dynamic prefix length cm.Prefixlen=%d key.Prefixlen=%d",
    80  			operation, cm.Prefixlen, key.Prefixlen)
    81  	}
    82  	return nil
    83  }
    84  
    85  // InsertCIDR inserts an entry to 'cm' with key 'cidr'. Value is currently not
    86  // used.
    87  func (cm *CIDRMap) InsertCIDR(cidr net.IPNet) error {
    88  	key := cm.cidrKeyInit(cidr)
    89  	entry := [LPM_MAP_VALUE_SIZE]byte{}
    90  	if err := cm.checkPrefixlen(&key, "update"); err != nil {
    91  		return err
    92  	}
    93  	log.WithField(logfields.Path, cm.path).Debugf("Inserting CIDR entry %s", cidr.String())
    94  	return bpf.UpdateElement(cm.Fd, unsafe.Pointer(&key), unsafe.Pointer(&entry), 0)
    95  }
    96  
    97  // DeleteCIDR deletes an entry from 'cm' with key 'cidr'.
    98  func (cm *CIDRMap) DeleteCIDR(cidr net.IPNet) error {
    99  	key := cm.cidrKeyInit(cidr)
   100  	if err := cm.checkPrefixlen(&key, "delete"); err != nil {
   101  		return err
   102  	}
   103  	log.WithField(logfields.Path, cm.path).Debugf("Removing CIDR entry %s", cidr.String())
   104  	return bpf.DeleteElement(cm.Fd, unsafe.Pointer(&key))
   105  }
   106  
   107  // CIDRExists returns true if 'cidr' exists in map 'cm'
   108  func (cm *CIDRMap) CIDRExists(cidr net.IPNet) bool {
   109  	key := cm.cidrKeyInit(cidr)
   110  	var entry [LPM_MAP_VALUE_SIZE]byte
   111  	return bpf.LookupElement(cm.Fd, unsafe.Pointer(&key), unsafe.Pointer(&entry)) == nil
   112  }
   113  
   114  // CIDRNext returns next CIDR entry in map 'cm'
   115  func (cm *CIDRMap) CIDRNext(cidr *net.IPNet) *net.IPNet {
   116  	var key, keyNext cidrKey
   117  	if cidr != nil {
   118  		key = cm.cidrKeyInit(*cidr)
   119  	}
   120  	err := bpf.GetNextKey(cm.Fd, unsafe.Pointer(&key), unsafe.Pointer(&keyNext))
   121  	if err != nil {
   122  		return nil
   123  	}
   124  	out := cm.keyCidrInit(keyNext)
   125  	return &out
   126  }
   127  
   128  // CIDRDump walks map 'cm' and dumps all CIDR entries
   129  func (cm *CIDRMap) CIDRDump(to []string) []string {
   130  	var key, keyNext *net.IPNet
   131  	for {
   132  		keyNext = cm.CIDRNext(key)
   133  		if keyNext == nil {
   134  			return to
   135  		}
   136  		key = keyNext
   137  		to = append(to, key.String())
   138  	}
   139  }
   140  
   141  // String returns the path of the map.
   142  func (cm *CIDRMap) String() string {
   143  	if cm == nil {
   144  		return ""
   145  	}
   146  	return cm.path
   147  }
   148  
   149  // Close closes the FD of the given CIDRMap
   150  func (cm *CIDRMap) Close() error {
   151  	if cm == nil {
   152  		return nil
   153  	}
   154  	return bpf.ObjClose(cm.Fd)
   155  }
   156  
   157  // OpenMap opens a new CIDRMap. 'bool' returns 'true' if the map was
   158  // created, and 'false' if the map already existed. prefixdyn denotes
   159  // whether element's prefixlen can vary and we thus need to use a LPM
   160  // trie instead of hash table.
   161  func OpenMap(path string, prefixlen int, prefixdyn bool) (*CIDRMap, bool, error) {
   162  	return OpenMapElems(path, prefixlen, prefixdyn, MaxEntries)
   163  }
   164  
   165  // OpenMapElems is the same as OpenMap only with defined maxelem as argument.
   166  func OpenMapElems(path string, prefixlen int, prefixdyn bool, maxelem uint32) (*CIDRMap, bool, error) {
   167  	var typeMap = bpf.BPF_MAP_TYPE_LPM_TRIE
   168  	var prefix = 0
   169  
   170  	if prefixdyn == false {
   171  		typeMap = bpf.BPF_MAP_TYPE_HASH
   172  		prefix = prefixlen
   173  	}
   174  	if prefixlen <= 0 {
   175  		return nil, false, fmt.Errorf("prefixlen must be > 0")
   176  	}
   177  	bytes := (prefixlen-1)/8 + 1
   178  	fd, isNewMap, err := bpf.OpenOrCreateMap(
   179  		path,
   180  		typeMap,
   181  		uint32(unsafe.Sizeof(uint32(0))+uintptr(bytes)),
   182  		uint32(LPM_MAP_VALUE_SIZE),
   183  		maxelem,
   184  		bpf.BPF_F_NO_PREALLOC, 0, true,
   185  	)
   186  
   187  	if err != nil {
   188  		log.Debug("Kernel does not support CIDR maps, using hash table instead.")
   189  		typeMap = bpf.BPF_MAP_TYPE_HASH
   190  		fd, isNewMap, err = bpf.OpenOrCreateMap(
   191  			path,
   192  			typeMap,
   193  			uint32(unsafe.Sizeof(uint32(0))+uintptr(bytes)),
   194  			uint32(LPM_MAP_VALUE_SIZE),
   195  			maxelem,
   196  			bpf.BPF_F_NO_PREALLOC, 0, true,
   197  		)
   198  		if err != nil {
   199  			scopedLog := log.WithError(err).WithField(logfields.Path, path)
   200  			scopedLog.Warning("Failed to create CIDR map")
   201  			return nil, false, err
   202  		}
   203  	}
   204  
   205  	m := &CIDRMap{
   206  		path:            path,
   207  		Fd:              fd,
   208  		AddrSize:        bytes,
   209  		Prefixlen:       uint32(prefix),
   210  		PrefixIsDynamic: prefixdyn,
   211  	}
   212  
   213  	log.WithFields(logrus.Fields{
   214  		logfields.Path: path,
   215  		"fd":           fd,
   216  		"LPM":          typeMap == bpf.BPF_MAP_TYPE_LPM_TRIE,
   217  	}).Debug("Created CIDR map")
   218  
   219  	return m, isNewMap, nil
   220  }