Skip to content

Commit

Permalink
Add method Run to program(BPF_PROG_RUN)
Browse files Browse the repository at this point in the history
  • Loading branch information
sc07kvm committed May 11, 2024
1 parent 282d443 commit 07f424b
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 0 deletions.
45 changes: 45 additions & 0 deletions libbpfgo.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,51 @@ void cgo_bpf_iter_attach_opts_free(struct bpf_iter_attach_opts *opts)
free(opts);
}

struct bpf_test_run_opts *cgo_bpf_test_run_opts_new(const void *data_in,
void *data_out,
__u32 data_size_in,
__u32 data_size_out,
const void *ctx_in,
void *ctx_out,
__u32 ctx_size_in,
__u32 ctx_size_out,
int repeat,
__u32 flags,
__u32 cpu,
__u32 batch_size)
{
struct bpf_test_run_opts *opts;
opts = calloc(1, sizeof(*opts));
if (!opts) {
return NULL;
}

opts->sz = sizeof(*opts);
opts->data_in = data_in;
opts->data_out = data_out;
opts->data_size_in = data_size_in;
opts->data_size_out = data_size_out;
opts->ctx_in = ctx_in;
opts->ctx_out = ctx_out;
opts->ctx_size_in = ctx_size_in;
opts->ctx_size_out = ctx_size_out;
opts->repeat = repeat;
opts->flags = flags;
opts->cpu = cpu;
opts->batch_size = batch_size;

return opts;
}

void cgo_bpf_test_run_opts_free(struct bpf_test_run_opts *opts)
{
if (!opts) {
return;
}

free(opts);
}

struct bpf_object_open_opts *cgo_bpf_object_open_opts_new(const char *btf_file_path,
const char *kconfig_path,
const char *bpf_obj_name,
Expand Down
14 changes: 14 additions & 0 deletions libbpfgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ struct bpf_iter_attach_opts *cgo_bpf_iter_attach_opts_new(__u32 map_fd,
__u32 pid_fd);
void cgo_bpf_iter_attach_opts_free(struct bpf_iter_attach_opts *opts);

struct bpf_test_run_opts *cgo_bpf_test_run_opts_new(const void *data_in,
void *data_out,
__u32 data_size_in,
__u32 data_size_out,
const void *ctx_in,
void *ctx_out,
__u32 ctx_size_in,
__u32 ctx_size_out,
int repeat,
__u32 flags,
__u32 cpu,
__u32 batch_size);
void cgo_bpf_test_run_opts_free(struct bpf_test_run_opts *opts);

struct bpf_object_open_opts *cgo_bpf_object_open_opts_new(const char *btf_file_path,
const char *kconfig_path,
const char *bpf_obj_name,
Expand Down
144 changes: 144 additions & 0 deletions prog.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"path/filepath"
"strings"
"syscall"
"time"
"unsafe"
)

Expand Down Expand Up @@ -629,3 +630,146 @@ func (p *BPFProg) DetachGenericFD(targetFd int, attachType BPFAttachType) error

return nil
}

type RunFlag uint32

const (
RunFlagRunOnCPU RunFlag = C.BPF_F_TEST_RUN_ON_CPU
RunFlagXDPLiveFrames RunFlag = C.BPF_F_TEST_XDP_LIVE_FRAMES
)

// BPFMapCreateOpts mirrors the C structure bpf_test_run_opts.
type RunOpts struct {
DataIn []byte
DataOut []byte
DataSizeIn uint
DataSizeOut uint
CtxIn []byte
CtxOut []byte
CtxSizeIn uint
CtxSizeOut uint
RetVal uint
Repeat int
Duration time.Duration
Flags RunFlag
CPU uint
BatchSize uint
}

func runOptsToC(runOpts *RunOpts) (*C.struct_bpf_test_run_opts, error) {
if runOpts == nil {
return nil, nil
}

var (
dataIn unsafe.Pointer
dataSizeIn C.uint
dataOut unsafe.Pointer
dataSizeOut C.uint
ctxIn unsafe.Pointer
ctxSizeIn C.uint
ctxOut unsafe.Pointer
ctxSizeOut C.uint
)

if runOpts.DataIn != nil {
dataIn = unsafe.Pointer(&runOpts.DataIn[0])
dataSizeIn = C.uint(runOpts.DataSizeIn)
}
if runOpts.DataOut != nil {
dataOut = unsafe.Pointer(&runOpts.DataOut[0])
dataSizeOut = C.uint(runOpts.DataSizeOut)
}
if runOpts.CtxIn != nil {
ctxIn = unsafe.Pointer(&runOpts.CtxIn[0])
ctxSizeIn = C.uint(runOpts.CtxSizeIn)
}
if runOpts.CtxOut != nil {
ctxOut = unsafe.Pointer(&runOpts.CtxOut[0])
ctxSizeOut = C.uint(runOpts.CtxSizeOut)
}
optsC, errno := C.cgo_bpf_test_run_opts_new(
dataIn, dataOut,
dataSizeIn, dataSizeOut,
ctxIn, ctxOut,
ctxSizeIn, ctxSizeOut,
C.int(runOpts.Repeat), C.uint(runOpts.Flags), C.uint(runOpts.CPU), C.uint(runOpts.BatchSize),
)
if optsC == nil {
return nil, fmt.Errorf("failed to create test_run_opts: %w", errno)
}

return optsC, nil
}

