Skip to content

Commit

Permalink
chore(map): logic refactoring and standardization
Browse files Browse the repository at this point in the history
Introduced struct helpers to manage the C struct lifecycle on the C side,
which avoids problems with structs which may contain bitfields. See aquasecurity#244.

  These are replacements for 'BPFMapBatchOpts' and 'bpfMapBatchOptsToC()'.
  - cgo_bpf_map_batch_opts_new
  - cgo_bpf_map_batch_opts_free

  These are replacements for 'bpfMapCreateOptsToC()'
  - cgo_bpf_map_create_opts_new
  - cgo_bpf_map_create_opts_free

Restructured libbpf BPF map logic into separate files:
- 'map-common.go' contains generic BPF map types, constants, and helpers
  like 'GetMapInfoByFD()'.
- 'map-low.go' includes 'BPFMapLow' and respective logic and helpers
  like 'CreateMap()'.
- 'map-iterator.go' contains related types and logic.

In the 'BPFMap' struct:
- Removed fields like 'fd' and 'name' since they must be retrieved
  directly from 'libbpf'.
- Added 'bpfMapLow' instance for access to low-level methods.
- Deprecated 'BPFMap.GetMaxEntries' in favor of using 'MaxEntries'.
- Exposed 'InitialValue' and 'SetInitialValue' as public wrappers.
- Introduced MapFlags(), IfIndex() and MapExtra() methods.

Introduced 'misc.go' as a place for miscellaneous generic helpers.

Bug Fixes:
- Fixed cases where 'BPFMap.ValueSize()', possibly 0, was being passed
  directly to 'make()' - detected on the map of maps effort aquasecurity#343.

Further Refactoring and Standardization on the map related logic and on
lines touched by other changes:
- Removed check for 'BPFMap.Name()' against nil, as 'C.GoString(C.NULL)'
  returns "".
- Changed error returns to 'retC' in functions that return error values
  to avoid confusion with possible use of errno.
- Changed returns to 'valueC' in functions that return values or
  pointers to values.
- Suffixed variable names that are C types with 'C'.
- Applied the use of 'defer' for better resource management.
  • Loading branch information
geyslan committed Aug 17, 2023
1 parent 270fdcd commit 2065362
Show file tree
Hide file tree
Showing 9 changed files with 1,119 additions and 505 deletions.
53 changes: 53 additions & 0 deletions libbpfgo.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,56 @@ void cgo_bpf_object_open_opts_free(struct bpf_object_open_opts *opts)
{
free(opts);
}

struct bpf_map_create_opts *cgo_bpf_map_create_opts_new(__u32 btf_fd,
__u32 btf_key_type_id,
__u32 btf_value_type_id,
__u32 btf_vmlinux_value_type_id,
__u32 inner_map_fd,
__u32 map_flags,
__u64 map_extra,
__u32 numa_node,
__u32 map_ifindex)
{
struct bpf_map_create_opts *opts;
opts = calloc(1, sizeof(*opts));
if (!opts)
return NULL;

opts->sz = sizeof(*opts);
opts->btf_fd = btf_fd;
opts->btf_key_type_id = btf_key_type_id;
opts->btf_value_type_id = btf_value_type_id;
opts->btf_vmlinux_value_type_id = btf_vmlinux_value_type_id;
opts->inner_map_fd = inner_map_fd;
opts->map_flags = map_flags;
opts->map_extra = map_extra;
opts->numa_node = numa_node;
opts->map_ifindex = map_ifindex;

return opts;
}

void cgo_bpf_map_create_opts_free(struct bpf_map_create_opts *opts)
{
free(opts);
}

struct bpf_map_batch_opts *cgo_bpf_map_batch_opts_new(__u64 elem_flags, __u64 flags)
{
struct bpf_map_batch_opts *opts;
opts = calloc(1, sizeof(*opts));
if (!opts)
return NULL;

opts->sz = sizeof(*opts);
opts->elem_flags = elem_flags;
opts->flags = flags;

return opts;
}

void cgo_bpf_map_batch_opts_free(struct bpf_map_batch_opts *opts)
{
free(opts);
}
96 changes: 82 additions & 14 deletions libbpfgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,10 @@ func (m *Module) InitGlobalVariable(name string, value interface{}) error {
}

// get current value
currMapValue := bpfMap.getInitialValue()
currMapValue, err := bpfMap.InitialValue()
if err != nil {
return err
}

// generate new value
newMapValue := make([]byte, bpfMap.ValueSize())
Expand All @@ -386,24 +389,71 @@ func (m *Module) InitGlobalVariable(name string, value interface{}) error {
copy(newMapValue[start:end], varValue)

// save new value
err = bpfMap.setInitialValue(unsafe.Pointer(&newMapValue[0]))
err = bpfMap.SetInitialValue(unsafe.Pointer(&newMapValue[0]))
return err
}

