syscalls.go 6.7 KB

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