From f17dc519ac96e24b7371326e1cb771e9b41e7e4a Mon Sep 17 00:00:00 2001 From: shun159 Date: Sun, 10 Aug 2025 14:48:00 +0900 Subject: [PATCH 1/3] link: allow attaching with ProgramFd (needed for struct_ops) This PR extends RawLinkOptions with an optional ProgramFd so callers can attach link types whose target isn't a bpf_prog (e.g. struct_ops maps). See: https://github.com/cilium/ebpf/discussions/1502 Signed-off-by: shun159 --- link/link.go | 3 +++ link/link_other.go | 5 ++++- link/link_test.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/link/link.go b/link/link.go index 425811e77..77aae2204 100644 --- a/link/link.go +++ b/link/link.go @@ -108,6 +108,9 @@ type RawLinkOptions struct { BTF btf.TypeID // Flags control the attach behaviour. Flags uint32 + // ProgramFd allows attaching with an arbitrary FD instead of Program. + // Useful for link types whose target is not a bpf_prog (e.g. struct_ops maps). + ProgramFd int } // Info contains metadata on a link. diff --git a/link/link_other.go b/link/link_other.go index cd9452fd8..b513cbda7 100644 --- a/link/link_other.go +++ b/link/link_other.go @@ -37,7 +37,10 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd) } - progFd := opts.Program.FD() + progFd := opts.ProgramFd + if opts.Program != nil { + progFd = opts.Program.FD() + } if progFd < 0 { return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) } diff --git a/link/link_test.go b/link/link_test.go index 8d928a341..9c15e4fc1 100644 --- a/link/link_test.go +++ b/link/link_test.go @@ -5,6 +5,7 @@ import ( "math" "path/filepath" "reflect" + "strings" "testing" "github.com/go-quicktest/qt" @@ -72,6 +73,41 @@ func TestRawLinkLoadPinnedWithOptions(t *testing.T) { } } +func TestRawLinkWithProgramFd(t *testing.T) { + cg, prog := mustCgroupFixtures(t) + t.Cleanup(func() { + _ = prog.Close() + _ = cg.Close() + }) + + ln, err := AttachRawLink(RawLinkOptions{ + Target: int(cg.Fd()), + Program: nil, + ProgramFd: prog.FD(), + Attach: ebpf.AttachCGroupInetEgress, + }) + testutils.SkipIfNotSupported(t, err) + if err != nil { + t.Fatalf("AttachRawLink with ProgramFd failed: %v", err) + } + t.Cleanup(func() { _ = ln.Close() }) +} + +func TestRawLinkWithInvalidProgramFd(t *testing.T) { + cg := testutils.CreateCgroup(t) + t.Cleanup(func() { _ = cg.Close() }) + + _, err := AttachRawLink(RawLinkOptions{ + Target: int(cg.Fd()), + Program: nil, + ProgramFd: -1, + Attach: ebpf.AttachCGroupInetEgress, + }) + if err == nil || !strings.Contains(err.Error(), "invalid program") { + t.Fatalf("expected invalid program error, got %v", err) + } +} + func TestIterator(t *testing.T) { tLink, _ := newPinnedRawLink(t) tLinkInfo, err := tLink.Info() From 0ab52f6cddb6ae7ceaf3998d126c5e9e22342146 Mon Sep 17 00:00:00 2001 From: shun159 Date: Sun, 10 Aug 2025 14:58:19 +0900 Subject: [PATCH 2/3] testing: Add testutils guard for the old kernel version Signed-off-by: shun159 --- link/link_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/link/link_test.go b/link/link_test.go index 9c15e4fc1..0bb0b87bf 100644 --- a/link/link_test.go +++ b/link/link_test.go @@ -103,6 +103,7 @@ func TestRawLinkWithInvalidProgramFd(t *testing.T) { ProgramFd: -1, Attach: ebpf.AttachCGroupInetEgress, }) + testutils.SkipIfNotSupported(t, err) if err == nil || !strings.Contains(err.Error(), "invalid program") { t.Fatalf("expected invalid program error, got %v", err) } From 6c385167a2e599e23a75abe7af8be7eaf1ec7662 Mon Sep 17 00:00:00 2001 From: shun159 Date: Tue, 12 Aug 2025 18:55:30 +0900 Subject: [PATCH 3/3] link: revert changes to main Signed-off-by: shun159 --- link/link.go | 3 --- link/link_other.go | 5 +---- link/link_test.go | 37 ------------------------------------- 3 files changed, 1 insertion(+), 44 deletions(-) diff --git a/link/link.go b/link/link.go index 77aae2204..425811e77 100644 --- a/link/link.go +++ b/link/link.go @@ -108,9 +108,6 @@ type RawLinkOptions struct { BTF btf.TypeID // Flags control the attach behaviour. Flags uint32 - // ProgramFd allows attaching with an arbitrary FD instead of Program. - // Useful for link types whose target is not a bpf_prog (e.g. struct_ops maps). - ProgramFd int } // Info contains metadata on a link. diff --git a/link/link_other.go b/link/link_other.go index b513cbda7..cd9452fd8 100644 --- a/link/link_other.go +++ b/link/link_other.go @@ -37,10 +37,7 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { return nil, fmt.Errorf("invalid target: %s", sys.ErrClosedFd) } - progFd := opts.ProgramFd - if opts.Program != nil { - progFd = opts.Program.FD() - } + progFd := opts.Program.FD() if progFd < 0 { return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) } diff --git a/link/link_test.go b/link/link_test.go index 0bb0b87bf..8d928a341 100644 --- a/link/link_test.go +++ b/link/link_test.go @@ -5,7 +5,6 @@ import ( "math" "path/filepath" "reflect" - "strings" "testing" "github.com/go-quicktest/qt" @@ -73,42 +72,6 @@ func TestRawLinkLoadPinnedWithOptions(t *testing.T) { } } -func TestRawLinkWithProgramFd(t *testing.T) { - cg, prog := mustCgroupFixtures(t) - t.Cleanup(func() { - _ = prog.Close() - _ = cg.Close() - }) - - ln, err := AttachRawLink(RawLinkOptions{ - Target: int(cg.Fd()), - Program: nil, - ProgramFd: prog.FD(), - Attach: ebpf.AttachCGroupInetEgress, - }) - testutils.SkipIfNotSupported(t, err) - if err != nil { - t.Fatalf("AttachRawLink with ProgramFd failed: %v", err) - } - t.Cleanup(func() { _ = ln.Close() }) -} - -func TestRawLinkWithInvalidProgramFd(t *testing.T) { - cg := testutils.CreateCgroup(t) - t.Cleanup(func() { _ = cg.Close() }) - - _, err := AttachRawLink(RawLinkOptions{ - Target: int(cg.Fd()), - Program: nil, - ProgramFd: -1, - Attach: ebpf.AttachCGroupInetEgress, - }) - testutils.SkipIfNotSupported(t, err) - if err == nil || !strings.Contains(err.Error(), "invalid program") { - t.Fatalf("expected invalid program error, got %v", err) - } -} - func TestIterator(t *testing.T) { tLink, _ := newPinnedRawLink(t) tLinkInfo, err := tLink.Info()