github.com/kubeshark/ebpf@v0.9.2/features/map.go (about) 1 package features 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "sync" 8 "unsafe" 9 10 "github.com/kubeshark/ebpf" 11 "github.com/kubeshark/ebpf/internal/sys" 12 "github.com/kubeshark/ebpf/internal/unix" 13 ) 14 15 func init() { 16 mc.mapTypes = make(map[ebpf.MapType]error) 17 } 18 19 var ( 20 mc mapCache 21 ) 22 23 type mapCache struct { 24 sync.Mutex 25 mapTypes map[ebpf.MapType]error 26 } 27 28 func createMapTypeAttr(mt ebpf.MapType) *sys.MapCreateAttr { 29 var ( 30 keySize uint32 = 4 31 valueSize uint32 = 4 32 maxEntries uint32 = 1 33 innerMapFd uint32 34 flags uint32 35 btfKeyTypeID uint32 36 btfValueTypeID uint32 37 btfFd uint32 38 ) 39 40 // switch on map types to generate correct MapCreateAttr 41 switch mt { 42 case ebpf.StackTrace: 43 // valueSize needs to be sizeof(uint64) 44 valueSize = 8 45 case ebpf.LPMTrie: 46 // keySize and valueSize need to be sizeof(struct{u32 + u8}) + 1 + padding = 8 47 // BPF_F_NO_PREALLOC needs to be set 48 // checked at allocation time for lpm_trie maps 49 keySize = 8 50 valueSize = 8 51 flags = unix.BPF_F_NO_PREALLOC 52 case ebpf.ArrayOfMaps, ebpf.HashOfMaps: 53 // assign invalid innerMapFd to pass validation check 54 // will return EBADF 55 innerMapFd = ^uint32(0) 56 case ebpf.CGroupStorage, ebpf.PerCPUCGroupStorage: 57 // keySize needs to be sizeof(struct{u32 + u64}) = 12 (+ padding = 16) 58 // by using unsafe.Sizeof(int) we are making sure that this works on 32bit and 64bit archs 59 // checked at allocation time 60 var align int 61 keySize = uint32(8 + unsafe.Sizeof(align)) 62 maxEntries = 0 63 case ebpf.Queue, ebpf.Stack: 64 // keySize needs to be 0, see alloc_check for queue and stack maps 65 keySize = 0 66 case ebpf.RingBuf: 67 // keySize and valueSize need to be 0 68 // maxEntries needs to be power of 2 and PAGE_ALIGNED 69 // checked at allocation time 70 keySize = 0 71 valueSize = 0 72 maxEntries = uint32(os.Getpagesize()) 73 case ebpf.SkStorage, ebpf.InodeStorage, ebpf.TaskStorage: 74 // maxEntries needs to be 0 75 // BPF_F_NO_PREALLOC needs to be set 76 // btf* fields need to be set 77 // see alloc_check for local_storage map types 78 maxEntries = 0 79 flags = unix.BPF_F_NO_PREALLOC 80 btfKeyTypeID = 1 // BTF_KIND_INT 81 btfValueTypeID = 3 // BTF_KIND_ARRAY 82 btfFd = ^uint32(0) 83 } 84 85 return &sys.MapCreateAttr{ 86 MapType: sys.MapType(mt), 87 KeySize: keySize, 88 ValueSize: valueSize, 89 MaxEntries: maxEntries, 90 InnerMapFd: innerMapFd, 91 MapFlags: flags, 92 BtfKeyTypeId: btfKeyTypeID, 93 BtfValueTypeId: btfValueTypeID, 94 BtfFd: btfFd, 95 } 96 } 97 98 // HaveMapType probes the running kernel for the availability of the specified map type. 99 // 100 // See the package documentation for the meaning of the error return value. 101 func HaveMapType(mt ebpf.MapType) error { 102 if err := validateMaptype(mt); err != nil { 103 return err 104 } 105 106 return haveMapType(mt) 107 } 108 109 func validateMaptype(mt ebpf.MapType) error { 110 if mt > mt.Max() { 111 return os.ErrInvalid 112 } 113 114 if mt == ebpf.StructOpsMap { 115 // A probe for StructOpsMap has vmlinux BTF requirements we currently 116 // cannot meet. Once we figure out how to add a working probe in this 117 // package, we can remove this check. 118 return errors.New("a probe for MapType StructOpsMap isn't implemented") 119 } 120 121 return nil 122 } 123 124 func haveMapType(mt ebpf.MapType) error { 125 mc.Lock() 126 defer mc.Unlock() 127 err, ok := mc.mapTypes[mt] 128 if ok { 129 return err 130 } 131 132 fd, err := sys.MapCreate(createMapTypeAttr(mt)) 133 134 switch { 135 // For nested and storage map types we accept EBADF as indicator that these maps are supported 136 case errors.Is(err, unix.EBADF): 137 if isMapOfMaps(mt) || isStorageMap(mt) { 138 err = nil 139 } 140 141 // EINVAL occurs when attempting to create a map with an unknown type. 142 // E2BIG occurs when MapCreateAttr contains non-zero bytes past the end 143 // of the struct known by the running kernel, meaning the kernel is too old 144 // to support the given map type. 145 case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG): 146 err = ebpf.ErrNotSupported 147 148 // EPERM is kept as-is and is not converted or wrapped. 149 case errors.Is(err, unix.EPERM): 150 break 151 152 // Wrap unexpected errors. 153 case err != nil: 154 err = fmt.Errorf("unexpected error during feature probe: %w", err) 155 156 default: 157 fd.Close() 158 } 159 160 mc.mapTypes[mt] = err 161 162 return err 163 } 164 165 func isMapOfMaps(mt ebpf.MapType) bool { 166 switch mt { 167 case ebpf.ArrayOfMaps, ebpf.HashOfMaps: 168 return true 169 } 170 return false 171 } 172 173 func isStorageMap(mt ebpf.MapType) bool { 174 switch mt { 175 case ebpf.SkStorage, ebpf.InodeStorage, ebpf.TaskStorage: 176 return true 177 } 178 179 return false 180 }