whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# eBPF Examples
|
||||
|
||||
* Kprobe - Attach a program to the entry or exit of an arbitrary kernel symbol (function).
|
||||
* [kprobe](kprobe/) - Kprobe using bpf2go.
|
||||
* [kprobepin](kprobepin/) - Reuse a pinned map for the kprobe example. It assumes the BPF FS is mounted at `/sys/fs/bpf`.
|
||||
* [kprobe_percpu](kprobe_percpu/) - Use a `BPF_MAP_TYPE_PERCPU_ARRAY` map.
|
||||
* [ringbuffer](ringbuffer/) - Use a `BPF_MAP_TYPE_RINGBUF` map.
|
||||
* Uprobe - Attach a program to the entry or exit of an arbitrary userspace binary symbol (function).
|
||||
* [uretprobe](uretprobe/) - Uretprobe using bpf2go.
|
||||
* Tracepoint - Attach a program to predetermined kernel tracepoints.
|
||||
* [tracepoint_in_c](tracepoint_in_c/) - Tracepoint using bpf2go.
|
||||
* [tracepoint_in_go](tracepoint_in_go/) - Tracepoint using the `ebpf.NewProgram` API and Go eBPF assembler.
|
||||
* Cgroup - Attach a program to control groups (cgroups).
|
||||
* [cgroup_skb](cgroup_skb/) - Count packets egressing the current cgroup.
|
||||
* Fentry - Attach a program to the entrypoint of a kernel function.
|
||||
Like kprobes, but with better performance and usability, for kernels 5.5 and later.
|
||||
* [tcp_connect](fentry/) - Trace outgoing IPv4 TCP connections.
|
||||
* [tcp_close](tcprtt/) - Log RTT of IPv4 TCP connections using eBPF CO-RE helpers.
|
||||
* XDP - Attach a program to a network interface to process incoming packets.
|
||||
* [xdp](xdp/) - Print packet counts by IPv4 source address.
|
||||
* Add your use case(s) here!
|
||||
|
||||
## How to run
|
||||
|
||||
```bash
|
||||
cd ebpf/examples/
|
||||
go run -exec sudo [./kprobe, ./uretprobe, ./ringbuffer, ...]
|
||||
```
|
||||
|
||||
## How to recompile
|
||||
|
||||
The examples are built via `go generate` invoked by the Makefile in the project root.
|
||||
|
||||
```
|
||||
make -C ../
|
||||
```
|
||||
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
CountEgressPackets *ebpf.ProgramSpec `ebpf:"count_egress_packets"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
PktCount *ebpf.MapSpec `ebpf:"pkt_count"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
PktCount *ebpf.Map `ebpf:"pkt_count"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.PktCount,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
CountEgressPackets *ebpf.Program `ebpf:"count_egress_packets"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.CountEgressPackets,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
CountEgressPackets *ebpf.ProgramSpec `ebpf:"count_egress_packets"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
PktCount *ebpf.MapSpec `ebpf:"pkt_count"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
PktCount *ebpf.Map `ebpf:"pkt_count"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.PktCount,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
CountEgressPackets *ebpf.Program `ebpf:"count_egress_packets"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.CountEgressPackets,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,27 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "common.h"
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
struct bpf_map_def SEC("maps") pkt_count = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(u32),
|
||||
.value_size = sizeof(u64),
|
||||
.max_entries = 1,
|
||||
};
|
||||
|
||||
SEC("cgroup_skb/egress")
|
||||
int count_egress_packets(struct __sk_buff *skb) {
|
||||
u32 key = 0;
|
||||
u64 init_val = 1;
|
||||
|
||||
u64 *count = bpf_map_lookup_elem(&pkt_count, &key);
|
||||
if (!count) {
|
||||
bpf_map_update_elem(&pkt_count, &key, &init_val, BPF_ANY);
|
||||
return 1;
|
||||
}
|
||||
__sync_fetch_and_add(count, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// This program demonstrates attaching an eBPF program to a control group.
|
||||
// The eBPF program will be attached as an egress filter,
|
||||
// receiving an `__sk_buff` pointer for each outgoing packet.
|
||||
// It prints the count of total packets every second.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf cgroup_skb.c -- -I../headers
|
||||
|
||||
func main() {
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
log.Fatalf("loading objects: %v", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
// Get the first-mounted cgroupv2 path.
|
||||
cgroupPath, err := detectCgroupPath()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Link the count_egress_packets program to the cgroup.
|
||||
l, err := link.AttachCgroup(link.CgroupOptions{
|
||||
Path: cgroupPath,
|
||||
Attach: ebpf.AttachCGroupInetEgress,
|
||||
Program: objs.CountEgressPackets,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
log.Println("Counting packets...")
|
||||
|
||||
// Read loop reporting the total amount of times the kernel
|
||||
// function was entered, once per second.
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
var value uint64
|
||||
if err := objs.PktCount.Lookup(uint32(0), &value); err != nil {
|
||||
log.Fatalf("reading map: %v", err)
|
||||
}
|
||||
log.Printf("number of packets: %d\n", value)
|
||||
}
|
||||
}
|
||||
|
||||
// detectCgroupPath returns the first-found mount point of type cgroup2
|
||||
// and stores it in the cgroupPath global variable.
|
||||
func detectCgroupPath() (string, error) {
|
||||
f, err := os.Open("/proc/mounts")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
// example fields: cgroup2 /sys/fs/cgroup/unified cgroup2 rw,nosuid,nodev,noexec,relatime 0 0
|
||||
fields := strings.Split(scanner.Text(), " ")
|
||||
if len(fields) >= 3 && fields[2] == "cgroup2" {
|
||||
return fields[1], nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("cgroup2 not mounted")
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfEvent struct {
|
||||
Comm [16]uint8
|
||||
Sport uint16
|
||||
Dport uint16
|
||||
Saddr uint32
|
||||
Daddr uint32
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
TcpConnect *ebpf.ProgramSpec `ebpf:"tcp_connect"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
Events *ebpf.MapSpec `ebpf:"events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
Events *ebpf.Map `ebpf:"events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.Events,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
TcpConnect *ebpf.Program `ebpf:"tcp_connect"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.TcpConnect,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,127 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfEvent struct {
|
||||
Comm [16]uint8
|
||||
Sport uint16
|
||||
Dport uint16
|
||||
Saddr uint32
|
||||
Daddr uint32
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
TcpConnect *ebpf.ProgramSpec `ebpf:"tcp_connect"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
Events *ebpf.MapSpec `ebpf:"events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
Events *ebpf.Map `ebpf:"events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.Events,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
TcpConnect *ebpf.Program `ebpf:"tcp_connect"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.TcpConnect,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,98 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "bpf_endian.h"
|
||||
#include "bpf_tracing.h"
|
||||
|
||||
#define AF_INET 2
|
||||
#define TASK_COMM_LEN 16
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
/**
|
||||
* This example copies parts of struct sock_common and struct sock from
|
||||
* the Linux kernel, but doesn't cause any CO-RE information to be emitted
|
||||
* into the ELF object. This requires the struct layout (up until the fields
|
||||
* that are being accessed) to match the kernel's, and the example will break
|
||||
* or misbehave when this is no longer the case.
|
||||
*
|
||||
* Also note that BTF-enabled programs like fentry, fexit, fmod_ret, tp_btf,
|
||||
* lsm, etc. declared using the BPF_PROG macro can read kernel memory without
|
||||
* needing to call bpf_probe_read*().
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct sock_common reflects the start of the kernel's struct sock_common.
|
||||
* It only contains the fields up until skc_family that are accessed in the
|
||||
* program, with padding to match the kernel's declaration.
|
||||
*/
|
||||
struct sock_common {
|
||||
union {
|
||||
struct {
|
||||
__be32 skc_daddr;
|
||||
__be32 skc_rcv_saddr;
|
||||
};
|
||||
};
|
||||
union {
|
||||
// Padding out union skc_hash.
|
||||
__u32 _;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
__be16 skc_dport;
|
||||
__u16 skc_num;
|
||||
};
|
||||
};
|
||||
short unsigned int skc_family;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sock reflects the start of the kernel's struct sock.
|
||||
*/
|
||||
struct sock {
|
||||
struct sock_common __sk_common;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 1 << 24);
|
||||
} events SEC(".maps");
|
||||
|
||||
/**
|
||||
* The sample submitted to userspace over a ring buffer.
|
||||
* Emit struct event's type info into the ELF's BTF so bpf2go
|
||||
* can generate a Go type from it.
|
||||
*/
|
||||
struct event {
|
||||
u8 comm[16];
|
||||
__u16 sport;
|
||||
__be16 dport;
|
||||
__be32 saddr;
|
||||
__be32 daddr;
|
||||
};
|
||||
struct event *unused __attribute__((unused));
|
||||
|
||||
SEC("fentry/tcp_connect")
|
||||
int BPF_PROG(tcp_connect, struct sock *sk) {
|
||||
if (sk->__sk_common.skc_family != AF_INET) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct event *tcp_info;
|
||||
tcp_info = bpf_ringbuf_reserve(&events, sizeof(struct event), 0);
|
||||
if (!tcp_info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tcp_info->saddr = sk->__sk_common.skc_rcv_saddr;
|
||||
tcp_info->daddr = sk->__sk_common.skc_daddr;
|
||||
tcp_info->dport = sk->__sk_common.skc_dport;
|
||||
tcp_info->sport = bpf_htons(sk->__sk_common.skc_num);
|
||||
|
||||
bpf_get_current_comm(&tcp_info->comm, TASK_COMM_LEN);
|
||||
|
||||
bpf_ringbuf_submit(tcp_info, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// This program demonstrates attaching a fentry eBPF program to
|
||||
// tcp_connect. It prints the command/IPs/ports information
|
||||
// once the host sent a TCP SYN packet to a destination.
|
||||
// It supports IPv4 at this example.
|
||||
//
|
||||
// Sample output:
|
||||
//
|
||||
// examples# go run -exec sudo ./fentry
|
||||
// 2021/11/06 17:51:15 Comm Src addr Port -> Dest addr Port
|
||||
// 2021/11/06 17:51:25 wget 10.0.2.15 49850 -> 142.250.72.228 443
|
||||
// 2021/11/06 17:51:46 ssh 10.0.2.15 58854 -> 10.0.2.1 22
|
||||
// 2021/11/06 18:13:15 curl 10.0.2.15 54268 -> 104.21.1.217 80
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/ringbuf"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -type event bpf fentry.c -- -I../headers
|
||||
|
||||
func main() {
|
||||
stopper := make(chan os.Signal, 1)
|
||||
signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
log.Fatalf("loading objects: %v", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
link, err := link.AttachTracing(link.TracingOptions{
|
||||
Program: objs.bpfPrograms.TcpConnect,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer link.Close()
|
||||
|
||||
rd, err := ringbuf.NewReader(objs.bpfMaps.Events)
|
||||
if err != nil {
|
||||
log.Fatalf("opening ringbuf reader: %s", err)
|
||||
}
|
||||
defer rd.Close()
|
||||
|
||||
go func() {
|
||||
<-stopper
|
||||
|
||||
if err := rd.Close(); err != nil {
|
||||
log.Fatalf("closing ringbuf reader: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf("%-16s %-15s %-6s -> %-15s %-6s",
|
||||
"Comm",
|
||||
"Src addr",
|
||||
"Port",
|
||||
"Dest addr",
|
||||
"Port",
|
||||
)
|
||||
|
||||
// bpfEvent is generated by bpf2go.
|
||||
var event bpfEvent
|
||||
for {
|
||||
record, err := rd.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, ringbuf.ErrClosed) {
|
||||
log.Println("received signal, exiting..")
|
||||
return
|
||||
}
|
||||
log.Printf("reading from reader: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse the ringbuf event entry into a bpfEvent structure.
|
||||
if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.BigEndian, &event); err != nil {
|
||||
log.Printf("parsing ringbuf event: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("%-16s %-15s %-6d -> %-15s %-6d",
|
||||
event.Comm,
|
||||
intToIP(event.Saddr),
|
||||
event.Sport,
|
||||
intToIP(event.Daddr),
|
||||
event.Dport,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// intToIP converts IPv4 number to net.IP
|
||||
func intToIP(ipNum uint32) net.IP {
|
||||
ip := make(net.IP, 4)
|
||||
binary.BigEndian.PutUint32(ip, ipNum)
|
||||
return ip
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
Valid-License-Identifier: BSD-2-Clause
|
||||
SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html
|
||||
Usage-Guide:
|
||||
To use the BSD 2-clause "Simplified" License put the following SPDX
|
||||
tag/value pair into a comment according to the placement guidelines in
|
||||
the licensing rules documentation:
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
License-Text:
|
||||
|
||||
Copyright (c) <year> <owner> . All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,99 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_ENDIAN__
|
||||
#define __BPF_ENDIAN__
|
||||
|
||||
/*
|
||||
* Isolate byte #n and put it into byte #m, for __u##b type.
|
||||
* E.g., moving byte #6 (nnnnnnnn) into byte #1 (mmmmmmmm) for __u64:
|
||||
* 1) xxxxxxxx nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx
|
||||
* 2) nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 00000000
|
||||
* 3) 00000000 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn
|
||||
* 4) 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 00000000
|
||||
*/
|
||||
#define ___bpf_mvb(x, b, n, m) ((__u##b)(x) << (b-(n+1)*8) >> (b-8) << (m*8))
|
||||
|
||||
#define ___bpf_swab16(x) ((__u16)( \
|
||||
___bpf_mvb(x, 16, 0, 1) | \
|
||||
___bpf_mvb(x, 16, 1, 0)))
|
||||
|
||||
#define ___bpf_swab32(x) ((__u32)( \
|
||||
___bpf_mvb(x, 32, 0, 3) | \
|
||||
___bpf_mvb(x, 32, 1, 2) | \
|
||||
___bpf_mvb(x, 32, 2, 1) | \
|
||||
___bpf_mvb(x, 32, 3, 0)))
|
||||
|
||||
#define ___bpf_swab64(x) ((__u64)( \
|
||||
___bpf_mvb(x, 64, 0, 7) | \
|
||||
___bpf_mvb(x, 64, 1, 6) | \
|
||||
___bpf_mvb(x, 64, 2, 5) | \
|
||||
___bpf_mvb(x, 64, 3, 4) | \
|
||||
___bpf_mvb(x, 64, 4, 3) | \
|
||||
___bpf_mvb(x, 64, 5, 2) | \
|
||||
___bpf_mvb(x, 64, 6, 1) | \
|
||||
___bpf_mvb(x, 64, 7, 0)))
|
||||
|
||||
/* LLVM's BPF target selects the endianness of the CPU
|
||||
* it compiles on, or the user specifies (bpfel/bpfeb),
|
||||
* respectively. The used __BYTE_ORDER__ is defined by
|
||||
* the compiler, we cannot rely on __BYTE_ORDER from
|
||||
* libc headers, since it doesn't reflect the actual
|
||||
* requested byte order.
|
||||
*
|
||||
* Note, LLVM's BPF target has different __builtin_bswapX()
|
||||
* semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE
|
||||
* in bpfel and bpfeb case, which means below, that we map
|
||||
* to cpu_to_be16(). We could use it unconditionally in BPF
|
||||
* case, but better not rely on it, so that this header here
|
||||
* can be used from application and BPF program side, which
|
||||
* use different targets.
|
||||
*/
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define __bpf_ntohs(x) __builtin_bswap16(x)
|
||||
# define __bpf_htons(x) __builtin_bswap16(x)
|
||||
# define __bpf_constant_ntohs(x) ___bpf_swab16(x)
|
||||
# define __bpf_constant_htons(x) ___bpf_swab16(x)
|
||||
# define __bpf_ntohl(x) __builtin_bswap32(x)
|
||||
# define __bpf_htonl(x) __builtin_bswap32(x)
|
||||
# define __bpf_constant_ntohl(x) ___bpf_swab32(x)
|
||||
# define __bpf_constant_htonl(x) ___bpf_swab32(x)
|
||||
# define __bpf_be64_to_cpu(x) __builtin_bswap64(x)
|
||||
# define __bpf_cpu_to_be64(x) __builtin_bswap64(x)
|
||||
# define __bpf_constant_be64_to_cpu(x) ___bpf_swab64(x)
|
||||
# define __bpf_constant_cpu_to_be64(x) ___bpf_swab64(x)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define __bpf_ntohs(x) (x)
|
||||
# define __bpf_htons(x) (x)
|
||||
# define __bpf_constant_ntohs(x) (x)
|
||||
# define __bpf_constant_htons(x) (x)
|
||||
# define __bpf_ntohl(x) (x)
|
||||
# define __bpf_htonl(x) (x)
|
||||
# define __bpf_constant_ntohl(x) (x)
|
||||
# define __bpf_constant_htonl(x) (x)
|
||||
# define __bpf_be64_to_cpu(x) (x)
|
||||
# define __bpf_cpu_to_be64(x) (x)
|
||||
# define __bpf_constant_be64_to_cpu(x) (x)
|
||||
# define __bpf_constant_cpu_to_be64(x) (x)
|
||||
#else
|
||||
# error "Fix your compiler's __BYTE_ORDER__?!"
|
||||
#endif
|
||||
|
||||
#define bpf_htons(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_htons(x) : __bpf_htons(x))
|
||||
#define bpf_ntohs(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_ntohs(x) : __bpf_ntohs(x))
|
||||
#define bpf_htonl(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_htonl(x) : __bpf_htonl(x))
|
||||
#define bpf_ntohl(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_ntohl(x) : __bpf_ntohl(x))
|
||||
#define bpf_cpu_to_be64(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x))
|
||||
#define bpf_be64_to_cpu(x) \
|
||||
(__builtin_constant_p(x) ? \
|
||||
__bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x))
|
||||
|
||||
#endif /* __BPF_ENDIAN__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,262 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_HELPERS__
|
||||
#define __BPF_HELPERS__
|
||||
|
||||
/*
|
||||
* Note that bpf programs need to include either
|
||||
* vmlinux.h (auto-generated from BTF) or linux/types.h
|
||||
* in advance since bpf_helper_defs.h uses such types
|
||||
* as __u64.
|
||||
*/
|
||||
#include "bpf_helper_defs.h"
|
||||
|
||||
#define __uint(name, val) int (*name)[val]
|
||||
#define __type(name, val) typeof(val) *name
|
||||
#define __array(name, val) typeof(val) *name[]
|
||||
|
||||
/*
|
||||
* Helper macro to place programs, maps, license in
|
||||
* different sections in elf_bpf file. Section names
|
||||
* are interpreted by libbpf depending on the context (BPF programs, BPF maps,
|
||||
* extern variables, etc).
|
||||
* To allow use of SEC() with externs (e.g., for extern .maps declarations),
|
||||
* make sure __attribute__((unused)) doesn't trigger compilation warning.
|
||||
*/
|
||||
#define SEC(name) \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \
|
||||
__attribute__((section(name), used)) \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
|
||||
/* Avoid 'linux/stddef.h' definition of '__always_inline'. */
|
||||
#undef __always_inline
|
||||
#define __always_inline inline __attribute__((always_inline))
|
||||
|
||||
#ifndef __noinline
|
||||
#define __noinline __attribute__((noinline))
|
||||
#endif
|
||||
#ifndef __weak
|
||||
#define __weak __attribute__((weak))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use __hidden attribute to mark a non-static BPF subprogram effectively
|
||||
* static for BPF verifier's verification algorithm purposes, allowing more
|
||||
* extensive and permissive BPF verification process, taking into account
|
||||
* subprogram's caller context.
|
||||
*/
|
||||
#define __hidden __attribute__((visibility("hidden")))
|
||||
|
||||
/* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include
|
||||
* any system-level headers (such as stddef.h, linux/version.h, etc), and
|
||||
* commonly-used macros like NULL and KERNEL_VERSION aren't available through
|
||||
* vmlinux.h. This just adds unnecessary hurdles and forces users to re-define
|
||||
* them on their own. So as a convenience, provide such definitions here.
|
||||
*/
|
||||
#ifndef NULL
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_VERSION
|
||||
#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c)))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper macros to manipulate data structures
|
||||
*/
|
||||
#ifndef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
#ifndef container_of
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
void *__mptr = (void *)(ptr); \
|
||||
((type *)(__mptr - offsetof(type, member))); \
|
||||
})
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper macro to throw a compilation error if __bpf_unreachable() gets
|
||||
* built into the resulting code. This works given BPF back end does not
|
||||
* implement __builtin_trap(). This is useful to assert that certain paths
|
||||
* of the program code are never used and hence eliminated by the compiler.
|
||||
*
|
||||
* For example, consider a switch statement that covers known cases used by
|
||||
* the program. __bpf_unreachable() can then reside in the default case. If
|
||||
* the program gets extended such that a case is not covered in the switch
|
||||
* statement, then it will throw a build error due to the default case not
|
||||
* being compiled out.
|
||||
*/
|
||||
#ifndef __bpf_unreachable
|
||||
# define __bpf_unreachable() __builtin_trap()
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper function to perform a tail call with a constant/immediate map slot.
|
||||
*/
|
||||
#if __clang_major__ >= 8 && defined(__bpf__)
|
||||
static __always_inline void
|
||||
bpf_tail_call_static(void *ctx, const void *map, const __u32 slot)
|
||||
{
|
||||
if (!__builtin_constant_p(slot))
|
||||
__bpf_unreachable();
|
||||
|
||||
/*
|
||||
* Provide a hard guarantee that LLVM won't optimize setting r2 (map
|
||||
* pointer) and r3 (constant map index) from _different paths_ ending
|
||||
* up at the _same_ call insn as otherwise we won't be able to use the
|
||||
* jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel
|
||||
* given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key
|
||||
* tracking for prog array pokes") for details on verifier tracking.
|
||||
*
|
||||
* Note on clobber list: we need to stay in-line with BPF calling
|
||||
* convention, so even if we don't end up using r0, r4, r5, we need
|
||||
* to mark them as clobber so that LLVM doesn't end up using them
|
||||
* before / after the call.
|
||||
*/
|
||||
asm volatile("r1 = %[ctx]\n\t"
|
||||
"r2 = %[map]\n\t"
|
||||
"r3 = %[slot]\n\t"
|
||||
"call 12"
|
||||
:: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot)
|
||||
: "r0", "r1", "r2", "r3", "r4", "r5");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper structure used by eBPF C program
|
||||
* to describe BPF map attributes to libbpf loader
|
||||
*/
|
||||
struct bpf_map_def {
|
||||
unsigned int type;
|
||||
unsigned int key_size;
|
||||
unsigned int value_size;
|
||||
unsigned int max_entries;
|
||||
unsigned int map_flags;
|
||||
};
|
||||
|
||||
enum libbpf_pin_type {
|
||||
LIBBPF_PIN_NONE,
|
||||
/* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
|
||||
LIBBPF_PIN_BY_NAME,
|
||||
};
|
||||
|
||||
enum libbpf_tristate {
|
||||
TRI_NO = 0,
|
||||
TRI_YES = 1,
|
||||
TRI_MODULE = 2,
|
||||
};
|
||||
|
||||
#define __kconfig __attribute__((section(".kconfig")))
|
||||
#define __ksym __attribute__((section(".ksyms")))
|
||||
|
||||
#ifndef ___bpf_concat
|
||||
#define ___bpf_concat(a, b) a ## b
|
||||
#endif
|
||||
#ifndef ___bpf_apply
|
||||
#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
|
||||
#endif
|
||||
#ifndef ___bpf_nth
|
||||
#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
|
||||
#endif
|
||||
#ifndef ___bpf_narg
|
||||
#define ___bpf_narg(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#endif
|
||||
|
||||
#define ___bpf_fill0(arr, p, x) do {} while (0)
|
||||
#define ___bpf_fill1(arr, p, x) arr[p] = x
|
||||
#define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args)
|
||||
#define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args)
|
||||
#define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args)
|
||||
#define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args)
|
||||
#define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args)
|
||||
#define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args)
|
||||
#define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args)
|
||||
#define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args)
|
||||
#define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args)
|
||||
#define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args)
|
||||
#define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args)
|
||||
#define ___bpf_fill(arr, args...) \
|
||||
___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args)
|
||||
|
||||
/*
|
||||
* BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values
|
||||
* in a structure.
|
||||
*/
|
||||
#define BPF_SEQ_PRINTF(seq, fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
/*
|
||||
* BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of
|
||||
* an array of u64.
|
||||
*/
|
||||
#define BPF_SNPRINTF(out, out_size, fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_snprintf(out, out_size, ___fmt, \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
#ifdef BPF_NO_GLOBAL_DATA
|
||||
#define BPF_PRINTK_FMT_MOD
|
||||
#else
|
||||
#define BPF_PRINTK_FMT_MOD static const
|
||||
#endif
|
||||
|
||||
#define __bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
BPF_PRINTK_FMT_MOD char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/*
|
||||
* __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments
|
||||
* instead of an array of u64.
|
||||
*/
|
||||
#define __bpf_vprintk(fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_trace_vprintk(___fmt, sizeof(___fmt), \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
/* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args
|
||||
* Otherwise use __bpf_vprintk
|
||||
*/
|
||||
#define ___bpf_pick_printk(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \
|
||||
__bpf_vprintk, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \
|
||||
__bpf_vprintk, __bpf_vprintk, __bpf_printk /*3*/, __bpf_printk /*2*/,\
|
||||
__bpf_printk /*1*/, __bpf_printk /*0*/)
|
||||
|
||||
/* Helper macro to print out debug messages */
|
||||
#define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,492 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_TRACING_H__
|
||||
#define __BPF_TRACING_H__
|
||||
|
||||
/* Scan the ARCH passed in from ARCH env variable (see Makefile) */
|
||||
#if defined(__TARGET_ARCH_x86)
|
||||
#define bpf_target_x86
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_s390)
|
||||
#define bpf_target_s390
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arm)
|
||||
#define bpf_target_arm
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arm64)
|
||||
#define bpf_target_arm64
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_mips)
|
||||
#define bpf_target_mips
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_powerpc)
|
||||
#define bpf_target_powerpc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_sparc)
|
||||
#define bpf_target_sparc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_riscv)
|
||||
#define bpf_target_riscv
|
||||
#define bpf_target_defined
|
||||
#else
|
||||
|
||||
/* Fall back to what the compiler says */
|
||||
#if defined(__x86_64__)
|
||||
#define bpf_target_x86
|
||||
#define bpf_target_defined
|
||||
#elif defined(__s390__)
|
||||
#define bpf_target_s390
|
||||
#define bpf_target_defined
|
||||
#elif defined(__arm__)
|
||||
#define bpf_target_arm
|
||||
#define bpf_target_defined
|
||||
#elif defined(__aarch64__)
|
||||
#define bpf_target_arm64
|
||||
#define bpf_target_defined
|
||||
#elif defined(__mips__)
|
||||
#define bpf_target_mips
|
||||
#define bpf_target_defined
|
||||
#elif defined(__powerpc__)
|
||||
#define bpf_target_powerpc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__sparc__)
|
||||
#define bpf_target_sparc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
#define bpf_target_riscv
|
||||
#define bpf_target_defined
|
||||
#endif /* no compiler target */
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __BPF_TARGET_MISSING
|
||||
#define __BPF_TARGET_MISSING "GCC error \"Must specify a BPF target arch via __TARGET_ARCH_xxx\""
|
||||
#endif
|
||||
|
||||
#if defined(bpf_target_x86)
|
||||
|
||||
#if defined(__KERNEL__) || defined(__VMLINUX_H__)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->di)
|
||||
#define PT_REGS_PARM2(x) ((x)->si)
|
||||
#define PT_REGS_PARM3(x) ((x)->dx)
|
||||
#define PT_REGS_PARM4(x) ((x)->cx)
|
||||
#define PT_REGS_PARM5(x) ((x)->r8)
|
||||
#define PT_REGS_RET(x) ((x)->sp)
|
||||
#define PT_REGS_FP(x) ((x)->bp)
|
||||
#define PT_REGS_RC(x) ((x)->ax)
|
||||
#define PT_REGS_SP(x) ((x)->sp)
|
||||
#define PT_REGS_IP(x) ((x)->ip)
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), di)
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), si)
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), dx)
|
||||
#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), cx)
|
||||
#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), r8)
|
||||
#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), sp)
|
||||
#define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), bp)
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), ax)
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), sp)
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), ip)
|
||||
|
||||
#else
|
||||
|
||||
#ifdef __i386__
|
||||
/* i386 kernel is built with -mregparm=3 */
|
||||
#define PT_REGS_PARM1(x) ((x)->eax)
|
||||
#define PT_REGS_PARM2(x) ((x)->edx)
|
||||
#define PT_REGS_PARM3(x) ((x)->ecx)
|
||||
#define PT_REGS_PARM4(x) 0
|
||||
#define PT_REGS_PARM5(x) 0
|
||||
#define PT_REGS_RET(x) ((x)->esp)
|
||||
#define PT_REGS_FP(x) ((x)->ebp)
|
||||
#define PT_REGS_RC(x) ((x)->eax)
|
||||
#define PT_REGS_SP(x) ((x)->esp)
|
||||
#define PT_REGS_IP(x) ((x)->eip)
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), eax)
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), edx)
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), ecx)
|
||||
#define PT_REGS_PARM4_CORE(x) 0
|
||||
#define PT_REGS_PARM5_CORE(x) 0
|
||||
#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), esp)
|
||||
#define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), ebp)
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), eax)
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), esp)
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), eip)
|
||||
|
||||
#else
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->rdi)
|
||||
#define PT_REGS_PARM2(x) ((x)->rsi)
|
||||
#define PT_REGS_PARM3(x) ((x)->rdx)
|
||||
#define PT_REGS_PARM4(x) ((x)->rcx)
|
||||
#define PT_REGS_PARM5(x) ((x)->r8)
|
||||
#define PT_REGS_RET(x) ((x)->rsp)
|
||||
#define PT_REGS_FP(x) ((x)->rbp)
|
||||
#define PT_REGS_RC(x) ((x)->rax)
|
||||
#define PT_REGS_SP(x) ((x)->rsp)
|
||||
#define PT_REGS_IP(x) ((x)->rip)
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), rdi)
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), rsi)
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), rdx)
|
||||
#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), rcx)
|
||||
#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), r8)
|
||||
#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), rsp)
|
||||
#define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), rbp)
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), rax)
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), rsp)
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), rip)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(bpf_target_s390)
|
||||
|
||||
/* s390 provides user_pt_regs instead of struct pt_regs to userspace */
|
||||
struct pt_regs;
|
||||
#define PT_REGS_S390 const volatile user_pt_regs
|
||||
#define PT_REGS_PARM1(x) (((PT_REGS_S390 *)(x))->gprs[2])
|
||||
#define PT_REGS_PARM2(x) (((PT_REGS_S390 *)(x))->gprs[3])
|
||||
#define PT_REGS_PARM3(x) (((PT_REGS_S390 *)(x))->gprs[4])
|
||||
#define PT_REGS_PARM4(x) (((PT_REGS_S390 *)(x))->gprs[5])
|
||||
#define PT_REGS_PARM5(x) (((PT_REGS_S390 *)(x))->gprs[6])
|
||||
#define PT_REGS_RET(x) (((PT_REGS_S390 *)(x))->gprs[14])
|
||||
/* Works only with CONFIG_FRAME_POINTER */
|
||||
#define PT_REGS_FP(x) (((PT_REGS_S390 *)(x))->gprs[11])
|
||||
#define PT_REGS_RC(x) (((PT_REGS_S390 *)(x))->gprs[2])
|
||||
#define PT_REGS_SP(x) (((PT_REGS_S390 *)(x))->gprs[15])
|
||||
#define PT_REGS_IP(x) (((PT_REGS_S390 *)(x))->psw.addr)
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[2])
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[3])
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[4])
|
||||
#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[5])
|
||||
#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[6])
|
||||
#define PT_REGS_RET_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[14])
|
||||
#define PT_REGS_FP_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[11])
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[2])
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[15])
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), psw.addr)
|
||||
|
||||
#elif defined(bpf_target_arm)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->uregs[0])
|
||||
#define PT_REGS_PARM2(x) ((x)->uregs[1])
|
||||
#define PT_REGS_PARM3(x) ((x)->uregs[2])
|
||||
#define PT_REGS_PARM4(x) ((x)->uregs[3])
|
||||
#define PT_REGS_PARM5(x) ((x)->uregs[4])
|
||||
#define PT_REGS_RET(x) ((x)->uregs[14])
|
||||
#define PT_REGS_FP(x) ((x)->uregs[11]) /* Works only with CONFIG_FRAME_POINTER */
|
||||
#define PT_REGS_RC(x) ((x)->uregs[0])
|
||||
#define PT_REGS_SP(x) ((x)->uregs[13])
|
||||
#define PT_REGS_IP(x) ((x)->uregs[12])
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), uregs[0])
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), uregs[1])
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), uregs[2])
|
||||
#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), uregs[3])
|
||||
#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), uregs[4])
|
||||
#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), uregs[14])
|
||||
#define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), uregs[11])
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), uregs[0])
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), uregs[13])
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), uregs[12])
|
||||
|
||||
#elif defined(bpf_target_arm64)
|
||||
|
||||
/* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */
|
||||
struct pt_regs;
|
||||
#define PT_REGS_ARM64 const volatile struct user_pt_regs
|
||||
#define PT_REGS_PARM1(x) (((PT_REGS_ARM64 *)(x))->regs[0])
|
||||
#define PT_REGS_PARM2(x) (((PT_REGS_ARM64 *)(x))->regs[1])
|
||||
#define PT_REGS_PARM3(x) (((PT_REGS_ARM64 *)(x))->regs[2])
|
||||
#define PT_REGS_PARM4(x) (((PT_REGS_ARM64 *)(x))->regs[3])
|
||||
#define PT_REGS_PARM5(x) (((PT_REGS_ARM64 *)(x))->regs[4])
|
||||
#define PT_REGS_RET(x) (((PT_REGS_ARM64 *)(x))->regs[30])
|
||||
/* Works only with CONFIG_FRAME_POINTER */
|
||||
#define PT_REGS_FP(x) (((PT_REGS_ARM64 *)(x))->regs[29])
|
||||
#define PT_REGS_RC(x) (((PT_REGS_ARM64 *)(x))->regs[0])
|
||||
#define PT_REGS_SP(x) (((PT_REGS_ARM64 *)(x))->sp)
|
||||
#define PT_REGS_IP(x) (((PT_REGS_ARM64 *)(x))->pc)
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[0])
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[1])
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[2])
|
||||
#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[3])
|
||||
#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[4])
|
||||
#define PT_REGS_RET_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[30])
|
||||
#define PT_REGS_FP_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[29])
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[0])
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), sp)
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), pc)
|
||||
|
||||
#elif defined(bpf_target_mips)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->regs[4])
|
||||
#define PT_REGS_PARM2(x) ((x)->regs[5])
|
||||
#define PT_REGS_PARM3(x) ((x)->regs[6])
|
||||
#define PT_REGS_PARM4(x) ((x)->regs[7])
|
||||
#define PT_REGS_PARM5(x) ((x)->regs[8])
|
||||
#define PT_REGS_RET(x) ((x)->regs[31])
|
||||
#define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */
|
||||
#define PT_REGS_RC(x) ((x)->regs[2])
|
||||
#define PT_REGS_SP(x) ((x)->regs[29])
|
||||
#define PT_REGS_IP(x) ((x)->cp0_epc)
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), regs[4])
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), regs[5])
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), regs[6])
|
||||
#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), regs[7])
|
||||
#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), regs[8])
|
||||
#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), regs[31])
|
||||
#define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), regs[30])
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), regs[2])
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), regs[29])
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), cp0_epc)
|
||||
|
||||
#elif defined(bpf_target_powerpc)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->gpr[3])
|
||||
#define PT_REGS_PARM2(x) ((x)->gpr[4])
|
||||
#define PT_REGS_PARM3(x) ((x)->gpr[5])
|
||||
#define PT_REGS_PARM4(x) ((x)->gpr[6])
|
||||
#define PT_REGS_PARM5(x) ((x)->gpr[7])
|
||||
#define PT_REGS_RC(x) ((x)->gpr[3])
|
||||
#define PT_REGS_SP(x) ((x)->sp)
|
||||
#define PT_REGS_IP(x) ((x)->nip)
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), gpr[3])
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), gpr[4])
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), gpr[5])
|
||||
#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), gpr[6])
|
||||
#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), gpr[7])
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), gpr[3])
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), sp)
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), nip)
|
||||
|
||||
#elif defined(bpf_target_sparc)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0])
|
||||
#define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1])
|
||||
#define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2])
|
||||
#define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3])
|
||||
#define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4])
|
||||
#define PT_REGS_RET(x) ((x)->u_regs[UREG_I7])
|
||||
#define PT_REGS_RC(x) ((x)->u_regs[UREG_I0])
|
||||
#define PT_REGS_SP(x) ((x)->u_regs[UREG_FP])
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I0])
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I1])
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I2])
|
||||
#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I3])
|
||||
#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I4])
|
||||
#define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I7])
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I0])
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), u_regs[UREG_FP])
|
||||
|
||||
/* Should this also be a bpf_target check for the sparc case? */
|
||||
#if defined(__arch64__)
|
||||
#define PT_REGS_IP(x) ((x)->tpc)
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), tpc)
|
||||
#else
|
||||
#define PT_REGS_IP(x) ((x)->pc)
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), pc)
|
||||
#endif
|
||||
|
||||
#elif defined(bpf_target_riscv)
|
||||
|
||||
struct pt_regs;
|
||||
#define PT_REGS_RV const volatile struct user_regs_struct
|
||||
#define PT_REGS_PARM1(x) (((PT_REGS_RV *)(x))->a0)
|
||||
#define PT_REGS_PARM2(x) (((PT_REGS_RV *)(x))->a1)
|
||||
#define PT_REGS_PARM3(x) (((PT_REGS_RV *)(x))->a2)
|
||||
#define PT_REGS_PARM4(x) (((PT_REGS_RV *)(x))->a3)
|
||||
#define PT_REGS_PARM5(x) (((PT_REGS_RV *)(x))->a4)
|
||||
#define PT_REGS_RET(x) (((PT_REGS_RV *)(x))->ra)
|
||||
#define PT_REGS_FP(x) (((PT_REGS_RV *)(x))->s5)
|
||||
#define PT_REGS_RC(x) (((PT_REGS_RV *)(x))->a5)
|
||||
#define PT_REGS_SP(x) (((PT_REGS_RV *)(x))->sp)
|
||||
#define PT_REGS_IP(x) (((PT_REGS_RV *)(x))->epc)
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a0)
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a1)
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a2)
|
||||
#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a3)
|
||||
#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a4)
|
||||
#define PT_REGS_RET_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), ra)
|
||||
#define PT_REGS_FP_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), fp)
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a5)
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), sp)
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), epc)
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(bpf_target_powerpc)
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; })
|
||||
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
|
||||
#elif defined(bpf_target_sparc)
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); })
|
||||
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
|
||||
#elif defined(bpf_target_defined)
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) \
|
||||
({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); })
|
||||
#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) \
|
||||
({ bpf_probe_read_kernel(&(ip), sizeof(ip), \
|
||||
(void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
|
||||
#endif
|
||||
|
||||
#if !defined(bpf_target_defined)
|
||||
|
||||
#define PT_REGS_PARM1(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM2(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM3(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM4(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM5(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RET(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_FP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RC(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_SP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_IP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM2_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM3_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM4_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM5_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RET_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_FP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RC_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_SP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_IP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#endif /* !defined(bpf_target_defined) */
|
||||
|
||||
#ifndef ___bpf_concat
|
||||
#define ___bpf_concat(a, b) a ## b
|
||||
#endif
|
||||
#ifndef ___bpf_apply
|
||||
#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
|
||||
#endif
|
||||
#ifndef ___bpf_nth
|
||||
#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
|
||||
#endif
|
||||
#ifndef ___bpf_narg
|
||||
#define ___bpf_narg(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#endif
|
||||
|
||||
#define ___bpf_ctx_cast0() ctx
|
||||
#define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0]
|
||||
#define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), (void *)ctx[1]
|
||||
#define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), (void *)ctx[2]
|
||||
#define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), (void *)ctx[3]
|
||||
#define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), (void *)ctx[4]
|
||||
#define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), (void *)ctx[5]
|
||||
#define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), (void *)ctx[6]
|
||||
#define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), (void *)ctx[7]
|
||||
#define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), (void *)ctx[8]
|
||||
#define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), (void *)ctx[9]
|
||||
#define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), (void *)ctx[10]
|
||||
#define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), (void *)ctx[11]
|
||||
#define ___bpf_ctx_cast(args...) \
|
||||
___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and
|
||||
* similar kinds of BPF programs, that accept input arguments as a single
|
||||
* pointer to untyped u64 array, where each u64 can actually be a typed
|
||||
* pointer or integer of different size. Instead of requring user to write
|
||||
* manual casts and work with array elements by index, BPF_PROG macro
|
||||
* allows user to declare a list of named and typed input arguments in the
|
||||
* same syntax as for normal C function. All the casting is hidden and
|
||||
* performed transparently, while user code can just assume working with
|
||||
* function arguments of specified type and name.
|
||||
*
|
||||
* Original raw context argument is preserved as well as 'ctx' argument.
|
||||
* This is useful when using BPF helpers that expect original context
|
||||
* as one of the parameters (e.g., for bpf_perf_event_output()).
|
||||
*/
|
||||
#define BPF_PROG(name, args...) \
|
||||
name(unsigned long long *ctx); \
|
||||
static __attribute__((always_inline)) typeof(name(0)) \
|
||||
____##name(unsigned long long *ctx, ##args); \
|
||||
typeof(name(0)) name(unsigned long long *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_ctx_cast(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __attribute__((always_inline)) typeof(name(0)) \
|
||||
____##name(unsigned long long *ctx, ##args)
|
||||
|
||||
struct pt_regs;
|
||||
|
||||
#define ___bpf_kprobe_args0() ctx
|
||||
#define ___bpf_kprobe_args1(x) \
|
||||
___bpf_kprobe_args0(), (void *)PT_REGS_PARM1(ctx)
|
||||
#define ___bpf_kprobe_args2(x, args...) \
|
||||
___bpf_kprobe_args1(args), (void *)PT_REGS_PARM2(ctx)
|
||||
#define ___bpf_kprobe_args3(x, args...) \
|
||||
___bpf_kprobe_args2(args), (void *)PT_REGS_PARM3(ctx)
|
||||
#define ___bpf_kprobe_args4(x, args...) \
|
||||
___bpf_kprobe_args3(args), (void *)PT_REGS_PARM4(ctx)
|
||||
#define ___bpf_kprobe_args5(x, args...) \
|
||||
___bpf_kprobe_args4(args), (void *)PT_REGS_PARM5(ctx)
|
||||
#define ___bpf_kprobe_args(args...) \
|
||||
___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_KPROBE serves the same purpose for kprobes as BPF_PROG for
|
||||
* tp_btf/fentry/fexit BPF programs. It hides the underlying platform-specific
|
||||
* low-level way of getting kprobe input arguments from struct pt_regs, and
|
||||
* provides a familiar typed and named function arguments syntax and
|
||||
* semantics of accessing kprobe input paremeters.
|
||||
*
|
||||
* Original struct pt_regs* context is preserved as 'ctx' argument. This might
|
||||
* be necessary when using BPF helpers like bpf_perf_event_output().
|
||||
*/
|
||||
#define BPF_KPROBE(name, args...) \
|
||||
name(struct pt_regs *ctx); \
|
||||
static __attribute__((always_inline)) typeof(name(0)) \
|
||||
____##name(struct pt_regs *ctx, ##args); \
|
||||
typeof(name(0)) name(struct pt_regs *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_kprobe_args(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __attribute__((always_inline)) typeof(name(0)) \
|
||||
____##name(struct pt_regs *ctx, ##args)
|
||||
|
||||
#define ___bpf_kretprobe_args0() ctx
|
||||
#define ___bpf_kretprobe_args1(x) \
|
||||
___bpf_kretprobe_args0(), (void *)PT_REGS_RC(ctx)
|
||||
#define ___bpf_kretprobe_args(args...) \
|
||||
___bpf_apply(___bpf_kretprobe_args, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_KRETPROBE is similar to BPF_KPROBE, except, it only provides optional
|
||||
* return value (in addition to `struct pt_regs *ctx`), but no input
|
||||
* arguments, because they will be clobbered by the time probed function
|
||||
* returns.
|
||||
*/
|
||||
#define BPF_KRETPROBE(name, args...) \
|
||||
name(struct pt_regs *ctx); \
|
||||
static __attribute__((always_inline)) typeof(name(0)) \
|
||||
____##name(struct pt_regs *ctx, ##args); \
|
||||
typeof(name(0)) name(struct pt_regs *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_kretprobe_args(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,148 @@
|
||||
// This is a compact version of `vmlinux.h` to be used in the examples using C code.
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef unsigned char __u8;
|
||||
typedef short int __s16;
|
||||
typedef short unsigned int __u16;
|
||||
typedef int __s32;
|
||||
typedef unsigned int __u32;
|
||||
typedef long long int __s64;
|
||||
typedef long long unsigned int __u64;
|
||||
typedef __u8 u8;
|
||||
typedef __s16 s16;
|
||||
typedef __u16 u16;
|
||||
typedef __s32 s32;
|
||||
typedef __u32 u32;
|
||||
typedef __s64 s64;
|
||||
typedef __u64 u64;
|
||||
typedef __u16 __le16;
|
||||
typedef __u16 __be16;
|
||||
typedef __u32 __be32;
|
||||
typedef __u64 __be64;
|
||||
typedef __u32 __wsum;
|
||||
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
enum bpf_map_type {
|
||||
BPF_MAP_TYPE_UNSPEC = 0,
|
||||
BPF_MAP_TYPE_HASH = 1,
|
||||
BPF_MAP_TYPE_ARRAY = 2,
|
||||
BPF_MAP_TYPE_PROG_ARRAY = 3,
|
||||
BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4,
|
||||
BPF_MAP_TYPE_PERCPU_HASH = 5,
|
||||
BPF_MAP_TYPE_PERCPU_ARRAY = 6,
|
||||
BPF_MAP_TYPE_STACK_TRACE = 7,
|
||||
BPF_MAP_TYPE_CGROUP_ARRAY = 8,
|
||||
BPF_MAP_TYPE_LRU_HASH = 9,
|
||||
BPF_MAP_TYPE_LRU_PERCPU_HASH = 10,
|
||||
BPF_MAP_TYPE_LPM_TRIE = 11,
|
||||
BPF_MAP_TYPE_ARRAY_OF_MAPS = 12,
|
||||
BPF_MAP_TYPE_HASH_OF_MAPS = 13,
|
||||
BPF_MAP_TYPE_DEVMAP = 14,
|
||||
BPF_MAP_TYPE_SOCKMAP = 15,
|
||||
BPF_MAP_TYPE_CPUMAP = 16,
|
||||
BPF_MAP_TYPE_XSKMAP = 17,
|
||||
BPF_MAP_TYPE_SOCKHASH = 18,
|
||||
BPF_MAP_TYPE_CGROUP_STORAGE = 19,
|
||||
BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 20,
|
||||
BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21,
|
||||
BPF_MAP_TYPE_QUEUE = 22,
|
||||
BPF_MAP_TYPE_STACK = 23,
|
||||
BPF_MAP_TYPE_SK_STORAGE = 24,
|
||||
BPF_MAP_TYPE_DEVMAP_HASH = 25,
|
||||
BPF_MAP_TYPE_STRUCT_OPS = 26,
|
||||
BPF_MAP_TYPE_RINGBUF = 27,
|
||||
BPF_MAP_TYPE_INODE_STORAGE = 28,
|
||||
};
|
||||
|
||||
enum xdp_action {
|
||||
XDP_ABORTED = 0,
|
||||
XDP_DROP = 1,
|
||||
XDP_PASS = 2,
|
||||
XDP_TX = 3,
|
||||
XDP_REDIRECT = 4,
|
||||
};
|
||||
|
||||
struct xdp_md {
|
||||
__u32 data;
|
||||
__u32 data_end;
|
||||
__u32 data_meta;
|
||||
__u32 ingress_ifindex;
|
||||
__u32 rx_queue_index;
|
||||
__u32 egress_ifindex;
|
||||
};
|
||||
|
||||
typedef __u16 __sum16;
|
||||
|
||||
#define ETH_P_IP 0x0800
|
||||
|
||||
struct ethhdr {
|
||||
unsigned char h_dest[6];
|
||||
unsigned char h_source[6];
|
||||
__be16 h_proto;
|
||||
};
|
||||
|
||||
struct iphdr {
|
||||
__u8 ihl: 4;
|
||||
__u8 version: 4;
|
||||
__u8 tos;
|
||||
__be16 tot_len;
|
||||
__be16 id;
|
||||
__be16 frag_off;
|
||||
__u8 ttl;
|
||||
__u8 protocol;
|
||||
__sum16 check;
|
||||
__be32 saddr;
|
||||
__be32 daddr;
|
||||
};
|
||||
|
||||
enum {
|
||||
BPF_ANY = 0,
|
||||
BPF_NOEXIST = 1,
|
||||
BPF_EXIST = 2,
|
||||
BPF_F_LOCK = 4,
|
||||
};
|
||||
|
||||
/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and
|
||||
* BPF_FUNC_perf_event_read_value flags.
|
||||
*/
|
||||
#define BPF_F_INDEX_MASK 0xffffffffULL
|
||||
#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK
|
||||
|
||||
#if defined(__TARGET_ARCH_x86)
|
||||
struct pt_regs {
|
||||
/*
|
||||
* C ABI says these regs are callee-preserved. They aren't saved on kernel entry
|
||||
* unless syscall needs a complete, fully filled "struct pt_regs".
|
||||
*/
|
||||
unsigned long r15;
|
||||
unsigned long r14;
|
||||
unsigned long r13;
|
||||
unsigned long r12;
|
||||
unsigned long rbp;
|
||||
unsigned long rbx;
|
||||
/* These regs are callee-clobbered. Always saved on kernel entry. */
|
||||
unsigned long r11;
|
||||
unsigned long r10;
|
||||
unsigned long r9;
|
||||
unsigned long r8;
|
||||
unsigned long rax;
|
||||
unsigned long rcx;
|
||||
unsigned long rdx;
|
||||
unsigned long rsi;
|
||||
unsigned long rdi;
|
||||
/*
|
||||
* On syscall entry, this is syscall#. On CPU exception, this is error code.
|
||||
* On hw interrupt, it's IRQ number:
|
||||
*/
|
||||
unsigned long orig_rax;
|
||||
/* Return frame for iretq */
|
||||
unsigned long rip;
|
||||
unsigned long cs;
|
||||
unsigned long eflags;
|
||||
unsigned long rsp;
|
||||
unsigned long ss;
|
||||
/* top of stack page */
|
||||
};
|
||||
#endif /* __TARGET_ARCH_x86 */
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Version of libbpf to fetch headers from
|
||||
LIBBPF_VERSION=0.6.1
|
||||
|
||||
# The headers we want
|
||||
prefix=libbpf-"$LIBBPF_VERSION"
|
||||
headers=(
|
||||
"$prefix"/LICENSE.BSD-2-Clause
|
||||
"$prefix"/src/bpf_endian.h
|
||||
"$prefix"/src/bpf_helper_defs.h
|
||||
"$prefix"/src/bpf_helpers.h
|
||||
"$prefix"/src/bpf_tracing.h
|
||||
)
|
||||
|
||||
# Fetch libbpf release and extract the desired headers
|
||||
curl -sL "https://github.com/libbpf/libbpf/archive/refs/tags/v${LIBBPF_VERSION}.tar.gz" | \
|
||||
tar -xz --xform='s#.*/##' "${headers[@]}"
|
||||
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
KprobeExecve *ebpf.ProgramSpec `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
KprobeMap *ebpf.MapSpec `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
KprobeMap *ebpf.Map `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.KprobeMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
KprobeExecve *ebpf.Program `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.KprobeExecve,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
KprobeExecve *ebpf.ProgramSpec `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
KprobeMap *ebpf.MapSpec `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
KprobeMap *ebpf.Map `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.KprobeMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
KprobeExecve *ebpf.Program `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.KprobeExecve,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,27 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "common.h"
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
struct bpf_map_def SEC("maps") kprobe_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(u32),
|
||||
.value_size = sizeof(u64),
|
||||
.max_entries = 1,
|
||||
};
|
||||
|
||||
SEC("kprobe/sys_execve")
|
||||
int kprobe_execve() {
|
||||
u32 key = 0;
|
||||
u64 initval = 1, *valp;
|
||||
|
||||
valp = bpf_map_lookup_elem(&kprobe_map, &key);
|
||||
if (!valp) {
|
||||
bpf_map_update_elem(&kprobe_map, &key, &initval, BPF_ANY);
|
||||
return 0;
|
||||
}
|
||||
__sync_fetch_and_add(valp, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// This program demonstrates attaching an eBPF program to a kernel symbol.
|
||||
// The eBPF program will be attached to the start of the sys_execve
|
||||
// kernel function and prints out the number of times it has been called
|
||||
// every second.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf kprobe.c -- -I../headers
|
||||
|
||||
const mapKey uint32 = 0
|
||||
|
||||
func main() {
|
||||
|
||||
// Name of the kernel function to trace.
|
||||
fn := "sys_execve"
|
||||
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
log.Fatalf("loading objects: %v", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
// Open a Kprobe at the entry point of the kernel function and attach the
|
||||
// pre-compiled program. Each time the kernel function enters, the program
|
||||
// will increment the execution counter by 1. The read loop below polls this
|
||||
// map value once per second.
|
||||
kp, err := link.Kprobe(fn, objs.KprobeExecve, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("opening kprobe: %s", err)
|
||||
}
|
||||
defer kp.Close()
|
||||
|
||||
// Read loop reporting the total amount of times the kernel
|
||||
// function was entered, once per second.
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
log.Println("Waiting for events..")
|
||||
|
||||
for range ticker.C {
|
||||
var value uint64
|
||||
if err := objs.KprobeMap.Lookup(mapKey, &value); err != nil {
|
||||
log.Fatalf("reading map: %v", err)
|
||||
}
|
||||
log.Printf("%s called %d times\n", fn, value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
KprobeExecve *ebpf.ProgramSpec `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
KprobeMap *ebpf.MapSpec `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
KprobeMap *ebpf.Map `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.KprobeMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
KprobeExecve *ebpf.Program `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.KprobeExecve,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
KprobeExecve *ebpf.ProgramSpec `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
KprobeMap *ebpf.MapSpec `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
KprobeMap *ebpf.Map `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.KprobeMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
KprobeExecve *ebpf.Program `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.KprobeExecve,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,27 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "common.h"
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
struct bpf_map_def SEC("maps") kprobe_map = {
|
||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
||||
.key_size = sizeof(u32),
|
||||
.value_size = sizeof(u64),
|
||||
.max_entries = 1,
|
||||
};
|
||||
|
||||
SEC("kprobe/sys_execve")
|
||||
int kprobe_execve() {
|
||||
u32 key = 0;
|
||||
u64 initval = 1, *valp;
|
||||
|
||||
valp = bpf_map_lookup_elem(&kprobe_map, &key);
|
||||
if (!valp) {
|
||||
bpf_map_update_elem(&kprobe_map, &key, &initval, BPF_ANY);
|
||||
return 0;
|
||||
}
|
||||
__sync_fetch_and_add(valp, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// This program demonstrates attaching an eBPF program to a kernel symbol and
|
||||
// using percpu map to collect data. The eBPF program will be attached to the
|
||||
// start of the sys_execve kernel function and prints out the number of called
|
||||
// times on each cpu every second.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf kprobe_percpu.c -- -I../headers
|
||||
|
||||
const mapKey uint32 = 0
|
||||
|
||||
func main() {
|
||||
|
||||
// Name of the kernel function to trace.
|
||||
fn := "sys_execve"
|
||||
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
log.Fatalf("loading objects: %v", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
// Open a Kprobe at the entry point of the kernel function and attach the
|
||||
// pre-compiled program. Each time the kernel function enters, the program
|
||||
// will increment the execution counter by 1. The read loop below polls this
|
||||
// map value once per second.
|
||||
kp, err := link.Kprobe(fn, objs.KprobeExecve, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("opening kprobe: %s", err)
|
||||
}
|
||||
defer kp.Close()
|
||||
|
||||
// Read loop reporting the total amount of times the kernel
|
||||
// function was entered, once per second.
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
log.Println("Waiting for events..")
|
||||
|
||||
for range ticker.C {
|
||||
var all_cpu_value []uint64
|
||||
if err := objs.KprobeMap.Lookup(mapKey, &all_cpu_value); err != nil {
|
||||
log.Fatalf("reading map: %v", err)
|
||||
}
|
||||
for cpuid, cpuvalue := range all_cpu_value {
|
||||
log.Printf("%s called %d times on CPU%v\n", fn, cpuvalue, cpuid)
|
||||
}
|
||||
log.Printf("\n")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
KprobeExecve *ebpf.ProgramSpec `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
KprobeMap *ebpf.MapSpec `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
KprobeMap *ebpf.Map `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.KprobeMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
KprobeExecve *ebpf.Program `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.KprobeExecve,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
KprobeExecve *ebpf.ProgramSpec `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
KprobeMap *ebpf.MapSpec `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
KprobeMap *ebpf.Map `ebpf:"kprobe_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.KprobeMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
KprobeExecve *ebpf.Program `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.KprobeExecve,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "common.h"
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, u32);
|
||||
__type(value, u64);
|
||||
__uint(max_entries, 1);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
} kprobe_map SEC(".maps");
|
||||
|
||||
SEC("kprobe/sys_execve")
|
||||
int kprobe_execve() {
|
||||
u32 key = 0;
|
||||
u64 initval = 1, *valp;
|
||||
|
||||
valp = bpf_map_lookup_elem(&kprobe_map, &key);
|
||||
if (!valp) {
|
||||
bpf_map_update_elem(&kprobe_map, &key, &initval, BPF_ANY);
|
||||
return 0;
|
||||
}
|
||||
__sync_fetch_and_add(valp, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// This program demonstrates attaching an eBPF program to a kernel symbol.
|
||||
// The eBPF program will be attached to the start of the sys_execve
|
||||
// kernel function and prints out the number of times it has been called
|
||||
// every second.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf kprobe_pin.c -- -I../headers
|
||||
|
||||
const (
|
||||
mapKey uint32 = 0
|
||||
bpfFSPath = "/sys/fs/bpf"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Name of the kernel function to trace.
|
||||
fn := "sys_execve"
|
||||
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
pinPath := path.Join(bpfFSPath, fn)
|
||||
if err := os.MkdirAll(pinPath, os.ModePerm); err != nil {
|
||||
log.Fatalf("failed to create bpf fs subpath: %+v", err)
|
||||
}
|
||||
|
||||
var objs bpfObjects
|
||||
if err := loadBpfObjects(&objs, &ebpf.CollectionOptions{
|
||||
Maps: ebpf.MapOptions{
|
||||
// Pin the map to the BPF filesystem and configure the
|
||||
// library to automatically re-write it in the BPF
|
||||
// program so it can be re-used if it already exists or
|
||||
// create it if not
|
||||
PinPath: pinPath,
|
||||
},
|
||||
}); err != nil {
|
||||
log.Fatalf("loading objects: %v", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
// Open a Kprobe at the entry point of the kernel function and attach the
|
||||
// pre-compiled program. Each time the kernel function enters, the program
|
||||
// will increment the execution counter by 1. The read loop below polls this
|
||||
// map value once per second.
|
||||
kp, err := link.Kprobe(fn, objs.KprobeExecve, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("opening kprobe: %s", err)
|
||||
}
|
||||
defer kp.Close()
|
||||
|
||||
// Read loop reporting the total amount of times the kernel
|
||||
// function was entered, once per second.
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
log.Println("Waiting for events..")
|
||||
|
||||
for range ticker.C {
|
||||
var value uint64
|
||||
if err := objs.KprobeMap.Lookup(mapKey, &value); err != nil {
|
||||
log.Fatalf("reading map: %v", err)
|
||||
}
|
||||
log.Printf("%s called %d times\n", fn, value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// An example of using maps within maps. This example demonstrates a few
|
||||
// features. Firstly, creating eBPF map specifications in pure Go
|
||||
// (typically you'd see them being generated from a loaded ELF).
|
||||
// Additionally, creating maps and placing them in other maps (with
|
||||
// dynamically sized inner maps).
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
)
|
||||
|
||||
const BPF_F_INNER_MAP = 0x1000
|
||||
|
||||
func main() {
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// We're creating a map spec in pure Go here, but a map spec like
|
||||
// this can be loaded from an ELF too.
|
||||
outerMapSpec := ebpf.MapSpec{
|
||||
Name: "outer_map",
|
||||
Type: ebpf.ArrayOfMaps,
|
||||
KeySize: 4, // 4 bytes for u32
|
||||
ValueSize: 4,
|
||||
MaxEntries: 5, // We'll have 5 maps inside this map
|
||||
Contents: make([]ebpf.MapKV, 5),
|
||||
InnerMap: &ebpf.MapSpec{
|
||||
Name: "inner_map",
|
||||
Type: ebpf.Array,
|
||||
KeySize: 4, // 4 bytes for u32
|
||||
ValueSize: 4, // 4 bytes for u32
|
||||
|
||||
// This flag is required for dynamically sized inner maps.
|
||||
// Added in linux 5.10.
|
||||
Flags: BPF_F_INNER_MAP,
|
||||
|
||||
// We set this to 1 now, but this inner map spec gets copied
|
||||
// and altered later.
|
||||
MaxEntries: 1,
|
||||
},
|
||||
}
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
// For each entry we want to create in the outer map...
|
||||
for i := uint32(0); i < outerMapSpec.MaxEntries; i++ {
|
||||
// Copy the inner map spec
|
||||
innerMapSpec := outerMapSpec.InnerMap.Copy()
|
||||
|
||||
// Randomly generate inner map length
|
||||
innerMapSpec.MaxEntries = uint32(r.Intn(50) + 1) // Can't be zero.
|
||||
|
||||
// populate the inner map contents
|
||||
innerMapSpec.Contents = make([]ebpf.MapKV, innerMapSpec.MaxEntries)
|
||||
|
||||
for j := range innerMapSpec.Contents {
|
||||
innerMapSpec.Contents[uint32(j)] = ebpf.MapKV{Key: uint32(j), Value: uint32(0xCAFE)}
|
||||
}
|
||||
|
||||
// Create the inner map
|
||||
innerMap, err := ebpf.NewMap(innerMapSpec)
|
||||
if err != nil {
|
||||
log.Fatalf("inner_map: %v", err)
|
||||
}
|
||||
// In this example we close all references to maps before exit.
|
||||
// But typically you may actually want to hold on to the map
|
||||
// reference so that you control the lifecycle of the map. For
|
||||
// the inner (nested) map though, it's safe to close the file
|
||||
// descriptor in userspace once the outer map holds a reference
|
||||
// in the kernel.
|
||||
defer innerMap.Close()
|
||||
|
||||
// Inner map is created successfully and lives in the kernel,
|
||||
// let's add it to the contents of the outer map spec.
|
||||
outerMapSpec.Contents[i] = ebpf.MapKV{Key: i, Value: innerMap}
|
||||
}
|
||||
|
||||
// All inner maps are created and inserted into the outer map spec,
|
||||
// time to create the outer map.
|
||||
outerMap, err := ebpf.NewMap(&outerMapSpec)
|
||||
if err != nil {
|
||||
log.Fatalf("outer_map: %v", err)
|
||||
}
|
||||
defer outerMap.Close()
|
||||
|
||||
// The outer map is created successfully and lives happily in the
|
||||
// kernel. Let's iterate over the map in the kernel to see what's
|
||||
// been made.
|
||||
mapIter := outerMap.Iterate()
|
||||
var outerMapKey uint32
|
||||
var innerMapID ebpf.MapID
|
||||
for mapIter.Next(&outerMapKey, &innerMapID) {
|
||||
// With maps that contain maps, performing a lookup doesn't give
|
||||
// you the map directly, instead it gives you an ID, which you
|
||||
// can then use to get a full map pointer.
|
||||
innerMap, err := ebpf.NewMapFromID(innerMapID)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
innerMapInfo, err := innerMap.Info()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("outerMapKey %d, innerMap.Info: %+v", outerMapKey, innerMapInfo)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfEvent struct {
|
||||
Pid uint32
|
||||
Comm [80]uint8
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
KprobeExecve *ebpf.ProgramSpec `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
Events *ebpf.MapSpec `ebpf:"events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
Events *ebpf.Map `ebpf:"events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.Events,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
KprobeExecve *ebpf.Program `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.KprobeExecve,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,124 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfEvent struct {
|
||||
Pid uint32
|
||||
Comm [80]uint8
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
KprobeExecve *ebpf.ProgramSpec `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
Events *ebpf.MapSpec `ebpf:"events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
Events *ebpf.Map `ebpf:"events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.Events,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
KprobeExecve *ebpf.Program `ebpf:"kprobe_execve"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.KprobeExecve,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/ringbuf"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -type event bpf ringbuffer.c -- -I../headers
|
||||
|
||||
func main() {
|
||||
// Name of the kernel function to trace.
|
||||
fn := "sys_execve"
|
||||
|
||||
// Subscribe to signals for terminating the program.
|
||||
stopper := make(chan os.Signal, 1)
|
||||
signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
log.Fatalf("loading objects: %v", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
// Open a Kprobe at the entry point of the kernel function and attach the
|
||||
// pre-compiled program. Each time the kernel function enters, the program
|
||||
// will emit an event containing pid and command of the execved task.
|
||||
kp, err := link.Kprobe(fn, objs.KprobeExecve, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("opening kprobe: %s", err)
|
||||
}
|
||||
defer kp.Close()
|
||||
|
||||
// Open a ringbuf reader from userspace RINGBUF map described in the
|
||||
// eBPF C program.
|
||||
rd, err := ringbuf.NewReader(objs.Events)
|
||||
if err != nil {
|
||||
log.Fatalf("opening ringbuf reader: %s", err)
|
||||
}
|
||||
defer rd.Close()
|
||||
|
||||
// Close the reader when the process receives a signal, which will exit
|
||||
// the read loop.
|
||||
go func() {
|
||||
<-stopper
|
||||
|
||||
if err := rd.Close(); err != nil {
|
||||
log.Fatalf("closing ringbuf reader: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Println("Waiting for events..")
|
||||
|
||||
// bpfEvent is generated by bpf2go.
|
||||
var event bpfEvent
|
||||
for {
|
||||
record, err := rd.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, ringbuf.ErrClosed) {
|
||||
log.Println("Received signal, exiting..")
|
||||
return
|
||||
}
|
||||
log.Printf("reading from reader: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse the ringbuf event entry into a bpfEvent structure.
|
||||
if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil {
|
||||
log.Printf("parsing ringbuf event: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("pid: %d\tcomm: %s\n", event.Pid, unix.ByteSliceToString(event.Comm[:]))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "common.h"
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
struct event {
|
||||
u32 pid;
|
||||
u8 comm[80];
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 1 << 24);
|
||||
} events SEC(".maps");
|
||||
|
||||
// Force emitting struct event into the ELF.
|
||||
const struct event *unused __attribute__((unused));
|
||||
|
||||
SEC("kprobe/sys_execve")
|
||||
int kprobe_execve(struct pt_regs *ctx) {
|
||||
u64 id = bpf_get_current_pid_tgid();
|
||||
u32 tgid = id >> 32;
|
||||
struct event *task_info;
|
||||
|
||||
task_info = bpf_ringbuf_reserve(&events, sizeof(struct event), 0);
|
||||
if (!task_info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
task_info->pid = tgid;
|
||||
bpf_get_current_comm(&task_info->comm, 80);
|
||||
|
||||
bpf_ringbuf_submit(task_info, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfEvent struct {
|
||||
Sport uint16
|
||||
Dport uint16
|
||||
Saddr uint32
|
||||
Daddr uint32
|
||||
Srtt uint32
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
TcpClose *ebpf.ProgramSpec `ebpf:"tcp_close"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
Events *ebpf.MapSpec `ebpf:"events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
Events *ebpf.Map `ebpf:"events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.Events,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
TcpClose *ebpf.Program `ebpf:"tcp_close"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.TcpClose,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,127 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfEvent struct {
|
||||
Sport uint16
|
||||
Dport uint16
|
||||
Saddr uint32
|
||||
Daddr uint32
|
||||
Srtt uint32
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
TcpClose *ebpf.ProgramSpec `ebpf:"tcp_close"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
Events *ebpf.MapSpec `ebpf:"events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
Events *ebpf.Map `ebpf:"events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.Events,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
TcpClose *ebpf.Program `ebpf:"tcp_close"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.TcpClose,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,114 @@
|
||||
// This program demonstrates attaching a fentry eBPF program to
|
||||
// tcp_close and reading the RTT from the TCP socket using CO-RE helpers.
|
||||
// It prints the IPs/ports/RTT information
|
||||
// once the host closes a TCP connection.
|
||||
// It supports only IPv4 for this example.
|
||||
//
|
||||
// Sample output:
|
||||
//
|
||||
// examples# go run -exec sudo ./tcprtt
|
||||
// 2022/03/19 22:30:34 Src addr Port -> Dest addr Port RTT
|
||||
// 2022/03/19 22:30:36 10.0.1.205 50578 -> 117.102.109.186 5201 195
|
||||
// 2022/03/19 22:30:53 10.0.1.205 0 -> 89.84.1.178 9200 30
|
||||
// 2022/03/19 22:30:53 10.0.1.205 36022 -> 89.84.1.178 9200 28
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/cilium/ebpf/internal"
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/ringbuf"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -type event bpf tcprtt.c -- -I../headers
|
||||
|
||||
func main() {
|
||||
stopper := make(chan os.Signal, 1)
|
||||
signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
log.Fatalf("loading objects: %v", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
link, err := link.AttachTracing(link.TracingOptions{
|
||||
Program: objs.bpfPrograms.TcpClose,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer link.Close()
|
||||
|
||||
rd, err := ringbuf.NewReader(objs.bpfMaps.Events)
|
||||
if err != nil {
|
||||
log.Fatalf("opening ringbuf reader: %s", err)
|
||||
}
|
||||
defer rd.Close()
|
||||
|
||||
log.Printf("%-15s %-6s -> %-15s %-6s %-6s",
|
||||
"Src addr",
|
||||
"Port",
|
||||
"Dest addr",
|
||||
"Port",
|
||||
"RTT",
|
||||
)
|
||||
go readLoop(rd)
|
||||
|
||||
// Wait
|
||||
<-stopper
|
||||
}
|
||||
|
||||
func readLoop(rd *ringbuf.Reader) {
|
||||
// bpfEvent is generated by bpf2go.
|
||||
var event bpfEvent
|
||||
for {
|
||||
record, err := rd.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, ringbuf.ErrClosed) {
|
||||
log.Println("received signal, exiting..")
|
||||
return
|
||||
}
|
||||
log.Printf("reading from reader: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse the ringbuf event entry into a bpfEvent structure.
|
||||
if err := binary.Read(bytes.NewBuffer(record.RawSample), internal.NativeEndian, &event); err != nil {
|
||||
log.Printf("parsing ringbuf event: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("%-15s %-6d -> %-15s %-6d %-6d",
|
||||
intToIP(event.Saddr),
|
||||
event.Sport,
|
||||
intToIP(event.Daddr),
|
||||
event.Dport,
|
||||
event.Srtt,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// intToIP converts IPv4 number to net.IP
|
||||
func intToIP(ipNum uint32) net.IP {
|
||||
ip := make(net.IP, 4)
|
||||
internal.NativeEndian.PutUint32(ip, ipNum)
|
||||
return ip
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "bpf_endian.h"
|
||||
#include "bpf_tracing.h"
|
||||
|
||||
#define AF_INET 2
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
/**
|
||||
* For CO-RE relocatable eBPF programs, __attribute__((preserve_access_index))
|
||||
* preserves the offset of the specified fields in the original kernel struct.
|
||||
* So here we don't need to include "vmlinux.h". Instead we only need to define
|
||||
* the kernel struct and their fields the eBPF program actually requires.
|
||||
*
|
||||
* Also note that BTF-enabled programs like fentry, fexit, fmod_ret, tp_btf,
|
||||
* lsm, etc. declared using the BPF_PROG macro can read kernel memory without
|
||||
* needing to call bpf_probe_read*().
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct sock_common is the minimal network layer representation of sockets.
|
||||
* This is a simplified copy of the kernel's struct sock_common.
|
||||
* This copy contains only the fields needed for this example to
|
||||
* fetch the source and destination port numbers and IP addresses.
|
||||
*/
|
||||
struct sock_common {
|
||||
union {
|
||||
struct {
|
||||
// skc_daddr is destination IP address
|
||||
__be32 skc_daddr;
|
||||
// skc_rcv_saddr is the source IP address
|
||||
__be32 skc_rcv_saddr;
|
||||
};
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
// skc_dport is the destination TCP/UDP port
|
||||
__be16 skc_dport;
|
||||
// skc_num is the source TCP/UDP port
|
||||
__u16 skc_num;
|
||||
};
|
||||
};
|
||||
// skc_family is the network address family (2 for IPV4)
|
||||
short unsigned int skc_family;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
/**
|
||||
* struct sock is the network layer representation of sockets.
|
||||
* This is a simplified copy of the kernel's struct sock.
|
||||
* This copy is needed only to access struct sock_common.
|
||||
*/
|
||||
struct sock {
|
||||
struct sock_common __sk_common;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
/**
|
||||
* struct tcp_sock is the Linux representation of a TCP socket.
|
||||
* This is a simplified copy of the kernel's struct tcp_sock.
|
||||
* For this example we only need srtt_us to read the smoothed RTT.
|
||||
*/
|
||||
struct tcp_sock {
|
||||
u32 srtt_us;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 1 << 24);
|
||||
} events SEC(".maps");
|
||||
|
||||
/**
|
||||
* The sample submitted to userspace over a ring buffer.
|
||||
* Emit struct event's type info into the ELF's BTF so bpf2go
|
||||
* can generate a Go type from it.
|
||||
*/
|
||||
struct event {
|
||||
u16 sport;
|
||||
u16 dport;
|
||||
u32 saddr;
|
||||
u32 daddr;
|
||||
u32 srtt;
|
||||
};
|
||||
struct event *unused_event __attribute__((unused));
|
||||
|
||||
SEC("fentry/tcp_close")
|
||||
int BPF_PROG(tcp_close, struct sock *sk) {
|
||||
if (sk->__sk_common.skc_family != AF_INET) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The input struct sock is actually a tcp_sock, so we can type-cast
|
||||
struct tcp_sock *ts = bpf_skc_to_tcp_sock(sk);
|
||||
if (!ts) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct event *tcp_info;
|
||||
tcp_info = bpf_ringbuf_reserve(&events, sizeof(struct event), 0);
|
||||
if (!tcp_info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
tcp_info->saddr = sk->__sk_common.skc_rcv_saddr;
|
||||
tcp_info->daddr = sk->__sk_common.skc_daddr;
|
||||
tcp_info->dport = bpf_ntohs(sk->__sk_common.skc_dport);
|
||||
tcp_info->sport = sk->__sk_common.skc_num;
|
||||
|
||||
tcp_info->srtt = ts->srtt_us >> 3;
|
||||
tcp_info->srtt /= 1000;
|
||||
|
||||
bpf_ringbuf_submit(tcp_info, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build (arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64) && linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfRttEvent struct {
|
||||
Sport uint16
|
||||
Dport uint16
|
||||
Saddr uint32
|
||||
Daddr uint32
|
||||
Srtt uint32
|
||||
}
|
||||
|
||||
type bpfSkInfo struct {
|
||||
SkKey bpfSkKey
|
||||
SkType uint8
|
||||
_ [3]byte
|
||||
}
|
||||
|
||||
type bpfSkKey struct {
|
||||
LocalIp4 uint32
|
||||
RemoteIp4 uint32
|
||||
LocalPort uint32
|
||||
RemotePort uint32
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
BpfSockopsCb *ebpf.ProgramSpec `ebpf:"bpf_sockops_cb"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
MapEstabSk *ebpf.MapSpec `ebpf:"map_estab_sk"`
|
||||
RttEvents *ebpf.MapSpec `ebpf:"rtt_events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
MapEstabSk *ebpf.Map `ebpf:"map_estab_sk"`
|
||||
RttEvents *ebpf.Map `ebpf:"rtt_events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.MapEstabSk,
|
||||
m.RttEvents,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
BpfSockopsCb *ebpf.Program `ebpf:"bpf_sockops_cb"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.BpfSockopsCb,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,143 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build (386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64) && linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfRttEvent struct {
|
||||
Sport uint16
|
||||
Dport uint16
|
||||
Saddr uint32
|
||||
Daddr uint32
|
||||
Srtt uint32
|
||||
}
|
||||
|
||||
type bpfSkInfo struct {
|
||||
SkKey bpfSkKey
|
||||
SkType uint8
|
||||
_ [3]byte
|
||||
}
|
||||
|
||||
type bpfSkKey struct {
|
||||
LocalIp4 uint32
|
||||
RemoteIp4 uint32
|
||||
LocalPort uint32
|
||||
RemotePort uint32
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
BpfSockopsCb *ebpf.ProgramSpec `ebpf:"bpf_sockops_cb"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
MapEstabSk *ebpf.MapSpec `ebpf:"map_estab_sk"`
|
||||
RttEvents *ebpf.MapSpec `ebpf:"rtt_events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
MapEstabSk *ebpf.Map `ebpf:"map_estab_sk"`
|
||||
RttEvents *ebpf.Map `ebpf:"rtt_events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.MapEstabSk,
|
||||
m.RttEvents,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
BpfSockopsCb *ebpf.Program `ebpf:"bpf_sockops_cb"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.BpfSockopsCb,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Note that this header file contains a subset of kernel
|
||||
* definitions needed for the tcprtt_sockops example.
|
||||
*/
|
||||
#ifndef BPF_SOCKOPS_H
|
||||
#define BPF_SOCKOPS_H
|
||||
|
||||
/*
|
||||
* Copy of TCP states.
|
||||
* See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L6347.
|
||||
*/
|
||||
enum {
|
||||
TCP_ESTABLISHED = 1,
|
||||
TCP_SYN_SENT = 2,
|
||||
TCP_SYN_RECV = 3,
|
||||
TCP_FIN_WAIT1 = 4,
|
||||
TCP_FIN_WAIT2 = 5,
|
||||
TCP_TIME_WAIT = 6,
|
||||
TCP_CLOSE = 7,
|
||||
TCP_CLOSE_WAIT = 8,
|
||||
TCP_LAST_ACK = 9,
|
||||
TCP_LISTEN = 10,
|
||||
TCP_CLOSING = 11,
|
||||
TCP_NEW_SYN_RECV = 12,
|
||||
TCP_MAX_STATES = 13,
|
||||
};
|
||||
|
||||
/*
|
||||
* Copy of sock_ops operations.
|
||||
* See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L6233.
|
||||
*/
|
||||
enum {
|
||||
BPF_SOCK_OPS_VOID = 0,
|
||||
BPF_SOCK_OPS_TIMEOUT_INIT = 1,
|
||||
BPF_SOCK_OPS_RWND_INIT = 2,
|
||||
BPF_SOCK_OPS_TCP_CONNECT_CB = 3,
|
||||
BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB = 4,
|
||||
BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB = 5,
|
||||
BPF_SOCK_OPS_NEEDS_ECN = 6,
|
||||
BPF_SOCK_OPS_BASE_RTT = 7,
|
||||
BPF_SOCK_OPS_RTO_CB = 8,
|
||||
BPF_SOCK_OPS_RETRANS_CB = 9,
|
||||
BPF_SOCK_OPS_STATE_CB = 10,
|
||||
BPF_SOCK_OPS_TCP_LISTEN_CB = 11,
|
||||
BPF_SOCK_OPS_RTT_CB = 12,
|
||||
BPF_SOCK_OPS_PARSE_HDR_OPT_CB = 13,
|
||||
BPF_SOCK_OPS_HDR_OPT_LEN_CB = 14,
|
||||
BPF_SOCK_OPS_WRITE_HDR_OPT_CB = 15,
|
||||
};
|
||||
|
||||
/*
|
||||
* Copy of definitions for bpf_sock_ops_cb_flags.
|
||||
* See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L6178.
|
||||
*/
|
||||
enum {
|
||||
BPF_SOCK_OPS_RTO_CB_FLAG = 1,
|
||||
BPF_SOCK_OPS_RETRANS_CB_FLAG = 2,
|
||||
BPF_SOCK_OPS_STATE_CB_FLAG = 4,
|
||||
BPF_SOCK_OPS_RTT_CB_FLAG = 8,
|
||||
BPF_SOCK_OPS_PARSE_ALL_HDR_OPT_CB_FLAG = 16,
|
||||
BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG = 32,
|
||||
BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG = 64,
|
||||
BPF_SOCK_OPS_ALL_CB_FLAGS = 127,
|
||||
};
|
||||
|
||||
/*
|
||||
* Copy of bpf.h's bpf_sock_ops with minimal subset
|
||||
* of fields used by the tcprtt_sockops example.
|
||||
* See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L6101.
|
||||
*/
|
||||
struct bpf_sock_ops {
|
||||
__u32 op;
|
||||
union {
|
||||
__u32 args[4];
|
||||
__u32 reply;
|
||||
__u32 replylong[4];
|
||||
};
|
||||
__u32 family;
|
||||
__u32 remote_ip4;
|
||||
__u32 local_ip4;
|
||||
__u32 remote_port;
|
||||
__u32 local_port;
|
||||
__u32 srtt_us;
|
||||
__u32 bpf_sock_ops_cb_flags;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,150 @@
|
||||
//go:build linux
|
||||
|
||||
// This program demonstrates attaching an eBPF program to
|
||||
// a cgroupv2 path and using sockops to process TCP socket events.
|
||||
// It prints the IPs/ports/RTT information every time TCP sockets
|
||||
// update their internal RTT value.
|
||||
// It supports only IPv4 for this example.
|
||||
//
|
||||
// Sample output:
|
||||
//
|
||||
// examples# go run -exec sudo ./tcprtt_sockops
|
||||
// 2022/08/14 20:58:03 eBPF program loaded and attached on cgroup /sys/fs/cgroup/unified
|
||||
// 2022/08/14 20:58:03 Src addr Port -> Dest addr Port RTT (ms)
|
||||
// 2022/08/14 20:58:09 10.0.1.205 54844 -> 20.42.73.25 443 67
|
||||
// 2022/08/14 20:58:09 10.0.1.205 54844 -> 20.42.73.25 443 67
|
||||
// 2022/08/14 20:58:33 10.0.1.205 38620 -> 140.82.121.4 443 26
|
||||
// 2022/08/14 20:58:33 10.0.1.205 38620 -> 140.82.121.4 443 26
|
||||
// 2022/08/14 20:58:43 34.67.40.146 45380 -> 10.0.1.205 5201 106
|
||||
// 2022/08/14 20:58:43 34.67.40.146 45380 -> 10.0.1.205 5201 106
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/internal"
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/ringbuf"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -tags "linux" -cc $BPF_CLANG -cflags $BPF_CFLAGS -type rtt_event bpf tcprtt_sockops.c -- -I../headers
|
||||
|
||||
func main() {
|
||||
stopper := make(chan os.Signal, 1)
|
||||
signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Find the path to a cgroup enabled to version 2
|
||||
cgroupPath, err := findCgroupPath()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
log.Fatalf("loading objects: %v", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
// Attach ebpf program to a cgroupv2
|
||||
link, err := link.AttachCgroup(link.CgroupOptions{
|
||||
Path: cgroupPath,
|
||||
Program: objs.bpfPrograms.BpfSockopsCb,
|
||||
Attach: ebpf.AttachCGroupSockOps,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer link.Close()
|
||||
|
||||
log.Printf("eBPF program loaded and attached on cgroup %s\n", cgroupPath)
|
||||
|
||||
rd, err := ringbuf.NewReader(objs.bpfMaps.RttEvents)
|
||||
if err != nil {
|
||||
log.Fatalf("opening ringbuf reader: %s", err)
|
||||
}
|
||||
defer rd.Close()
|
||||
|
||||
log.Printf("%-15s %-6s -> %-15s %-6s %-6s",
|
||||
"Src addr",
|
||||
"Port",
|
||||
"Dest addr",
|
||||
"Port",
|
||||
"RTT (ms)",
|
||||
)
|
||||
go readLoop(rd)
|
||||
|
||||
// Wait
|
||||
<-stopper
|
||||
}
|
||||
|
||||
func findCgroupPath() (string, error) {
|
||||
cgroupPath := "/sys/fs/cgroup"
|
||||
|
||||
var st syscall.Statfs_t
|
||||
err := syscall.Statfs(cgroupPath, &st)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
isCgroupV2Enabled := st.Type == unix.CGROUP2_SUPER_MAGIC
|
||||
if !isCgroupV2Enabled {
|
||||
cgroupPath = filepath.Join(cgroupPath, "unified")
|
||||
}
|
||||
return cgroupPath, nil
|
||||
}
|
||||
|
||||
func readLoop(rd *ringbuf.Reader) {
|
||||
// bpfRttEvent is generated by bpf2go.
|
||||
var event bpfRttEvent
|
||||
for {
|
||||
record, err := rd.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, ringbuf.ErrClosed) {
|
||||
log.Println("received signal, exiting..")
|
||||
return
|
||||
}
|
||||
log.Printf("reading from reader: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse the ringbuf event entry into a bpfRttEvent structure.
|
||||
if err := binary.Read(bytes.NewBuffer(record.RawSample), internal.NativeEndian, &event); err != nil {
|
||||
log.Printf("parsing ringbuf event: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("%-15s %-6d -> %-15s %-6d %-6d",
|
||||
intToIP(event.Saddr),
|
||||
event.Sport,
|
||||
intToIP(event.Daddr),
|
||||
event.Dport,
|
||||
event.Srtt,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// intToIP converts IPv4 number to net.IP
|
||||
func intToIP(ipNum uint32) net.IP {
|
||||
ip := make(net.IP, 4)
|
||||
binary.BigEndian.PutUint32(ip, ipNum)
|
||||
return ip
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "bpf_endian.h"
|
||||
#include "bpf_sockops.h"
|
||||
#include "bpf_tracing.h"
|
||||
|
||||
#define AF_INET 2
|
||||
#define SOCKOPS_MAP_SIZE 65535
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
enum {
|
||||
SOCK_TYPE_ACTIVE = 0,
|
||||
SOCK_TYPE_PASSIVE = 1,
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, SOCKOPS_MAP_SIZE);
|
||||
__type(key, struct sk_key);
|
||||
__type(value, struct sk_info);
|
||||
} map_estab_sk SEC(".maps");
|
||||
|
||||
struct sk_key {
|
||||
u32 local_ip4;
|
||||
u32 remote_ip4;
|
||||
u32 local_port;
|
||||
u32 remote_port;
|
||||
};
|
||||
|
||||
struct sk_info {
|
||||
struct sk_key sk_key;
|
||||
u8 sk_type;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 1 << 24);
|
||||
} rtt_events SEC(".maps");
|
||||
|
||||
struct rtt_event {
|
||||
u16 sport;
|
||||
u16 dport;
|
||||
u32 saddr;
|
||||
u32 daddr;
|
||||
u32 srtt;
|
||||
};
|
||||
struct rtt_event *unused_event __attribute__((unused));
|
||||
|
||||
static inline void init_sk_key(struct bpf_sock_ops *skops, struct sk_key *sk_key) {
|
||||
sk_key->local_ip4 = bpf_ntohl(skops->local_ip4);
|
||||
sk_key->remote_ip4 = bpf_ntohl(skops->remote_ip4);
|
||||
sk_key->local_port = skops->local_port;
|
||||
sk_key->remote_port = bpf_ntohl(skops->remote_port);
|
||||
}
|
||||
|
||||
static inline void bpf_sock_ops_establish_cb(struct bpf_sock_ops *skops, u8 sock_type) {
|
||||
int err;
|
||||
struct sk_info sk_info = {};
|
||||
// Only process IPv4 sockets
|
||||
if (skops == NULL || skops->family != AF_INET)
|
||||
return;
|
||||
|
||||
// Initialize the 4-tuple key
|
||||
init_sk_key(skops, &sk_info.sk_key);
|
||||
sk_info.sk_type = sock_type;
|
||||
|
||||
// Store the socket info in map using the 4-tuple as key
|
||||
// We keep track of TCP connections in 'established' state
|
||||
err = bpf_map_update_elem(&map_estab_sk, &sk_info.sk_key, &sk_info, BPF_NOEXIST);
|
||||
if (err != 0) {
|
||||
// Storing the 4-tuple in map has failed, return early.
|
||||
// This can happen in case the 4-tuple already exists in the map (i.e. BPF_NOEXIST flag)
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable sockops callbacks for RTT and TCP state change
|
||||
bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_RTT_CB_FLAG | BPF_SOCK_OPS_STATE_CB_FLAG);
|
||||
}
|
||||
|
||||
static inline void bpf_sock_ops_rtt_cb(struct bpf_sock_ops *skops) {
|
||||
struct sk_key sk_key = {};
|
||||
struct sk_info *sk_info;
|
||||
struct rtt_event *rtt_event;
|
||||
|
||||
// Initialize the 4-tuple key
|
||||
init_sk_key(skops, &sk_key);
|
||||
|
||||
// Retrieve the socket info from map of established connections
|
||||
sk_info = bpf_map_lookup_elem(&map_estab_sk, &sk_key);
|
||||
if (!sk_info)
|
||||
return;
|
||||
|
||||
rtt_event = bpf_ringbuf_reserve(&rtt_events, sizeof(struct rtt_event), 0);
|
||||
if (!rtt_event) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (sk_info->sk_type) {
|
||||
case SOCK_TYPE_ACTIVE:
|
||||
// If socket is 'active', 'local' means 'source'
|
||||
// and 'remote' means 'destination'
|
||||
rtt_event->saddr = sk_info->sk_key.local_ip4;
|
||||
rtt_event->daddr = sk_info->sk_key.remote_ip4;
|
||||
rtt_event->sport = sk_info->sk_key.local_port;
|
||||
rtt_event->dport = sk_info->sk_key.remote_port;
|
||||
break;
|
||||
case SOCK_TYPE_PASSIVE:
|
||||
// If socket is 'passive', 'local' means 'destination'
|
||||
// and 'remote' means 'source'
|
||||
rtt_event->saddr = sk_info->sk_key.remote_ip4;
|
||||
rtt_event->daddr = sk_info->sk_key.local_ip4;
|
||||
rtt_event->sport = sk_info->sk_key.remote_port;
|
||||
rtt_event->dport = sk_info->sk_key.local_port;
|
||||
break;
|
||||
}
|
||||
|
||||
// Extract smoothed RTT
|
||||
rtt_event->srtt = skops->srtt_us >> 3;
|
||||
rtt_event->srtt /= 1000;
|
||||
|
||||
// Send RTT event data to userspace app via ring buffer
|
||||
bpf_ringbuf_submit(rtt_event, 0);
|
||||
}
|
||||
|
||||
static inline void bpf_sock_ops_state_cb(struct bpf_sock_ops *skops) {
|
||||
struct sk_key sk_key = {};
|
||||
|
||||
// Socket changed state. args[0] stores the previous state.
|
||||
// Perform cleanup of map entry if socket is exiting
|
||||
// the 'established' state,
|
||||
if (skops->args[0] == TCP_ESTABLISHED) {
|
||||
init_sk_key(skops, &sk_key);
|
||||
bpf_map_delete_elem(&map_estab_sk, &sk_key);
|
||||
}
|
||||
}
|
||||
|
||||
SEC("sockops")
|
||||
int bpf_sockops_cb(struct bpf_sock_ops *skops) {
|
||||
u32 op;
|
||||
op = skops->op;
|
||||
|
||||
switch (op) {
|
||||
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
|
||||
bpf_sock_ops_establish_cb(skops, SOCK_TYPE_ACTIVE);
|
||||
break;
|
||||
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
|
||||
bpf_sock_ops_establish_cb(skops, SOCK_TYPE_PASSIVE);
|
||||
break;
|
||||
case BPF_SOCK_OPS_RTT_CB:
|
||||
bpf_sock_ops_rtt_cb(skops);
|
||||
break;
|
||||
case BPF_SOCK_OPS_STATE_CB:
|
||||
bpf_sock_ops_state_cb(skops);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
MmPageAlloc *ebpf.ProgramSpec `ebpf:"mm_page_alloc"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
CountingMap *ebpf.MapSpec `ebpf:"counting_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
CountingMap *ebpf.Map `ebpf:"counting_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.CountingMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
MmPageAlloc *ebpf.Program `ebpf:"mm_page_alloc"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.MmPageAlloc,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
MmPageAlloc *ebpf.ProgramSpec `ebpf:"mm_page_alloc"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
CountingMap *ebpf.MapSpec `ebpf:"counting_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
CountingMap *ebpf.Map `ebpf:"counting_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.CountingMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
MmPageAlloc *ebpf.Program `ebpf:"mm_page_alloc"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.MmPageAlloc,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,58 @@
|
||||
// This program demonstrates attaching an eBPF program to a kernel tracepoint.
|
||||
// The eBPF program will be attached to the page allocation tracepoint and
|
||||
// prints out the number of times it has been reached. The tracepoint fields
|
||||
// are printed into /sys/kernel/tracing/trace_pipe.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf tracepoint.c -- -I../headers
|
||||
|
||||
const mapKey uint32 = 0
|
||||
|
||||
func main() {
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
log.Fatalf("loading objects: %v", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
// Open a tracepoint and attach the pre-compiled program. Each time
|
||||
// the kernel function enters, the program will increment the execution
|
||||
// counter by 1. The read loop below polls this map value once per
|
||||
// second.
|
||||
// The first two arguments are taken from the following pathname:
|
||||
// /sys/kernel/tracing/events/kmem/mm_page_alloc
|
||||
kp, err := link.Tracepoint("kmem", "mm_page_alloc", objs.MmPageAlloc, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("opening tracepoint: %s", err)
|
||||
}
|
||||
defer kp.Close()
|
||||
|
||||
// Read loop reporting the total amount of times the kernel
|
||||
// function was entered, once per second.
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
log.Println("Waiting for events..")
|
||||
for range ticker.C {
|
||||
var value uint64
|
||||
if err := objs.CountingMap.Lookup(mapKey, &value); err != nil {
|
||||
log.Fatalf("reading map: %v", err)
|
||||
}
|
||||
log.Printf("%v times", value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "common.h"
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
struct bpf_map_def SEC("maps") counting_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(u32),
|
||||
.value_size = sizeof(u64),
|
||||
.max_entries = 1,
|
||||
};
|
||||
|
||||
// This struct is defined according to the following format file:
|
||||
// /sys/kernel/tracing/events/kmem/mm_page_alloc/format
|
||||
struct alloc_info {
|
||||
/* The first 8 bytes is not allowed to read */
|
||||
unsigned long pad;
|
||||
|
||||
unsigned long pfn;
|
||||
unsigned int order;
|
||||
unsigned int gfp_flags;
|
||||
int migratetype;
|
||||
};
|
||||
|
||||
// This tracepoint is defined in mm/page_alloc.c:__alloc_pages_nodemask()
|
||||
// Userspace pathname: /sys/kernel/tracing/events/kmem/mm_page_alloc
|
||||
SEC("tracepoint/kmem/mm_page_alloc")
|
||||
int mm_page_alloc(struct alloc_info *info) {
|
||||
u32 key = 0;
|
||||
u64 initval = 1, *valp;
|
||||
|
||||
valp = bpf_map_lookup_elem(&counting_map, &key);
|
||||
if (!valp) {
|
||||
bpf_map_update_elem(&counting_map, &key, &initval, BPF_ANY);
|
||||
return 0;
|
||||
}
|
||||
__sync_fetch_and_add(valp, 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// This program demonstrates how to attach an eBPF program to a tracepoint.
|
||||
// The program is attached to the syscall/sys_enter_openat tracepoint and
|
||||
// prints out the integer 123 every time the syscall is entered.
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/asm"
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/perf"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
)
|
||||
|
||||
// Metadata for the eBPF program used in this example.
|
||||
var progSpec = &ebpf.ProgramSpec{
|
||||
Name: "my_trace_prog", // non-unique name, will appear in `bpftool prog list` while attached
|
||||
Type: ebpf.TracePoint, // only TracePoint programs can be attached to trace events created by link.Tracepoint()
|
||||
License: "GPL", // license must be GPL for calling kernel helpers like perf_event_output
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// Subscribe to signals for terminating the program.
|
||||
stopper := make(chan os.Signal, 1)
|
||||
signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Create a perf event array for the kernel to write perf records to.
|
||||
// These records will be read by userspace below.
|
||||
events, err := ebpf.NewMap(&ebpf.MapSpec{
|
||||
Type: ebpf.PerfEventArray,
|
||||
Name: "my_perf_array",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("creating perf event array: %s", err)
|
||||
}
|
||||
defer events.Close()
|
||||
|
||||
// Open a perf reader from userspace into the perf event array
|
||||
// created earlier.
|
||||
rd, err := perf.NewReader(events, os.Getpagesize())
|
||||
if err != nil {
|
||||
log.Fatalf("creating event reader: %s", err)
|
||||
}
|
||||
defer rd.Close()
|
||||
|
||||
// Close the reader when the process receives a signal, which will exit
|
||||
// the read loop.
|
||||
go func() {
|
||||
<-stopper
|
||||
rd.Close()
|
||||
}()
|
||||
|
||||
// Minimal program that writes the static value '123' to the perf ring on
|
||||
// each event. Note that this program refers to the file descriptor of
|
||||
// the perf event array created above, which needs to be created prior to the
|
||||
// program being verified by and inserted into the kernel.
|
||||
progSpec.Instructions = asm.Instructions{
|
||||
// store the integer 123 at FP[-8]
|
||||
asm.Mov.Imm(asm.R2, 123),
|
||||
asm.StoreMem(asm.RFP, -8, asm.R2, asm.Word),
|
||||
|
||||
// load registers with arguments for call of FnPerfEventOutput
|
||||
asm.LoadMapPtr(asm.R2, events.FD()), // file descriptor of the perf event array
|
||||
asm.LoadImm(asm.R3, 0xffffffff, asm.DWord),
|
||||
asm.Mov.Reg(asm.R4, asm.RFP),
|
||||
asm.Add.Imm(asm.R4, -8),
|
||||
asm.Mov.Imm(asm.R5, 4),
|
||||
|
||||
// call FnPerfEventOutput, an eBPF kernel helper
|
||||
asm.FnPerfEventOutput.Call(),
|
||||
|
||||
// set exit code to 0
|
||||
asm.Mov.Imm(asm.R0, 0),
|
||||
asm.Return(),
|
||||
}
|
||||
|
||||
// Instantiate and insert the program into the kernel.
|
||||
prog, err := ebpf.NewProgram(progSpec)
|
||||
if err != nil {
|
||||
log.Fatalf("creating ebpf program: %s", err)
|
||||
}
|
||||
defer prog.Close()
|
||||
|
||||
// Open a trace event based on a pre-existing kernel hook (tracepoint).
|
||||
// Each time a userspace program uses the 'openat()' syscall, the eBPF
|
||||
// program specified above will be executed and a '123' value will appear
|
||||
// in the perf ring.
|
||||
tp, err := link.Tracepoint("syscalls", "sys_enter_openat", prog, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("opening tracepoint: %s", err)
|
||||
}
|
||||
defer tp.Close()
|
||||
|
||||
log.Println("Waiting for events..")
|
||||
|
||||
for {
|
||||
record, err := rd.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, perf.ErrClosed) {
|
||||
log.Println("Received signal, exiting..")
|
||||
return
|
||||
}
|
||||
log.Printf("reading from reader: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Println("Record:", record)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfEvent struct {
|
||||
Pid uint32
|
||||
Line [80]uint8
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
UretprobeBashReadline *ebpf.ProgramSpec `ebpf:"uretprobe_bash_readline"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
Events *ebpf.MapSpec `ebpf:"events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
Events *ebpf.Map `ebpf:"events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.Events,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
UretprobeBashReadline *ebpf.Program `ebpf:"uretprobe_bash_readline"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.UretprobeBashReadline,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel_x86.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,113 @@
|
||||
// This program demonstrates how to attach an eBPF program to a uretprobe.
|
||||
// The program will be attached to the 'readline' symbol in the binary '/bin/bash' and print out
|
||||
// the line which 'readline' functions returns to the caller.
|
||||
|
||||
//go:build amd64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/perf"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -target amd64 -type event bpf uretprobe.c -- -I../headers
|
||||
|
||||
const (
|
||||
// The path to the ELF binary containing the function to trace.
|
||||
// On some distributions, the 'readline' function is provided by a
|
||||
// dynamically-linked library, so the path of the library will need
|
||||
// to be specified instead, e.g. /usr/lib/libreadline.so.8.
|
||||
// Use `ldd /bin/bash` to find these paths.
|
||||
binPath = "/bin/bash"
|
||||
symbol = "readline"
|
||||
)
|
||||
|
||||
func main() {
|
||||
stopper := make(chan os.Signal, 1)
|
||||
signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
log.Fatalf("loading objects: %s", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
// Open an ELF binary and read its symbols.
|
||||
ex, err := link.OpenExecutable(binPath)
|
||||
if err != nil {
|
||||
log.Fatalf("opening executable: %s", err)
|
||||
}
|
||||
|
||||
// Open a Uretprobe at the exit point of the symbol and attach
|
||||
// the pre-compiled eBPF program to it.
|
||||
up, err := ex.Uretprobe(symbol, objs.UretprobeBashReadline, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("creating uretprobe: %s", err)
|
||||
}
|
||||
defer up.Close()
|
||||
|
||||
// Open a perf event reader from userspace on the PERF_EVENT_ARRAY map
|
||||
// described in the eBPF C program.
|
||||
rd, err := perf.NewReader(objs.Events, os.Getpagesize())
|
||||
if err != nil {
|
||||
log.Fatalf("creating perf event reader: %s", err)
|
||||
}
|
||||
defer rd.Close()
|
||||
|
||||
go func() {
|
||||
// Wait for a signal and close the perf reader,
|
||||
// which will interrupt rd.Read() and make the program exit.
|
||||
<-stopper
|
||||
log.Println("Received signal, exiting program..")
|
||||
|
||||
if err := rd.Close(); err != nil {
|
||||
log.Fatalf("closing perf event reader: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf("Listening for events..")
|
||||
|
||||
// bpfEvent is generated by bpf2go.
|
||||
var event bpfEvent
|
||||
for {
|
||||
record, err := rd.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, perf.ErrClosed) {
|
||||
return
|
||||
}
|
||||
log.Printf("reading from perf event reader: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if record.LostSamples != 0 {
|
||||
log.Printf("perf event ring buffer full, dropped %d samples", record.LostSamples)
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse the perf event entry into a bpfEvent structure.
|
||||
if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil {
|
||||
log.Printf("parsing perf event: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("%s:%s return value: %s", binPath, symbol, unix.ByteSliceToString(event.Line[:]))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "bpf_tracing.h"
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
struct event {
|
||||
u32 pid;
|
||||
u8 line[80];
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||
} events SEC(".maps");
|
||||
|
||||
// Force emitting struct event into the ELF.
|
||||
const struct event *unused __attribute__((unused));
|
||||
|
||||
SEC("uretprobe/bash_readline")
|
||||
int uretprobe_bash_readline(struct pt_regs *ctx) {
|
||||
struct event event;
|
||||
|
||||
event.pid = bpf_get_current_pid_tgid();
|
||||
bpf_probe_read(&event.line, sizeof(event.line), (void *)PT_REGS_RC(ctx));
|
||||
|
||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
XdpProgFunc *ebpf.ProgramSpec `ebpf:"xdp_prog_func"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
XdpStatsMap *ebpf.MapSpec `ebpf:"xdp_stats_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
XdpStatsMap *ebpf.Map `ebpf:"xdp_stats_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.XdpStatsMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
XdpProgFunc *ebpf.Program `ebpf:"xdp_prog_func"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.XdpProgFunc,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,119 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
XdpProgFunc *ebpf.ProgramSpec `ebpf:"xdp_prog_func"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
XdpStatsMap *ebpf.MapSpec `ebpf:"xdp_stats_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
XdpStatsMap *ebpf.Map `ebpf:"xdp_stats_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.XdpStatsMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
XdpProgFunc *ebpf.Program `ebpf:"xdp_prog_func"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.XdpProgFunc,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
Binary file not shown.
@@ -0,0 +1,84 @@
|
||||
// This program demonstrates attaching an eBPF program to a network interface
|
||||
// with XDP (eXpress Data Path). The program parses the IPv4 source address
|
||||
// from packets and writes the packet count by IP to an LRU hash map.
|
||||
// The userspace program (Go code in this file) prints the contents
|
||||
// of the map to stdout every second.
|
||||
// It is possible to modify the XDP program to drop or redirect packets
|
||||
// as well -- give it a try!
|
||||
// This example depends on bpf_link, available in Linux kernel version 5.7 or newer.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/link"
|
||||
)
|
||||
|
||||
// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf xdp.c -- -I../headers
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatalf("Please specify a network interface")
|
||||
}
|
||||
|
||||
// Look up the network interface by name.
|
||||
ifaceName := os.Args[1]
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
log.Fatalf("lookup network iface %q: %s", ifaceName, err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
log.Fatalf("loading objects: %s", err)
|
||||
}
|
||||
defer objs.Close()
|
||||
|
||||
// Attach the program.
|
||||
l, err := link.AttachXDP(link.XDPOptions{
|
||||
Program: objs.XdpProgFunc,
|
||||
Interface: iface.Index,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("could not attach XDP program: %s", err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index)
|
||||
log.Printf("Press Ctrl-C to exit and remove the program")
|
||||
|
||||
// Print the contents of the BPF hash map (source IP address -> packet count).
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
s, err := formatMapContents(objs.XdpStatsMap)
|
||||
if err != nil {
|
||||
log.Printf("Error reading map: %s", err)
|
||||
continue
|
||||
}
|
||||
log.Printf("Map contents:\n%s", s)
|
||||
}
|
||||
}
|
||||
|
||||
func formatMapContents(m *ebpf.Map) (string, error) {
|
||||
var (
|
||||
sb strings.Builder
|
||||
key []byte
|
||||
val uint32
|
||||
)
|
||||
iter := m.Iterate()
|
||||
for iter.Next(&key, &val) {
|
||||
sourceIP := net.IP(key) // IPv4 source address in network byte order.
|
||||
packetCount := val
|
||||
sb.WriteString(fmt.Sprintf("\t%s => %d\n", sourceIP, packetCount))
|
||||
}
|
||||
return sb.String(), iter.Err()
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
//go:build ignore
|
||||
|
||||
#include "bpf_endian.h"
|
||||
#include "common.h"
|
||||
|
||||
char __license[] SEC("license") = "Dual MIT/GPL";
|
||||
|
||||
#define MAX_MAP_ENTRIES 16
|
||||
|
||||
/* Define an LRU hash map for storing packet count by source IPv4 address */
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||
__uint(max_entries, MAX_MAP_ENTRIES);
|
||||
__type(key, __u32); // source IPv4 address
|
||||
__type(value, __u32); // packet count
|
||||
} xdp_stats_map SEC(".maps");
|
||||
|
||||
/*
|
||||
Attempt to parse the IPv4 source address from the packet.
|
||||
Returns 0 if there is no IPv4 header field; otherwise returns non-zero.
|
||||
*/
|
||||
static __always_inline int parse_ip_src_addr(struct xdp_md *ctx, __u32 *ip_src_addr) {
|
||||
void *data_end = (void *)(long)ctx->data_end;
|
||||
void *data = (void *)(long)ctx->data;
|
||||
|
||||
// First, parse the ethernet header.
|
||||
struct ethhdr *eth = data;
|
||||
if ((void *)(eth + 1) > data_end) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (eth->h_proto != bpf_htons(ETH_P_IP)) {
|
||||
// The protocol is not IPv4, so we can't parse an IPv4 source address.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Then parse the IP header.
|
||||
struct iphdr *ip = (void *)(eth + 1);
|
||||
if ((void *)(ip + 1) > data_end) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return the source IP address in network byte order.
|
||||
*ip_src_addr = (__u32)(ip->saddr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("xdp")
|
||||
int xdp_prog_func(struct xdp_md *ctx) {
|
||||
__u32 ip;
|
||||
if (!parse_ip_src_addr(ctx, &ip)) {
|
||||
// Not an IPv4 packet, so don't count it.
|
||||
goto done;
|
||||
}
|
||||
|
||||
__u32 *pkt_count = bpf_map_lookup_elem(&xdp_stats_map, &ip);
|
||||
if (!pkt_count) {
|
||||
// No entry in the map for this IP address yet, so set the initial value to 1.
|
||||
__u32 init_pkt_count = 1;
|
||||
bpf_map_update_elem(&xdp_stats_map, &ip, &init_pkt_count, BPF_ANY);
|
||||
} else {
|
||||
// Entry already exists for this IP address,
|
||||
// so increment it atomically using an LLVM built-in.
|
||||
__sync_fetch_and_add(pkt_count, 1);
|
||||
}
|
||||
|
||||
done:
|
||||
// Try changing this to XDP_DROP and see what happens!
|
||||
return XDP_PASS;
|
||||
}
|
||||
Reference in New Issue
Block a user