github.com/cilium/ebpf@v0.15.0/features/prog.go (about)

     1  package features
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/cilium/ebpf"
     9  	"github.com/cilium/ebpf/asm"
    10  	"github.com/cilium/ebpf/btf"
    11  	"github.com/cilium/ebpf/internal"
    12  	"github.com/cilium/ebpf/internal/sys"
    13  	"github.com/cilium/ebpf/internal/unix"
    14  )
    15  
    16  // HaveProgType probes the running kernel for the availability of the specified program type.
    17  //
    18  // Deprecated: use HaveProgramType() instead.
    19  var HaveProgType = HaveProgramType
    20  
    21  // HaveProgramType probes the running kernel for the availability of the specified program type.
    22  //
    23  // See the package documentation for the meaning of the error return value.
    24  func HaveProgramType(pt ebpf.ProgramType) (err error) {
    25  	return haveProgramTypeMatrix.Result(pt)
    26  }
    27  
    28  func probeProgram(spec *ebpf.ProgramSpec) error {
    29  	if spec.Instructions == nil {
    30  		spec.Instructions = asm.Instructions{
    31  			asm.LoadImm(asm.R0, 0, asm.DWord),
    32  			asm.Return(),
    33  		}
    34  	}
    35  	prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{
    36  		LogDisabled: true,
    37  	})
    38  	if err == nil {
    39  		prog.Close()
    40  	}
    41  
    42  	switch {
    43  	// EINVAL occurs when attempting to create a program with an unknown type.
    44  	// E2BIG occurs when ProgLoadAttr contains non-zero bytes past the end
    45  	// of the struct known by the running kernel, meaning the kernel is too old
    46  	// to support the given prog type.
    47  	case errors.Is(err, unix.EINVAL), errors.Is(err, unix.E2BIG):
    48  		err = ebpf.ErrNotSupported
    49  	}
    50  
    51  	return err
    52  }
    53  
    54  var haveProgramTypeMatrix = internal.FeatureMatrix[ebpf.ProgramType]{
    55  	ebpf.SocketFilter:  {Version: "3.19"},
    56  	ebpf.Kprobe:        {Version: "4.1"},
    57  	ebpf.SchedCLS:      {Version: "4.1"},
    58  	ebpf.SchedACT:      {Version: "4.1"},
    59  	ebpf.TracePoint:    {Version: "4.7"},
    60  	ebpf.XDP:           {Version: "4.8"},
    61  	ebpf.PerfEvent:     {Version: "4.9"},
    62  	ebpf.CGroupSKB:     {Version: "4.10"},
    63  	ebpf.CGroupSock:    {Version: "4.10"},
    64  	ebpf.LWTIn:         {Version: "4.10"},
    65  	ebpf.LWTOut:        {Version: "4.10"},
    66  	ebpf.LWTXmit:       {Version: "4.10"},
    67  	ebpf.SockOps:       {Version: "4.13"},
    68  	ebpf.SkSKB:         {Version: "4.14"},
    69  	ebpf.CGroupDevice:  {Version: "4.15"},
    70  	ebpf.SkMsg:         {Version: "4.17"},
    71  	ebpf.RawTracepoint: {Version: "4.17"},
    72  	ebpf.CGroupSockAddr: {
    73  		Version: "4.17",
    74  		Fn: func() error {
    75  			return probeProgram(&ebpf.ProgramSpec{
    76  				Type:       ebpf.CGroupSockAddr,
    77  				AttachType: ebpf.AttachCGroupInet4Connect,
    78  			})
    79  		},
    80  	},
    81  	ebpf.LWTSeg6Local:          {Version: "4.18"},
    82  	ebpf.LircMode2:             {Version: "4.18"},
    83  	ebpf.SkReuseport:           {Version: "4.19"},
    84  	ebpf.FlowDissector:         {Version: "4.20"},
    85  	ebpf.CGroupSysctl:          {Version: "5.2"},
    86  	ebpf.RawTracepointWritable: {Version: "5.2"},
    87  	ebpf.CGroupSockopt: {
    88  		Version: "5.3",
    89  		Fn: func() error {
    90  			return probeProgram(&ebpf.ProgramSpec{
    91  				Type:       ebpf.CGroupSockopt,
    92  				AttachType: ebpf.AttachCGroupGetsockopt,
    93  			})
    94  		},
    95  	},
    96  	ebpf.Tracing: {
    97  		Version: "5.5",
    98  		Fn: func() error {
    99  			return probeProgram(&ebpf.ProgramSpec{
   100  				Type:       ebpf.Tracing,
   101  				AttachType: ebpf.AttachTraceFEntry,
   102  				AttachTo:   "bpf_init",
   103  			})
   104  		},
   105  	},
   106  	ebpf.StructOps: {
   107  		Version: "5.6",
   108  		Fn: func() error {
   109  			err := probeProgram(&ebpf.ProgramSpec{
   110  				Type:    ebpf.StructOps,
   111  				License: "GPL",
   112  			})
   113  			if errors.Is(err, sys.ENOTSUPP) {
   114  				// ENOTSUPP means the program type is at least known to the kernel.
   115  				return nil
   116  			}
   117  			return err
   118  		},
   119  	},
   120  	ebpf.Extension: {
   121  		Version: "5.6",
   122  		Fn: func() error {
   123  			// create btf.Func to add to first ins of target and extension so both progs are btf powered
   124  			btfFn := btf.Func{
   125  				Name: "a",
   126  				Type: &btf.FuncProto{
   127  					Return: &btf.Int{},
   128  				},
   129  				Linkage: btf.GlobalFunc,
   130  			}
   131  			insns := asm.Instructions{
   132  				btf.WithFuncMetadata(asm.Mov.Imm(asm.R0, 0), &btfFn),
   133  				asm.Return(),
   134  			}
   135  
   136  			// create target prog
   137  			prog, err := ebpf.NewProgramWithOptions(
   138  				&ebpf.ProgramSpec{
   139  					Type:         ebpf.XDP,
   140  					Instructions: insns,
   141  				},
   142  				ebpf.ProgramOptions{
   143  					LogDisabled: true,
   144  				},
   145  			)
   146  			if err != nil {
   147  				return err
   148  			}
   149  			defer prog.Close()
   150  
   151  			// probe for Extension prog with target
   152  			return probeProgram(&ebpf.ProgramSpec{
   153  				Type:         ebpf.Extension,
   154  				Instructions: insns,
   155  				AttachTarget: prog,
   156  				AttachTo:     btfFn.Name,
   157  			})
   158  		},
   159  	},
   160  	ebpf.LSM: {
   161  		Version: "5.7",
   162  		Fn: func() error {
   163  			return probeProgram(&ebpf.ProgramSpec{
   164  				Type:       ebpf.LSM,
   165  				AttachType: ebpf.AttachLSMMac,
   166  				AttachTo:   "file_mprotect",
   167  				License:    "GPL",
   168  			})
   169  		},
   170  	},
   171  	ebpf.SkLookup: {
   172  		Version: "5.9",
   173  		Fn: func() error {
   174  			return probeProgram(&ebpf.ProgramSpec{
   175  				Type:       ebpf.SkLookup,
   176  				AttachType: ebpf.AttachSkLookup,
   177  			})
   178  		},
   179  	},
   180  	ebpf.Syscall: {
   181  		Version: "5.14",
   182  		Fn: func() error {
   183  			return probeProgram(&ebpf.ProgramSpec{
   184  				Type:  ebpf.Syscall,
   185  				Flags: unix.BPF_F_SLEEPABLE,
   186  			})
   187  		},
   188  	},
   189  }
   190  
   191  func init() {
   192  	for key, ft := range haveProgramTypeMatrix {
   193  		ft.Name = key.String()
   194  		if ft.Fn == nil {
   195  			key := key // avoid the dreaded loop variable problem
   196  			ft.Fn = func() error { return probeProgram(&ebpf.ProgramSpec{Type: key}) }
   197  		}
   198  	}
   199  }
   200  
   201  type helperKey struct {
   202  	typ    ebpf.ProgramType
   203  	helper asm.BuiltinFunc
   204  }
   205  
   206  var helperCache = internal.NewFeatureCache(func(key helperKey) *internal.FeatureTest {
   207  	return &internal.FeatureTest{
   208  		Name: fmt.Sprintf("%s for program type %s", key.helper, key.typ),
   209  		Fn: func() error {
   210  			return haveProgramHelper(key.typ, key.helper)
   211  		},
   212  	}
   213  })
   214  
   215  // HaveProgramHelper probes the running kernel for the availability of the specified helper
   216  // function to a specified program type.
   217  // Return values have the following semantics:
   218  //
   219  //	err == nil: The feature is available.
   220  //	errors.Is(err, ebpf.ErrNotSupported): The feature is not available.
   221  //	err != nil: Any errors encountered during probe execution, wrapped.
   222  //
   223  // Note that the latter case may include false negatives, and that program creation may
   224  // succeed despite an error being returned.
   225  // Only `nil` and `ebpf.ErrNotSupported` are conclusive.
   226  //
   227  // Probe results are cached and persist throughout any process capability changes.
   228  func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {
   229  	if helper > helper.Max() {
   230  		return os.ErrInvalid
   231  	}
   232  
   233  	return helperCache.Result(helperKey{pt, helper})
   234  }
   235  
   236  func haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error {
   237  	if ok := helperProbeNotImplemented(pt); ok {
   238  		return fmt.Errorf("no feature probe for %v/%v", pt, helper)
   239  	}
   240  
   241  	if err := HaveProgramType(pt); err != nil {
   242  		return err
   243  	}
   244  
   245  	spec := &ebpf.ProgramSpec{
   246  		Type: pt,
   247  		Instructions: asm.Instructions{
   248  			helper.Call(),
   249  			asm.LoadImm(asm.R0, 0, asm.DWord),
   250  			asm.Return(),
   251  		},
   252  		License: "GPL",
   253  	}
   254  
   255  	switch pt {
   256  	case ebpf.CGroupSockAddr:
   257  		spec.AttachType = ebpf.AttachCGroupInet4Connect
   258  	case ebpf.CGroupSockopt:
   259  		spec.AttachType = ebpf.AttachCGroupGetsockopt
   260  	case ebpf.SkLookup:
   261  		spec.AttachType = ebpf.AttachSkLookup
   262  	case ebpf.Syscall:
   263  		spec.Flags = unix.BPF_F_SLEEPABLE
   264  	}
   265  
   266  	prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{
   267  		LogDisabled: true,
   268  	})
   269  	if err == nil {
   270  		prog.Close()
   271  	}
   272  
   273  	switch {
   274  	// EACCES occurs when attempting to create a program probe with a helper
   275  	// while the register args when calling this helper aren't set up properly.
   276  	// We interpret this as the helper being available, because the verifier
   277  	// returns EINVAL if the helper is not supported by the running kernel.
   278  	case errors.Is(err, unix.EACCES):
   279  		// TODO: possibly we need to check verifier output here to be sure
   280  		err = nil
   281  
   282  	// EINVAL occurs when attempting to create a program with an unknown helper.
   283  	case errors.Is(err, unix.EINVAL):
   284  		// TODO: possibly we need to check verifier output here to be sure
   285  		err = ebpf.ErrNotSupported
   286  	}
   287  
   288  	return err
   289  }
   290  
   291  func helperProbeNotImplemented(pt ebpf.ProgramType) bool {
   292  	switch pt {
   293  	case ebpf.Extension, ebpf.LSM, ebpf.StructOps, ebpf.Tracing:
   294  		return true
   295  	}
   296  	return false
   297  }