github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/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 nil, fmt.Errorf("recovering perf event fd: %w", ErrNotSupported) 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 default: 130 return raw, nil 131 } 132 } 133 134 // ID uniquely identifies a BPF link. 135 type ID = sys.LinkID 136 137 // RawLinkOptions control the creation of a raw link. 138 type RawLinkOptions struct { 139 // File descriptor to attach to. This differs for each attach type. 140 Target int 141 // Program to attach. 142 Program *ebpf.Program 143 // Attach must match the attach type of Program. 144 Attach ebpf.AttachType 145 // BTF is the BTF of the attachment target. 146 BTF btf.TypeID 147 // Flags control the attach behaviour. 148 Flags uint32 149 } 150 151 // Info contains metadata on a link. 152 type Info struct { 153 Type Type 154 ID ID 155 Program ebpf.ProgramID 156 extra interface{} 157 } 158 159 type TracingInfo struct { 160 AttachType sys.AttachType 161 TargetObjId uint32 162 TargetBtfId sys.TypeID 163 } 164 165 type CgroupInfo struct { 166 CgroupId uint64 167 AttachType sys.AttachType 168 _ [4]byte 169 } 170 171 type NetNsInfo struct { 172 NetnsIno uint32 173 AttachType sys.AttachType 174 } 175 176 type TCXInfo struct { 177 Ifindex uint32 178 AttachType sys.AttachType 179 } 180 181 type XDPInfo struct { 182 Ifindex uint32 183 } 184 185 type NetfilterInfo struct { 186 Pf uint32 187 Hooknum uint32 188 Priority int32 189 Flags uint32 190 } 191 192 type NetkitInfo struct { 193 Ifindex uint32 194 AttachType sys.AttachType 195 } 196 197 type KprobeMultiInfo struct { 198 count uint32 199 flags uint32 200 missed uint64 201 } 202 203 // AddressCount is the number of addresses hooked by the kprobe. 204 func (kpm *KprobeMultiInfo) AddressCount() (uint32, bool) { 205 return kpm.count, kpm.count > 0 206 } 207 208 func (kpm *KprobeMultiInfo) Flags() (uint32, bool) { 209 return kpm.flags, kpm.count > 0 210 } 211 212 func (kpm *KprobeMultiInfo) Missed() (uint64, bool) { 213 return kpm.missed, kpm.count > 0 214 } 215 216 type PerfEventInfo struct { 217 Type sys.PerfEventType 218 extra interface{} 219 } 220 221 func (r *PerfEventInfo) Kprobe() *KprobeInfo { 222 e, _ := r.extra.(*KprobeInfo) 223 return e 224 } 225 226 type KprobeInfo struct { 227 address uint64 228 missed uint64 229 } 230 231 func (kp *KprobeInfo) Address() (uint64, bool) { 232 return kp.address, kp.address > 0 233 } 234 235 func (kp *KprobeInfo) Missed() (uint64, bool) { 236 return kp.missed, kp.address > 0 237 } 238 239 // Tracing returns tracing type-specific link info. 240 // 241 // Returns nil if the type-specific link info isn't available. 242 func (r Info) Tracing() *TracingInfo { 243 e, _ := r.extra.(*TracingInfo) 244 return e 245 } 246 247 // Cgroup returns cgroup type-specific link info. 248 // 249 // Returns nil if the type-specific link info isn't available. 250 func (r Info) Cgroup() *CgroupInfo { 251 e, _ := r.extra.(*CgroupInfo) 252 return e 253 } 254 255 // NetNs returns netns type-specific link info. 256 // 257 // Returns nil if the type-specific link info isn't available. 258 func (r Info) NetNs() *NetNsInfo { 259 e, _ := r.extra.(*NetNsInfo) 260 return e 261 } 262 263 // XDP returns XDP type-specific link info. 264 // 265 // Returns nil if the type-specific link info isn't available. 266 func (r Info) XDP() *XDPInfo { 267 e, _ := r.extra.(*XDPInfo) 268 return e 269 } 270 271 // TCX returns TCX type-specific link info. 272 // 273 // Returns nil if the type-specific link info isn't available. 274 func (r Info) TCX() *TCXInfo { 275 e, _ := r.extra.(*TCXInfo) 276 return e 277 } 278 279 // Netfilter returns netfilter type-specific link info. 280 // 281 // Returns nil if the type-specific link info isn't available. 282 func (r Info) Netfilter() *NetfilterInfo { 283 e, _ := r.extra.(*NetfilterInfo) 284 return e 285 } 286 287 // Netkit returns netkit type-specific link info. 288 // 289 // Returns nil if the type-specific link info isn't available. 290 func (r Info) Netkit() *NetkitInfo { 291 e, _ := r.extra.(*NetkitInfo) 292 return e 293 } 294 295 // KprobeMulti returns kprobe-multi type-specific link info. 296 // 297 // Returns nil if the type-specific link info isn't available. 298 func (r Info) KprobeMulti() *KprobeMultiInfo { 299 e, _ := r.extra.(*KprobeMultiInfo) 300 return e 301 } 302 303 // PerfEvent returns perf-event type-specific link info. 304 // 305 // Returns nil if the type-specific link info isn't available. 306 func (r Info) PerfEvent() *PerfEventInfo { 307 e, _ := r.extra.(*PerfEventInfo) 308 return e 309 } 310 311 // RawLink is the low-level API to bpf_link. 312 // 313 // You should consider using the higher level interfaces in this 314 // package instead. 315 type RawLink struct { 316 fd *sys.FD 317 pinnedPath string 318 } 319 320 // AttachRawLink creates a raw link. 321 func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { 322 if err := haveBPFLink(); err != nil { 323 return nil, err 324 } 325 326 if opts.Target < 0 { 327 return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd) 328 } 329 330 progFd := opts.Program.FD() 331 if progFd < 0 { 332 return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) 333 } 334 335 attr := sys.LinkCreateAttr{ 336 TargetFd: uint32(opts.Target), 337 ProgFd: uint32(progFd), 338 AttachType: sys.AttachType(opts.Attach), 339 TargetBtfId: opts.BTF, 340 Flags: opts.Flags, 341 } 342 fd, err := sys.LinkCreate(&attr) 343 if err != nil { 344 return nil, fmt.Errorf("create link: %w", err) 345 } 346 347 return &RawLink{fd, ""}, nil 348 } 349 350 func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) { 351 fd, err := sys.ObjGet(&sys.ObjGetAttr{ 352 Pathname: sys.NewStringPointer(fileName), 353 FileFlags: opts.Marshal(), 354 }) 355 if err != nil { 356 return nil, fmt.Errorf("load pinned link: %w", err) 357 } 358 359 return &RawLink{fd, fileName}, nil 360 } 361 362 func (l *RawLink) isLink() {} 363 364 // FD returns the raw file descriptor. 365 func (l *RawLink) FD() int { 366 return l.fd.Int() 367 } 368 369 // Close breaks the link. 370 // 371 // Use Pin if you want to make the link persistent. 372 func (l *RawLink) Close() error { 373 return l.fd.Close() 374 } 375 376 // Pin persists a link past the lifetime of the process. 377 // 378 // Calling Close on a pinned Link will not break the link 379 // until the pin is removed. 380 func (l *RawLink) Pin(fileName string) error { 381 if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil { 382 return err 383 } 384 l.pinnedPath = fileName 385 return nil 386 } 387 388 // Unpin implements the Link interface. 389 func (l *RawLink) Unpin() error { 390 if err := internal.Unpin(l.pinnedPath); err != nil { 391 return err 392 } 393 l.pinnedPath = "" 394 return nil 395 } 396 397 // IsPinned returns true if the Link has a non-empty pinned path. 398 func (l *RawLink) IsPinned() bool { 399 return l.pinnedPath != "" 400 } 401 402 // Update implements the Link interface. 403 func (l *RawLink) Update(new *ebpf.Program) error { 404 return l.UpdateArgs(RawLinkUpdateOptions{ 405 New: new, 406 }) 407 } 408 409 // RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs. 410 type RawLinkUpdateOptions struct { 411 New *ebpf.Program 412 Old *ebpf.Program 413 Flags uint32 414 } 415 416 // UpdateArgs updates a link based on args. 417 func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error { 418 newFd := opts.New.FD() 419 if newFd < 0 { 420 return fmt.Errorf("invalid program: %s", sys.ErrClosedFd) 421 } 422 423 var oldFd int 424 if opts.Old != nil { 425 oldFd = opts.Old.FD() 426 if oldFd < 0 { 427 return fmt.Errorf("invalid replacement program: %s", sys.ErrClosedFd) 428 } 429 } 430 431 attr := sys.LinkUpdateAttr{ 432 LinkFd: l.fd.Uint(), 433 NewProgFd: uint32(newFd), 434 OldProgFd: uint32(oldFd), 435 Flags: opts.Flags, 436 } 437 return sys.LinkUpdate(&attr) 438 } 439 440 // Info returns metadata about the link. 441 func (l *RawLink) Info() (*Info, error) { 442 var info sys.LinkInfo 443 444 if err := sys.ObjInfo(l.fd, &info); err != nil { 445 return nil, fmt.Errorf("link info: %s", err) 446 } 447 448 var extra interface{} 449 switch info.Type { 450 case CgroupType: 451 var cgroupInfo sys.CgroupLinkInfo 452 if err := sys.ObjInfo(l.fd, &cgroupInfo); err != nil { 453 return nil, fmt.Errorf("cgroup link info: %s", err) 454 } 455 extra = &CgroupInfo{ 456 CgroupId: cgroupInfo.CgroupId, 457 AttachType: cgroupInfo.AttachType, 458 } 459 case NetNsType: 460 var netnsInfo sys.NetNsLinkInfo 461 if err := sys.ObjInfo(l.fd, &netnsInfo); err != nil { 462 return nil, fmt.Errorf("netns link info: %s", err) 463 } 464 extra = &NetNsInfo{ 465 NetnsIno: netnsInfo.NetnsIno, 466 AttachType: netnsInfo.AttachType, 467 } 468 case TracingType: 469 var tracingInfo sys.TracingLinkInfo 470 if err := sys.ObjInfo(l.fd, &tracingInfo); err != nil { 471 return nil, fmt.Errorf("tracing link info: %s", err) 472 } 473 extra = &TracingInfo{ 474 TargetObjId: tracingInfo.TargetObjId, 475 TargetBtfId: tracingInfo.TargetBtfId, 476 AttachType: tracingInfo.AttachType, 477 } 478 case XDPType: 479 var xdpInfo sys.XDPLinkInfo 480 if err := sys.ObjInfo(l.fd, &xdpInfo); err != nil { 481 return nil, fmt.Errorf("xdp link info: %s", err) 482 } 483 extra = &XDPInfo{ 484 Ifindex: xdpInfo.Ifindex, 485 } 486 case RawTracepointType, IterType, UprobeMultiType: 487 // Extra metadata not supported. 488 case TCXType: 489 var tcxInfo sys.TcxLinkInfo 490 if err := sys.ObjInfo(l.fd, &tcxInfo); err != nil { 491 return nil, fmt.Errorf("tcx link info: %s", err) 492 } 493 extra = &TCXInfo{ 494 Ifindex: tcxInfo.Ifindex, 495 AttachType: tcxInfo.AttachType, 496 } 497 case NetfilterType: 498 var netfilterInfo sys.NetfilterLinkInfo 499 if err := sys.ObjInfo(l.fd, &netfilterInfo); err != nil { 500 return nil, fmt.Errorf("netfilter link info: %s", err) 501 } 502 extra = &NetfilterInfo{ 503 Pf: netfilterInfo.Pf, 504 Hooknum: netfilterInfo.Hooknum, 505 Priority: netfilterInfo.Priority, 506 Flags: netfilterInfo.Flags, 507 } 508 case NetkitType: 509 var netkitInfo sys.NetkitLinkInfo 510 if err := sys.ObjInfo(l.fd, &netkitInfo); err != nil { 511 return nil, fmt.Errorf("tcx link info: %s", err) 512 } 513 extra = &NetkitInfo{ 514 Ifindex: netkitInfo.Ifindex, 515 AttachType: netkitInfo.AttachType, 516 } 517 case KprobeMultiType: 518 var kprobeMultiInfo sys.KprobeMultiLinkInfo 519 if err := sys.ObjInfo(l.fd, &kprobeMultiInfo); err != nil { 520 return nil, fmt.Errorf("kprobe multi link info: %s", err) 521 } 522 extra = &KprobeMultiInfo{ 523 count: kprobeMultiInfo.Count, 524 flags: kprobeMultiInfo.Flags, 525 missed: kprobeMultiInfo.Missed, 526 } 527 case PerfEventType: 528 var perfEventInfo sys.PerfEventLinkInfo 529 if err := sys.ObjInfo(l.fd, &perfEventInfo); err != nil { 530 return nil, fmt.Errorf("perf event link info: %s", err) 531 } 532 533 var extra2 interface{} 534 switch perfEventInfo.PerfEventType { 535 case sys.BPF_PERF_EVENT_KPROBE, sys.BPF_PERF_EVENT_KRETPROBE: 536 var kprobeInfo sys.KprobeLinkInfo 537 if err := sys.ObjInfo(l.fd, &kprobeInfo); err != nil { 538 return nil, fmt.Errorf("kprobe multi link info: %s", err) 539 } 540 extra2 = &KprobeInfo{ 541 address: kprobeInfo.Addr, 542 missed: kprobeInfo.Missed, 543 } 544 } 545 546 extra = &PerfEventInfo{ 547 Type: perfEventInfo.PerfEventType, 548 extra: extra2, 549 } 550 default: 551 return nil, fmt.Errorf("unknown link info type: %d", info.Type) 552 } 553 554 return &Info{ 555 info.Type, 556 info.Id, 557 ebpf.ProgramID(info.ProgId), 558 extra, 559 }, nil 560 } 561 562 // Iterator allows iterating over links attached into the kernel. 563 type Iterator struct { 564 // The ID of the current link. Only valid after a call to Next 565 ID ID 566 // The current link. Only valid until a call to Next. 567 // See Take if you want to retain the link. 568 Link Link 569 err error 570 } 571 572 // Next retrieves the next link. 573 // 574 // Returns true if another link was found. Call [Iterator.Err] after the function returns false. 575 func (it *Iterator) Next() bool { 576 id := it.ID 577 for { 578 getIdAttr := &sys.LinkGetNextIdAttr{Id: id} 579 err := sys.LinkGetNextId(getIdAttr) 580 if errors.Is(err, os.ErrNotExist) { 581 // There are no more links. 582 break 583 } else if err != nil { 584 it.err = fmt.Errorf("get next link ID: %w", err) 585 break 586 } 587 588 id = getIdAttr.NextId 589 l, err := NewFromID(id) 590 if errors.Is(err, os.ErrNotExist) { 591 // Couldn't load the link fast enough. Try next ID. 592 continue 593 } else if err != nil { 594 it.err = fmt.Errorf("get link for ID %d: %w", id, err) 595 break 596 } 597 598 if it.Link != nil { 599 it.Link.Close() 600 } 601 it.ID, it.Link = id, l 602 return true 603 } 604 605 // No more links or we encountered an error. 606 if it.Link != nil { 607 it.Link.Close() 608 } 609 it.Link = nil 610 return false 611 } 612 613 // Take the ownership of the current link. 614 // 615 // It's the callers responsibility to close the link. 616 func (it *Iterator) Take() Link { 617 l := it.Link 618 it.Link = nil 619 return l 620 } 621 622 // Err returns an error if iteration failed for some reason. 623 func (it *Iterator) Err() error { 624 return it.err 625 } 626 627 func (it *Iterator) Close() { 628 if it.Link != nil { 629 it.Link.Close() 630 } 631 }