A nice and convenient way to work with eBPF
programs / perf events from Go.
- Go 1.11+
- Linux Kernel 4.15+
- eBPF programs
SocketFilter
XDP
Kprobe
/Kretprobe
tc-cls
(tc-act
is partially implemented, currently)
- Perf Events
Support for other program types / features can be added in future. Meanwhile your contributions are warmly welcomed.. :)
# Main library
go get github.com/dropbox/goebpf
# Mock version (if needed)
go get github.com/dropbox/goebpf/goebpf_mock
Consider very simple example of Read / Load / Attach
// In order to be simple this examples does not handle errors
bpf := goebpf.NewDefaultEbpfSystem()
// Read clang compiled binary
bpf.LoadElf("test.elf")
// Load XDP program into kernel (name matches function name in C)
xdp := bpf.GetProgramByName("xdp_test")
xdp.Load()
// Attach to interface
xdp.Attach("eth0")
defer xdp.Detach()
// Work with maps
test := bpf.GetMapByName("test")
value, _ := test.LookupInt(0)
fmt.Printf("Value at index 0 of map 'test': %d\n", value)
Like it? Check our examples
Currently library has support for one, most popular use case of perf_events: where eBPF
map key maps to cpu_id
.
So eBPF
and go
parts actually bind cpu_id
to map index. It maybe as simple as:
// Define special, perf_events map where key maps to CPU_ID
BPF_MAP_DEF(perfmap) = {
.map_type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.max_entries = 128, // Max supported CPUs
};
BPF_MAP_ADD(perfmap);
// ...
// Emit perf event with "data" to map "perfmap" where index is current CPU_ID
bpf_perf_event_output(ctx, &perfmap, BPF_F_CURRENT_CPU, &data, sizeof(data));
And the go
part:
perf, err := goebpf.NewPerfEvents("perfmap")
// 4096 is ring buffer size
perfEvents, err := perf.StartForAllProcessesAndCPUs(4096)
defer perf.Stop()
for {
select {
case data := <-perfEvents:
fmt.Println(data)
}
}
Looks simple? Check our full XDP dump example
Library currently has support for kprobes
and kretprobes
.
It can be as simple as:
// kprobe handler function
SEC("kprobe/guess_execve")
int execve_entry(struct pt_regs *ctx) {
// ...
buf_perf_output(ctx);
return 0;
}
And the go
part:
// Cleanup old probes
err := goebpf.CleanupProbes()
// Attach all probe programs
for _, prog := range bpf.GetPrograms() {
err := prog.Attach(nil)
}
// Create perf events
eventsMap := p.bpf.GetMapByName("events")
p.pe, err = goebpf.NewPerfEvents(eventsMap)
events, err := p.pe.StartForAllProcessesAndCPUs(4096)
defer events.Stop()
for {
select {
case data := <-events:
fmt.Println(data) // kProbe event
}
}
Simple? Check exec dump example