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 }