Repository: cilium/ebpf
Pull request #491 (2021-11-17 13:59:55)

link: add LoadPinnedLink
Add a function that allows loading any pinned link. This allows us to
deprecate all of the separate LoadPinned* functions. In the future we
can use LoadPinnedLink to build APIs that work with any kind of link.

mehrdadrad: https://github.com/cilium/ebpf/blob/c301abe35264aadf77e260d0416f1a890f2e673e/link/link.go#L168
```go
 return nil, fmt.Errorf("get pinned link info: %w", err) 
```

Patch:
diff --git a/elf_reader.go b/elf_reader.go
index bcc65ff..0e29429 100644
--- a/elf_reader.go
+++ b/elf_reader.go
@@ -990,8 +990,8 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
 		"lwt_seg6local":         {LWTSeg6Local, AttachNone, 0},
 		"sockops":               {SockOps, AttachCGroupSockOps, 0},
 		"sk_skb/stream_parser":  {SkSKB, AttachSkSKBStreamParser, 0},
-		"sk_skb/stream_verdict": {SkSKB, AttachSkSKBStreamParser, 0},
-		"sk_msg":                {SkMsg, AttachSkSKBStreamVerdict, 0},
+		"sk_skb/stream_verdict": {SkSKB, AttachSkSKBStreamVerdict, 0},
+		"sk_msg":                {SkMsg, AttachSkMsgVerdict, 0},
 		"lirc_mode2":            {LircMode2, AttachLircMode2, 0},
 		"flow_dissector":        {FlowDissector, AttachFlowDissector, 0},
 		"iter/":                 {Tracing, AttachTraceIter, 0},
