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  })