link.go (7231B)
1 package link 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 8 "github.com/cilium/ebpf" 9 "github.com/cilium/ebpf/btf" 10 "github.com/cilium/ebpf/internal" 11 "github.com/cilium/ebpf/internal/sys" 12 ) 13 14 var ErrNotSupported = internal.ErrNotSupported 15 16 // Link represents a Program attached to a BPF hook. 17 type Link interface { 18 // Replace the current program with a new program. 19 // 20 // Passing a nil program is an error. May return an error wrapping ErrNotSupported. 21 Update(*ebpf.Program) error 22 23 // Persist a link by pinning it into a bpffs. 24 // 25 // May return an error wrapping ErrNotSupported. 26 Pin(string) error 27 28 // Undo a previous call to Pin. 29 // 30 // May return an error wrapping ErrNotSupported. 31 Unpin() error 32 33 // Close frees resources. 34 // 35 // The link will be broken unless it has been successfully pinned. 36 // A link may continue past the lifetime of the process if Close is 37 // not called. 38 Close() error 39 40 // Info returns metadata on a link. 41 // 42 // May return an error wrapping ErrNotSupported. 43 Info() (*Info, error) 44 45 // Prevent external users from implementing this interface. 46 isLink() 47 } 48 49 // LoadPinnedLink loads a link that was persisted into a bpffs. 50 func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) { 51 raw, err := loadPinnedRawLink(fileName, opts) 52 if err != nil { 53 return nil, err 54 } 55 56 return wrapRawLink(raw) 57 } 58 59 // wrap a RawLink in a more specific type if possible. 60 // 61 // The function takes ownership of raw and closes it on error. 62 func wrapRawLink(raw *RawLink) (Link, error) { 63 info, err := raw.Info() 64 if err != nil { 65 raw.Close() 66 return nil, err 67 } 68 69 switch info.Type { 70 case RawTracepointType: 71 return &rawTracepoint{*raw}, nil 72 case TracingType: 73 return &tracing{*raw}, nil 74 case CgroupType: 75 return &linkCgroup{*raw}, nil 76 case IterType: 77 return &Iter{*raw}, nil 78 case NetNsType: 79 return &NetNsLink{*raw}, nil 80 default: 81 return raw, nil 82 } 83 } 84 85 // ID uniquely identifies a BPF link. 86 type ID = sys.LinkID 87 88 // RawLinkOptions control the creation of a raw link. 89 type RawLinkOptions struct { 90 // File descriptor to attach to. This differs for each attach type. 91 Target int 92 // Program to attach. 93 Program *ebpf.Program 94 // Attach must match the attach type of Program. 95 Attach ebpf.AttachType 96 // BTF is the BTF of the attachment target. 97 BTF btf.TypeID 98 // Flags control the attach behaviour. 99 Flags uint32 100 } 101 102 // Info contains metadata on a link. 103 type Info struct { 104 Type Type 105 ID ID 106 Program ebpf.ProgramID 107 extra interface{} 108 } 109 110 type TracingInfo sys.TracingLinkInfo 111 type CgroupInfo sys.CgroupLinkInfo 112 type NetNsInfo sys.NetNsLinkInfo 113 type XDPInfo sys.XDPLinkInfo 114 115 // Tracing returns tracing type-specific link info. 116 // 117 // Returns nil if the type-specific link info isn't available. 118 func (r Info) Tracing() *TracingInfo { 119 e, _ := r.extra.(*TracingInfo) 120 return e 121 } 122 123 // Cgroup returns cgroup type-specific link info. 124 // 125 // Returns nil if the type-specific link info isn't available. 126 func (r Info) Cgroup() *CgroupInfo { 127 e, _ := r.extra.(*CgroupInfo) 128 return e 129 } 130 131 // NetNs returns netns type-specific link info. 132 // 133 // Returns nil if the type-specific link info isn't available. 134 func (r Info) NetNs() *NetNsInfo { 135 e, _ := r.extra.(*NetNsInfo) 136 return e 137 } 138 139 // ExtraNetNs returns XDP type-specific link info. 140 // 141 // Returns nil if the type-specific link info isn't available. 142 func (r Info) XDP() *XDPInfo { 143 e, _ := r.extra.(*XDPInfo) 144 return e 145 } 146 147 // RawLink is the low-level API to bpf_link. 148 // 149 // You should consider using the higher level interfaces in this 150 // package instead. 151 type RawLink struct { 152 fd *sys.FD 153 pinnedPath string 154 } 155 156 // AttachRawLink creates a raw link. 157 func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { 158 if err := haveBPFLink(); err != nil { 159 return nil, err 160 } 161 162 if opts.Target < 0 { 163 return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd) 164 } 165 166 progFd := opts.Program.FD() 167 if progFd < 0 { 168 return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) 169 } 170 171 attr := sys.LinkCreateAttr{ 172 TargetFd: uint32(opts.Target), 173 ProgFd: uint32(progFd), 174 AttachType: sys.AttachType(opts.Attach), 175 TargetBtfId: uint32(opts.BTF), 176 Flags: opts.Flags, 177 } 178 fd, err := sys.LinkCreate(&attr) 179 if err != nil { 180 return nil, fmt.Errorf("can't create link: %s", err) 181 } 182 183 return &RawLink{fd, ""}, nil 184 } 185 186 func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) { 187 fd, err := sys.ObjGet(&sys.ObjGetAttr{ 188 Pathname: sys.NewStringPointer(fileName), 189 FileFlags: opts.Marshal(), 190 }) 191 if err != nil { 192 return nil, fmt.Errorf("load pinned link: %w", err) 193 } 194 195 return &RawLink{fd, fileName}, nil 196 } 197 198 func (l *RawLink) isLink() {} 199 200 // FD returns the raw file descriptor. 201 func (l *RawLink) FD() int { 202 return l.fd.Int() 203 } 204 205 // Close breaks the link. 206 // 207 // Use Pin if you want to make the link persistent. 208 func (l *RawLink) Close() error { 209 return l.fd.Close() 210 } 211 212 // Pin persists a link past the lifetime of the process. 213 // 214 // Calling Close on a pinned Link will not break the link 215 // until the pin is removed. 216 func (l *RawLink) Pin(fileName string) error { 217 if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil { 218 return err 219 } 220 l.pinnedPath = fileName 221 return nil 222 } 223 224 // Unpin implements the Link interface. 225 func (l *RawLink) Unpin() error { 226 if err := internal.Unpin(l.pinnedPath); err != nil { 227 return err 228 } 229 l.pinnedPath = "" 230 return nil 231 } 232 233 // Update implements the Link interface. 234 func (l *RawLink) Update(new *ebpf.Program) error { 235 return l.UpdateArgs(RawLinkUpdateOptions{ 236 New: new, 237 }) 238 } 239 240 // RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs. 241 type RawLinkUpdateOptions struct { 242 New *ebpf.Program 243 Old *ebpf.Program 244 Flags uint32 245 } 246 247 // UpdateArgs updates a link based on args. 248 func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error { 249 newFd := opts.New.FD() 250 if newFd < 0 { 251 return fmt.Errorf("invalid program: %s", sys.ErrClosedFd) 252 } 253 254 var oldFd int 255 if opts.Old != nil { 256 oldFd = opts.Old.FD() 257 if oldFd < 0 { 258 return fmt.Errorf("invalid replacement program: %s", sys.ErrClosedFd) 259 } 260 } 261 262 attr := sys.LinkUpdateAttr{ 263 LinkFd: l.fd.Uint(), 264 NewProgFd: uint32(newFd), 265 OldProgFd: uint32(oldFd), 266 Flags: opts.Flags, 267 } 268 return sys.LinkUpdate(&attr) 269 } 270 271 // Info returns metadata about the link. 272 func (l *RawLink) Info() (*Info, error) { 273 var info sys.LinkInfo 274 275 if err := sys.ObjInfo(l.fd, &info); err != nil { 276 return nil, fmt.Errorf("link info: %s", err) 277 } 278 279 var extra interface{} 280 switch info.Type { 281 case CgroupType: 282 extra = &CgroupInfo{} 283 case IterType: 284 // not supported 285 case NetNsType: 286 extra = &NetNsInfo{} 287 case RawTracepointType: 288 // not supported 289 case TracingType: 290 extra = &TracingInfo{} 291 case XDPType: 292 extra = &XDPInfo{} 293 case PerfEventType: 294 // no extra 295 default: 296 return nil, fmt.Errorf("unknown link info type: %d", info.Type) 297 } 298 299 if info.Type != RawTracepointType && info.Type != IterType && info.Type != PerfEventType { 300 buf := bytes.NewReader(info.Extra[:]) 301 err := binary.Read(buf, internal.NativeEndian, extra) 302 if err != nil { 303 return nil, fmt.Errorf("can not read extra link info: %w", err) 304 } 305 } 306 307 return &Info{ 308 info.Type, 309 info.Id, 310 ebpf.ProgramID(info.ProgId), 311 extra, 312 }, nil 313 }