-
Notifications
You must be signed in to change notification settings - Fork 716
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pin: new package for loading bpf pins and walking bpffs directories
This commit adds a new package pin with two main APIs: Load() and WalkDir(). It's split off from the root package since ebpf cannot import link. Signed-off-by: Timo Beckers <timo@isovalent.com>
- Loading branch information
Showing
5 changed files
with
231 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Package pin provides utility functions for working with pinned objects on bpffs. | ||
|
||
package pin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package pin | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/cilium/ebpf" | ||
"github.com/cilium/ebpf/internal/sys" | ||
"github.com/cilium/ebpf/link" | ||
) | ||
|
||
// Pinner is an interface implemented by all eBPF objects that support pinning | ||
// to a bpf virtual filesystem. | ||
type Pinner interface { | ||
Pin(string) error | ||
} | ||
|
||
// Load retrieves a pinned object from a bpf virtual filesystem. It returns one | ||
// of [ebpf.Map], [ebpf.Program], or [link.Link]. | ||
// | ||
// Trying to open anything other than a bpf object is an error. | ||
func Load(path string, opts *ebpf.LoadPinOptions) (Pinner, error) { | ||
fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{ | ||
Pathname: sys.NewStringPointer(path), | ||
FileFlags: opts.Marshal(), | ||
}) | ||
if err != nil { | ||
return nil, fmt.Errorf("opening pin %s: %w", path, err) | ||
} | ||
|
||
switch typ { | ||
case sys.BPF_TYPE_MAP: | ||
return ebpf.NewMapFromFD(fd.Disown()) | ||
case sys.BPF_TYPE_PROG: | ||
return ebpf.NewProgramFromFD(fd.Disown()) | ||
case sys.BPF_TYPE_LINK: | ||
return link.NewFromFD(fd.Disown()) | ||
} | ||
|
||
return nil, fmt.Errorf("unknown object type %d", typ) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package pin | ||
|
||
import ( | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/go-quicktest/qt" | ||
|
||
"github.com/cilium/ebpf" | ||
"github.com/cilium/ebpf/asm" | ||
"github.com/cilium/ebpf/internal/testutils" | ||
) | ||
|
||
func mustPinnedProgram(t *testing.T, path string) *ebpf.Program { | ||
t.Helper() | ||
|
||
spec := &ebpf.ProgramSpec{ | ||
Name: "test", | ||
Type: ebpf.SocketFilter, | ||
Instructions: asm.Instructions{ | ||
asm.LoadImm(asm.R0, 2, asm.DWord), | ||
asm.Return(), | ||
}, | ||
License: "MIT", | ||
} | ||
|
||
p, err := ebpf.NewProgram(spec) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
t.Cleanup(func() { p.Close() }) | ||
|
||
if err := p.Pin(path); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
return p | ||
} | ||
|
||
func mustPinnedMap(t *testing.T, path string) *ebpf.Map { | ||
t.Helper() | ||
|
||
spec := &ebpf.MapSpec{ | ||
Name: "test", | ||
Type: ebpf.Array, | ||
KeySize: 4, | ||
ValueSize: 4, | ||
MaxEntries: 1, | ||
} | ||
|
||
m, err := ebpf.NewMap(spec) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
t.Cleanup(func() { m.Close() }) | ||
|
||
if err := m.Pin(path); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
return m | ||
} | ||
|
||
func TestLoad(t *testing.T) { | ||
testutils.SkipOnOldKernel(t, "4.10", "reading program fdinfo") | ||
|
||
tmp := testutils.TempBPFFS(t) | ||
|
||
mpath := filepath.Join(tmp, "map") | ||
ppath := filepath.Join(tmp, "prog") | ||
|
||
mustPinnedMap(t, mpath) | ||
mustPinnedProgram(t, ppath) | ||
|
||
_, err := Load(tmp, nil) | ||
qt.Assert(t, qt.IsNotNil(err)) | ||
|
||
m, err := Load(mpath, nil) | ||
qt.Assert(t, qt.IsNil(err)) | ||
qt.Assert(t, qt.Satisfies(m, testutils.Contains[*ebpf.Map])) | ||
|
||
p, err := Load(ppath, nil) | ||
qt.Assert(t, qt.IsNil(err)) | ||
qt.Assert(t, qt.Satisfies(p, testutils.Contains[*ebpf.Program])) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package pin | ||
|
||
import ( | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/cilium/ebpf/internal/linux" | ||
"github.com/cilium/ebpf/internal/unix" | ||
) | ||
|
||
// WalkDirFunc is the type of the function called for each object visited by | ||
// [WalkDir]. It's identical to [fs.WalkDirFunc], but with an extra [Pinner] | ||
// argument. If the visited node is a directory, obj is nil. | ||
// | ||
// err contains any errors encountered during bpffs traversal or object loading. | ||
type WalkDirFunc func(path string, d fs.DirEntry, obj Pinner, err error) error | ||
|
||
// WalkDir walks the file tree rooted at path, calling bpffn for each node in | ||
// the tree, including directories. Running WalkDir on a non-bpf filesystem is | ||
// an error. Otherwise identical in behavior to [fs.WalkDir]. | ||
// | ||
// See the [WalkDirFunc] for more information. | ||
func WalkDir(root string, bpffn WalkDirFunc) error { | ||
fsType, err := linux.FSType(root) | ||
if err != nil { | ||
return err | ||
} | ||
if fsType != unix.BPF_FS_MAGIC { | ||
return fmt.Errorf("%s is not on a bpf filesystem", root) | ||
} | ||
|
||
fn := func(path string, d fs.DirEntry, err error) error { | ||
if err != nil { | ||
return bpffn(path, nil, nil, err) | ||
} | ||
|
||
if d.IsDir() { | ||
return bpffn(path, d, nil, err) | ||
} | ||
|
||
obj, err := Load(filepath.Join(root, path), nil) | ||
|
||
return bpffn(path, d, obj, err) | ||
} | ||
|
||
return fs.WalkDir(os.DirFS(root), ".", fn) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package pin | ||
|
||
import ( | ||
"io/fs" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/go-quicktest/qt" | ||
|
||
"github.com/cilium/ebpf" | ||
"github.com/cilium/ebpf/internal/testutils" | ||
) | ||
|
||
func TestWalkDir(t *testing.T) { | ||
testutils.SkipOnOldKernel(t, "4.10", "reading program fdinfo") | ||
|
||
tmp := testutils.TempBPFFS(t) | ||
dir := filepath.Join(tmp, "dir") | ||
qt.Assert(t, qt.IsNil(os.Mkdir(dir, 0755))) | ||
|
||
mustPinnedProgram(t, filepath.Join(tmp, "pinned_prog")) | ||
mustPinnedMap(t, filepath.Join(dir, "pinned_map")) | ||
|
||
entries := make(map[string]string) | ||
|
||
bpffn := func(path string, d fs.DirEntry, obj Pinner, err error) error { | ||
qt.Assert(t, qt.IsNil(err)) | ||
|
||
if path == "." { | ||
return nil | ||
} | ||
|
||
switch obj.(type) { | ||
case *ebpf.Program: | ||
entries[path] = "prog" | ||
case *ebpf.Map: | ||
entries[path] = "map" | ||
default: | ||
entries[path] = "" | ||
} | ||
|
||
return nil | ||
} | ||
qt.Assert(t, qt.IsNil(WalkDir(tmp, bpffn))) | ||
|
||
qt.Assert(t, qt.DeepEquals(entries, map[string]string{ | ||
"pinned_prog": "prog", | ||
"dir": "", | ||
"dir/pinned_map": "map", | ||
})) | ||
|
||
qt.Assert(t, qt.IsNotNil(WalkDir("/", nil))) | ||
} |