Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map of maps #366

Merged
merged 8 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions btf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package libbpfgo

/*
#cgo LDFLAGS: -lelf -lz
#include "libbpfgo.h"
*/
import "C"

import (
"fmt"
"syscall"
)

//
// BTF (low-level API)
//

// GetBTFFDByID returns a file descriptor for the BTF with the given ID.
func GetBTFFDByID(id uint32) (int, error) {
fdC := C.bpf_btf_get_fd_by_id(C.uint(id))
if fdC < 0 {
return int(fdC), fmt.Errorf("could not find BTF id %d: %w", id, syscall.Errno(-fdC))
}

return int(fdC), nil
}
10 changes: 10 additions & 0 deletions map-common.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ type BPFMapInfo struct {
MapExtra uint64
}

// GetMapFDByID returns a file descriptor for the map with the given ID.
func GetMapFDByID(id uint32) (int, error) {
fdC := C.bpf_map_get_fd_by_id(C.uint(id))
if fdC < 0 {
return int(fdC), fmt.Errorf("could not find map id %d: %w", id, syscall.Errno(-fdC))
}

return int(fdC), nil
}

// GetMapInfoByFD returns the BPFMapInfo for the map with the given file descriptor.
func GetMapInfoByFD(fd int) (*BPFMapInfo, error) {
var info C.struct_bpf_map_info
Expand Down
18 changes: 18 additions & 0 deletions map-low.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,24 @@ func CreateMap(mapType MapType, mapName string, keySize, valueSize, maxEntries i
}, nil
}

// GetMapByID returns a BPFMapLow instance for the map with the given ID.
func GetMapByID(id uint32) (*BPFMapLow, error) {
fd, err := GetMapFDByID(id)
if err != nil {
return nil, err
}

info, err := GetMapInfoByFD(fd)
if err != nil {
return nil, err
}

return &BPFMapLow{
fd: fd,
info: info,
}, nil
}

//
// BPFMapLow Specs
//
Expand Down
75 changes: 63 additions & 12 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,13 @@ func (m *BPFMap) SetValueSize(size uint32) error {
return nil
}

// TODO: implement `bpf_map__btf_key_type_id` wrapper
// func (m *BPFMap) BTFKeyTypeID() uint32 {
// }
func (m *BPFMap) BTFKeyTypeID() uint32 {
return uint32(C.bpf_map__btf_key_type_id(m.bpfMap))
}

// TODO: implement `bpf_map__btf_value_type_id` wrapper
// func (m *BPFMap) BTFValueTypeID() uint32 {
// }
func (m *BPFMap) BTFValueTypeID() uint32 {
return uint32(C.bpf_map__btf_value_type_id(m.bpfMap))
}