func (m *Module) GetMap(mapName string) (*BPFMap, error) {
cs := C.CString(mapName)
bpfMap, errno := C.bpf_object__find_map_by_name(m.obj, cs)
bpfMapC, errno := C.bpf_object__find_map_by_name(m.obj, cs)
C.free(unsafe.Pointer(cs))
if bpfMap == nil {
if bpfMapC == nil {
return nil, fmt.Errorf("failed to find BPF map %s: %w", mapName, errno)
}

return &BPFMap{
bpfMap: bpfMap,
name: mapName,
fd: C.bpf_map__fd(bpfMap),
bpfMap := &BPFMap{
bpfMap: bpfMapC,
module: m,
}, nil
}

if !m.loaded {
bpfMap.bpfMapLow = &BPFMapLow{
fd: -1,
info: &BPFMapInfo{},
}

return bpfMap, nil
}

fd := bpfMap.FileDescriptor()
info, err := GetMapInfoByFD(fd)
if err != nil {
// Compatibility Note: Some older kernels lack BTF (BPF Type Format)
// support for specific BPF map types. In such scenarios, libbpf may
// fail (EPERM) when attempting to retrieve information for these maps.
// Reference: https://elixir.bootlin.com/linux/v5.15.75/source/tools/lib/bpf/gen_loader.c#L401
//
// However, we can still get some map info from the BPF map high level API.
bpfMap.bpfMapLow = &BPFMapLow{
fd: fd,
info: &BPFMapInfo{
Type: bpfMap.Type(),
ID: 0,
KeySize: uint32(bpfMap.KeySize()),
ValueSize: uint32(bpfMap.ValueSize()),
MaxEntries: bpfMap.MaxEntries(),
MapFlags: uint32(bpfMap.MapFlags()),
Name: bpfMap.Name(),
IfIndex: bpfMap.IfIndex(),
BTFVmlinuxValueTypeID: 0,
NetnsDev: 0,
NetnsIno: 0,
BTFID: 0,
BTFKeyTypeID: 0,
BTFValueTypeID: 0,
MapExtra: bpfMap.MapExtra(),
},
}

return bpfMap, nil
}

bpfMap.bpfMapLow = &BPFMapLow{
fd: fd,
info: info,
}

return bpfMap, nil
}

// BPFObjectProgramIterator iterates over maps in a BPF object
Expand Down Expand Up @@ -431,15 +481,33 @@ func (it *BPFObjectIterator) NextMap() *BPFMap {
if m == nil {
return nil
}
cName := C.bpf_map__name(m)

bpfMap := &BPFMap{
name: C.GoString(cName),
bpfMap: m,
module: it.m,
}

it.prevMap = bpfMap

if !bpfMap.module.loaded {
bpfMap.bpfMapLow = &BPFMapLow{
fd: -1,
info: &BPFMapInfo{},
}

return bpfMap
}

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

bpfMap.bpfMapLow = &BPFMapLow{
fd: fd,
info: info,
}

return bpfMap
}

Expand Down Expand Up @@ -1150,7 +1218,7 @@ func (m *Module) InitRingBuf(mapName string, eventsChan chan []byte) (*RingBuffe
return nil, fmt.Errorf("max ring buffers reached")
}

rb := C.cgo_init_ring_buf(bpfMap.fd, C.uintptr_t(slot))
rb := C.cgo_init_ring_buf(C.int(bpfMap.FileDescriptor()), C.uintptr_t(slot))
if rb == nil {
return nil, fmt.Errorf("failed to initialize ring buffer")
}
Expand Down Expand Up @@ -1264,7 +1332,7 @@ func (m *Module) InitPerfBuf(mapName string, eventsChan chan []byte, lostChan ch
return nil, fmt.Errorf("max number of ring/perf buffers reached")
}

pb := C.cgo_init_perf_buf(bpfMap.fd, C.int(pageCnt), C.uintptr_t(slot))
pb := C.cgo_init_perf_buf(C.int(bpfMap.FileDescriptor()), C.int(pageCnt), C.uintptr_t(slot))
if pb == nil {
eventChannels.remove(uint(slot))
return nil, fmt.Errorf("failed to initialize perf buffer")
Expand Down
14 changes: 14 additions & 0 deletions libbpfgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,18 @@ struct bpf_object_open_opts *cgo_bpf_object_open_opts_new(const char *btf_file_p
const char *bpf_obj_name);
void cgo_bpf_object_open_opts_free(struct bpf_object_open_opts *opts);

struct bpf_map_create_opts *cgo_bpf_map_create_opts_new(__u32 btf_fd,
__u32 btf_key_type_id,
__u32 btf_value_type_id,
__u32 btf_vmlinux_value_type_id,
__u32 inner_map_fd,
__u32 map_flags,
__u64 map_extra,
__u32 numa_node,
__u32 map_ifindex);
void cgo_bpf_map_create_opts_free(struct bpf_map_create_opts *opts);

struct bpf_map_batch_opts *cgo_bpf_map_batch_opts_new(__u64 elem_flags, __u64 flags);
void cgo_bpf_map_batch_opts_free(struct bpf_map_batch_opts *opts);

#endif
Loading

0 comments on commit 2065362

Please sign in to comment.