github.com/cilium/ebpf@v0.16.0/link/link.go (about) 1 package link 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 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 // NewLinkFromFD creates a link from a raw fd. 50 // 51 // Deprecated: use [NewFromFD] instead. 52 func NewLinkFromFD(fd int) (Link, error) { 53 return NewFromFD(fd) 54 } 55 56 // NewFromFD creates a link from a raw fd. 57 // 58 // You should not use fd after calling this function. 59 func NewFromFD(fd int) (Link, error) { 60 sysFD, err := sys.NewFD(fd) 61 if err != nil { 62 return nil, err 63 } 64 65 return wrapRawLink(&RawLink{fd: sysFD}) 66 } 67 68 // NewFromID returns the link associated with the given id. 69 // 70 // Returns ErrNotExist if there is no link with the given id. 71 func NewFromID(id ID) (Link, error) { 72 getFdAttr := &sys.LinkGetFdByIdAttr{Id: id} 73 fd, err := sys.LinkGetFdById(getFdAttr) 74 if err != nil { 75 return nil, fmt.Errorf("get link fd from ID %d: %w", id, err) 76 } 77 78 return wrapRawLink(&RawLink{fd, ""}) 79 } 80 81 // LoadPinnedLink loads a link that was persisted into a bpffs. 82 func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) { 83 raw, err := loadPinnedRawLink(fileName, opts) 84 if err != nil { 85 return nil, err 86 } 87 88 return wrapRawLink(raw) 89 } 90 91 // wrap a RawLink in a more specific type if possible. 92 // 93 // The function takes ownership of raw and closes it on error. 94 func wrapRawLink(raw *RawLink) (_ Link, err error) { 95 defer func() { 96 if err != nil { 97 raw.Close() 98 } 99 }() 100 101 info, err := raw.Info() 102 if err != nil { 103 return nil, err 104 } 105 106 switch info.Type { 107 case RawTracepointType: 108 return &rawTracepoint{*raw}, nil 109 case TracingType: 110 return &tracing{*raw}, nil 111 case CgroupType: 112 return &linkCgroup{*raw}, nil 113 case IterType: 114 return &Iter{*raw}, nil 115 case NetNsType: 116 return &NetNsLink{*raw}, nil 117 case KprobeMultiType: 118 return &kprobeMultiLink{*raw}, nil 119 case UprobeMultiType: 120 return &uprobeMultiLink{*raw}, nil 121 case PerfEventType: 122 return &perfEventLink{*raw, nil}, nil 123 case TCXType: 124 return &tcxLink{*raw}, nil 125 case NetfilterType: 126 return &netfilterLink{*raw}, nil 127 case NetkitType: 128 return &netkitLink{*raw}, nil 129 case XDPType: 130 return &xdpLink{*raw}, nil 131 default: 132 return raw, nil 133 } 134 } 135 136 // ID uniquely identifies a BPF link. 137 type ID = sys.LinkID 138 139 // RawLinkOptions control the creation of a raw link. 140 type RawLinkOptions struct { 141 // File descriptor to attach to. This differs for each attach type. 142 Target int 143 // Program to attach. 144 Program *ebpf.Program 145 // Attach must match the attach type of Program. 146 Attach ebpf.AttachType 147 // BTF is the BTF of the attachment target. 148 BTF btf.TypeID 149 // Flags control the attach behaviour. 150 Flags uint32 151 } 152 153 // Info contains metadata on a link. 154 type Info struct { 155 Type Type 156 ID ID 157 Program ebpf.ProgramID 158 extra interface{} 159 } 160 161 type TracingInfo struct { 162 AttachType sys.AttachType 163 TargetObjId uint32 164 TargetBtfId sys.TypeID 165 } 166 167 type CgroupInfo struct { 168 CgroupId uint64 169 AttachType sys.AttachType 170 _ [4]byte 171 } 172 173 type NetNsInfo struct { 174 NetnsIno uint32 175 AttachType sys.AttachType 176 } 177 178 type TCXInfo struct { 179 Ifindex uint32 180 AttachType sys.AttachType 181 } 182 183 type XDPInfo struct { 184 Ifindex uint32 185 } 186 187 type NetfilterInfo struct { 188 Pf uint32 189 Hooknum uint32 190 Priority int32 191 Flags uint32 192 } 193 194 type NetkitInfo struct { 195 Ifindex uint32 196 AttachType sys.AttachType 197 } 198 199 type KprobeMultiInfo struct { 200 count uint32 201 flags uint32 202 missed uint64 203 } 204 205 // AddressCount is the number of addresses hooked by the kprobe. 206 func (kpm *KprobeMultiInfo) AddressCount() (uint32, bool) { 207 return kpm.count, kpm.count > 0 208 } 209 210 func (kpm *KprobeMultiInfo) Flags() (uint32, bool) { 211 return kpm.flags, kpm.count > 0 212 } 213 214 func (kpm *KprobeMultiInfo) Missed() (uint64, bool) { 215 return kpm.missed, kpm.count > 0 216 } 217 218 type PerfEventInfo struct { 219 Type sys.PerfEventType 220 extra interface{} 221 } 222 223 func (r *PerfEventInfo) Kprobe() *KprobeInfo { 224 e, _ := r.extra.(*KprobeInfo) 225 return e 226 } 227 228 type KprobeInfo struct { 229 address uint64 230 missed uint64 231 } 232 233 func (kp *KprobeInfo) Address() (uint64, bool) { 234 return kp.address, kp.address > 0 235 } 236 237 func (kp *KprobeInfo) Missed() (uint64, bool) { 238 return kp.missed, kp.address > 0 239 } 240 241 // Tracing returns tracing type-specific link info. 242 // 243 // Returns nil if the type-specific link info isn't available. 244 func (r Info) Tracing() *TracingInfo { 245 e, _ := r.extra.(*TracingInfo) 246 return e 247 } 248 249 // Cgroup returns cgroup type-specific link info. 250 // 251 // Returns nil if the type-specific link info isn't available. 252 func (r Info) Cgroup() *CgroupInfo { 253 e, _ := r.extra.(*CgroupInfo) 254 return e 255 } 256 257 // NetNs returns netns type-specific link info. 258 // 259 // Returns nil if the type-specific link info isn't available. 260 func (r Info) NetNs() *NetNsInfo { 261 e, _ := r.extra.(*NetNsInfo) 262 return e 263 } 264 265 // XDP returns XDP type-specific link info. 266 // 267 // Returns nil if the type-specific link info isn't available. 268 func (r Info) XDP() *XDPInfo { 269 e, _ := r.extra.(*XDPInfo) 270 return e 271 } 272 273 // TCX returns TCX type-specific link info. 274 // 275 // Returns nil if the type-specific link info isn't available. 276 func (r Info) TCX() *TCXInfo { 277 e, _ := r.extra.(*TCXInfo) 278 return e 279 } 280 281 // Netfilter returns netfilter type-specific link info. 282 // 283 // Returns nil if the type-specific link info isn't available. 284 func (r Info) Netfilter() *NetfilterInfo { 285 e, _ := r.extra.(*NetfilterInfo) 286 return e 287 } 288 289 // Netkit returns netkit type-specific link info. 290 // 291 // Returns nil if the type-specific link info isn't available. 292 func (r Info) Netkit() *NetkitInfo { 293 e, _ := r.extra.(*NetkitInfo) 294 return e 295 } 296 297 // KprobeMulti returns kprobe-multi type-specific link info. 298 // 299 // Returns nil if the type-specific link info isn't available. 300 func (r Info) KprobeMulti() *KprobeMultiInfo { 301 e, _ := r.extra.(*KprobeMultiInfo) 302 return e 303 } 304 305 // PerfEvent returns perf-event type-specific link info. 306 // 307 // Returns nil if the type-specific link info isn't available. 308 func (r Info) PerfEvent() *PerfEventInfo { 309 e, _ := r.extra.(*PerfEventInfo) 310 return e 311 } 312 313 // RawLink is the low-level API to bpf_link. 314 // 315 // You should consider using the higher level interfaces in this 316 // package instead. 317 type RawLink struct { 318 fd *sys.FD 319 pinnedPath string 320 } 321 322 // AttachRawLink creates a raw link. 323 func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { 324 if err := haveBPFLink(); err != nil { 325 return nil, err 326 } 327 328 if opts.Target < 0 { 329 return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd) 330 } 331 332 progFd := opts.Program.FD() 333 if progFd < 0 { 334 return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) 335 } 336 337 attr := sys.LinkCreateAttr{ 338 TargetFd: uint32(opts.Target), 339 ProgFd: uint32(progFd), 340 AttachType: sys.AttachType(opts.Attach), 341 TargetBtfId: opts.BTF, 342 Flags: opts.Flags, 343 } 344 fd, err := sys.LinkCreate(&attr) 345 if err != nil { 346 return nil, fmt.Errorf("create link: %w", err) 347 } 348 349 return &RawLink{fd, ""}, nil 350 } 351 352 func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) { 353 fd, err := sys.ObjGet(&sys.ObjGetAttr{ 354 Pathname: sys.NewStringPointer(fileName), 355 FileFlags: opts.Marshal(), 356 }) 357 if err != nil { 358 return nil, fmt.Errorf("load pinned link: %w", err) 359 } 360 361 return &RawLink{fd, fileName}, nil 362 } 363 364 func (l *RawLink) isLink() {} 365 366 // FD returns the raw file descriptor. 367 func (l *RawLink) FD() int { 368 return l.fd.Int() 369 } 370 371 // Close breaks the link. 372 // 373 // Use Pin if you want to make the link persistent. 374 func (l *RawLink) Close() error { 375 return l.fd.Close() 376 } 377 378 // Pin persists a link past the lifetime of the process. 379 // 380 // Calling Close on a pinned Link will not break the link 381 // until the pin is removed. 382 func (l *RawLink) Pin(fileName string) error { 383 if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil { 384 return err 385 } 386 l.pinnedPath = fileName 387 return nil 388 } 389 390 // Unpin implements the Link interface. 391 func (l *RawLink) Unpin() error { 392 if err := internal.Unpin(l.pinnedPath); err != nil { 393 return err 394 } 395 l.pinnedPath = "" 396 return nil 397 } 398 399 // IsPinned returns true if the Link has a non-empty pinned path. 400 func (l *RawLink) IsPinned() bool { 401 return l.pinnedPath != "" 402 } 403 404 // Update implements the Link interface. 405 func (l *RawLink) Update(new *ebpf.Program) error { 406 return l.UpdateArgs(RawLinkUpdateOptions{ 407 New: new, 408 }) 409 } 410 411 // RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs. 412 type RawLinkUpdateOptions struct { 413 New *ebpf.Program 414 Old *ebpf.Program 415 Flags uint32 416 } 417 418 // UpdateArgs updates a link based on args. 419 func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error { 420 newFd := opts.New.FD() 421 if newFd < 0 { 422 return fmt.Errorf("invalid program: %s", sys.ErrClosedFd) 423 } 424 425 var oldFd int 426 if opts.Old != nil { 427 oldFd = opts.Old.FD() 428 if oldFd < 0 { 429 return fmt.Errorf("invalid replacement program: %s", sys.ErrClosedFd) 430 } 431 } 432 433 attr := sys.LinkUpdateAttr{ 434 LinkFd: l.fd.Uint(), 435 NewProgFd: uint32(newFd), 436 OldProgFd: uint32(oldFd), 437 Flags: opts.Flags, 438 } 439 return sys.LinkUpdate(&attr) 440 } 441 442 // Info returns metadata about the link. 443 // 444 // Linktype specific metadata is not included and can be retrieved 445 // via the linktype specific Info() method. 446 func (l *RawLink) Info() (*Info, error) { 447 var info sys.LinkInfo 448 449 if err := sys.ObjInfo(l.fd, &info); err != nil { 450 return nil, fmt.Errorf("link info: %s", err) 451 } 452 453 return &Info{ 454 info.Type, 455 info.Id, 456 ebpf.ProgramID(info.ProgId), 457 nil, 458 }, nil 459 } 460 461 // Iterator allows iterating over links attached into the kernel. 462 type Iterator struct { 463 // The ID of the current link. Only valid after a call to Next 464 ID ID 465 // The current link. Only valid until a call to Next. 466 // See Take if you want to retain the link. 467 Link Link 468 err error 469 } 470 471 // Next retrieves the next link. 472 // 473 // Returns true if another link was found. Call [Iterator.Err] after the function returns false. 474 func (it *Iterator) Next() bool { 475 id := it.ID 476 for { 477 getIdAttr := &sys.LinkGetNextIdAttr{Id: id} 478 err := sys.LinkGetNextId(getIdAttr) 479 if errors.Is(err, os.ErrNotExist) { 480 // There are no more links. 481 break 482 } else if err != nil { 483 it.err = fmt.Errorf("get next link ID: %w", err) 484 break 485 } 486 487 id = getIdAttr.NextId 488 l, err := NewFromID(id) 489 if errors.Is(err, os.ErrNotExist) { 490 // Couldn't load the link fast enough. Try next ID. 491 continue 492 } else if err != nil { 493 it.err = fmt.Errorf("get link for ID %d: %w", id, err) 494 break 495 } 496 497 if it.Link != nil { 498 it.Link.Close() 499 } 500 it.ID, it.Link = id, l 501 return true 502 } 503 504 // No more links or we encountered an error. 505 if it.Link != nil { 506 it.Link.Close() 507 } 508 it.Link = nil 509 return false 510 } 511 512 // Take the ownership of the current link. 513 // 514 // It's the callers responsibility to close the link. 515 func (it *Iterator) Take() Link { 516 l := it.Link 517 it.Link = nil 518 return l 519 } 520 521 // Err returns an error if iteration failed for some reason. 522 func (it *Iterator) Err() error { 523 return it.err 524 } 525 526 func (it *Iterator) Close() { 527 if it.Link != nil { 528 it.Link.Close() 529 } 530 }