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

Add libbpfgo API functions for attaching generic bpf program types, selftests #144

Merged
merged 7 commits into from Apr 14, 2022
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
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 @@ -843,6 +876,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 @@ -891,7 +928,7 @@ func (p *BPFProg) GetPinPath() string {
type BPFProgType uint32

const (
BPFProgTypeUnspec uint32 = iota
BPFProgTypeUnspec BPFProgType = iota
BPFProgTypeSocketFilter
BPFProgTypeKprobe
BPFProgTypeSchedCls
Expand Down Expand Up @@ -922,10 +959,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 @@ -945,6 +1073,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";