package link import ( "fmt" "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" ) var ErrNotSupported = internal.ErrNotSupported // Link represents a Program attached to a BPF hook. type Link interface { // Replace the current program with a new program. // // Passing a nil program is an error. May return an error wrapping ErrNotSupported. Update(*ebpf.Program) error // Persist a link by pinning it into a bpffs. // // May return an error wrapping ErrNotSupported. Pin(string) error // Undo a previous call to Pin. // // May return an error wrapping ErrNotSupported. Unpin() error // Close frees resources. // // The link will be broken unless it has been pinned. A link // may continue past the lifetime of the process if Close is // not called. Close() error // Prevent external users from implementing this interface. isLink() } // ID uniquely identifies a BPF link. type ID uint32 // RawLinkOptions control the creation of a raw link. type RawLinkOptions struct { // File descriptor to attach to. This differs for each attach type. Target int // Program to attach. Program *ebpf.Program // Attach must match the attach type of Program. Attach ebpf.AttachType } // RawLinkInfo contains metadata on a link. type RawLinkInfo struct { Type Type ID ID Program ebpf.ProgramID } // RawLink is the low-level API to bpf_link. // // You should consider using the higher level interfaces in this // package instead. type RawLink struct { fd *internal.FD pinnedPath string } // AttachRawLink creates a raw link. func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { if err := haveBPFLink(); err != nil { return nil, err } if opts.Target < 0 { return nil, fmt.Errorf("invalid target: %s", internal.ErrClosedFd) } progFd := opts.Program.FD() if progFd < 0 { return nil, fmt.Errorf("invalid program: %s", internal.ErrClosedFd) } attr := bpfLinkCreateAttr{ targetFd: uint32(opts.Target), progFd: uint32(progFd), attachType: opts.Attach, } fd, err := bpfLinkCreate(&attr) if err != nil { return nil, fmt.Errorf("can't create link: %s", err) } return &RawLink{fd, ""}, nil } // LoadPinnedRawLink loads a persisted link from a bpffs. // // Returns an error if the pinned link type doesn't match linkType. Pass // UnspecifiedType to disable this behaviour. func LoadPinnedRawLink(fileName string, linkType Type, opts *ebpf.LoadPinOptions) (*RawLink, error) { fd, err := internal.BPFObjGet(fileName, opts.Marshal()) if err != nil { return nil, fmt.Errorf("load pinned link: %w", err) } link := &RawLink{fd, fileName} if linkType == UnspecifiedType { return link, nil } info, err := link.Info() if err != nil { link.Close() return nil, fmt.Errorf("get pinned link info: %s", err) } if info.Type != linkType { link.Close() return nil, fmt.Errorf("link type %v doesn't match %v", info.Type, linkType) } return link, nil } func (l *RawLink) isLink() {} // FD returns the raw file descriptor. func (l *RawLink) FD() int { fd, err := l.fd.Value() if err != nil { return -1 } return int(fd) } // Close breaks the link. // // Use Pin if you want to make the link persistent. func (l *RawLink) Close() error { return l.fd.Close() } // Pin persists a link past the lifetime of the process. // // Calling Close on a pinned Link will not break the link // until the pin is removed. func (l *RawLink) Pin(fileName string) error { if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil { return err } l.pinnedPath = fileName return nil } // Unpin implements the Link interface. func (l *RawLink) Unpin() error { if err := internal.Unpin(l.pinnedPath); err != nil { return err } l.pinnedPath = "" return nil } // Update implements the Link interface. func (l *RawLink) Update(new *ebpf.Program) error { return l.UpdateArgs(RawLinkUpdateOptions{ New: new, }) } // RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs. type RawLinkUpdateOptions struct { New *ebpf.Program Old *ebpf.Program Flags uint32 } // UpdateArgs updates a link based on args. func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error { newFd := opts.New.FD() if newFd < 0 { return fmt.Errorf("invalid program: %s", internal.ErrClosedFd) } var oldFd int if opts.Old != nil { oldFd = opts.Old.FD() if oldFd < 0 { return fmt.Errorf("invalid replacement program: %s", internal.ErrClosedFd) } } linkFd, err := l.fd.Value() if err != nil { return fmt.Errorf("can't update link: %s", err) } attr := bpfLinkUpdateAttr{ linkFd: linkFd, newProgFd: uint32(newFd), oldProgFd: uint32(oldFd), flags: opts.Flags, } return bpfLinkUpdate(&attr) } // struct bpf_link_info type bpfLinkInfo struct { typ uint32 id uint32 prog_id uint32 } // Info returns metadata about the link. func (l *RawLink) Info() (*RawLinkInfo, error) { var info bpfLinkInfo err := internal.BPFObjGetInfoByFD(l.fd, unsafe.Pointer(&info), unsafe.Sizeof(info)) if err != nil { return nil, fmt.Errorf("link info: %s", err) } return &RawLinkInfo{ Type(info.typ), ID(info.id), ebpf.ProgramID(info.prog_id), }, nil }