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 }