Skip to content

Commit c30f689

Browse files
jcrambbelyalov
andauthored
[kprobe] add support for kprobes (dropbox#33)
Co-authored-by: Konstantin Belyalov <[email protected]>
1 parent 28461c0 commit c30f689

28 files changed

+1350
-85
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*.so
66
*.dylib
77
*.elf
8+
*.swp
89
itest_test
910

1011
# Test binary, built with `go test -c`

.travis.yml

+5
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ go:
1212
before_install:
1313
- sudo apt-get update
1414
- sudo apt-get install -y clang llvm
15+
- export GO111MODULE=on
1516

1617
script:
18+
# ad-hoc for golang versions < 1.11
19+
# testify now uses go 1.13 features like errors.Is(), so switching to last supported version
20+
- rm -rf $GOPATH/src/github.com/stretchr/testify
21+
- git clone -b v1.6.1 https://github.com/stretchr/testify.git $GOPATH/src/github.com/stretchr/testify
1722
# Verify for formatting issues
1823
- if [ -n "$(gofmt -s -l .)" ]; then echo "Please fix source formatting by 'go fmt ./...'"; false; fi
1924
# Run unit tests

README.md

+46
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,52 @@ And the `go` part:
8181
```
8282
Simple? Check [full XDP dump example](https://github.com/dropbox/goebpf/tree/master/examples/xdp/xdp_dump)
8383

84+
## Kprobes
85+
Library currently has support for `kprobes` and `kretprobes`.
86+
It can be as simple as:
87+
88+
```c
89+
// kprobe handler function
90+
SEC("kprobe/SyS_execve")
91+
int execve_entry(struct pt_regs *ctx) {
92+
93+
// read comm
94+
char comm[32];
95+
bpf_get_current_comm(&e.comm, sizeof(e.comm));
96+
97+
// read first argument of execve
98+
char filename[64];
99+
bpf_probe_read(filename, sizeof(filename), PT_REGS_PARM1(ctx));
100+
101+
// display execve call details
102+
bpf_printk("[exec] comm: '%s', filename: '%s'\n", comm, filename);
103+
104+
return 0;
105+
}
106+
```
107+
108+
And the `go` part:
109+
```go
110+
// cleanup old probes
111+
if err := goebpf.CleanupProbes(); err != nil {
112+
log.Println(err)
113+
}
114+
115+
// load ebpf program
116+
p, err := LoadProgram("ebpf_prog/kprobe.elf")
117+
if err != nil {
118+
log.Fatal(err)
119+
}
120+
p.ShowInfo()
121+
122+
// attach ebpf kprobes
123+
if err := p.AttachProbes(); err != nil {
124+
log.Fatal(err)
125+
}
126+
defer p.DetachProbes()
127+
```
128+
Simple? Check [exec dump example](https://github.com/dropbox/goebpf/tree/master/examples/kprobe/exec_dump)
129+
84130
## Good readings
85131
- [Cilium BPF and XDP Reference Guide](https://docs.cilium.io/en/latest/bpf/)
86132
- [Prototype Kernel: XDP](https://prototype-kernel.readthedocs.io/en/latest/networking/XDP/index.html)

bpf_helpers.h

+71-33
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,16 @@ enum bpf_map_type {
5050
};
5151

5252
/* flags for BPF_MAP_UPDATE_ELEM command */
53-
#define BPF_ANY 0 /* create new element or update existing */
53+
#define BPF_ANY 0 /* create new element or update existing */
5454
#define BPF_NOEXIST 1 /* create new element if it didn't exist */
55-
#define BPF_EXIST 2 /* update existing element */
56-
#define BPF_F_LOCK 4 /* spin_lock-ed map_lookup/map_update */
55+
#define BPF_EXIST 2 /* update existing element */
56+
#define BPF_F_LOCK 4 /* spin_lock-ed map_lookup/map_update */
5757

5858
/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and
5959
* BPF_FUNC_perf_event_read_value flags.
6060
*/
61-
#define BPF_F_INDEX_MASK 0xffffffffULL
62-
#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK
61+
#define BPF_F_INDEX_MASK 0xffffffffULL
62+
#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK
6363

6464
// A helper structure used by eBPF C program
6565
// to describe map attributes to BPF program loader
@@ -96,6 +96,44 @@ enum socket_filter_action {
9696
SOCKET_FILTER_ALLOW,
9797
};
9898

99+
// Kprobe required constants / structs
100+
// (arch/x86/include/asm/ptrace.h)
101+
#define PT_REGS_PARM1(x) ((x)->di)
102+
#define PT_REGS_PARM2(x) ((x)->si)
103+
#define PT_REGS_PARM3(x) ((x)->dx)
104+
#define PT_REGS_PARM4(x) ((x)->r10)
105+
#define PT_REGS_PARM5(x) ((x)->r8)
106+
#define PT_REGS_PARM6(x) ((x)->r9)
107+
#define PT_REGS_RET(x) ((x)->sp)
108+
#define PT_REGS_FP(x) ((x)->bp)
109+
#define PT_REGS_RC(x) ((x)->ax)
110+
#define PT_REGS_SP(x) ((x)->sp)
111+
#define PT_REGS_IP(x) ((x)->ip)
112+
113+
struct pt_regs {
114+
unsigned long r15;
115+
unsigned long r14;
116+
unsigned long r13;
117+
unsigned long r12;
118+
unsigned long bp;
119+
unsigned long bx;
120+
unsigned long r11;
121+
unsigned long r10;
122+
unsigned long r9;
123+
unsigned long r8;
124+
unsigned long ax;
125+
unsigned long cx;
126+
unsigned long dx;
127+
unsigned long si;
128+
unsigned long di;
129+
unsigned long orig_ax;
130+
unsigned long ip;
131+
unsigned long cs;
132+
unsigned long flags;
133+
unsigned long sp;
134+
unsigned long ss;
135+
};
136+
99137
#define bpf_likely(X) __builtin_expect(!!(X), 1)
100138
#define bpf_unlikely(X) __builtin_expect(!!(X), 0)
101139
#define UNUSED __attribute__((unused))
@@ -157,45 +195,45 @@ struct __sk_buff {
157195

158196
/* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */
159197
__u32 family;
160-
__u32 remote_ip4; /* Stored in network byte order */
161-
__u32 local_ip4; /* Stored in network byte order */
162-
__u32 remote_ip6[4]; /* Stored in network byte order */
198+
__u32 remote_ip4; /* Stored in network byte order */
199+
__u32 local_ip4; /* Stored in network byte order */
200+
__u32 remote_ip6[4]; /* Stored in network byte order */
163201
__u32 local_ip6[4]; /* Stored in network byte order */
164-
__u32 remote_port; /* Stored in network byte order */
165-
__u32 local_port; /* stored in host byte order */
202+
__u32 remote_port; /* Stored in network byte order */
203+
__u32 local_port; /* stored in host byte order */
166204
/* ... here. */
167205

168206
__u32 data_meta;
169207
};
170208

171209
struct bpf_sock_tuple {
172-
union {
173-
struct {
174-
__be32 saddr;
175-
__be32 daddr;
176-
__be16 sport;
177-
__be16 dport;
178-
} ipv4;
179-
struct {
180-
__be32 saddr[4];
181-
__be32 daddr[4];
182-
__be16 sport;
183-
__be16 dport;
184-
} ipv6;
185-
};
210+
union {
211+
struct {
212+
__be32 saddr;
213+
__be32 daddr;
214+
__be16 sport;
215+
__be16 dport;
216+
} ipv4;
217+
struct {
218+
__be32 saddr[4];
219+
__be32 daddr[4];
220+
__be16 sport;
221+
__be16 dport;
222+
} ipv6;
223+
};
186224
};
187225

188226
struct bpf_spin_lock {
189-
__u32 val;
227+
__u32 val;
190228
};
191229

192230
struct bpf_sysctl {
193-
__u32 write; /* Sysctl is being read (= 0) or written (= 1).
194-
* Allows 1,2,4-byte read, but no write.
195-
*/
196-
__u32 file_pos; /* Sysctl file position to read from, write to.
197-
* Allows 1,2,4-byte read an 4-byte write.
198-
*/
231+
__u32 write; /* Sysctl is being read (= 0) or written (= 1).
232+
* Allows 1,2,4-byte read, but no write.
233+
*/
234+
__u32 file_pos; /* Sysctl file position to read from, write to.
235+
* Allows 1,2,4-byte read an 4-byte write.
236+
*/
199237
};
200238

201239
// BPF helper functions supported on linux kernel 5.2+
@@ -801,8 +839,8 @@ UNUSED static int bpf_xdp_adjust_head(struct xdp_md *ctx, int offset) {
801839
return 0;
802840
}
803841

804-
UNUSED static int bpf_perf_event_output(void *ctx, void *map, __u64 index, void *data, __u32 size)
805-
{
842+
UNUSED static int bpf_perf_event_output(void *ctx, void *map, __u64 index,
843+
void *data, __u32 size) {
806844
return 0;
807845
}
808846

doc.go

+52
Original file line numberDiff line numberDiff line change
@@ -119,5 +119,57 @@ A simple example could be to log all TCP SYN packets into user space from XDP pr
119119
}
120120
}
121121
122+
Kprobes
123+
124+
There are currently two types of supported probes: kprobes, and kretprobes
125+
(also called return probes). A kprobe can be inserted on virtually
126+
any instruction in the kernel. A return probe fires when a specified
127+
function returns.
128+
129+
For example, you can trigger eBPF code to run when a kernel function starts
130+
by attaching the program to a “kprobe” event. Because it runs in the kernel,
131+
eBPF code is extremely high performance.
132+
133+
A simple example could be to log all process execution events into user space
134+
from Kprobe program:
135+
136+
BPF_MAP_DEF(events) = {
137+
.map_type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
138+
.max_entries = 1024,
139+
};
140+
BPF_MAP_ADD(events);
141+
142+
SEC("kprobe/guess_execve")
143+
int execve_entry(struct pt_regs *ctx) {
144+
// ...
145+
146+
event_t e = {0};
147+
e.ktime_ns = bpf_ktime_get_ns();
148+
e.pid = bpf_get_current_pid_tgid() >> 32;
149+
e.uid = bpf_get_current_uid_gid() >> 32;
150+
e.gid = bpf_get_current_uid_gid();
151+
bpf_get_current_comm(&e.comm, sizeof(e.comm));
152+
153+
buf_write(buf, (void *)&e, sizeof(e));
154+
buf_strcat(buf, (void *)args[0]);
155+
buf_strcat_argv(buf, (void *)args[1]);
156+
buf_perf_output(ctx);
157+
158+
return 0;
159+
}
160+
161+
162+
// Cleanup old probes
163+
err := goebpf.CleanupProbes()
164+
165+
// Load eBPF compiled binary
166+
bpf := goebpf.NewDefaultEbpfSystem()
167+
bpf.LoadElf("kprobe.elf")
168+
program := bpf.GetProgramByName("kprobe") // name matches function name in C
169+
170+
// Attach kprobes
171+
err = p.AttachProbes()
172+
// Detach them once done
173+
defer p.DetachProbes()
122174
*/
123175
package goebpf

ebpf.go

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ type Program interface {
3939
Detach() error
4040
// Returns program name as it defined in C code
4141
GetName() string
42+
// Returns section name for the program
43+
GetSection() string
4244
// Returns program file descriptor (given by kernel)
4345
GetFd() int
4446
// Returns size of program in bytes

examples/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Please note that `eBPF` is supported only by Linux, it will not work on `MacOS`!
77
- *XDP*: [Simple packets protocol counter](https://github.com/dropbox/goebpf/tree/master/examples/xdp/packet_counter)
88
- *XDP*: [Basic Firewall](https://github.com/dropbox/goebpf/tree/master/examples/xdp/basic_firewall)
99
- *PerfEvents*: [XDP Dump](https://github.com/dropbox/goebpf/tree/master/examples/xdp/xdp_dump)
10+
- *Kprobes*: [Exec Dump](https://github.com/dropbox/goebpf/tree/master/examples/kprobe/exec_dump)
1011

1112
## How to run
1213
All examples actually contain 2 parts:

examples/kprobe/exec_dump/Makefile

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright (c) 2020 Dropbox, Inc.
2+
# Full license can be found in the LICENSE file.
3+
4+
GOCMD := go
5+
GOBUILD := $(GOCMD) build
6+
GOCLEAN := $(GOCMD) clean
7+
CLANG := clang
8+
CLANG_INCLUDE := -I../../..
9+
10+
GO_SOURCE := main.go
11+
GO_BINARY := main
12+
13+
EBPF_SOURCE := ebpf_prog/kprobe.c
14+
EBPF_BINARY := ebpf_prog/kprobe.elf
15+
16+
all: build_bpf build_go
17+
18+
build_bpf: $(EBPF_BINARY)
19+
20+
build_go: $(GO_BINARY)
21+
22+
clean:
23+
$(GOCLEAN)
24+
rm -f $(GO_BINARY)
25+
rm -f $(EBPF_BINARY)
26+
27+
$(EBPF_BINARY): $(EBPF_SOURCE)
28+
$(CLANG) $(CLANG_INCLUDE) -O2 -target bpf -c $^ -o $@
29+
30+
$(GO_BINARY): $(GO_SOURCE)
31+
$(GOBUILD) -v -o $@

0 commit comments

Comments
 (0)