github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/map_lpm_trie.go (about)

     1  package gobpfld
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"reflect"
     7  
     8  	"github.com/dylandreimerink/gobpfld/bpfsys"
     9  	"github.com/dylandreimerink/gobpfld/bpftypes"
    10  	"github.com/dylandreimerink/gobpfld/kernelsupport"
    11  )
    12  
    13  var _ BPFMap = (*LPMTrieMap)(nil)
    14  
    15  // LPMTrieMap is a specialized map type which used Longest Prefix Matching on the key when getting values from the map.
    16  // LPM is commonly used in routing tables or any other application where the most specific network range should
    17  // overrule settings of less specific network ranges.
    18  type LPMTrieMap struct {
    19  	AbstractMap
    20  }
    21  
    22  func (m *LPMTrieMap) Load() error {
    23  	if m.Definition.Type != bpftypes.BPF_MAP_TYPE_LPM_TRIE {
    24  		return fmt.Errorf("map type in definition must be BPF_MAP_TYPE_LPM_TRIE when using an LPMTrieMap")
    25  	}
    26  
    27  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrie) {
    28  		return fmt.Errorf("LPM trie map type is not supported by the current kernel version")
    29  	}
    30  
    31  	err := m.load(nil)
    32  	if err != nil {
    33  		return err
    34  	}
    35  
    36  	err = mapRegister.add(m)
    37  	if err != nil {
    38  		return fmt.Errorf("map register: %w", err)
    39  	}
    40  
    41  	return nil
    42  }
    43  
    44  // Close closes the file descriptor associate with the map, this will cause the map to unload from the kernel
    45  // if it is not still in use by a eBPF program, bpf FS, or a userspace program still holding a fd to the map.
    46  func (m *LPMTrieMap) Close() error {
    47  	err := mapRegister.delete(m)
    48  	if err != nil {
    49  		return fmt.Errorf("map register: %w", err)
    50  	}
    51  
    52  	return m.close()
    53  }
    54  
    55  func (m *LPMTrieMap) Get(key LPMTrieKey, value interface{}) error {
    56  	return m.get(key, value)
    57  }
    58  
    59  // GetBatch fills the keys and values array/slice with the keys and values inside the map up to a maximum of
    60  // maxBatchSize entries. The keys and values array/slice must have at least a length of maxBatchSize.
    61  // The key and value of an entry is has the same index, so for example the value for keys[2] is in values[2].
    62  // Count is the amount of entries returns, partial is true if not all elements of keys and values could be set.
    63  //
    64  // This function is intended for small maps which can be read into userspace all at once since
    65  // GetBatch can only read from the beginning of the map. If the map is to large to read all at once
    66  // a iterator should be used instead of the Get or GetBatch function.
    67  func (m *LPMTrieMap) GetBatch(
    68  	keys interface{},
    69  	values interface{},
    70  	maxBatchSize uint32,
    71  ) (
    72  	count int,
    73  	partial bool,
    74  	err error,
    75  ) {
    76  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieBatchOps) {
    77  		return 0, false, fmt.Errorf("batch get operation not support on LPM trie map type on current kernel version")
    78  	}
    79  
    80  	if err := m.checkLPMKeyBatch(keys); err != nil {
    81  		return 0, false, err
    82  	}
    83  
    84  	return m.getBatch(keys, values, maxBatchSize)
    85  }
    86  
    87  func (m *LPMTrieMap) checkLPMKeyBatch(keys interface{}) error {
    88  	keyType := reflect.TypeOf(keys)
    89  	if keyType.Kind() != reflect.Ptr {
    90  		return fmt.Errorf("keys argument must be a pointer")
    91  	}
    92  
    93  	collection := keyType.Elem()
    94  	var elem reflect.Type
    95  
    96  	switch collection.Kind() {
    97  	case reflect.Array:
    98  		elem = collection.Elem()
    99  	case reflect.Slice:
   100  		elem = collection.Elem()
   101  	default:
   102  		return fmt.Errorf("keys argument must be a pointer to an array or slice")
   103  	}
   104  
   105  	// Check if a pointer to elements of the slice would implement LPMTrieKey
   106  	var keyInterface LPMTrieKey
   107  	elemPtr := reflect.PtrTo(elem)
   108  	if !elemPtr.Implements(reflect.TypeOf(&keyInterface).Elem()) {
   109  		return fmt.Errorf("keys argument must be a pointer to an array or slice op LPMKey elements")
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func (m *LPMTrieMap) Set(key LPMTrieKey, value interface{}, flags bpfsys.BPFAttrMapElemFlags) error {
   116  	return m.set(key, value, flags)
   117  }
   118  
   119  func (m *LPMTrieMap) SetBatch(
   120  	keys interface{},
   121  	values interface{},
   122  	flags bpfsys.BPFAttrMapElemFlags,
   123  	maxBatchSize uint32,
   124  ) (
   125  	count int,
   126  	err error,
   127  ) {
   128  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieBatchOps) {
   129  		return 0, fmt.Errorf("batch set operation not support on LPM trie map type on current kernel version")
   130  	}
   131  
   132  	if err := m.checkLPMKeyBatch(keys); err != nil {
   133  		return 0, err
   134  	}
   135  
   136  	return m.setBatch(keys, values, flags, maxBatchSize)
   137  }
   138  
   139  func (m *LPMTrieMap) Delete(key LPMTrieKey) error {
   140  	return m.delete(key)
   141  }
   142  
   143  func (m *LPMTrieMap) DeleteBatch(
   144  	keys interface{},
   145  	maxBatchSize uint32,
   146  ) (
   147  	count int,
   148  	err error,
   149  ) {
   150  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieBatchOps) {
   151  		return 0, fmt.Errorf("batch delete operation not support on LPM trie map type on current kernel version")
   152  	}
   153  
   154  	if err := m.checkLPMKeyBatch(keys); err != nil {
   155  		return 0, err
   156  	}
   157  
   158  	return m.deleteBatch(keys, maxBatchSize)
   159  }
   160  
   161  func (m *LPMTrieMap) Iterator() MapIterator {
   162  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieNextKey) {
   163  		return &errIterator{
   164  			// TODO provide this feature by maintaining a map of all keys in userspace and returning a custom
   165  			//  iterator.
   166  			err: fmt.Errorf("current kernel version doesn't support LPM map iteration"),
   167  		}
   168  	}
   169  
   170  	// If the kernel doesn't have support for batch lookup, use single lookup
   171  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapLPMTrieBatchOps) {
   172  		return &singleLookupIterator{
   173  			BPFMap: m,
   174  		}
   175  	}
   176  
   177  	// If there is no reason not to use the batch lookup iterator, use it
   178  	return &batchLookupIterator{
   179  		BPFMap: m,
   180  	}
   181  }
   182  
   183  // LPMKeyFromNetwork converts an net.IPNet struct into an LPMTrieKey
   184  func LPMKeyFromNetwork(n net.IPNet) LPMTrieKey {
   185  	ones, _ := n.Mask.Size()
   186  	if n.IP.To4() != nil {
   187  		key := &LPMTrieIPv4Key{
   188  			Prefix: uint32(ones),
   189  		}
   190  		copy(key.Address[:], n.IP)
   191  		return key
   192  	}
   193  
   194  	key := &LPMTrieIPv6Key{
   195  		Prefix: uint32(ones),
   196  	}
   197  	copy(key.Address[:], n.IP)
   198  	return key
   199  }
   200  
   201  type LPMTrieKey interface {
   202  	LPMTrieKey()
   203  }
   204  
   205  var _ LPMTrieKey = (*LPMTrieIPv4Key)(nil)
   206  
   207  type LPMTrieIPv4Key struct {
   208  	Prefix  uint32
   209  	Address [4]byte
   210  }
   211  
   212  // LPMTrieKey does nothing, it is just defined so LPMTrieIPv4Key implements LPMTrieKey
   213  func (k *LPMTrieIPv4Key) LPMTrieKey() {}
   214  
   215  var _ LPMTrieKey = (*LPMTrieIPv6Key)(nil)
   216  
   217  type LPMTrieIPv6Key struct {
   218  	Prefix  uint32
   219  	Address [16]byte
   220  }
   221  
   222  // LPMTrieKey does nothing, it is just defined so LPMTrieIPv6Key implements LPMTrieKey
   223  func (k *LPMTrieIPv6Key) LPMTrieKey() {}