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 }