diff --git a/internal/cmd/gentypes/main.go b/internal/cmd/gentypes/main.go
index 47a3a64..5be21fe 100644
--- a/internal/cmd/gentypes/main.go
+++ b/internal/cmd/gentypes/main.go
@@ -70,6 +70,7 @@ func run(args []string) error {
 
 func generateTypes(spec *btf.Spec) ([]byte, error) {
 	objName := &btf.Array{Nelems: 16, Type: &btf.Int{Encoding: btf.Char, Size: 1}}
+	linkID := &btf.Int{Size: 4}
 	pointer := &btf.Int{Size: 8}
 
 	// Pre-declare handwritten types sys.ObjName and sys.Pointer so that
@@ -77,11 +78,13 @@ func generateTypes(spec *btf.Spec) ([]byte, error) {
 	var (
 		_ sys.Pointer
 		_ sys.ObjName
+		_ sys.LinkID
 	)
 
 	gf := &btf.GoFormatter{
 		Names: map[btf.Type]string{
 			objName: "ObjName",
+			linkID:  "LinkID",
 			pointer: "Pointer",
 		},
 		Identifier: internal.Identifier,
@@ -173,6 +176,7 @@ import (
 			"LinkInfo", "bpf_link_info",
 			[]patch{
 				replace(enumTypes["LinkType"], "type"),
+				replace(linkID, "id"),
 				truncateAfter("prog_id"),
 			},
 		},
diff --git a/internal/sys/syscall.go b/internal/sys/syscall.go
index 2da57ef..dd515f0 100644
--- a/internal/sys/syscall.go
+++ b/internal/sys/syscall.go
@@ -90,6 +90,9 @@ func NewObjName(name string) ObjName {
 	return result
 }
 
+// LinkID uniquely identifies a bpf_link.
+type LinkID uint32
+
 // wrappedErrno wraps syscall.Errno to prevent direct comparisons with
 // syscall.E* or unix.E* constants.
 //
diff --git a/internal/sys/types.go b/internal/sys/types.go
index 67088d4..f9702d4 100644
--- a/internal/sys/types.go
+++ b/internal/sys/types.go
@@ -425,7 +425,7 @@ type LineInfo struct {
 
 type LinkInfo struct {
 	Type   LinkType
-	Id     uint32
+	Id     LinkID
 	ProgId uint32
 }
 
diff --git a/link/cgroup.go b/link/cgroup.go
index 5540bb0..4241bb0 100644
--- a/link/cgroup.go
+++ b/link/cgroup.go
@@ -57,6 +57,8 @@ func AttachCgroup(opts CgroupOptions) (Link, error) {
 }
 
 // LoadPinnedCgroup loads a pinned cgroup from a bpffs.
+//
+// Deprecated: use LoadPinnedLink instead.
 func LoadPinnedCgroup(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
 	link, err := LoadPinnedRawLink(fileName, CgroupType, opts)
 	if err != nil {
diff --git a/link/iter.go b/link/iter.go
index a7f0c33..289733e 100644
--- a/link/iter.go
+++ b/link/iter.go
@@ -59,6 +59,8 @@ func AttachIter(opts IterOptions) (*Iter, error) {
 }
 
 // LoadPinnedIter loads a pinned iterator from a bpffs.
+//
+// Deprecated: use LoadPinnedLink instead.
 func LoadPinnedIter(fileName string, opts *ebpf.LoadPinOptions) (*Iter, error) {
 	link, err := LoadPinnedRawLink(fileName, IterType, opts)
 	if err != nil {
diff --git a/link/link.go b/link/link.go
index 9ab775e..01047f1 100644
--- a/link/link.go
+++ b/link/link.go
@@ -39,8 +39,44 @@ type Link interface {
 	isLink()
 }
 
+// LoadPinnedLink loads a link that was persisted into a bpffs.
+func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
+	raw, err := loadPinnedRawLink(fileName, opts)
+	if err != nil {
+		return nil, err
+	}
+
+	return wrapRawLink(raw)
+}
+
+// wrap a RawLink in a more specific type if possible.
+//
+// The function takes ownership of raw and closes it on error.
+func wrapRawLink(raw *RawLink) (Link, error) {
+	info, err := raw.Info()
+	if err != nil {
+		raw.Close()
+		return nil, err
+	}
+
+	switch info.Type {
+	case RawTracepointType:
+		return &rawTracepoint{*raw}, nil
+	case TracingType:
+		return &tracing{*raw}, nil
+	case CgroupType:
+		return &linkCgroup{*raw}, nil
+	case IterType:
+		return &Iter{*raw}, nil
+	case NetNsType:
+		return &NetNsLink{*raw}, nil
+	default:
+		return raw, nil
+	}
+}
+
 // ID uniquely identifies a BPF link.
-type ID uint32
+type ID = sys.LinkID
 
 // RawLinkOptions control the creation of a raw link.
 type RawLinkOptions struct {
@@ -106,16 +142,14 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
 //
 // Returns an error if the pinned link type doesn't match linkType. Pass
 // UnspecifiedType to disable this behaviour.
+//
+// Deprecated: use LoadPinnedLink instead.
 func LoadPinnedRawLink(fileName string, linkType Type, opts *ebpf.LoadPinOptions) (*RawLink, error) {
-	fd, err := sys.ObjGet(&sys.ObjGetAttr{
-		Pathname:  sys.NewStringPointer(fileName),
-		FileFlags: opts.Marshal(),
-	})
+	link, err := loadPinnedRawLink(fileName, opts)
 	if err != nil {
-		return nil, fmt.Errorf("load pinned link: %w", err)
+		return nil, err
 	}
 
-	link := &RawLink{fd, fileName}
 	if linkType == UnspecifiedType {
 		return link, nil
 	}
@@ -123,7 +157,7 @@ func LoadPinnedRawLink(fileName string, linkType Type, opts *ebpf.LoadPinOptions
 	info, err := link.Info()
 	if err != nil {
 		link.Close()
-		return nil, fmt.Errorf("get pinned link info: %s", err)
+		return nil, fmt.Errorf("get pinned link info: %w", err)
 	}
 
 	if info.Type != linkType {
@@ -134,6 +168,18 @@ func LoadPinnedRawLink(fileName string, linkType Type, opts *ebpf.LoadPinOptions
 	return link, nil
 }
 
+func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) {
+	fd, err := sys.ObjGet(&sys.ObjGetAttr{
+		Pathname:  sys.NewStringPointer(fileName),
+		FileFlags: opts.Marshal(),
+	})
+	if err != nil {
+		return nil, fmt.Errorf("load pinned link: %w", err)
+	}
+
+	return &RawLink{fd, fileName}, nil
+}
+
 func (l *RawLink) isLink() {}
 
 // FD returns the raw file descriptor.
@@ -215,8 +261,8 @@ func (l *RawLink) Info() (*RawLinkInfo, error) {
 	}
 
 	return &RawLinkInfo{
-		Type(info.Type),
-		ID(info.Id),
+		info.Type,
+		info.Id,
 		ebpf.ProgramID(info.ProgId),
 	}, nil
 }
diff --git a/link/netns.go b/link/netns.go
index 37e5b84..411423e 100644
--- a/link/netns.go
+++ b/link/netns.go
@@ -13,7 +13,7 @@ type NetNsInfo struct {
 
 // NetNsLink is a program attached to a network namespace.
 type NetNsLink struct {
-	*RawLink
+	RawLink
 }
 
 // AttachNetNs attaches a program to a network namespace.
@@ -37,17 +37,19 @@ func AttachNetNs(ns int, prog *ebpf.Program) (*NetNsLink, error) {
 		return nil, err
 	}
 
-	return &NetNsLink{link}, nil
+	return &NetNsLink{*link}, nil
 }
 
 // LoadPinnedNetNs loads a network namespace link from bpffs.
+//
+// Deprecated: use LoadPinnedLink instead.
 func LoadPinnedNetNs(fileName string, opts *ebpf.LoadPinOptions) (*NetNsLink, error) {
 	link, err := LoadPinnedRawLink(fileName, NetNsType, opts)
 	if err != nil {
 		return nil, err
 	}
 
-	return &NetNsLink{link}, nil
+	return &NetNsLink{*link}, nil
 }
 
 // Info returns information about the link.
diff --git a/link/raw_tracepoint.go b/link/raw_tracepoint.go
index 183994e..a9972ff 100644
--- a/link/raw_tracepoint.go
+++ b/link/raw_tracepoint.go
@@ -48,16 +48,6 @@ func AttachRawTracepoint(opts RawTracepointOptions) (Link, error) {
 	return &rawTracepoint{RawLink{fd: fd}}, nil
 }
 
-// LoadPinnedRawTracepoint loads a raw_tracepoint link from a bpffs.
-func LoadPinnedRawTracepoint(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
-	link, err := LoadPinnedRawLink(fileName, RawTracepointType, opts)
-	if err != nil {
-		return nil, err
-	}
-
-	return &rawTracepoint{*link}, err
-}
-
 type simpleRawTracepoint struct {
 	fd *sys.FD
 }
diff --git a/link/syscalls.go b/link/syscalls.go
index 00b4acf..072dfad 100644
--- a/link/syscalls.go
+++ b/link/syscalls.go
@@ -11,19 +11,17 @@ import (
 )
 
 // Type is the kind of link.
-type Type uint32
+type Type = sys.LinkType
 
 // Valid link types.
-//
-// Equivalent to enum bpf_link_type.
 const (
-	UnspecifiedType Type = iota
-	RawTracepointType
-	TracingType
-	CgroupType
-	IterType
-	NetNsType
-	XDPType
+	UnspecifiedType   = sys.BPF_LINK_TYPE_UNSPEC
+	RawTracepointType = sys.BPF_LINK_TYPE_RAW_TRACEPOINT
+	TracingType       = sys.BPF_LINK_TYPE_TRACING
+	CgroupType        = sys.BPF_LINK_TYPE_CGROUP
+	IterType          = sys.BPF_LINK_TYPE_ITER
+	NetNsType         = sys.BPF_LINK_TYPE_NETNS
+	XDPType           = sys.BPF_LINK_TYPE_XDP
 )
 
 var haveProgAttach = internal.FeatureTest("BPF_PROG_ATTACH", "4.10", func() error {
@@ -88,27 +86,13 @@ var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replace
 })
 
 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 := sys.LinkCreateAttr{
 		// This is a hopefully invalid file descriptor, which triggers EBADF.
 		TargetFd:   ^uint32(0),
-		ProgFd:     uint32(prog.FD()),
+		ProgFd:     ^uint32(0),
 		AttachType: sys.AttachType(ebpf.AttachCGroupInetIngress),
 	}
-	_, err = sys.LinkCreate(&attr)
+	_, err := sys.LinkCreate(&attr)
 	if errors.Is(err, unix.EINVAL) {
 		return internal.ErrNotSupported
 	}
diff --git a/link/tracing.go b/link/tracing.go
index 0d3e371..581a455 100644
--- a/link/tracing.go
+++ b/link/tracing.go
@@ -78,6 +78,8 @@ func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (
 }
 
 // LoadPinnedFreplace loads a pinned iterator from a bpffs.
+//
+// Deprecated: use LoadPinnedLink instead.
 func LoadPinnedFreplace(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
 	link, err := LoadPinnedRawLink(fileName, TracingType, opts)
 	if err != nil {
@@ -149,13 +151,3 @@ func AttachLSM(opts LSMOptions) (Link, error) {
 
 	return attachBTFID(opts.Program)
 }
-
-// LoadPinnedTrace loads a tracing/LSM link from a bpffs.
-func LoadPinnedTrace(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
-	link, err := LoadPinnedRawLink(fileName, TracingType, opts)
-	if err != nil {
-		return nil, err
-	}
-
-	return &tracing{*link}, err
-}


Test patch:
diff --git a/link/cgroup_test.go b/link/cgroup_test.go
index f80c3ce..a8ed93c 100644
--- a/link/cgroup_test.go
+++ b/link/cgroup_test.go
@@ -39,9 +39,7 @@ func TestProgAttachCgroup(t *testing.T) {
 		t.Fatal("Can't create link:", err)
 	}
 
-	testLink(t, link, testLinkOptions{
-		prog: prog,
-	})
+	testLink(t, link, prog)
 }
 
 func TestProgAttachCgroupAllowMulti(t *testing.T) {
@@ -56,9 +54,7 @@ func TestProgAttachCgroupAllowMulti(t *testing.T) {
 	// It's currently not possible for a program to replace
 	// itself.
 	prog2 := mustCgroupEgressProgram(t)
-	testLink(t, link, testLinkOptions{
-		prog: prog2,
-	})
+	testLink(t, link, prog2)
 }
 
 func TestLinkCgroup(t *testing.T) {
@@ -70,8 +66,5 @@ func TestLinkCgroup(t *testing.T) {
 		t.Fatal("Can't create link:", err)
 	}
 
-	testLink(t, link, testLinkOptions{
-		prog:       prog,
-		loadPinned: LoadPinnedCgroup,
-	})
+	testLink(t, link, prog)
 }
diff --git a/link/iter_test.go b/link/iter_test.go
index 04615eb..a0d6186 100644
--- a/link/iter_test.go
+++ b/link/iter_test.go
@@ -48,12 +48,7 @@ func TestIter(t *testing.T) {
 		t.Error("Non-empty output from no-op iterator:", string(contents))
 	}
 
-	testLink(t, it, testLinkOptions{
-		prog: prog,
-		loadPinned: func(s string, opts *ebpf.LoadPinOptions) (Link, error) {
-			return LoadPinnedIter(s, opts)
-		},
-	})
+	testLink(t, it, prog)
 }
 
 func TestIterMapElements(t *testing.T) {
diff --git a/link/kprobe_test.go b/link/kprobe_test.go
index 6b3466b..e41aea5 100644
--- a/link/kprobe_test.go
+++ b/link/kprobe_test.go
@@ -36,9 +36,7 @@ func TestKprobe(t *testing.T) {
 	c.Assert(err, qt.IsNil)
 	defer k.Close()
 
-	testLink(t, k, testLinkOptions{
-		prog: prog,
-	})
+	testLink(t, k, prog)
 
 	k, err = Kprobe("bogus", prog)
 	c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err))
@@ -60,9 +58,7 @@ func TestKretprobe(t *testing.T) {
 	c.Assert(err, qt.IsNil)
 	defer k.Close()
 
-	testLink(t, k, testLinkOptions{
-		prog: prog,
-	})
+	testLink(t, k, prog)
 
 	k, err = Kretprobe("bogus", prog)
 	c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err))
diff --git a/link/link_test.go b/link/link_test.go
index 7db0550..bf46223 100644
--- a/link/link_test.go
+++ b/link/link_test.go
@@ -46,12 +46,7 @@ func TestRawLink(t *testing.T) {
 		t.Error("Link program ID doesn't match program ID")
 	}
 
-	testLink(t, link, testLinkOptions{
-		prog: prog,
-		loadPinned: func(f string, opts *ebpf.LoadPinOptions) (Link, error) {
-			return LoadPinnedRawLink(f, UnspecifiedType, opts)
-		},
-	})
+	testLink(t, &linkCgroup{*link}, prog)
 }
 
 func TestRawLinkLoadPinnedWithOptions(t *testing.T) {
@@ -76,7 +71,7 @@ func TestRawLinkLoadPinnedWithOptions(t *testing.T) {
 
 	// It seems like the kernel ignores BPF_F_RDONLY when updating a link,
 	// so we can't test this.
-	_, err = LoadPinnedRawLink(path, UnspecifiedType, &ebpf.LoadPinOptions{
+	_, err = loadPinnedRawLink(path, &ebpf.LoadPinOptions{
 		Flags: math.MaxUint32,
 	})
 	if !errors.Is(err, unix.EINVAL) {
@@ -115,12 +110,7 @@ func mustCgroupEgressProgram(t *testing.T) *ebpf.Program {
 	return prog
 }
 
-type testLinkOptions struct {
-	prog       *ebpf.Program
-	loadPinned func(string, *ebpf.LoadPinOptions) (Link, error)
-}
-
-func testLink(t *testing.T, link Link, opts testLinkOptions) {
+func testLink(t *testing.T, link Link, prog *ebpf.Program) {
 	t.Helper()
 
 	tmp, err := os.MkdirTemp("/sys/fs/bpf", "ebpf-test")
@@ -137,7 +127,7 @@ func testLink(t *testing.T, link Link, opts testLinkOptions) {
 			t.Fatalf("Can't pin %T: %s", link, err)
 		}
 
-		link2, err := opts.loadPinned(path, nil)
+		link2, err := LoadPinnedLink(path, nil)
 		if err != nil {
 			t.Fatalf("Can't load pinned %T: %s", link, err)
 		}
@@ -147,7 +137,7 @@ func testLink(t *testing.T, link Link, opts testLinkOptions) {
 			t.Errorf("Loading a pinned %T returns a %T", link, link2)
 		}
 
-		_, err = opts.loadPinned(path, &ebpf.LoadPinOptions{
+		_, err = LoadPinnedLink(path, &ebpf.LoadPinOptions{
 			Flags: math.MaxUint32,
 		})
 		if !errors.Is(err, unix.EINVAL) {
@@ -156,7 +146,7 @@ func testLink(t *testing.T, link Link, opts testLinkOptions) {
 	})
 
 	t.Run("update", func(t *testing.T) {
-		err := link.Update(opts.prog)
+		err := link.Update(prog)
 		testutils.SkipIfNotSupported(t, err)
 		if err != nil {
 			t.Fatal("Update returns an error:", err)
diff --git a/link/netns_test.go b/link/netns_test.go
index 4121d7d..a1c3ec2 100644
--- a/link/netns_test.go
+++ b/link/netns_test.go
@@ -30,12 +30,7 @@ func TestSkLookup(t *testing.T) {
 		t.Fatal("Info returns an error:", err)
 	}
 
-	testLink(t, link, testLinkOptions{
-		prog: prog,
-		loadPinned: func(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
-			return LoadPinnedNetNs(fileName, opts)
-		},
-	})
+	testLink(t, link, prog)
 }
 
 func mustCreateSkLookupProgram(tb testing.TB) *ebpf.Program {
diff --git a/link/raw_tracepoint_test.go b/link/raw_tracepoint_test.go
index 470fefb..31eaddb 100644
--- a/link/raw_tracepoint_test.go
+++ b/link/raw_tracepoint_test.go
@@ -33,12 +33,7 @@ func TestRawTracepoint(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	testLink(t, link, testLinkOptions{
-		prog: prog,
-		loadPinned: func(s string, opts *ebpf.LoadPinOptions) (Link, error) {
-			return LoadPinnedRawTracepoint(s, opts)
-		},
-	})
+	testLink(t, link, prog)
 }
 
 func TestRawTracepoint_writable(t *testing.T) {
@@ -66,10 +61,5 @@ func TestRawTracepoint_writable(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	testLink(t, link, testLinkOptions{
-		prog: prog,
-		loadPinned: func(s string, opts *ebpf.LoadPinOptions) (Link, error) {
-			return LoadPinnedRawTracepoint(s, opts)
-		},
-	})
+	testLink(t, link, prog)
 }
diff --git a/link/tracing_test.go b/link/tracing_test.go
index ab85151..fab196a 100644
--- a/link/tracing_test.go
+++ b/link/tracing_test.go
@@ -43,12 +43,7 @@ func TestFreplace(t *testing.T) {
 			t.Fatal("Can't create freplace:", err)
 		}
 
-		testLink(t, freplace, testLinkOptions{
-			prog: replacement,
-			loadPinned: func(s string, opts *ebpf.LoadPinOptions) (Link, error) {
-				return LoadPinnedFreplace(s, opts)
-			},
-		})
+		testLink(t, freplace, replacement)
 	})
 }
 
@@ -108,15 +103,7 @@ func TestTracing(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			testLink(t, link, testLinkOptions{
-				prog: prog,
-				loadPinned: func(s string, opts *ebpf.LoadPinOptions) (Link, error) {
-					if tt.attachType != ebpf.AttachTraceRawTp {
-						return LoadPinnedTrace(s, opts)
-					}
-					return LoadPinnedRawTracepoint(s, opts)
-				},
-			})
+			testLink(t, link, prog)
 		})
 	}
 }
@@ -144,10 +131,5 @@ func TestLSM(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	testLink(t, link, testLinkOptions{
-		prog: prog,
-		loadPinned: func(s string, opts *ebpf.LoadPinOptions) (Link, error) {
-			return LoadPinnedTrace(s, opts)
-		},
-	})
+	testLink(t, link, prog)
 }
diff --git a/link/uprobe_test.go b/link/uprobe_test.go
index ef3b6cf..4de26a0 100644
--- a/link/uprobe_test.go
+++ b/link/uprobe_test.go
@@ -55,9 +55,7 @@ func TestUprobe(t *testing.T) {
 	c.Assert(err, qt.IsNil)
 	defer up.Close()
 
-	testLink(t, up, testLinkOptions{
-		prog: prog,
-	})
+	testLink(t, up, prog)
 }
 
 func TestUprobeExtNotFound(t *testing.T) {
@@ -131,9 +129,7 @@ func TestUretprobe(t *testing.T) {
 	c.Assert(err, qt.IsNil)
 	defer up.Close()
 
-	testLink(t, up, testLinkOptions{
-		prog: prog,
-	})
+	testLink(t, up, prog)
 }
 
 // Test u(ret)probe creation using perf_uprobe PMU.
diff --git a/link/xdp_test.go b/link/xdp_test.go
index ae8006e..c3c81b1 100644
--- a/link/xdp_test.go
+++ b/link/xdp_test.go
@@ -33,10 +33,5 @@ func TestAttachXDP(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	testLink(t, l, testLinkOptions{
-		prog: prog,
-		loadPinned: func(s string, opts *ebpf.LoadPinOptions) (Link, error) {
-			return LoadPinnedRawLink(s, XDPType, opts)
-		},
-	})
+	testLink(t, l, prog)
 }