Preface¶
This article documents setting up the libbpfgo development environment and writing a simple eBPF sample application using libbpfgo.
Setting up a development environment¶
For simplicity, I'm using vagrant to build the VM development environment:
- install vagrant-env plugin:
$ vagrant plugin install vagrant-env
- clone sample codes:
$ mkdir -p $GOPATH/src/github.com/mozillazg && \
cd $GOPATH/src/github.com/mozillazg && \
git clone https://github.com/mozillazg/hello-libbpfgo.git && \
cd hello-libbpfgo && \
git submodule update --init --recursive
- setting up .env file:
$ cp .env.example .env
- start the VM:
$ vagrant up
- ssh into the VM:
$ vagrant ssh
Write sample program¶
Here is a brief introduction to the sample program using the sample program in the 01-hello-world directory of hello-libbpfgo as an example.
main.ebpf.c:
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
SEC("kprobe/do_sys_openat2")
int kprobe__do_sys_openat2(struct pt_regs *ctx)
{
char file_name[256];
bpf_probe_read(file_name, sizeof(file_name), PT_REGS_PARM2(ctx));
char fmt[] = "open file %s\n";
bpf_trace_printk(fmt, sizeof(fmt), &file_name);
return 0;
}
main.ebpf.c is a simple program that hooks do_sys_openat2, a kernel function call. The filename information in the function call parameters is recorded.
The main point is to see how to call this ebpf program in a go program using libbpfgo.
main.go:
package main
import (
"fmt"
"time"
bpf "github.com/aquasecurity/libbpfgo"
)
func main() {
bpfModule, err := bpf.NewModuleFromFile("main.bpf.o")
if err != nil {
panic(err)
}
defer bpfModule.Close()
if err := bpfModule.BPFLoadObject(); err != nil {
panic(err)
}
prog, err := bpfModule.GetProgram("kprobe__do_sys_openat2")
if err != nil {
panic(err)
}
if _, err := prog.AttachKprobe("do_sys_openat2"); err != nil {
panic(err)
}
for {
fmt.Println("Waiting...")
time.Sleep(10 * time.Second)
}
}
As you can see from the go program above, there are four main steps in calling the ebpf program using libbpfgo:
- Read the compiled .o file with the bpf.NewModuleFromFile method.
- Use bpfModule.BPFLoadObject() to load the object information from the read .o file.
- Use bpfModule.GetProgram to get the corresponding hook function in the ebpf program.
- If the function is a kprobe hook, then the prog.AttachKprobe method is called to attach it to the corresponding kernel function hook.
Let's compile the program and then looks at the corresponding results:
$ vagrant ssh
$ cd $GOPATH/src/github.com/mozillazg/hello-libbpfgo/01-hello-world
$ make
$ sudo ./main
Open another terminal to see output:
$ vagrant ssh
$ sudo cat /sys/kernel/debug/tracing/trace_pipe
runc:[2:INIT]-100616 [000] d... 5527.233315: bpf_trace_printk: open file /proc/self/fd
runc:[2:INIT]-100616 [000] d... 5527.233641: bpf_trace_printk: open file /proc/self/status
runc:[2:INIT]-100616 [000] d... 5527.233802: bpf_trace_printk: open file /etc/passwd
runc:[2:INIT]-100616 [000] d... 5527.233829: bpf_trace_printk: open file /etc/group
You can check out full codes on Github: https://github.com/mozillazg/hello-libbpfgo/tree/master/01-hello-world
References¶
- How to Build eBPF Programs with libbpfgo
- aquasecurity/btfhub: BTFHub, together with BTFHub Archive repository, provides BTF files for existing published kernels that don't support embedded BTF.
- aquasecurity/libbpfgo: eBPF library for Go, wrapping libbpf
- aquasecurity/tracee: Linux Runtime Security and Forensics using eBPF
Comments