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 }