func (m *BPFMap) IfIndex() uint32 {
return uint32(C.bpf_map__ifindex(m.bpfMap))
Expand Down Expand Up @@ -279,13 +279,64 @@ func (m *BPFMap) Unpin(pinPath string) error {
// BPFMap Map of Maps
//

// TODO: implement `bpf_map__inner_map` wrapper
// func (m *BPFMap) InnerMap() *BPFMap {
// }
// InnerMap retrieves the inner map prototype information associated with a
// BPFMap that represents a map of maps.
//
// NOTE: It must be called before the module is loaded, since it is a prototype
// destroyed right after the outer map is created.
//
// Reference:
// https://lore.kernel.org/bpf/20200429002739.48006-4-andriin@fb.com/
func (m *BPFMap) InnerMapInfo() (*BPFMapInfo, error) {
innerMapC, errno := C.bpf_map__inner_map(m.bpfMap)
if innerMapC == nil {
return nil, fmt.Errorf("failed to get inner map for %s: %w", m.Name(), errno)
}

// TODO: implement `bpf_map__set_inner_map_fd` wrapper
// func (m *BPFMap) SetInnerMapFD(innerMapFD int) error {
// }
innerBPFMap := &BPFMap{
bpfMap: innerMapC,
module: m.module,
}

return &BPFMapInfo{
// as it is a prototype, some values are not available
Type: innerBPFMap.Type(),
ID: 0,
KeySize: uint32(innerBPFMap.KeySize()),
ValueSize: uint32(innerBPFMap.ValueSize()),
MaxEntries: innerBPFMap.MaxEntries(),
MapFlags: uint32(innerBPFMap.MapFlags()),
Name: innerBPFMap.Name(),
IfIndex: innerBPFMap.IfIndex(),
BTFVmlinuxValueTypeID: 0,
NetnsDev: 0,
NetnsIno: 0,
BTFID: 0,
BTFKeyTypeID: innerBPFMap.BTFKeyTypeID(),
BTFValueTypeID: innerBPFMap.BTFValueTypeID(),
MapExtra: innerBPFMap.MapExtra(),
}, nil
}

// SetInnerMap configures the inner map prototype for a BPFMap that represents
// a map of maps.
//
// This function accepts the file descriptor of another map, which will serve as
// a prototype.
//
// NOTE: It must be called before the module is loaded.
func (m *BPFMap) SetInnerMap(templateMapFD int) error {
if templateMapFD < 0 {
return fmt.Errorf("invalid inner map fd %d", templateMapFD)
}

retC := C.bpf_map__set_inner_map_fd(m.bpfMap, C.int(templateMapFD))
if retC < 0 {
return fmt.Errorf("failed to set inner map for %s: %w", m.Name(), syscall.Errno(-retC))
}

return nil
}

//
// BPFMap Operations
Expand Down
1 change: 1 addition & 0 deletions selftest/getbtffdbyid/Makefile
7 changes: 7 additions & 0 deletions selftest/getbtffdbyid/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module github.com/aquasecurity/libbpfgo/selftest/map-update

go 1.18

require github.com/aquasecurity/libbpfgo v0.4.7-libbpf-1.2.0-b2e29a1

replace github.com/aquasecurity/libbpfgo => ../../
4 changes: 4 additions & 0 deletions selftest/getbtffdbyid/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
14 changes: 14 additions & 0 deletions selftest/getbtffdbyid/main.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//+build ignore

#include <vmlinux.h>

#include <bpf/bpf_helpers.h>

struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, __u32);
} inner_array_proto SEC(".maps");

char LICENSE[] SEC("license") = "Dual BSD/GPL";
60 changes: 60 additions & 0 deletions selftest/getbtffdbyid/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import "C"

import (
"log"
"unsafe"

bpf "github.com/aquasecurity/libbpfgo"
)

func main() {
bpfModule, err := bpf.NewModuleFromFile("main.bpf.o")
if err != nil {
log.Fatal(err)
}
defer bpfModule.Close()

err = bpfModule.BPFLoadObject()
if err != nil {
log.Fatal(err)
}

// Use the "inner_array_proto" map BTF ID to create a new map with the same
// BTF type.
innerArrayProto, err := bpfModule.GetMap("inner_array_proto")
if err != nil {
log.Fatal(err)
}

optsProto, err := bpf.GetMapInfoByFD(innerArrayProto.FileDescriptor())
if err != nil {
log.Fatal(err)
}

// The "inner_array_proto" map is a BTF map, so its ID can be used to create
// a new map with the same BTF type.
btfFD, err := bpf.GetBTFFDByID(optsProto.BTFID)
if err != nil {
log.Fatal(err)
}

createOpts := &bpf.BPFMapCreateOpts{
BTFFD: uint32(btfFD),
}
innerArray, err := bpf.CreateMap(bpf.MapTypeArray, "inner_array", 4, 4, 1, createOpts)
if err != nil {
log.Fatal(err)
}

// Save an element in the "inner_array" map.
key := uint32(0) // index 0
keyUnsafe := unsafe.Pointer(&key)
value := uint32(191711)
valueUnsafe := unsafe.Pointer(&value)
err = innerArray.Update(keyUnsafe, valueUnsafe)
if err != nil {
log.Fatal(err)
}
}
1 change: 1 addition & 0 deletions selftest/getbtffdbyid/run.sh
1 change: 1 addition & 0 deletions selftest/map-getbyid/Makefile
7 changes: 7 additions & 0 deletions selftest/map-getbyid/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module github.com/aquasecurity/libbpfgo/selftest/map-update

