Skip to content

Commit

Permalink
Add *GetNextID
Browse files Browse the repository at this point in the history
Signed-off-by: Lehner Florian <[email protected]>
  • Loading branch information
florianl authored and lmb committed Feb 17, 2020
1 parent 78530ec commit de57e91
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 0 deletions.
11 changes: 11 additions & 0 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ var (
ErrIterationAborted = xerrors.New("iteration aborted")
)

// MapID represents the unique ID of an eBPF map
type MapID uint32

// MapSpec defines a Map.
type MapSpec struct {
// Name is passed to the kernel as a debug aid. Must only contain
Expand Down Expand Up @@ -765,3 +768,11 @@ func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {
func (mi *MapIterator) Err() error {
return mi.err
}

// MapGetNextID returns the ID of the next eBPF map.
//
// Returns ErrNotExist, if there is no next eBPF map.
func MapGetNextID(startID MapID) (MapID, error) {
id, err := objGetNextID(_MapGetNextID, uint32(startID))
return MapID(id), err
}
31 changes: 31 additions & 0 deletions map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,37 @@ func TestMapFreeze(t *testing.T) {
}
}

func TestMapGetNextID(t *testing.T) {
testutils.SkipOnOldKernel(t, "4.13", "bpf_map_get_next_id")
var next MapID
var err error

hash := createHash()
defer hash.Close()

if next, err = MapGetNextID(MapID(0)); err != nil {
t.Fatal("Can't get next ID:", err)
}
if next == MapID(0) {
t.Fatal("Expected next ID other than 0")
}

// As there can be multiple eBPF maps, we loop over all of them and
// make sure, the IDs increase and the last call will return ErrNotExist
for {
last := next
if next, err = MapGetNextID(last); err != nil {
if !xerrors.Is(err, ErrNotExist) {
t.Fatal("Expected ErrNotExist, got:", err)
}
break
}
if next <= last {
t.Fatalf("Expected next ID (%d) to be higher than the last ID (%d)", next, last)
}
}
}

type benchValue struct {
ID uint32
Val16 uint16
Expand Down
11 changes: 11 additions & 0 deletions prog.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import (
// ErrNotSupported is returned whenever the kernel doesn't support a feature.
var ErrNotSupported = internal.ErrNotSupported

// ProgramID represents the unique ID of an eBPF program
type ProgramID uint32

const (
// Number of bytes to pad the output buffer for BPF_PROG_TEST_RUN.
// This is currently the maximum of spare space allocated for SKB
Expand Down Expand Up @@ -517,3 +520,11 @@ func SanitizeName(name string, replacement rune) string {
return char
}, name)
}

// ProgramGetNextID returns the ID of the next eBPF program.
//
// // Returns ErrNotExist, if there is no next eBPF program.
func ProgramGetNextID(startID ProgramID) (ProgramID, error) {
id, err := objGetNextID(_ProgGetNextID, uint32(startID))
return ProgramID(id), err
}
41 changes: 41 additions & 0 deletions prog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/testutils"
"golang.org/x/xerrors"
)

func TestProgramRun(t *testing.T) {
Expand Down Expand Up @@ -319,6 +320,46 @@ func TestHaveProgTestRun(t *testing.T) {
testutils.CheckFeatureTest(t, haveProgTestRun)
}

func TestProgramGetNextID(t *testing.T) {
testutils.SkipOnOldKernel(t, "4.13", "bpf_prog_get_next_id")
var next ProgramID

prog, err := NewProgram(&ProgramSpec{
Type: SkSKB,
Instructions: asm.Instructions{
asm.LoadImm(asm.R0, 0, asm.DWord),
asm.Return(),
},
License: "MIT",
})
if err != nil {
t.Fatal(err)
}
defer prog.Close()

if next, err = ProgramGetNextID(ProgramID(0)); err != nil {
t.Fatal("Can't get next ID:", err)
}
if next == ProgramID(0) {
t.Fatal("Expected next ID other than 0")
}

// As there can be multiple eBPF programs, we loop over all of them and
// make sure, the IDs increase and the last call will return ErrNotExist
for {
last := next
if next, err = ProgramGetNextID(last); err != nil {
if !xerrors.Is(err, ErrNotExist) {
t.Fatal("Expected ErrNotExist, got:", err)
}
break
}
if next <= last {
t.Fatalf("Expected next ID (%d) to be higher than the last ID (%d)", next, last)
}
}
}

func createProgramArray(t *testing.T) *Map {
t.Helper()

Expand Down
30 changes: 30 additions & 0 deletions syscalls.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import (
"golang.org/x/xerrors"
)

// Generic errors returned by BPF syscalls.
var (
ErrNotExist = xerrors.New("requested object does not exit")
)

// bpfObjName is a null-terminated string made up of
// 'A-Za-z0-9_' characters.
type bpfObjName [unix.BPF_OBJ_NAME_LEN]byte
Expand Down Expand Up @@ -150,6 +155,12 @@ type bpfMapFreezeAttr struct {
mapFd uint32
}

type bpfObjGetNextIDAttr struct {
startID uint32
nextID uint32
openFlags uint32
}

func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) {
for {
fd, err := internal.BPF(_ProgLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
Expand Down Expand Up @@ -301,6 +312,25 @@ func bpfMapGetNextKey(m *internal.FD, key, nextKeyOut internal.Pointer) error {
return wrapMapError(err)
}

func objGetNextID(cmd int, start uint32) (uint32, error) {
attr := bpfObjGetNextIDAttr{
startID: start,
}
_, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return attr.nextID, wrapObjError(err)
}

func wrapObjError(err error) error {
if err == nil {
return nil
}
if xerrors.Is(err, unix.ENOENT) {
return xerrors.Errorf("%w", ErrNotExist)
}

return xerrors.New(err.Error())
}

func wrapMapError(err error) error {
if err == nil {
return nil
Expand Down
1 change: 1 addition & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ const (
_TaskFDQuery
_MapLookupAndDeleteElem
_MapFreeze
_BTFGetNextID
)

const (
Expand Down

0 comments on commit de57e91

Please sign in to comment.