Skip to content

Commit

Permalink
Add libbpfgo API functions for attaching generic bpf program types, s…
Browse files Browse the repository at this point in the history
…elftests (#144)

* Add libbpfgo API functions for attaching generic bpf program types such as fentry/fexit, and add selftests for demonstration

* Add APIs for setting attach targets so they're not just implied by SEC and name labels

* Add selftest for setting attachments

Signed-off-by: grantseltzer <grantseltzer@gmail.com>
  • Loading branch information
grantseltzer committed Apr 14, 2022
1 parent 3cddef5 commit 54e568b
Show file tree
Hide file tree
Showing 15 changed files with 494 additions and 3 deletions.
171 changes: 168 additions & 3 deletions libbpfgo.go
Expand Up @@ -60,6 +60,18 @@ struct ring_buffer * init_ring_buf(int map_fd, uintptr_t ctx)
return rb;
}
void list_programs(struct bpf_object* obj) {
struct bpf_program *pos;
const char *cs;
const char *css;
bpf_object__for_each_program(pos, obj) {
cs = bpf_program__section_name(pos);
css = bpf_program__name(pos);
printf("section: %s\tname: %s\n", cs, css);
}
}
struct perf_buffer * init_perf_buf(int map_fd, int page_cnt, uintptr_t ctx)
{
struct perf_buffer_opts pb_opts = {};
Expand Down Expand Up @@ -215,6 +227,7 @@ const (
PerfEvent
Uprobe
Uretprobe
Tracing
)

type BPFLink struct {
Expand All @@ -236,6 +249,26 @@ func (l *BPFLink) GetFd() int {
return int(C.bpf_link__fd(l.link))
}

func (l *BPFLink) Pin(pinPath string) error {
path := C.CString(pinPath)
errC := C.bpf_link__pin(l.link, path)
C.free(unsafe.Pointer(path))
if errC != 0 {
return fmt.Errorf("failed to pin link %s to path %s: %w", l.eventName, pinPath, syscall.Errno(-errC))
}
return nil
}

func (l *BPFLink) Unpin(pinPath string) error {
path := C.CString(pinPath)
errC := C.bpf_link__unpin(l.link)
C.free(unsafe.Pointer(path))
if errC != 0 {
return fmt.Errorf("failed to unpin link %s from path %s: %w", l.eventName, pinPath, syscall.Errno(-errC))
}
return nil
}

type PerfBuffer struct {
pb *C.struct_perf_buffer
bpfMap *BPFMap
Expand Down Expand Up @@ -819,6 +852,10 @@ func (m *Module) GetProgram(progName string) (*BPFProg, error) {
}, nil
}

func (m *Module) ListProgramNames() {
C.list_programs(m.obj)
}

func (p *BPFProg) GetFd() int {
return int(C.bpf_program__fd(p.prog))
}
Expand Down Expand Up @@ -867,7 +904,7 @@ func (p *BPFProg) GetPinPath() string {
type BPFProgType uint32

const (
BPFProgTypeUnspec uint32 = iota
BPFProgTypeUnspec BPFProgType = iota
BPFProgTypeSocketFilter
BPFProgTypeKprobe
BPFProgTypeSchedCls
Expand Down Expand Up @@ -898,10 +935,101 @@ const (
BPFProgTypeExt
BPFProgTypeLsm
BPFProgTypeSkLookup
BPFProgTypeSyscall
)

func (p *BPFProg) GetType() uint32 {
return C.bpf_program__get_type(p.prog)
func (b BPFProgType) String() (str string) {
x := map[BPFProgType]string{
BPFProgTypeUnspec: "BPF_PROG_TYPE_UNSPEC",
BPFProgTypeSocketFilter: "BPF_PROG_TYPE_SOCKET_FILTER",
BPFProgTypeKprobe: "BPF_PROG_TYPE_KPROBE",
BPFProgTypeSchedCls: "BPF_PROG_TYPE_SCHED_CLS",
BPFProgTypeSchedAct: "BPF_PROG_TYPE_SCHED_ACT",
BPFProgTypeTracepoint: "BPF_PROG_TYPE_TRACEPOINT",
BPFProgTypeXdp: "BPF_PROG_TYPE_XDP",
BPFProgTypePerfEvent: "BPF_PROG_TYPE_PERF_EVENT",
BPFProgTypeCgroupSkb: "BPF_PROG_TYPE_CGROUP_SKB",
BPFProgTypeCgroupSock: "BPF_PROG_TYPE_CGROUP_SOCK",
BPFProgTypeLwtIn: "BPF_PROG_TYPE_LWT_IN",
BPFProgTypeLwtOut: "BPF_PROG_TYPE_LWT_OUT",
BPFProgTypeLwtXmit: "BPF_PROG_TYPE_LWT_XMIT",
BPFProgTypeSockOps: "BPF_PROG_TYPE_SOCK_OPS",
BPFProgTypeSkSkb: "BPF_PROG_TYPE_SK_SKB",
BPFProgTypeCgroupDevice: "BPF_PROG_TYPE_CGROUP_DEVICE",
BPFProgTypeSkMsg: "BPF_PROG_TYPE_SK_MSG",
BPFProgTypeRawTracepoint: "BPF_PROG_TYPE_RAW_TRACEPOINT",
BPFProgTypeCgroupSockAddr: "BPF_PROG_TYPE_CGROUP_SOCK_ADDR",
BPFProgTypeLwtSeg6Local: "BPF_PROG_TYPE_LWT_SEG6LOCAL",
BPFProgTypeLircMode2: "BPF_PROG_TYPE_LIRC_MODE2",
BPFProgTypeSkReuseport: "BPF_PROG_TYPE_SK_REUSEPORT",
BPFProgTypeFlowDissector: "BPF_PROG_TYPE_FLOW_DISSECTOR",
BPFProgTypeCgroupSysctl: "BPF_PROG_TYPE_CGROUP_SYSCTL",
BPFProgTypeRawTracepointWritable: "BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE",
BPFProgTypeCgroupSockopt: "BPF_PROG_TYPE_CGROUP_SOCKOPT",
BPFProgTypeTracing: "BPF_PROG_TYPE_TRACING",
BPFProgTypeStructOps: "BPF_PROG_TYPE_STRUCT_OPS",
BPFProgTypeExt: "BPF_PROG_TYPE_EXT",
BPFProgTypeLsm: "BPF_PROG_TYPE_LSM",
BPFProgTypeSkLookup: "BPF_PROG_TYPE_SK_LOOKUP",
BPFProgTypeSyscall: "BPF_PROG_TYPE_SYSCALL",
}
str = x[b]
if str == "" {
str = BPFProgTypeUnspec.String()
}
return str
}

type BPFAttachType uint32

const (
BPFAttachTypeCgroupInetIngress BPFAttachType = iota
BPFAttachTypeCgroupInetEgress
BPFAttachTypeCgroupInetSockCreate
BPFAttachTypeCgroupSockOps
BPFAttachTypeSKSKBStreamParser
BPFAttachTypeSKSKBStreamVerdict
BPFAttachTypeCgroupDevice
BPFAttachTypeSKMSGVerdict
BPFAttachTypeCgroupInet4Bind
BPFAttachTypeCgroupInet6Bind
BPFAttachTypeCgroupInet4Connect
BPFAttachTypeCgroupInet6Connect
BPFAttachTypeCgroupInet4PostBind
BPFAttachTypeCgroupInet6PostBind
BPFAttachTypeCgroupUDP4SendMsg
BPFAttachTypeCgroupUDP6SendMsg
BPFAttachTypeLircMode2
BPFAttachTypeFlowDissector
BPFAttachTypeCgroupSysctl
BPFAttachTypeCgroupUDP4RecvMsg
BPFAttachTypeCgroupUDP6RecvMsg
BPFAttachTypeCgroupGetSockOpt
BPFAttachTypeCgroupSetSockOpt
BPFAttachTypeTraceRawTP
BPFAttachTypeTraceFentry
BPFAttachTypeTraceFexit
BPFAttachTypeModifyReturn
BPFAttachTypeLSMMac
BPFAttachTypeTraceIter
BPFAttachTypeCgroupInet4GetPeerName
BPFAttachTypeCgroupInet6GetPeerName
BPFAttachTypeCgroupInet4GetSockName
BPFAttachTypeCgroupInet6GetSockName
BPFAttachTypeXDPDevMap
BPFAttachTypeCgroupInetSockRelease
BPFAttachTypeXDPCPUMap
BPFAttachTypeSKLookup
BPFAttachTypeXDP
BPFAttachTypeSKSKBVerdict
BPFAttachTypeSKReusePortSelect
BPFAttachTypeSKReusePortSelectorMigrate
BPFAttachTypePerfEvent
BPFAttachTypeTraceKprobeMulti
)

func (p *BPFProg) GetType() BPFProgType {
return BPFProgType(C.bpf_program__get_type(p.prog))
}

func (p *BPFProg) SetAutoload(autoload bool) error {
Expand All @@ -921,6 +1049,43 @@ func (p *BPFProg) SetTracepoint() error {
return nil
}

// AttachGeneric is used to attach the BPF program using autodetection
// for the attach target. You can specify the destination in BPF code
// via the SEC() such as `SEC("fentry/some_kernel_func")`
func (p *BPFProg) AttachGeneric() (*BPFLink, error) {
link, errno := C.bpf_program__attach(p.prog)
if C.IS_ERR_OR_NULL(unsafe.Pointer(link)) {
return nil, fmt.Errorf("failed to attach program: %w", errno)
}
bpfLink := &BPFLink{
link: link,
prog: p,
linkType: Tracing,
eventName: fmt.Sprintf("tracing-%s", p.name),
}
return bpfLink, nil
}

// SetAttachTarget can be used to specify the program and/or function to attach
// the BPF program to. To attach to a kernel function specify attachProgFD as 0
func (p *BPFProg) SetAttachTarget(attachProgFD int, attachFuncName string) error {
cs := C.CString(attachFuncName)
errC := C.bpf_program__set_attach_target(p.prog, C.int(attachProgFD), cs)
C.free(unsafe.Pointer(cs))
if errC != 0 {
return fmt.Errorf("failed to set attach target for program %s %s %w", p.name, attachFuncName, syscall.Errno(-errC))
}
return nil
}

func (p *BPFProg) SetProgramType(progType BPFProgType) {
C.bpf_program__set_type(p.prog, C.enum_bpf_prog_type(int(progType)))
}

func (p *BPFProg) SetAttachType(attachType BPFAttachType) {
C.bpf_program__set_expected_attach_type(p.prog, C.enum_bpf_attach_type(int(attachType)))
}

func (p *BPFProg) AttachTracepoint(category, name string) (*BPFLink, error) {
tpCategory := C.CString(category)
tpName := C.CString(name)
Expand Down
18 changes: 18 additions & 0 deletions samples/fentry/main.bpf.c
@@ -0,0 +1,18 @@
//+build ignore
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

#ifdef asm_inline
#undef asm_inline
#define asm_inline asm
#endif

SEC("fentry/commit_creds")
int BPF_PROG(commit_creds, struct cred *foobar)
{
bpf_printk("%d", foobar->uid.val);
return 0;
}

char LICENSE[] SEC("license") = "GPL";
44 changes: 44 additions & 0 deletions samples/fentry/main.go
@@ -0,0 +1,44 @@
package main

import "C"

import (
"os"

"fmt"

"github.com/aquasecurity/libbpfgo"
)

func main() {

bpfModule, err := libbpfgo.NewModuleFromFile("main.bpf.o")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(-1)
}
defer bpfModule.Close()

err = bpfModule.BPFLoadObject()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(-1)
}

bpfModule.ListProgramNames()
prog1, err := bpfModule.GetProgram("commit_creds")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(-1)
}
link1, err := prog1.AttachGeneric()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(-1)
}
if link1.GetFd() == 0 {
os.Exit(-1)
}

fmt.Println(prog1.GetType().String())
}
1 change: 1 addition & 0 deletions selftest/set-attach/Makefile
7 changes: 7 additions & 0 deletions selftest/set-attach/go.mod
@@ -0,0 +1,7 @@
module github.com/aquasecurity/libbpfgo/selftest/perfbuffers

go 1.16

require github.com/aquasecurity/libbpfgo v0.2.1-libbpf-0.4.0

replace github.com/aquasecurity/libbpfgo => ../../
11 changes: 11 additions & 0 deletions selftest/set-attach/go.sum
@@ -0,0 +1,11 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
32 changes: 32 additions & 0 deletions selftest/set-attach/main.bpf.c
@@ -0,0 +1,32 @@
//+build ignore
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

#ifdef asm_inline
#undef asm_inline
#define asm_inline asm
#endif

struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
} events SEC(".maps");
long ringbuffer_flags = 0;

SEC("fentry/FUNC")
int BPF_PROG(foobar)
{
int *process;
// Reserve space on the ringbuffer for the sample
process = bpf_ringbuf_reserve(&events, sizeof(int), ringbuffer_flags);
if (!process) {
return 0;
}
*process = 2021;
bpf_ringbuf_submit(process, ringbuffer_flags);
return 0;
}

char LICENSE[] SEC("license") = "GPL";

0 comments on commit 54e568b

Please sign in to comment.