go 1.18

require github.com/aquasecurity/libbpfgo v0.4.7-libbpf-1.2.0-b2e29a1

replace github.com/aquasecurity/libbpfgo => ../../
4 changes: 4 additions & 0 deletions selftest/map-getbyid/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
14 changes: 14 additions & 0 deletions selftest/map-getbyid/main.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//+build ignore

#include <vmlinux.h>

#include <bpf/bpf_helpers.h>

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, u32);
__uint(max_entries, 1);
} tester SEC(".maps");

char LICENSE[] SEC("license") = "Dual BSD/GPL";
85 changes: 85 additions & 0 deletions selftest/map-getbyid/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package main

import "C"

import (
"encoding/binary"
"log"
"unsafe"

bpf "github.com/aquasecurity/libbpfgo"
)

func main() {
bpfModule, err := bpf.NewModuleFromFile("main.bpf.o")
if err != nil {
log.Fatal(err)
}
defer bpfModule.Close()

bpfModule.BPFLoadObject()

testerMap, err := bpfModule.GetMap("tester")
if err != nil {
log.Fatal(err)
}

// Get info about the "tester" map
infoTester, err := bpf.GetMapInfoByFD(testerMap.FileDescriptor())
if err != nil {
log.Fatal(err)
}

// Get a new BPFMapLow object pointing to the "tester" map
testerMapLow, err := bpf.GetMapByID(infoTester.ID)
if err != nil {
log.Fatal(err)
}

if testerMapLow.Name() != testerMap.Name() {
log.Fatal("Names do not match")
}
if testerMapLow.Type() != testerMap.Type() {
log.Fatal("Types do not match")
}
if testerMapLow.MaxEntries() != testerMap.MaxEntries() {
log.Fatal("Max entries do not match")
}
if testerMapLow.KeySize() != testerMap.KeySize() {
log.Fatal("Key sizes do not match")
}
if testerMapLow.ValueSize() != testerMap.ValueSize() {
log.Fatal("Value sizes do not match")
}

// Save a value in the "tester" map using the original BPFMap object
key1 := uint32(11)
value1 := uint32(1917)
key1Unsafe := unsafe.Pointer(&key1)
value1Unsafe := unsafe.Pointer(&value1)
err = testerMap.Update(key1Unsafe, value1Unsafe)
if err != nil {
log.Fatal(err)
}

// Get the value from the "tester" map using the new BPFMapLow object
v, err := testerMapLow.GetValue(key1Unsafe)
if err != nil {
log.Fatal(err)
}
if endian().Uint32(v) != value1 {
log.Fatal("Value mismatch")
}
}

func endian() binary.ByteOrder {
var i int32 = 0x01020304
u := unsafe.Pointer(&i)
pb := (*byte)(u)
b := *pb
if b == 0x04 {
return binary.LittleEndian
}

return binary.BigEndian
}
1 change: 1 addition & 0 deletions selftest/map-getbyid/run.sh
1 change: 1 addition & 0 deletions selftest/map-getfdbyid/Makefile
7 changes: 7 additions & 0 deletions selftest/map-getfdbyid/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module github.com/aquasecurity/libbpfgo/selftest/map-update

go 1.18

require github.com/aquasecurity/libbpfgo v0.4.7-libbpf-1.2.0-b2e29a1

replace github.com/aquasecurity/libbpfgo => ../../
4 changes: 4 additions & 0 deletions selftest/map-getfdbyid/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=