func runOptsFromC(runOpts *RunOpts, optsC *C.struct_bpf_test_run_opts) {
if optsC == nil {
return
}

if optsC.data_in != nil {
runOpts.DataIn = C.GoBytes(optsC.data_in, C.int(optsC.data_size_in))
}
if optsC.data_out != nil {
runOpts.DataOut = C.GoBytes(optsC.data_out, C.int(optsC.data_size_out))
}
if optsC.ctx_in != nil {
runOpts.CtxIn = C.GoBytes(optsC.ctx_in, C.int(optsC.ctx_size_in))
}
if optsC.ctx_out != nil {
runOpts.CtxOut = C.GoBytes(optsC.ctx_out, C.int(optsC.ctx_size_out))
}

runOpts.RetVal = uint(optsC.retval)
runOpts.Repeat = int(optsC.repeat)
runOpts.Duration = time.Duration(optsC.duration) * time.Nanosecond
runOpts.Flags = RunFlag(optsC.flags)
runOpts.CPU = uint(optsC.cpu)
runOpts.BatchSize = uint(optsC.batch_size)
}

// Run is used to executes the BPF program in the kernel and return the results to userspace.
// Reference:
// - https://docs.kernel.org/bpf/bpf_prog_run.html
// - https://docs.kernel.org/userspace-api/ebpf/syscall.html
//
// Example Usage:
//
// /*
// SEC("tc")
// int test(struct __sk_buff *skb)
// {
// return foo()?1:0;
// }
// */
//
// func TestFunc(t *testing.T) {
// ...
// prog, _ := module.GetProgram("test")
// opts := RunOpts{
// DataIn: make([]byte, 0, 14), DataSizeIn: 14,
// DataOut: make([]byte, 0, 14), DataSizeOut: 14,
// Repeat: 1,
// }
// prog.Run(&opts)
// if opts.RetVal != 1 {
// t.Errorf("result = %d; want 1", opts.RetVal)
// }
// }
func (p *BPFProg) Run(runOpts *RunOpts) error {
optsC, err := runOptsToC(runOpts)
if err != nil {
return err
}
defer C.cgo_bpf_test_run_opts_free(optsC)

retC := C.bpf_prog_test_run_opts(C.int(p.FileDescriptor()), optsC)
if retC < 0 {
return fmt.Errorf("failed to run program: %w", syscall.Errno(-retC))
}

// update runOpts with the values from the libbpf.
runOptsFromC(runOpts, optsC)

return nil
}
22 changes: 22 additions & 0 deletions selftest/common/run-4.12.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

# SETTINGS

TEST=$(dirname $0)/$1 # execute
TIMEOUT=10 # seconds

# COMMON

COMMON="$(dirname $0)/../common/common.sh"
[[ -f $COMMON ]] && { . $COMMON; } || { error "no common"; exit 1; }

# MAIN

kern_version gt 4.12

check_build
check_ppid
test_exec
test_finish

exit 0
1 change: 1 addition & 0 deletions selftest/prog-run/Makefile
7 changes: 7 additions & 0 deletions selftest/prog-run/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module github.com/aquasecurity/libbpfgo/selftest/testrun

go 1.21

require github.com/aquasecurity/libbpfgo v0.0.0

replace github.com/aquasecurity/libbpfgo => ../../
8 changes: 8 additions & 0 deletions selftest/prog-run/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
25 changes: 25 additions & 0 deletions selftest/prog-run/main.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//+build ignore

#include <vmlinux.h>

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

SEC("tc")
int test_tc(struct __sk_buff *skb)
{
void *data = (void *) (long) skb->data;
void *data_end = (void *) (long) skb->data_end;
if (data + 4 > data_end) {
return -1;
}
if (*(__u32 *) data == 0xdeadbeef) {
char new_data[] = {0x01, 0x02, 0x03, 0x04};
bpf_skb_store_bytes(skb, 0, new_data, 4, 0);
bpf_skb_change_tail(skb, 14, 0);
return 1;
}
return 2;
}

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

import "C"

import (
"encoding/binary"
"log"

bpf "github.com/aquasecurity/libbpfgo"
)

func main() {
bpfModule, err := bpf.NewModuleFromFile("main.bpf.o")
if err != nil {
log.Fatalf("Failed to load BPF module: %v", err)
}
defer bpfModule.Close()

err = bpfModule.BPFLoadObject()
if err != nil {
log.Fatalf("Failed to load object: %v", err)
}

tcProg, err := bpfModule.GetProgram("test_tc")
if err != nil || tcProg == nil {
log.Fatalf("Failed to get prog: %v", err)
}

dataIn := make([]byte, 16)
binary.LittleEndian.PutUint32(dataIn, 0xdeadbeef)
opts := bpf.RunOpts{
DataIn: dataIn,
DataSizeIn: 16,
DataOut: make([]byte, 32),
DataSizeOut: 32,
Repeat: 1,
}
err = tcProg.Run(&opts)
if err != nil {
log.Fatalf("Failed to run prog: %v", err)
}
if opts.RetVal != 1 {
log.Fatalf("retVal %d should be 1", opts.RetVal)
}
if len(opts.DataOut) != 14 {
log.Fatalf("dataOut len %v should be 14", opts.DataOut)
}
if binary.LittleEndian.Uint32(opts.DataOut) != 0x04030201 {
log.Fatalf("dataOut 0x%x should be 0x04030201", binary.LittleEndian.Uint32(opts.DataOut))
}
}
1 change: 1 addition & 0 deletions selftest/prog-run/run.sh

0 comments on commit 07f424b

Please sign in to comment.