summaryrefslogtreecommitdiff
path: root/vendor/github.com/cilium/ebpf/link
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cilium/ebpf/link')
-rw-r--r--vendor/github.com/cilium/ebpf/link/cgroup.go169
-rw-r--r--vendor/github.com/cilium/ebpf/link/doc.go2
-rw-r--r--vendor/github.com/cilium/ebpf/link/iter.go91
-rw-r--r--vendor/github.com/cilium/ebpf/link/link.go214
-rw-r--r--vendor/github.com/cilium/ebpf/link/netns.go60
-rw-r--r--vendor/github.com/cilium/ebpf/link/program.go76
-rw-r--r--vendor/github.com/cilium/ebpf/link/raw_tracepoint.go57
-rw-r--r--vendor/github.com/cilium/ebpf/link/syscalls.go173
8 files changed, 842 insertions, 0 deletions
diff --git a/vendor/github.com/cilium/ebpf/link/cgroup.go b/vendor/github.com/cilium/ebpf/link/cgroup.go
new file mode 100644
index 000000000..16a943930
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/cgroup.go
@@ -0,0 +1,169 @@
+package link
+
+import (
+ "errors"
+ "fmt"
+ "os"
+
+ "github.com/cilium/ebpf"
+)
+
+type cgroupAttachFlags uint32
+
+// cgroup attach flags
+const (
+ flagAllowOverride cgroupAttachFlags = 1 << iota
+ flagAllowMulti
+ flagReplace
+)
+
+type CgroupOptions struct {
+ // Path to a cgroupv2 folder.
+ Path string
+ // One of the AttachCgroup* constants
+ Attach ebpf.AttachType
+ // Program must be of type CGroup*, and the attach type must match Attach.
+ Program *ebpf.Program
+}
+
+// AttachCgroup links a BPF program to a cgroup.
+func AttachCgroup(opts CgroupOptions) (Link, error) {
+ cgroup, err := os.Open(opts.Path)
+ if err != nil {
+ return nil, fmt.Errorf("can't open cgroup: %s", err)
+ }
+
+ clone, err := opts.Program.Clone()
+ if err != nil {
+ cgroup.Close()
+ return nil, err
+ }
+
+ var cg Link
+ cg, err = newLinkCgroup(cgroup, opts.Attach, clone)
+ if errors.Is(err, ErrNotSupported) {
+ cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowMulti)
+ }
+ if errors.Is(err, ErrNotSupported) {
+ cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowOverride)
+ }
+ if err != nil {
+ cgroup.Close()
+ clone.Close()
+ return nil, err
+ }
+
+ return cg, nil
+}
+
+// LoadPinnedCgroup loads a pinned cgroup from a bpffs.
+func LoadPinnedCgroup(fileName string) (Link, error) {
+ link, err := LoadPinnedRawLink(fileName)
+ if err != nil {
+ return nil, err
+ }
+
+ return &linkCgroup{link}, nil
+}
+
+type progAttachCgroup struct {
+ cgroup *os.File
+ current *ebpf.Program
+ attachType ebpf.AttachType
+ flags cgroupAttachFlags
+}
+
+var _ Link = (*progAttachCgroup)(nil)
+
+func (cg *progAttachCgroup) isLink() {}
+
+func newProgAttachCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program, flags cgroupAttachFlags) (*progAttachCgroup, error) {
+ if flags&flagAllowMulti > 0 {
+ if err := haveProgAttachReplace(); err != nil {
+ return nil, fmt.Errorf("can't support multiple programs: %w", err)
+ }
+ }
+
+ err := RawAttachProgram(RawAttachProgramOptions{
+ Target: int(cgroup.Fd()),
+ Program: prog,
+ Flags: uint32(flags),
+ Attach: attach,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("cgroup: %w", err)
+ }
+
+ return &progAttachCgroup{cgroup, prog, attach, flags}, nil
+}
+
+func (cg *progAttachCgroup) Close() error {
+ defer cg.cgroup.Close()
+ defer cg.current.Close()
+
+ err := RawDetachProgram(RawDetachProgramOptions{
+ Target: int(cg.cgroup.Fd()),
+ Program: cg.current,
+ Attach: cg.attachType,
+ })
+ if err != nil {
+ return fmt.Errorf("close cgroup: %s", err)
+ }
+ return nil
+}
+
+func (cg *progAttachCgroup) Update(prog *ebpf.Program) error {
+ new, err := prog.Clone()
+ if err != nil {
+ return err
+ }
+
+ args := RawAttachProgramOptions{
+ Target: int(cg.cgroup.Fd()),
+ Program: prog,
+ Attach: cg.attachType,
+ Flags: uint32(cg.flags),
+ }
+
+ if cg.flags&flagAllowMulti > 0 {
+ // Atomically replacing multiple programs requires at least
+ // 5.5 (commit 7dd68b3279f17921 "bpf: Support replacing cgroup-bpf
+ // program in MULTI mode")
+ args.Flags |= uint32(flagReplace)
+ args.Replace = cg.current
+ }
+
+ if err := RawAttachProgram(args); err != nil {
+ new.Close()
+ return fmt.Errorf("can't update cgroup: %s", err)
+ }
+
+ cg.current.Close()
+ cg.current = new
+ return nil
+}
+
+func (cg *progAttachCgroup) Pin(string) error {
+ return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported)
+}
+
+type linkCgroup struct {
+ *RawLink
+}
+
+var _ Link = (*linkCgroup)(nil)
+
+func (cg *linkCgroup) isLink() {}
+
+func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) {
+ link, err := AttachRawLink(RawLinkOptions{
+ Target: int(cgroup.Fd()),
+ Program: prog,
+ Attach: attach,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return &linkCgroup{link}, err
+}
diff --git a/vendor/github.com/cilium/ebpf/link/doc.go b/vendor/github.com/cilium/ebpf/link/doc.go
new file mode 100644
index 000000000..2bde35ed7
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/doc.go
@@ -0,0 +1,2 @@
+// Package link allows attaching eBPF programs to various kernel hooks.
+package link
diff --git a/vendor/github.com/cilium/ebpf/link/iter.go b/vendor/github.com/cilium/ebpf/link/iter.go
new file mode 100644
index 000000000..2b5f2846a
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/iter.go
@@ -0,0 +1,91 @@
+package link
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/cilium/ebpf"
+)
+
+type IterOptions struct {
+ // Program must be of type Tracing with attach type
+ // AttachTraceIter. The kind of iterator to attach to is
+ // determined at load time via the AttachTo field.
+ //
+ // AttachTo requires the kernel to include BTF of itself,
+ // and it to be compiled with a recent pahole (>= 1.16).
+ Program *ebpf.Program
+}
+
+// AttachIter attaches a BPF seq_file iterator.
+func AttachIter(opts IterOptions) (*Iter, error) {
+ link, err := AttachRawLink(RawLinkOptions{
+ Program: opts.Program,
+ Attach: ebpf.AttachTraceIter,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("can't link iterator: %w", err)
+ }
+
+ return &Iter{link}, err
+}
+
+// LoadPinnedIter loads a pinned iterator from a bpffs.
+func LoadPinnedIter(fileName string) (*Iter, error) {
+ link, err := LoadPinnedRawLink(fileName)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Iter{link}, err
+}
+
+// Iter represents an attached bpf_iter.
+type Iter struct {
+ link *RawLink
+}
+
+var _ Link = (*Iter)(nil)
+
+func (it *Iter) isLink() {}
+
+// FD returns the underlying file descriptor.
+func (it *Iter) FD() int {
+ return it.link.FD()
+}
+
+// Close implements Link.
+func (it *Iter) Close() error {
+ return it.link.Close()
+}
+
+// Pin implements Link.
+func (it *Iter) Pin(fileName string) error {
+ return it.link.Pin(fileName)
+}
+
+// Update implements Link.
+func (it *Iter) Update(new *ebpf.Program) error {
+ return it.link.Update(new)
+}
+
+// Open creates a new instance of the iterator.
+//
+// Reading from the returned reader triggers the BPF program.
+func (it *Iter) Open() (io.ReadCloser, error) {
+ linkFd, err := it.link.fd.Value()
+ if err != nil {
+ return nil, err
+ }
+
+ attr := &bpfIterCreateAttr{
+ linkFd: linkFd,
+ }
+
+ fd, err := bpfIterCreate(attr)
+ if err != nil {
+ return nil, fmt.Errorf("can't create iterator: %w", err)
+ }
+
+ return fd.File("bpf_iter"), nil
+}
diff --git a/vendor/github.com/cilium/ebpf/link/link.go b/vendor/github.com/cilium/ebpf/link/link.go
new file mode 100644
index 000000000..48f1a5529
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/link.go
@@ -0,0 +1,214 @@
+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
+
+ // 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
+}
+
+// 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.
+func LoadPinnedRawLink(fileName string) (*RawLink, error) {
+ return loadPinnedRawLink(fileName, UnspecifiedType)
+}
+
+func loadPinnedRawLink(fileName string, typ Type) (*RawLink, error) {
+ fd, err := internal.BPFObjGet(fileName)
+ if err != nil {
+ return nil, fmt.Errorf("load pinned link: %s", err)
+ }
+
+ link := &RawLink{fd}
+ if typ == 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 != typ {
+ link.Close()
+ return nil, fmt.Errorf("link type %v doesn't match %v", info.Type, typ)
+ }
+
+ 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.BPFObjPin(fileName, l.fd); err != nil {
+ return fmt.Errorf("can't pin link: %s", err)
+ }
+ return nil
+}
+
+// Update implements Link.
+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
+}
diff --git a/vendor/github.com/cilium/ebpf/link/netns.go b/vendor/github.com/cilium/ebpf/link/netns.go
new file mode 100644
index 000000000..3533ff0fa
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/netns.go
@@ -0,0 +1,60 @@
+package link
+
+import (
+ "fmt"
+
+ "github.com/cilium/ebpf"
+)
+
+// NetNsInfo contains metadata about a network namespace link.
+type NetNsInfo struct {
+ RawLinkInfo
+}
+
+// NetNsLink is a program attached to a network namespace.
+type NetNsLink struct {
+ *RawLink
+}
+
+// AttachNetNs attaches a program to a network namespace.
+func AttachNetNs(ns int, prog *ebpf.Program) (*NetNsLink, error) {
+ var attach ebpf.AttachType
+ switch t := prog.Type(); t {
+ case ebpf.FlowDissector:
+ attach = ebpf.AttachFlowDissector
+ case ebpf.SkLookup:
+ attach = ebpf.AttachSkLookup
+ default:
+ return nil, fmt.Errorf("can't attach %v to network namespace", t)
+ }
+
+ link, err := AttachRawLink(RawLinkOptions{
+ Target: ns,
+ Program: prog,
+ Attach: attach,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return &NetNsLink{link}, nil
+}
+
+// LoadPinnedNetNs loads a network namespace link from bpffs.
+func LoadPinnedNetNs(fileName string) (*NetNsLink, error) {
+ link, err := loadPinnedRawLink(fileName, NetNsType)
+ if err != nil {
+ return nil, err
+ }
+
+ return &NetNsLink{link}, nil
+}
+
+// Info returns information about the link.
+func (nns *NetNsLink) Info() (*NetNsInfo, error) {
+ info, err := nns.RawLink.Info()
+ if err != nil {
+ return nil, err
+ }
+ return &NetNsInfo{*info}, nil
+}
diff --git a/vendor/github.com/cilium/ebpf/link/program.go b/vendor/github.com/cilium/ebpf/link/program.go
new file mode 100644
index 000000000..0fe9d37c4
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/program.go
@@ -0,0 +1,76 @@
+package link
+
+import (
+ "fmt"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/internal"
+)
+
+type RawAttachProgramOptions struct {
+ // File descriptor to attach to. This differs for each attach type.
+ Target int
+ // Program to attach.
+ Program *ebpf.Program
+ // Program to replace (cgroups).
+ Replace *ebpf.Program
+ // Attach must match the attach type of Program (and Replace).
+ Attach ebpf.AttachType
+ // Flags control the attach behaviour. This differs for each attach type.
+ Flags uint32
+}
+
+// RawAttachProgram is a low level wrapper around BPF_PROG_ATTACH.
+//
+// You should use one of the higher level abstractions available in this
+// package if possible.
+func RawAttachProgram(opts RawAttachProgramOptions) error {
+ if err := haveProgAttach(); err != nil {
+ return err
+ }
+
+ var replaceFd uint32
+ if opts.Replace != nil {
+ replaceFd = uint32(opts.Replace.FD())
+ }
+
+ attr := internal.BPFProgAttachAttr{
+ TargetFd: uint32(opts.Target),
+ AttachBpfFd: uint32(opts.Program.FD()),
+ ReplaceBpfFd: replaceFd,
+ AttachType: uint32(opts.Attach),
+ AttachFlags: uint32(opts.Flags),
+ }
+
+ if err := internal.BPFProgAttach(&attr); err != nil {
+ return fmt.Errorf("can't attach program: %s", err)
+ }
+ return nil
+}
+
+type RawDetachProgramOptions struct {
+ Target int
+ Program *ebpf.Program
+ Attach ebpf.AttachType
+}
+
+// RawDetachProgram is a low level wrapper around BPF_PROG_DETACH.
+//
+// You should use one of the higher level abstractions available in this
+// package if possible.
+func RawDetachProgram(opts RawDetachProgramOptions) error {
+ if err := haveProgAttach(); err != nil {
+ return err
+ }
+
+ attr := internal.BPFProgDetachAttr{
+ TargetFd: uint32(opts.Target),
+ AttachBpfFd: uint32(opts.Program.FD()),
+ AttachType: uint32(opts.Attach),
+ }
+ if err := internal.BPFProgDetach(&attr); err != nil {
+ return fmt.Errorf("can't detach program: %s", err)
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go b/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
new file mode 100644
index 000000000..65652486f
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go
@@ -0,0 +1,57 @@
+package link
+
+import (
+ "fmt"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/internal"
+)
+
+type RawTracepointOptions struct {
+ // Tracepoint name.
+ Name string
+ // Program must be of type RawTracepoint*
+ Program *ebpf.Program
+}
+
+// AttachRawTracepoint links a BPF program to a raw_tracepoint.
+//
+// Requires at least Linux 4.17.
+func AttachRawTracepoint(opts RawTracepointOptions) (Link, error) {
+ if t := opts.Program.Type(); t != ebpf.RawTracepoint && t != ebpf.RawTracepointWritable {
+ return nil, fmt.Errorf("invalid program type %s, expected RawTracepoint(Writable)", t)
+ }
+ if opts.Program.FD() < 0 {
+ return nil, fmt.Errorf("invalid program: %w", internal.ErrClosedFd)
+ }
+
+ fd, err := bpfRawTracepointOpen(&bpfRawTracepointOpenAttr{
+ name: internal.NewStringPointer(opts.Name),
+ fd: uint32(opts.Program.FD()),
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return &progAttachRawTracepoint{fd: fd}, nil
+}
+
+type progAttachRawTracepoint struct {
+ fd *internal.FD
+}
+
+var _ Link = (*progAttachRawTracepoint)(nil)
+
+func (rt *progAttachRawTracepoint) isLink() {}
+
+func (rt *progAttachRawTracepoint) Close() error {
+ return rt.fd.Close()
+}
+
+func (rt *progAttachRawTracepoint) Update(_ *ebpf.Program) error {
+ return fmt.Errorf("can't update raw_tracepoint: %w", ErrNotSupported)
+}
+
+func (rt *progAttachRawTracepoint) Pin(_ string) error {
+ return fmt.Errorf("can't pin raw_tracepoint: %w", ErrNotSupported)
+}
diff --git a/vendor/github.com/cilium/ebpf/link/syscalls.go b/vendor/github.com/cilium/ebpf/link/syscalls.go
new file mode 100644
index 000000000..19326c8af
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/link/syscalls.go
@@ -0,0 +1,173 @@
+package link
+
+import (
+ "errors"
+ "unsafe"
+
+ "github.com/cilium/ebpf"
+ "github.com/cilium/ebpf/asm"
+ "github.com/cilium/ebpf/internal"
+ "github.com/cilium/ebpf/internal/unix"
+)
+
+// Type is the kind of link.
+type Type uint32
+
+// Valid link types.
+//
+// Equivalent to enum bpf_link_type.
+const (
+ UnspecifiedType Type = iota
+ RawTracepointType
+ TracingType
+ CgroupType
+ IterType
+ NetNsType
+ XDPType
+)
+
+var haveProgAttach = internal.FeatureTest("BPF_PROG_ATTACH", "4.10", func() error {
+ prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+ Type: ebpf.CGroupSKB,
+ AttachType: ebpf.AttachCGroupInetIngress,
+ License: "MIT",
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 0),
+ asm.Return(),
+ },
+ })
+ if err != nil {
+ return internal.ErrNotSupported
+ }
+
+ // BPF_PROG_ATTACH was introduced at the same time as CGgroupSKB,
+ // so being able to load the program is enough to infer that we
+ // have the syscall.
+ prog.Close()
+ return nil
+})
+
+var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replacement", "5.5", func() error {
+ if err := haveProgAttach(); err != nil {
+ return err
+ }
+
+ prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+ Type: ebpf.CGroupSKB,
+ AttachType: ebpf.AttachCGroupInetIngress,
+ License: "MIT",
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 0),
+ asm.Return(),
+ },
+ })
+ if err != nil {
+ return internal.ErrNotSupported
+ }
+ defer prog.Close()
+
+ // We know that we have BPF_PROG_ATTACH since we can load CGroupSKB programs.
+ // If passing BPF_F_REPLACE gives us EINVAL we know that the feature isn't
+ // present.
+ attr := internal.BPFProgAttachAttr{
+ // We rely on this being checked after attachFlags.
+ TargetFd: ^uint32(0),
+ AttachBpfFd: uint32(prog.FD()),
+ AttachType: uint32(ebpf.AttachCGroupInetIngress),
+ AttachFlags: uint32(flagReplace),
+ }
+
+ err = internal.BPFProgAttach(&attr)
+ if errors.Is(err, unix.EINVAL) {
+ return internal.ErrNotSupported
+ }
+ if errors.Is(err, unix.EBADF) {
+ return nil
+ }
+ return err
+})
+
+type bpfLinkCreateAttr struct {
+ progFd uint32
+ targetFd uint32
+ attachType ebpf.AttachType
+ flags uint32
+}
+
+func bpfLinkCreate(attr *bpfLinkCreateAttr) (*internal.FD, error) {
+ ptr, err := internal.BPF(internal.BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
+ if err != nil {
+ return nil, err
+ }
+ return internal.NewFD(uint32(ptr)), nil
+}
+
+type bpfLinkUpdateAttr struct {
+ linkFd uint32
+ newProgFd uint32
+ flags uint32
+ oldProgFd uint32
+}
+
+func bpfLinkUpdate(attr *bpfLinkUpdateAttr) error {
+ _, err := internal.BPF(internal.BPF_LINK_UPDATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
+ return err
+}
+
+var haveBPFLink = internal.FeatureTest("bpf_link", "5.7", func() error {
+ prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+ Type: ebpf.CGroupSKB,
+ AttachType: ebpf.AttachCGroupInetIngress,
+ License: "MIT",
+ Instructions: asm.Instructions{
+ asm.Mov.Imm(asm.R0, 0),
+ asm.Return(),
+ },
+ })
+ if err != nil {
+ return internal.ErrNotSupported
+ }
+ defer prog.Close()
+
+ attr := bpfLinkCreateAttr{
+ // This is a hopefully invalid file descriptor, which triggers EBADF.
+ targetFd: ^uint32(0),
+ progFd: uint32(prog.FD()),
+ attachType: ebpf.AttachCGroupInetIngress,
+ }
+ _, err = bpfLinkCreate(&attr)
+ if errors.Is(err, unix.EINVAL) {
+ return internal.ErrNotSupported
+ }
+ if errors.Is(err, unix.EBADF) {
+ return nil
+ }
+ return err
+})
+
+type bpfIterCreateAttr struct {
+ linkFd uint32
+ flags uint32
+}
+
+func bpfIterCreate(attr *bpfIterCreateAttr) (*internal.FD, error) {
+ ptr, err := internal.BPF(internal.BPF_ITER_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
+ if err == nil {
+ return internal.NewFD(uint32(ptr)), nil
+ }
+ return nil, err
+}
+
+type bpfRawTracepointOpenAttr struct {
+ name internal.Pointer
+ fd uint32
+ _ uint32
+}
+
+func bpfRawTracepointOpen(attr *bpfRawTracepointOpenAttr) (*internal.FD, error) {
+ ptr, err := internal.BPF(internal.BPF_RAW_TRACEPOINT_OPEN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
+ if err == nil {
+ return internal.NewFD(uint32(ptr)), nil
+ }
+ return nil, err
+}