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