github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/syscalls.go (about) 1 package ebpf 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "math" 8 "os" 9 "runtime" 10 11 "github.com/cilium/ebpf/asm" 12 "github.com/cilium/ebpf/internal" 13 "github.com/cilium/ebpf/internal/sys" 14 "github.com/cilium/ebpf/internal/tracefs" 15 "github.com/cilium/ebpf/internal/unix" 16 ) 17 18 var ( 19 // pre-allocating these here since they may 20 // get called in hot code paths and cause 21 // unnecessary memory allocations 22 sysErrKeyNotExist = sys.Error(ErrKeyNotExist, unix.ENOENT) 23 sysErrKeyExist = sys.Error(ErrKeyExist, unix.EEXIST) 24 sysErrNotSupported = sys.Error(ErrNotSupported, sys.ENOTSUPP) 25 ) 26 27 // invalidBPFObjNameChar returns true if char may not appear in 28 // a BPF object name. 29 func invalidBPFObjNameChar(char rune) bool { 30 dotAllowed := objNameAllowsDot() == nil 31 32 switch { 33 case char >= 'A' && char <= 'Z': 34 return false 35 case char >= 'a' && char <= 'z': 36 return false 37 case char >= '0' && char <= '9': 38 return false 39 case dotAllowed && char == '.': 40 return false 41 case char == '_': 42 return false 43 default: 44 return true 45 } 46 } 47 48 func progLoad(insns asm.Instructions, typ ProgramType, license string) (*sys.FD, error) { 49 buf := bytes.NewBuffer(make([]byte, 0, insns.Size())) 50 if err := insns.Marshal(buf, internal.NativeEndian); err != nil { 51 return nil, err 52 } 53 bytecode := buf.Bytes() 54 55 return sys.ProgLoad(&sys.ProgLoadAttr{ 56 ProgType: sys.ProgType(typ), 57 License: sys.NewStringPointer(license), 58 Insns: sys.NewSlicePointer(bytecode), 59 InsnCnt: uint32(len(bytecode) / asm.InstructionSize), 60 }) 61 } 62 63 var haveNestedMaps = internal.NewFeatureTest("nested maps", "4.12", func() error { 64 _, err := sys.MapCreate(&sys.MapCreateAttr{ 65 MapType: sys.MapType(ArrayOfMaps), 66 KeySize: 4, 67 ValueSize: 4, 68 MaxEntries: 1, 69 // Invalid file descriptor. 70 InnerMapFd: ^uint32(0), 71 }) 72 if errors.Is(err, unix.EINVAL) { 73 return internal.ErrNotSupported 74 } 75 if errors.Is(err, unix.EBADF) { 76 return nil 77 } 78 return err 79 }) 80 81 var haveMapMutabilityModifiers = internal.NewFeatureTest("read- and write-only maps", "5.2", func() error { 82 // This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since 83 // BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check. 84 m, err := sys.MapCreate(&sys.MapCreateAttr{ 85 MapType: sys.MapType(Array), 86 KeySize: 4, 87 ValueSize: 4, 88 MaxEntries: 1, 89 MapFlags: unix.BPF_F_RDONLY_PROG, 90 }) 91 if err != nil { 92 return internal.ErrNotSupported 93 } 94 _ = m.Close() 95 return nil 96 }) 97 98 var haveMmapableMaps = internal.NewFeatureTest("mmapable maps", "5.5", func() error { 99 // This checks BPF_F_MMAPABLE, which appeared in 5.5 for array maps. 100 m, err := sys.MapCreate(&sys.MapCreateAttr{ 101 MapType: sys.MapType(Array), 102 KeySize: 4, 103 ValueSize: 4, 104 MaxEntries: 1, 105 MapFlags: unix.BPF_F_MMAPABLE, 106 }) 107 if err != nil { 108 return internal.ErrNotSupported 109 } 110 _ = m.Close() 111 return nil 112 }) 113 114 var haveInnerMaps = internal.NewFeatureTest("inner maps", "5.10", func() error { 115 // This checks BPF_F_INNER_MAP, which appeared in 5.10. 116 m, err := sys.MapCreate(&sys.MapCreateAttr{ 117 MapType: sys.MapType(Array), 118 KeySize: 4, 119 ValueSize: 4, 120 MaxEntries: 1, 121 MapFlags: unix.BPF_F_INNER_MAP, 122 }) 123 124 if err != nil { 125 return internal.ErrNotSupported 126 } 127 _ = m.Close() 128 return nil 129 }) 130 131 var haveNoPreallocMaps = internal.NewFeatureTest("prealloc maps", "4.6", func() error { 132 // This checks BPF_F_NO_PREALLOC, which appeared in 4.6. 133 m, err := sys.MapCreate(&sys.MapCreateAttr{ 134 MapType: sys.MapType(Hash), 135 KeySize: 4, 136 ValueSize: 4, 137 MaxEntries: 1, 138 MapFlags: unix.BPF_F_NO_PREALLOC, 139 }) 140 141 if err != nil { 142 return internal.ErrNotSupported 143 } 144 _ = m.Close() 145 return nil 146 }) 147 148 func wrapMapError(err error) error { 149 if err == nil { 150 return nil 151 } 152 153 if errors.Is(err, unix.ENOENT) { 154 return sysErrKeyNotExist 155 } 156 157 if errors.Is(err, unix.EEXIST) { 158 return sysErrKeyExist 159 } 160 161 if errors.Is(err, sys.ENOTSUPP) { 162 return sysErrNotSupported 163 } 164 165 if errors.Is(err, unix.E2BIG) { 166 return fmt.Errorf("key too big for map: %w", err) 167 } 168 169 return err 170 } 171 172 var haveObjName = internal.NewFeatureTest("object names", "4.15", func() error { 173 attr := sys.MapCreateAttr{ 174 MapType: sys.MapType(Array), 175 KeySize: 4, 176 ValueSize: 4, 177 MaxEntries: 1, 178 MapName: sys.NewObjName("feature_test"), 179 } 180 181 fd, err := sys.MapCreate(&attr) 182 if err != nil { 183 return internal.ErrNotSupported 184 } 185 186 _ = fd.Close() 187 return nil 188 }) 189 190 var objNameAllowsDot = internal.NewFeatureTest("dot in object names", "5.2", func() error { 191 if err := haveObjName(); err != nil { 192 return err 193 } 194 195 attr := sys.MapCreateAttr{ 196 MapType: sys.MapType(Array), 197 KeySize: 4, 198 ValueSize: 4, 199 MaxEntries: 1, 200 MapName: sys.NewObjName(".test"), 201 } 202 203 fd, err := sys.MapCreate(&attr) 204 if err != nil { 205 return internal.ErrNotSupported 206 } 207 208 _ = fd.Close() 209 return nil 210 }) 211 212 var haveBatchAPI = internal.NewFeatureTest("map batch api", "5.6", func() error { 213 var maxEntries uint32 = 2 214 attr := sys.MapCreateAttr{ 215 MapType: sys.MapType(Hash), 216 KeySize: 4, 217 ValueSize: 4, 218 MaxEntries: maxEntries, 219 } 220 221 fd, err := sys.MapCreate(&attr) 222 if err != nil { 223 return internal.ErrNotSupported 224 } 225 defer fd.Close() 226 227 keys := []uint32{1, 2} 228 values := []uint32{3, 4} 229 kp, _ := marshalMapSyscallInput(keys, 8) 230 vp, _ := marshalMapSyscallInput(values, 8) 231 232 err = sys.MapUpdateBatch(&sys.MapUpdateBatchAttr{ 233 MapFd: fd.Uint(), 234 Keys: kp, 235 Values: vp, 236 Count: maxEntries, 237 }) 238 if err != nil { 239 return internal.ErrNotSupported 240 } 241 return nil 242 }) 243 244 var haveProbeReadKernel = internal.NewFeatureTest("bpf_probe_read_kernel", "5.5", func() error { 245 insns := asm.Instructions{ 246 asm.Mov.Reg(asm.R1, asm.R10), 247 asm.Add.Imm(asm.R1, -8), 248 asm.Mov.Imm(asm.R2, 8), 249 asm.Mov.Imm(asm.R3, 0), 250 asm.FnProbeReadKernel.Call(), 251 asm.Return(), 252 } 253 254 fd, err := progLoad(insns, Kprobe, "GPL") 255 if err != nil { 256 return internal.ErrNotSupported 257 } 258 _ = fd.Close() 259 return nil 260 }) 261 262 var haveBPFToBPFCalls = internal.NewFeatureTest("bpf2bpf calls", "4.16", func() error { 263 insns := asm.Instructions{ 264 asm.Call.Label("prog2").WithSymbol("prog1"), 265 asm.Return(), 266 asm.Mov.Imm(asm.R0, 0).WithSymbol("prog2"), 267 asm.Return(), 268 } 269 270 fd, err := progLoad(insns, SocketFilter, "MIT") 271 if err != nil { 272 return internal.ErrNotSupported 273 } 274 _ = fd.Close() 275 return nil 276 }) 277 278 var haveSyscallWrapper = internal.NewFeatureTest("syscall wrapper", "4.17", func() error { 279 prefix := internal.PlatformPrefix() 280 if prefix == "" { 281 return fmt.Errorf("unable to find the platform prefix for (%s)", runtime.GOARCH) 282 } 283 284 args := tracefs.ProbeArgs{ 285 Type: tracefs.Kprobe, 286 Symbol: prefix + "sys_bpf", 287 Pid: -1, 288 } 289 290 var err error 291 args.Group, err = tracefs.RandomGroup("ebpf_probe") 292 if err != nil { 293 return err 294 } 295 296 evt, err := tracefs.NewEvent(args) 297 if errors.Is(err, os.ErrNotExist) { 298 return internal.ErrNotSupported 299 } 300 if err != nil { 301 return err 302 } 303 304 return evt.Close() 305 }) 306 307 var haveProgramExtInfos = internal.NewFeatureTest("program ext_infos", "5.0", func() error { 308 insns := asm.Instructions{ 309 asm.Mov.Imm(asm.R0, 0), 310 asm.Return(), 311 } 312 313 buf := bytes.NewBuffer(make([]byte, 0, insns.Size())) 314 if err := insns.Marshal(buf, internal.NativeEndian); err != nil { 315 return err 316 } 317 bytecode := buf.Bytes() 318 319 _, err := sys.ProgLoad(&sys.ProgLoadAttr{ 320 ProgType: sys.ProgType(SocketFilter), 321 License: sys.NewStringPointer("MIT"), 322 Insns: sys.NewSlicePointer(bytecode), 323 InsnCnt: uint32(len(bytecode) / asm.InstructionSize), 324 FuncInfoCnt: 1, 325 ProgBtfFd: math.MaxUint32, 326 }) 327 328 if errors.Is(err, unix.EBADF) { 329 return nil 330 } 331 332 if errors.Is(err, unix.E2BIG) { 333 return ErrNotSupported 334 } 335 336 return err 337 })