...

Source file src/github.com/cilium/ebpf/features/misc.go

Documentation: github.com/cilium/ebpf/features

     1  package features
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"sync"
     8  
     9  	"github.com/cilium/ebpf"
    10  	"github.com/cilium/ebpf/asm"
    11  	"github.com/cilium/ebpf/internal"
    12  	"github.com/cilium/ebpf/internal/sys"
    13  	"github.com/cilium/ebpf/internal/unix"
    14  )
    15  
    16  func init() {
    17  	miscs.miscTypes = make(map[miscType]error)
    18  }
    19  
    20  var (
    21  	miscs miscCache
    22  )
    23  
    24  type miscCache struct {
    25  	sync.Mutex
    26  	miscTypes map[miscType]error
    27  }
    28  
    29  type miscType uint32
    30  
    31  const (
    32  	// largeInsn support introduced in Linux 5.2
    33  	// commit c04c0d2b968ac45d6ef020316808ef6c82325a82
    34  	largeInsn miscType = iota
    35  	// boundedLoops support introduced in Linux 5.3
    36  	// commit 2589726d12a1b12eaaa93c7f1ea64287e383c7a5
    37  	boundedLoops
    38  	// v2ISA support introduced in Linux 4.14
    39  	// commit 92b31a9af73b3a3fc801899335d6c47966351830
    40  	v2ISA
    41  	// v3ISA support introduced in Linux 5.1
    42  	// commit 092ed0968bb648cd18e8a0430cd0a8a71727315c
    43  	v3ISA
    44  )
    45  
    46  const (
    47  	maxInsns = 4096
    48  )
    49  
    50  // HaveLargeInstructions probes the running kernel if more than 4096 instructions
    51  // per program are supported.
    52  //
    53  // See the package documentation for the meaning of the error return value.
    54  func HaveLargeInstructions() error {
    55  	return probeMisc(largeInsn)
    56  }
    57  
    58  // HaveBoundedLoops probes the running kernel if bounded loops are supported.
    59  //
    60  // See the package documentation for the meaning of the error return value.
    61  func HaveBoundedLoops() error {
    62  	return probeMisc(boundedLoops)
    63  }
    64  
    65  // HaveV2ISA probes the running kernel if instructions of the v2 ISA are supported.
    66  //
    67  // See the package documentation for the meaning of the error return value.
    68  func HaveV2ISA() error {
    69  	return probeMisc(v2ISA)
    70  }
    71  
    72  // HaveV3ISA probes the running kernel if instructions of the v3 ISA are supported.
    73  //
    74  // See the package documentation for the meaning of the error return value.
    75  func HaveV3ISA() error {
    76  	return probeMisc(v3ISA)
    77  }
    78  
    79  // probeMisc checks the kernel for a given supported misc by creating
    80  // a specialized program probe and loading it.
    81  func probeMisc(mt miscType) error {
    82  	mc.Lock()
    83  	defer mc.Unlock()
    84  	err, ok := miscs.miscTypes[mt]
    85  	if ok {
    86  		return err
    87  	}
    88  
    89  	attr, err := createMiscProbeAttr(mt)
    90  	if err != nil {
    91  		return fmt.Errorf("couldn't create the attributes for the probe: %w", err)
    92  	}
    93  
    94  	fd, err := sys.ProgLoad(attr)
    95  
    96  	switch {
    97  	// EINVAL occurs when attempting to create a program with an unknown type.
    98  	// E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end
    99  	// of the struct known by the running kernel, meaning the kernel is too old
   100  	// to support the given map type.
   101  	case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
   102  		err = ebpf.ErrNotSupported
   103  
   104  	// EPERM is kept as-is and is not converted or wrapped.
   105  	case errors.Is(err, unix.EPERM):
   106  		break
   107  
   108  	// Wrap unexpected errors.
   109  	case err != nil:
   110  		err = fmt.Errorf("unexpected error during feature probe: %w", err)
   111  
   112  	default:
   113  		fd.Close()
   114  	}
   115  
   116  	miscs.miscTypes[mt] = err
   117  
   118  	return err
   119  }
   120  
   121  func createMiscProbeAttr(mt miscType) (*sys.ProgLoadAttr, error) {
   122  	var insns asm.Instructions
   123  	switch mt {
   124  	case largeInsn:
   125  		for i := 0; i < maxInsns; i++ {
   126  			insns = append(insns, asm.Mov.Imm(asm.R0, 1))
   127  		}
   128  		insns = append(insns, asm.Return())
   129  	case boundedLoops:
   130  		insns = asm.Instructions{
   131  			asm.Mov.Imm(asm.R0, 10),
   132  			asm.Sub.Imm(asm.R0, 1).WithSymbol("loop"),
   133  			asm.JNE.Imm(asm.R0, 0, "loop"),
   134  			asm.Return(),
   135  		}
   136  	case v2ISA:
   137  		insns = asm.Instructions{
   138  			asm.Mov.Imm(asm.R0, 0),
   139  			asm.JLT.Imm(asm.R0, 0, "exit"),
   140  			asm.Mov.Imm(asm.R0, 1),
   141  			asm.Return().WithSymbol("exit"),
   142  		}
   143  	case v3ISA:
   144  		insns = asm.Instructions{
   145  			asm.Mov.Imm(asm.R0, 0),
   146  			asm.JLT.Imm32(asm.R0, 0, "exit"),
   147  			asm.Mov.Imm(asm.R0, 1),
   148  			asm.Return().WithSymbol("exit"),
   149  		}
   150  	default:
   151  		return nil, fmt.Errorf("misc probe %d not implemented", mt)
   152  	}
   153  
   154  	buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
   155  	if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	bytecode := buf.Bytes()
   160  	instructions := sys.NewSlicePointer(bytecode)
   161  
   162  	return &sys.ProgLoadAttr{
   163  		ProgType: sys.BPF_PROG_TYPE_SOCKET_FILTER,
   164  		Insns:    instructions,
   165  		InsnCnt:  uint32(len(bytecode) / asm.InstructionSize),
   166  		License:  sys.NewStringPointer("MIT"),
   167  	}, nil
   168  }
   169  

View as plain text