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