github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/features/map.go (about) 1 package features 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "unsafe" 8 9 "github.com/cilium/ebpf" 10 "github.com/cilium/ebpf/internal" 11 "github.com/cilium/ebpf/internal/sys" 12 "github.com/cilium/ebpf/internal/unix" 13 ) 14 15 // HaveMapType probes the running kernel for the availability of the specified map type. 16 // 17 // See the package documentation for the meaning of the error return value. 18 func HaveMapType(mt ebpf.MapType) error { 19 return haveMapTypeMatrix.Result(mt) 20 } 21 22 func probeCgroupStorageMap(mt sys.MapType) error { 23 // keySize needs to be sizeof(struct{u32 + u64}) = 12 (+ padding = 16) 24 // by using unsafe.Sizeof(int) we are making sure that this works on 32bit and 64bit archs 25 return createMap(&sys.MapCreateAttr{ 26 MapType: mt, 27 ValueSize: 4, 28 KeySize: uint32(8 + unsafe.Sizeof(int(0))), 29 MaxEntries: 0, 30 }) 31 } 32 33 func probeStorageMap(mt sys.MapType) error { 34 // maxEntries needs to be 0 35 // BPF_F_NO_PREALLOC needs to be set 36 // btf* fields need to be set 37 // see alloc_check for local_storage map types 38 err := createMap(&sys.MapCreateAttr{ 39 MapType: mt, 40 KeySize: 4, 41 ValueSize: 4, 42 MaxEntries: 0, 43 MapFlags: unix.BPF_F_NO_PREALLOC, 44 BtfKeyTypeId: 1, 45 BtfValueTypeId: 1, 46 BtfFd: ^uint32(0), 47 }) 48 if errors.Is(err, unix.EBADF) { 49 // Triggered by BtfFd. 50 return nil 51 } 52 return err 53 } 54 55 func probeNestedMap(mt sys.MapType) error { 56 // assign invalid innerMapFd to pass validation check 57 // will return EBADF 58 err := probeMap(&sys.MapCreateAttr{ 59 MapType: mt, 60 InnerMapFd: ^uint32(0), 61 }) 62 if errors.Is(err, unix.EBADF) { 63 return nil 64 } 65 return err 66 } 67 68 func probeMap(attr *sys.MapCreateAttr) error { 69 if attr.KeySize == 0 { 70 attr.KeySize = 4 71 } 72 if attr.ValueSize == 0 { 73 attr.ValueSize = 4 74 } 75 attr.MaxEntries = 1 76 return createMap(attr) 77 } 78 79 func createMap(attr *sys.MapCreateAttr) error { 80 fd, err := sys.MapCreate(attr) 81 if err == nil { 82 fd.Close() 83 return nil 84 } 85 86 switch { 87 // EINVAL occurs when attempting to create a map with an unknown type. 88 // E2BIG occurs when MapCreateAttr contains non-zero bytes past the end 89 // of the struct known by the running kernel, meaning the kernel is too old 90 // to support the given map type. 91 case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG): 92 return ebpf.ErrNotSupported 93 } 94 95 return err 96 } 97 98 var haveMapTypeMatrix = internal.FeatureMatrix[ebpf.MapType]{ 99 ebpf.Hash: {Version: "3.19"}, 100 ebpf.Array: {Version: "3.19"}, 101 ebpf.ProgramArray: {Version: "4.2"}, 102 ebpf.PerfEventArray: {Version: "4.3"}, 103 ebpf.PerCPUHash: {Version: "4.6"}, 104 ebpf.PerCPUArray: {Version: "4.6"}, 105 ebpf.StackTrace: { 106 Version: "4.6", 107 Fn: func() error { 108 return probeMap(&sys.MapCreateAttr{ 109 MapType: sys.BPF_MAP_TYPE_STACK_TRACE, 110 ValueSize: 8, // sizeof(uint64) 111 }) 112 }, 113 }, 114 ebpf.CGroupArray: {Version: "4.8"}, 115 ebpf.LRUHash: {Version: "4.10"}, 116 ebpf.LRUCPUHash: {Version: "4.10"}, 117 ebpf.LPMTrie: { 118 Version: "4.11", 119 Fn: func() error { 120 // keySize and valueSize need to be sizeof(struct{u32 + u8}) + 1 + padding = 8 121 // BPF_F_NO_PREALLOC needs to be set 122 return probeMap(&sys.MapCreateAttr{ 123 MapType: sys.BPF_MAP_TYPE_LPM_TRIE, 124 KeySize: 8, 125 ValueSize: 8, 126 MapFlags: unix.BPF_F_NO_PREALLOC, 127 }) 128 }, 129 }, 130 ebpf.ArrayOfMaps: { 131 Version: "4.12", 132 Fn: func() error { return probeNestedMap(sys.BPF_MAP_TYPE_ARRAY_OF_MAPS) }, 133 }, 134 ebpf.HashOfMaps: { 135 Version: "4.12", 136 Fn: func() error { return probeNestedMap(sys.BPF_MAP_TYPE_HASH_OF_MAPS) }, 137 }, 138 ebpf.DevMap: {Version: "4.14"}, 139 ebpf.SockMap: {Version: "4.14"}, 140 ebpf.CPUMap: {Version: "4.15"}, 141 ebpf.XSKMap: {Version: "4.18"}, 142 ebpf.SockHash: {Version: "4.18"}, 143 ebpf.CGroupStorage: { 144 Version: "4.19", 145 Fn: func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_CGROUP_STORAGE) }, 146 }, 147 ebpf.ReusePortSockArray: {Version: "4.19"}, 148 ebpf.PerCPUCGroupStorage: { 149 Version: "4.20", 150 Fn: func() error { return probeCgroupStorageMap(sys.BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) }, 151 }, 152 ebpf.Queue: { 153 Version: "4.20", 154 Fn: func() error { 155 return createMap(&sys.MapCreateAttr{ 156 MapType: sys.BPF_MAP_TYPE_QUEUE, 157 KeySize: 0, 158 ValueSize: 4, 159 MaxEntries: 1, 160 }) 161 }, 162 }, 163 ebpf.Stack: { 164 Version: "4.20", 165 Fn: func() error { 166 return createMap(&sys.MapCreateAttr{ 167 MapType: sys.BPF_MAP_TYPE_STACK, 168 KeySize: 0, 169 ValueSize: 4, 170 MaxEntries: 1, 171 }) 172 }, 173 }, 174 ebpf.SkStorage: { 175 Version: "5.2", 176 Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_SK_STORAGE) }, 177 }, 178 ebpf.DevMapHash: {Version: "5.4"}, 179 ebpf.StructOpsMap: { 180 Version: "5.6", 181 Fn: func() error { 182 // StructOps requires setting a vmlinux type id, but id 1 will always 183 // resolve to some type of integer. This will cause ENOTSUPP. 184 err := probeMap(&sys.MapCreateAttr{ 185 MapType: sys.BPF_MAP_TYPE_STRUCT_OPS, 186 BtfVmlinuxValueTypeId: 1, 187 }) 188 if errors.Is(err, sys.ENOTSUPP) { 189 // ENOTSUPP means the map type is at least known to the kernel. 190 return nil 191 } 192 return err 193 }, 194 }, 195 ebpf.RingBuf: { 196 Version: "5.8", 197 Fn: func() error { 198 // keySize and valueSize need to be 0 199 // maxEntries needs to be power of 2 and PAGE_ALIGNED 200 return createMap(&sys.MapCreateAttr{ 201 MapType: sys.BPF_MAP_TYPE_RINGBUF, 202 KeySize: 0, 203 ValueSize: 0, 204 MaxEntries: uint32(os.Getpagesize()), 205 }) 206 }, 207 }, 208 ebpf.InodeStorage: { 209 Version: "5.10", 210 Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_INODE_STORAGE) }, 211 }, 212 ebpf.TaskStorage: { 213 Version: "5.11", 214 Fn: func() error { return probeStorageMap(sys.BPF_MAP_TYPE_TASK_STORAGE) }, 215 }, 216 } 217 218 func init() { 219 for mt, ft := range haveMapTypeMatrix { 220 ft.Name = mt.String() 221 if ft.Fn == nil { 222 // Avoid referring to the loop variable in the closure. 223 mt := sys.MapType(mt) 224 ft.Fn = func() error { return probeMap(&sys.MapCreateAttr{MapType: mt}) } 225 } 226 } 227 } 228 229 // MapFlags document which flags may be feature probed. 230 type MapFlags = sys.MapFlags 231 232 // Flags which may be feature probed. 233 const ( 234 BPF_F_NO_PREALLOC = sys.BPF_F_NO_PREALLOC 235 BPF_F_RDONLY_PROG = sys.BPF_F_RDONLY_PROG 236 BPF_F_WRONLY_PROG = sys.BPF_F_WRONLY_PROG 237 BPF_F_MMAPABLE = sys.BPF_F_MMAPABLE 238 BPF_F_INNER_MAP = sys.BPF_F_INNER_MAP 239 ) 240 241 // HaveMapFlag probes the running kernel for the availability of the specified map flag. 242 // 243 // Returns an error if flag is not one of the flags declared in this package. 244 // See the package documentation for the meaning of the error return value. 245 func HaveMapFlag(flag MapFlags) (err error) { 246 return haveMapFlagsMatrix.Result(flag) 247 } 248 249 func probeMapFlag(attr *sys.MapCreateAttr) error { 250 // For now, we do not check if the map type is supported because we only support 251 // probing for flags defined on arrays and hashes that are always supported. 252 // In the future, if we allow probing on flags defined on newer types, checking for map type 253 // support will be required. 254 if attr.MapType == sys.BPF_MAP_TYPE_UNSPEC { 255 attr.MapType = sys.BPF_MAP_TYPE_ARRAY 256 } 257 258 attr.KeySize = 4 259 attr.ValueSize = 4 260 attr.MaxEntries = 1 261 262 fd, err := sys.MapCreate(attr) 263 if err == nil { 264 fd.Close() 265 } else if errors.Is(err, unix.EINVAL) { 266 // EINVAL occurs when attempting to create a map with an unknown type or an unknown flag. 267 err = ebpf.ErrNotSupported 268 } 269 270 return err 271 } 272 273 var haveMapFlagsMatrix = internal.FeatureMatrix[MapFlags]{ 274 BPF_F_NO_PREALLOC: { 275 Version: "4.6", 276 Fn: func() error { 277 return probeMapFlag(&sys.MapCreateAttr{ 278 MapType: sys.BPF_MAP_TYPE_HASH, 279 MapFlags: BPF_F_NO_PREALLOC, 280 }) 281 }, 282 }, 283 BPF_F_RDONLY_PROG: { 284 Version: "5.2", 285 Fn: func() error { 286 return probeMapFlag(&sys.MapCreateAttr{ 287 MapFlags: BPF_F_RDONLY_PROG, 288 }) 289 }, 290 }, 291 BPF_F_WRONLY_PROG: { 292 Version: "5.2", 293 Fn: func() error { 294 return probeMapFlag(&sys.MapCreateAttr{ 295 MapFlags: BPF_F_WRONLY_PROG, 296 }) 297 }, 298 }, 299 BPF_F_MMAPABLE: { 300 Version: "5.5", 301 Fn: func() error { 302 return probeMapFlag(&sys.MapCreateAttr{ 303 MapFlags: BPF_F_MMAPABLE, 304 }) 305 }, 306 }, 307 BPF_F_INNER_MAP: { 308 Version: "5.10", 309 Fn: func() error { 310 return probeMapFlag(&sys.MapCreateAttr{ 311 MapFlags: BPF_F_INNER_MAP, 312 }) 313 }, 314 }, 315 } 316 317 func init() { 318 for mf, ft := range haveMapFlagsMatrix { 319 ft.Name = fmt.Sprint(mf) 320 } 321 }