github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/map.go (about) 1 package gobpfld 2 3 import ( 4 "fmt" 5 "sync" 6 "unsafe" 7 8 "github.com/dylandreimerink/gobpfld/bpfsys" 9 "github.com/dylandreimerink/gobpfld/bpftypes" 10 "github.com/dylandreimerink/gobpfld/internal/cstr" 11 ) 12 13 type BPFMap interface { 14 GetName() ObjName 15 GetFD() bpfsys.BPFfd 16 IsLoaded() bool 17 GetDefinition() BPFMapDef 18 GetBTF() *BTF 19 GetBTFMapType() BTFMap 20 GetInitialData() map[interface{}]interface{} 21 22 // Pin pins the map to a location in the bpf filesystem, since the file system now also holds a reference 23 // to the map the original creator of the map can terminate without triggering the map to be closed as well. 24 // A map can be unpinned from the bpf FS by another process thus transferring it or persisting it across 25 // multiple runs of the same program. 26 Pin(relativePath string) error 27 28 // Unpin captures the file descriptor of the map at the given 'relativePath' from the kernel. 29 // The definition in this map must match the definition of the pinned map, otherwise this function 30 // will return an error since mismatched definitions might cause seemingly unrelated bugs in other functions. 31 // If 'deletePin' is true the bpf FS pin will be removed after successfully loading the map, thus transferring 32 // ownership of the map in a scenario where the map is not shared between multiple programs. 33 // Otherwise the pin will keep existing which will cause the map to not be deleted when this program exits. 34 Unpin(relativePath string, deletePin bool) error 35 36 // Load validates and loads the userspace map definition into the kernel. 37 Load() error 38 39 // Close closes the file descriptor associated with the map. The map can't be used after it is closed. 40 // If this is the last file descriptor pointing to the map, the map will be unloaded from the kernel. 41 // If a map is pinned to the filesystem, in use by a bpf program or referenced any other way it will stay loaded 42 // until all references are closed/removed. 43 Close() error 44 } 45 46 // MapFromFD creates a BPFMap object from a map that is already loaded into the kernel and for which we already have 47 // a file descriptor. 48 func MapFromFD(fd bpfsys.BPFfd) (BPFMap, error) { 49 // Check if there already is a map in the register with this FD. 50 m := mapRegister.getByFD(fd) 51 if m != nil { 52 return m, nil 53 } 54 55 // Otherwise get all required info from the kernel and create a userspace representation. 56 57 mapInfo, err := getMapInfo(fd) 58 if err != nil { 59 return nil, err 60 } 61 62 // TODO if the type is an memory mmap-able array, we should mmap it so it can be used. 63 64 m = bpfMapFromAbstractMap( 65 AbstractMap{ 66 Name: ObjName{ 67 cname: mapInfo.Name, 68 str: cstr.BytesToString(mapInfo.Name[:]), 69 }, 70 loaded: true, 71 fd: fd, 72 Definition: BPFMapDef{ 73 Type: mapInfo.Type, 74 KeySize: mapInfo.KeySize, 75 ValueSize: mapInfo.ValueSize, 76 MaxEntries: mapInfo.MaxEntries, 77 Flags: bpftypes.BPFMapFlags(mapInfo.MapFlags), 78 }, 79 definition: BPFMapDef{ 80 Type: mapInfo.Type, 81 KeySize: mapInfo.KeySize, 82 ValueSize: mapInfo.ValueSize, 83 MaxEntries: mapInfo.MaxEntries, 84 Flags: bpftypes.BPFMapFlags(mapInfo.MapFlags), 85 }, 86 }, 87 ) 88 89 err = mapRegister.add(m) 90 if err != nil { 91 return nil, fmt.Errorf("map register: %w", err) 92 } 93 94 return m, nil 95 } 96 97 // MapFromID creates a BPFMap object from a map that is already loaded into the kernel. 98 func MapFromID(id uint32) (BPFMap, error) { 99 // First check if we already have a version of this map in the register 100 m := mapRegister.getByID(id) 101 if m != nil { 102 return m, nil 103 } 104 105 // If no, get a new FD from the ID and construct a map. 106 107 fd, err := bpfsys.MapGetFDByID(&bpfsys.BPFAttrGetID{ 108 ID: id, 109 }) 110 if err != nil { 111 return nil, fmt.Errorf("bpf syscall error: %w", err) 112 } 113 114 return MapFromFD(fd) 115 } 116 117 // mapIDRegister holds a reference to all maps currently loaded into the kernel. The map is index by the object ID 118 // of the map which uniquely identifies it within the kernel. The purpose of this is that if the user re-gets a map 119 // via any mechanism(FS pinning, ID, FD, or map-in-map) that the user always gets the same instance(pointer) to the map. 120 // Because the user will always have the same object, we will also have only one FD which is easier for the user to 121 // manage. 122 var mapRegister = _mapRegister{ 123 idToMap: make(map[uint32]BPFMap), 124 fdToMap: make(map[bpfsys.BPFfd]BPFMap), 125 } 126 127 type _mapRegister struct { 128 mu sync.Mutex 129 idToMap map[uint32]BPFMap 130 fdToMap map[bpfsys.BPFfd]BPFMap 131 } 132 133 func (r *_mapRegister) add(m BPFMap) error { 134 if !m.IsLoaded() { 135 return fmt.Errorf("can only add loaded maps to the register") 136 } 137 138 // Get info from kernel via the FD so we can get the ID of this map 139 info, err := getMapInfo(m.GetFD()) 140 if err != nil { 141 return fmt.Errorf("get map info: %w", err) 142 } 143 144 r.mu.Lock() 145 defer r.mu.Unlock() 146 147 r.idToMap[info.ID] = m 148 r.fdToMap[m.GetFD()] = m 149 150 return nil 151 } 152 153 func (r *_mapRegister) delete(m BPFMap) error { 154 if !m.IsLoaded() { 155 return fmt.Errorf("can only delete loaded maps from the register") 156 } 157 158 // Get info from kernel via the FD so we can get the ID of this map 159 info, err := getMapInfo(m.GetFD()) 160 if err != nil { 161 return fmt.Errorf("get map info: %w", err) 162 } 163 164 r.mu.Lock() 165 defer r.mu.Unlock() 166 167 delete(r.idToMap, info.ID) 168 delete(r.fdToMap, m.GetFD()) 169 170 return nil 171 } 172 173 func (r *_mapRegister) getByID(id uint32) BPFMap { 174 r.mu.Lock() 175 defer r.mu.Unlock() 176 177 return r.idToMap[id] 178 } 179 180 func (r *_mapRegister) getByFD(fd bpfsys.BPFfd) BPFMap { 181 r.mu.Lock() 182 defer r.mu.Unlock() 183 184 return r.fdToMap[fd] 185 } 186 187 func getMapInfo(fd bpfsys.BPFfd) (bpftypes.BPFMapInfo, error) { 188 mapInfo := bpftypes.BPFMapInfo{} 189 err := bpfsys.ObjectGetInfoByFD(&bpfsys.BPFAttrGetInfoFD{ 190 BPFFD: fd, 191 Info: uintptr(unsafe.Pointer(&mapInfo)), 192 InfoLen: uint32(bpftypes.BPFMapInfoSize), 193 }) 194 if err != nil { 195 return mapInfo, fmt.Errorf("bpf obj get info by fd syscall error: %w", err) 196 } 197 198 return mapInfo, nil 199 } 200 201 // bpfMapFromAbstractMap takes in an abstract map and uses the values in the definion to construct a specific map type 202 // which implements BPFMap 203 func bpfMapFromAbstractMap(am AbstractMap) BPFMap { 204 switch am.Definition.Type { 205 case bpftypes.BPF_MAP_TYPE_HASH: 206 return &HashMap{ 207 AbstractMap: am, 208 } 209 210 case bpftypes.BPF_MAP_TYPE_ARRAY: 211 return &ArrayMap{ 212 AbstractMap: am, 213 } 214 215 case bpftypes.BPF_MAP_TYPE_PROG_ARRAY: 216 return &ProgArrayMap{ 217 AbstractMap: am, 218 } 219 220 // TODO BPF_MAP_TYPE_PERF_EVENT_ARRAY 221 222 case bpftypes.BPF_MAP_TYPE_PERCPU_HASH: 223 return &HashMap{ 224 AbstractMap: am, 225 } 226 227 case bpftypes.BPF_MAP_TYPE_PERCPU_ARRAY: 228 return &PerCPUArrayMap{ 229 AbstractMap: am, 230 } 231 232 // TODO BPF_MAP_TYPE_STACK_TRACE 233 234 case bpftypes.BPF_MAP_TYPE_LRU_HASH, 235 bpftypes.BPF_MAP_TYPE_LRU_PERCPU_HASH: 236 return &HashMap{ 237 AbstractMap: am, 238 } 239 240 case bpftypes.BPF_MAP_TYPE_LPM_TRIE: 241 return &LPMTrieMap{ 242 AbstractMap: am, 243 } 244 245 case bpftypes.BPF_MAP_TYPE_ARRAY_OF_MAPS: 246 return &ArrayOfMapsMap{ 247 AbstractMap: am, 248 } 249 250 case bpftypes.BPF_MAP_TYPE_HASH_OF_MAPS: 251 return &HashOfMapsMap{ 252 AbstractMap: am, 253 } 254 255 // TODO BPF_MAP_TYPE_DEVMAP 256 // TODO BPF_MAP_TYPE_SOCKMAP 257 // TODO BPF_MAP_TYPE_CPUMAP 258 259 case bpftypes.BPF_MAP_TYPE_XSKMAP: 260 return &XSKMap{ 261 AbstractMap: am, 262 } 263 264 // TODO BPF_MAP_TYPE_SOCKHASH 265 // TODO BPF_MAP_TYPE_CGROUP_STORAGE 266 // TODO BPF_MAP_TYPE_REUSEPORT_SOCKARRAY 267 // TODO BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE 268 case bpftypes.BPF_MAP_TYPE_QUEUE: 269 return &QueueMap{ 270 AbstractMap: am, 271 } 272 273 case bpftypes.BPF_MAP_TYPE_STACK: 274 return &StackMap{ 275 AbstractMap: am, 276 } 277 // TODO BPF_MAP_TYPE_SK_STORAGE 278 // TODO BPF_MAP_TYPE_DEVMAP_HASH 279 // TODO BPF_MAP_TYPE_STRUCT_OPS 280 // TODO BPF_MAP_TYPE_RINGBUF 281 // TODO BPF_MAP_TYPE_INODE_STORAGE 282 // TODO BPF_MAP_TYPE_TASK_STORAGE 283 284 default: 285 return &HashMap{ 286 AbstractMap: am, 287 } 288 } 289 } 290 291 const maxUint32 = int(^uint32(0))