github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/link/tracing.go (about)

     1  package link
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/cilium/ebpf"
     8  	"github.com/cilium/ebpf/btf"
     9  	"github.com/cilium/ebpf/internal/sys"
    10  	"github.com/cilium/ebpf/internal/unix"
    11  )
    12  
    13  type tracing struct {
    14  	RawLink
    15  }
    16  
    17  func (f *tracing) Update(new *ebpf.Program) error {
    18  	return fmt.Errorf("tracing update: %w", ErrNotSupported)
    19  }
    20  
    21  // AttachFreplace attaches the given eBPF program to the function it replaces.
    22  //
    23  // The program and name can either be provided at link time, or can be provided
    24  // at program load time. If they were provided at load time, they should be nil
    25  // and empty respectively here, as they will be ignored by the kernel.
    26  // Examples:
    27  //
    28  //	AttachFreplace(dispatcher, "function", replacement)
    29  //	AttachFreplace(nil, "", replacement)
    30  func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (Link, error) {
    31  	if (name == "") != (targetProg == nil) {
    32  		return nil, fmt.Errorf("must provide both or neither of name and targetProg: %w", errInvalidInput)
    33  	}
    34  	if prog == nil {
    35  		return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
    36  	}
    37  	if prog.Type() != ebpf.Extension {
    38  		return nil, fmt.Errorf("eBPF program type %s is not an Extension: %w", prog.Type(), errInvalidInput)
    39  	}
    40  
    41  	var (
    42  		target int
    43  		typeID btf.TypeID
    44  	)
    45  	if targetProg != nil {
    46  		btfHandle, err := targetProg.Handle()
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  		defer btfHandle.Close()
    51  
    52  		spec, err := btfHandle.Spec(nil)
    53  		if err != nil {
    54  			return nil, err
    55  		}
    56  
    57  		var function *btf.Func
    58  		if err := spec.TypeByName(name, &function); err != nil {
    59  			return nil, err
    60  		}
    61  
    62  		target = targetProg.FD()
    63  		typeID, err = spec.TypeID(function)
    64  		if err != nil {
    65  			return nil, err
    66  		}
    67  	}
    68  
    69  	link, err := AttachRawLink(RawLinkOptions{
    70  		Target:  target,
    71  		Program: prog,
    72  		Attach:  ebpf.AttachNone,
    73  		BTF:     typeID,
    74  	})
    75  	if errors.Is(err, sys.ENOTSUPP) {
    76  		// This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke.
    77  		return nil, fmt.Errorf("create raw tracepoint: %w", ErrNotSupported)
    78  	}
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	return &tracing{*link}, nil
    84  }
    85  
    86  type TracingOptions struct {
    87  	// Program must be of type Tracing with attach type
    88  	// AttachTraceFEntry/AttachTraceFExit/AttachModifyReturn or
    89  	// AttachTraceRawTp.
    90  	Program *ebpf.Program
    91  	// Program attach type. Can be one of:
    92  	// 	- AttachTraceFEntry
    93  	// 	- AttachTraceFExit
    94  	// 	- AttachModifyReturn
    95  	// 	- AttachTraceRawTp
    96  	// This field is optional.
    97  	AttachType ebpf.AttachType
    98  	// Arbitrary value that can be fetched from an eBPF program
    99  	// via `bpf_get_attach_cookie()`.
   100  	Cookie uint64
   101  }
   102  
   103  type LSMOptions struct {
   104  	// Program must be of type LSM with attach type
   105  	// AttachLSMMac.
   106  	Program *ebpf.Program
   107  	// Arbitrary value that can be fetched from an eBPF program
   108  	// via `bpf_get_attach_cookie()`.
   109  	Cookie uint64
   110  }
   111  
   112  // attachBTFID links all BPF program types (Tracing/LSM) that they attach to a btf_id.
   113  func attachBTFID(program *ebpf.Program, at ebpf.AttachType, cookie uint64) (Link, error) {
   114  	if program.FD() < 0 {
   115  		return nil, fmt.Errorf("invalid program %w", sys.ErrClosedFd)
   116  	}
   117  
   118  	var (
   119  		fd  *sys.FD
   120  		err error
   121  	)
   122  	switch at {
   123  	case ebpf.AttachTraceFEntry, ebpf.AttachTraceFExit, ebpf.AttachTraceRawTp,
   124  		ebpf.AttachModifyReturn, ebpf.AttachLSMMac:
   125  		// Attach via BPF link
   126  		fd, err = sys.LinkCreateTracing(&sys.LinkCreateTracingAttr{
   127  			ProgFd:     uint32(program.FD()),
   128  			AttachType: sys.AttachType(at),
   129  			Cookie:     cookie,
   130  		})
   131  		if err == nil {
   132  			break
   133  		}
   134  		if !errors.Is(err, unix.EINVAL) && !errors.Is(err, sys.ENOTSUPP) {
   135  			return nil, fmt.Errorf("create tracing link: %w", err)
   136  		}
   137  		fallthrough
   138  	case ebpf.AttachNone:
   139  		// Attach via RawTracepointOpen
   140  		if cookie > 0 {
   141  			return nil, fmt.Errorf("create raw tracepoint with cookie: %w", ErrNotSupported)
   142  		}
   143  
   144  		fd, err = sys.RawTracepointOpen(&sys.RawTracepointOpenAttr{
   145  			ProgFd: uint32(program.FD()),
   146  		})
   147  		if errors.Is(err, sys.ENOTSUPP) {
   148  			// This may be returned by bpf_tracing_prog_attach via bpf_arch_text_poke.
   149  			return nil, fmt.Errorf("create raw tracepoint: %w", ErrNotSupported)
   150  		}
   151  		if err != nil {
   152  			return nil, fmt.Errorf("create raw tracepoint: %w", err)
   153  		}
   154  	default:
   155  		return nil, fmt.Errorf("invalid attach type: %s", at.String())
   156  	}
   157  
   158  	raw := RawLink{fd: fd}
   159  	info, err := raw.Info()
   160  	if err != nil {
   161  		raw.Close()
   162  		return nil, err
   163  	}
   164  
   165  	if info.Type == RawTracepointType {
   166  		// Sadness upon sadness: a Tracing program with AttachRawTp returns
   167  		// a raw_tracepoint link. Other types return a tracing link.
   168  		return &rawTracepoint{raw}, nil
   169  	}
   170  	return &tracing{raw}, nil
   171  }
   172  
   173  // AttachTracing links a tracing (fentry/fexit/fmod_ret) BPF program or
   174  // a BTF-powered raw tracepoint (tp_btf) BPF Program to a BPF hook defined
   175  // in kernel modules.
   176  func AttachTracing(opts TracingOptions) (Link, error) {
   177  	if t := opts.Program.Type(); t != ebpf.Tracing {
   178  		return nil, fmt.Errorf("invalid program type %s, expected Tracing", t)
   179  	}
   180  
   181  	switch opts.AttachType {
   182  	case ebpf.AttachTraceFEntry, ebpf.AttachTraceFExit, ebpf.AttachModifyReturn,
   183  		ebpf.AttachTraceRawTp, ebpf.AttachNone:
   184  	default:
   185  		return nil, fmt.Errorf("invalid attach type: %s", opts.AttachType.String())
   186  	}
   187  
   188  	return attachBTFID(opts.Program, opts.AttachType, opts.Cookie)
   189  }
   190  
   191  // AttachLSM links a Linux security module (LSM) BPF Program to a BPF
   192  // hook defined in kernel modules.
   193  func AttachLSM(opts LSMOptions) (Link, error) {
   194  	if t := opts.Program.Type(); t != ebpf.LSM {
   195  		return nil, fmt.Errorf("invalid program type %s, expected LSM", t)
   196  	}
   197  
   198  	return attachBTFID(opts.Program, ebpf.AttachLSMMac, opts.Cookie)
   199  }