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

     1  package gobpfld
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/dylandreimerink/gobpfld/bpfsys"
     7  	"github.com/dylandreimerink/gobpfld/bpftypes"
     8  	"github.com/dylandreimerink/gobpfld/kernelsupport"
     9  )
    10  
    11  var _ BPFMap = (*HashOfMapsMap)(nil)
    12  
    13  // HashOfMapsMap is a map with as value another map, the value type must be any loaded BPF map. The key type can be
    14  // anything, the keys are hashed and thus do not need to be contiguous.
    15  type HashOfMapsMap struct {
    16  	AbstractMap
    17  
    18  	// InnerMapDef is the definition of the inner map
    19  	// TODO: Once BTF is implemented we can infer this map type from the BTF debug symbols
    20  	InnerMapDef BPFMapDef
    21  	// innerMapDef is a copy of the publicly available map def, so it can't be change while the map is loaded
    22  	innerMapDef BPFMapDef
    23  }
    24  
    25  func (m *HashOfMapsMap) Load() error {
    26  	if m.Definition.Type != bpftypes.BPF_MAP_TYPE_HASH_OF_MAPS {
    27  		return fmt.Errorf("map type in definition must be BPF_MAP_TYPE_HASH_OF_MAPS when using an HashOfMapsMap")
    28  	}
    29  
    30  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapHashOfMaps) {
    31  		return fmt.Errorf("Hash of maps map type is not supported by the current kernel version")
    32  	}
    33  
    34  	err := m.loadHashOfMaps()
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	err = mapRegister.add(m)
    40  	if err != nil {
    41  		return fmt.Errorf("map register: %w", err)
    42  	}
    43  
    44  	return nil
    45  }
    46  
    47  // loadPseudoInnerMap will load a map into the kernel with m.InnerMapDef as definition and returns the FD for this map.
    48  func (m *HashOfMapsMap) loadPseudoInnerMap() (*AbstractMap, error) {
    49  	if kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapDynamicInnerMap) {
    50  		// Set the inner map flag
    51  		m.InnerMapDef.Flags |= bpftypes.BPFMapFlagsInnerMap
    52  	}
    53  
    54  	innerMap := &AbstractMap{
    55  		Name:       MustNewObjName("pseudoinnermap"),
    56  		Definition: m.InnerMapDef,
    57  	}
    58  
    59  	return innerMap, innerMap.load(nil)
    60  }
    61  
    62  // loadArrayOfMaps will first create a pseudo map, the FD of which we need to pass when loading the outermap to provide
    63  // type information. The verifier uses this type information to verify how values from the inner map are used.
    64  func (m *HashOfMapsMap) loadHashOfMaps() error {
    65  	err := m.Definition.Validate()
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	innerMap, err := m.loadPseudoInnerMap()
    71  	if err != nil {
    72  		return fmt.Errorf("load pseudo inner map: %w", err)
    73  	}
    74  	// Copy the def so it can't be changed after loading
    75  	m.innerMapDef = m.InnerMapDef
    76  
    77  	err = m.load(func(attr *bpfsys.BPFAttrMapCreate) {
    78  		attr.InnerMapFD = innerMap.fd
    79  	})
    80  	if err != nil {
    81  		return fmt.Errorf("load: %w", err)
    82  	}
    83  
    84  	// After the outer map has been loaded, the inner map type info is copied so we can unload the pseudo inner map.
    85  	err = innerMap.close()
    86  	if err != nil {
    87  		return fmt.Errorf("inner pseudomap unload: %w", err)
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  // Close closes the file descriptor associate with the map, this will cause the map to unload from the kernel
    94  // if it is not still in use by a eBPF program, bpf FS, or a userspace program still holding a fd to the map.
    95  func (m *HashOfMapsMap) Close() error {
    96  	err := mapRegister.delete(m)
    97  	if err != nil {
    98  		return fmt.Errorf("map register: %w", err)
    99  	}
   100  
   101  	return m.close()
   102  }
   103  
   104  func (m *HashOfMapsMap) Get(key interface{}) (BPFMap, error) {
   105  	var id uint32
   106  	err := m.get(&key, &id)
   107  	if err != nil {
   108  		return nil, fmt.Errorf("map get: %w", err)
   109  	}
   110  
   111  	return MapFromID(id)
   112  }
   113  
   114  func (m *HashOfMapsMap) Set(key interface{}, value BPFMap, flags bpfsys.BPFAttrMapElemFlags) error {
   115  	def := value.GetDefinition()
   116  	if def.Flags&bpftypes.BPFMapFlagsInnerMap != 0 {
   117  		// If the inner map flag is set, max entries can be ignored when comparing inner maps.
   118  		// Since the Equal function doesn't take this edge case
   119  		// into account we will just make the MaxEntries of def equal.
   120  		// This doesn't update the actual value of the map since we are working with a copy of the definition
   121  		def.MaxEntries = m.innerMapDef.MaxEntries
   122  	}
   123  	if !def.Equal(m.innerMapDef) {
   124  		return fmt.Errorf("map definition of the 'value' doesn't match the inner map definition")
   125  	}
   126  
   127  	fd := value.GetFD()
   128  	return m.set(key, &fd, flags)
   129  }
   130  
   131  func (m *HashOfMapsMap) Iterator() MapIterator {
   132  	return &singleMapLookupIterator{
   133  		BPFMap: m,
   134  	}
   135  }