1
1
[ ![ OCaml-CI Build Status] ( https://img.shields.io/endpoint?url=https://ocaml.ci.dev/badge/koonwen/ocaml-libbpf/main&logo=ocaml )] ( https://ocaml.ci.dev/github/koonwen/ocaml-libbpf )
2
2
3
3
# ocaml-libbpf
4
- Libbpf C-bindings for eBPF userspace programs .
4
+ Libbpf C-bindings for loading eBPF ELF files into the kernel .
5
5
6
6
Writing eBPF programs consist of two distinct parts. Implementing the
7
7
code that executes in-kernel ** and** user-level code responsible for
@@ -12,16 +12,23 @@ the raw low-level bindings as well as a set of high-level API's for
12
12
handling your eBPF objects. As of now, the kernel part must still be
13
13
written in [ restricted
14
14
C] ( https://stackoverflow.com/questions/57688344/what-is-not-allowed-in-restricted-c-for-ebpf )
15
- and compiled to eBPF bytecode.
15
+ and compiled with llvm to eBPF bytecode.
16
16
17
17
The full API set of Libbpf is quite large, see [ supported] ( supported.json ) for the list
18
18
of currently bound API's. Contributions are welcome.
19
19
20
+ ### External dependencies
21
+ ocaml\_ libbpf depends on the system package of ` libbpf ` . This is
22
+ automatically installed along with ocaml_libbpf
23
+
20
24
# Usage
21
- See ` examples ` directory on how ocaml\_ libbpf can be used to interact
22
- with eBPF kernel programs defined in * .bpf.c source files. The
23
- high-level API's provided in ocaml\_ libbpf make it easy to perform
24
- repetitive tasks like open/load/linking/initializing/teardown.
25
+ See ` examples ` directory on how ocaml\_ libbpf can be used to load eBPF
26
+ ELF files into the kernel and interact with the loaded kernel program.
27
+ The eBPF kernel programs are defined in * .bpf.c source files and are
28
+ compiled with clang as specified in the ` dune ` rules. ocaml\_ libbpf
29
+ exposes some high-level API's provided in ocaml\_ libbpf make it easy
30
+ to perform repetitive tasks like
31
+ open/load/linking/initializing/teardown.
25
32
26
33
To run these examples, clone this repository and set up the package with
27
34
``` bash
@@ -31,17 +38,31 @@ opam install . --deps-only
31
38
eval $( opam env)
32
39
```
33
40
34
- then use ` make <minimal/kprobe/bootstrap> ` . These examples are taken
35
- from [ libbpf-bootstrap] ( https://github.com/libbpf/libbpf-bootstrap )
41
+ then run ` make < minimal | kprobe | bootstrap | tc > ` to try out the
42
+ different bpf programs. These examples are all taken from
43
+ [ libbpf-bootstrap] ( https://github.com/libbpf/libbpf-bootstrap )
36
44
repository and rewritten in OCaml.
37
45
38
46
### Open/Load/Link
39
47
Now let's run through an example of how we would use
40
48
ocaml\_ libbpf. This usage tutorial assumes some knowledge of how to
41
- write eBPF programs in C. If not, you can check out this
49
+ write eBPF kernel programs in C compile them to ELF files. If not, you
50
+ can check out this
42
51
[ resource] ( https://nakryiko.com/posts/libbpf-bootstrap/#the-bpf-side ) . ocaml\_ libbpf
43
52
provides an easy API to install your eBPF program into the kernel. Say
44
- your eBPF kernel program looks like this.
53
+ your eBPF kernel program looks like this where we print something
54
+ whenever the syscall ` write ` event occurs. We also want to implement a
55
+ filtering mechanism to only print on ` write ` calls for our process. To
56
+ do this, we initialize a BPF array map with a single entry that works
57
+ like a holder for our global variable. The BPF map is neccessary to
58
+ because it allows us to communicate values between user and kernel
59
+ space.
60
+
61
+ > libbpf in fact already supports declarations of global variables in
62
+ > the usual form with the ability to manage them in user
63
+ > space. However for various technical reasons, ocaml\_ libbpf does not
64
+ > enable that feature yet. So we use the old style of working with
65
+ > global variables here.
45
66
46
67
``` c
47
68
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
@@ -82,24 +103,49 @@ int handle_tp(void *ctx) {
82
103
83
104
```
84
105
85
- Users just need to provide the path to the compiled bpf
86
- object, the name of the program and optionally an initialization
87
- function.
106
+ After compilation to eBPF ELF file as `minimal.o`. Users just need to
107
+ provide the path to this ELF file along with the name of the program
108
+ and optionally an initialization function. Note that the name of the
109
+ program refers to the function identifier under the SEC(...)
110
+ attribute, in this case it is "handle_tp".
88
111
89
112
```ocaml
90
113
let obj_path = "minimal.bpf.o"
91
114
let program_names = [ "handle_tp" ]
92
- let map = "globals"
93
115
94
- (* Load PID into BPF map*)
95
- let before_link obj =
96
- let pid = Unix.getpid () |> Signed.Long.of_int in
97
- let global_map = bpf_object_find_map_by_name obj map in
98
- bpf_map_update_elem ~key_ty:Ctypes.int ~val_ty:Ctypes.long global_map 0 pid
116
+ let () =
117
+ with_bpf_object_open_load_link ~obj_path ~program_names ~before_link
118
+ (fun obj link ->
119
+
120
+ < user code to interact with bpf program running in kernel >
121
+
122
+ )
123
+ ```
124
+
125
+ The API provided by ocaml\_ libbpf ` with_bpf_object_open_load_link ` is
126
+ a context manager that ensures the proper cleanup of resources if a
127
+ failure is encountered. Right now our loaded kernel program is
128
+ attached to the kernel and then immediately unloaded, users are
129
+ responsible for keeping the bpf program alive by looping within the
130
+ function block.
131
+
132
+ > Users may also pin the bpf program to persist after user code
133
+ > exits. Do note that if pinning is desired, users should not use the
134
+ > ` with_bpf_object_open_load_link ` API and instead manually load and
135
+ > attach their bpf program since the context manager shutdowns all
136
+ > resources on exit.
137
+
138
+ Now let's add some looping logic to keep the loaded bpf program alive.
139
+
140
+ ``` ocaml
141
+ let obj_path = "minimal.bpf.o"
142
+ let program_names = [ "handle_tp" ]
99
143
100
144
let () =
101
145
with_bpf_object_open_load_link ~obj_path ~program_names ~before_link
102
- (fun _obj _link ->
146
+ (fun obj link ->
147
+
148
+ (* Set up signal handlers *)
103
149
let exitting = ref true in
104
150
let sig_handler = Sys.Signal_handle (fun _ -> exitting := false) in
105
151
Sys.(set_signal sigint sig_handler);
@@ -109,26 +155,44 @@ let () =
109
155
"Successfully started! Please run `sudo cat \
110
156
/sys/kernel/debug/tracing/trace_pipe` to see output of the BPF \
111
157
programs.\n\
112
- %!";
158
+ %!"
113
159
114
- (* Loop until Ctrl-C is called *)
160
+ (* Loop until Ctrl-C is called *)
115
161
while !exitting do
116
162
Printf.eprintf ".%!";
117
163
Unix.sleepf 1.0
118
164
done)
119
165
```
120
166
167
+ Our bpf program is now running in the kernel until we decide to
168
+ interrupt it. However, it doesn't do exactly what we want. In
169
+ particular, it doesn't filter for our process PID. This is because we
170
+ haven't loaded our process PID into the BPF map. To do this, we need
171
+ the name of the map we declared in the ` minimal.bpf.c ` program. In
172
+ this case, our BPF array map was named ` globals ` .
173
+
174
+ ``` ocaml
175
+ let map = "globals"
176
+
177
+ (* Load PID into BPF map *)
178
+ let before_link obj =
179
+ let pid = Unix.getpid () |> Signed.Long.of_int in
180
+ let global_map = bpf_object_find_map_by_name obj map in
181
+ (* When updating an element, users need to specify the type of the key and value
182
+ declared by the map which checks that the key and value size are consistent. *)
183
+ bpf_map_update_elem ~key_ty:Ctypes.int ~val_ty:Ctypes.long global_map 0 pid
184
+ ```
185
+
186
+ Put together in [ minimal.ml] ( ./examples/minimal.ml ) , your bpf program
187
+ runs in kernel and print to the trace pipe every second.
188
+
121
189
### Maps
122
190
` ocaml_libbpf_maps ` is an optional convenience package that provides
123
191
wrappers for BPF maps. Currently only Ringbuffer maps are added. An
124
192
example usage of them can be found in
125
193
[ examples/bootstrap.ml] ( ./examples/bootstrap.ml ) . This has been
126
194
packaged separately since it drags in ` libffi ` dependency.
127
195
128
- ### External dependencies
129
- ocaml\_ libbpf depends on the system package of ` libbpf ` . This is
130
- automatically installed along with ocaml_libbpf
131
-
132
196
## Notes on compatibility
133
197
> Libbpf is designed to be kernel-agnostic and work across multitude
134
198
> of kernel versions. It has built-in mechanisms to gracefully handle
0 commit comments