Skip to content

Commit 106e753

Browse files
committed
update README
1 parent 71492cb commit 106e753

File tree

1 file changed

+90
-26
lines changed

1 file changed

+90
-26
lines changed

README.md

Lines changed: 90 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[![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)
22

33
# ocaml-libbpf
4-
Libbpf C-bindings for eBPF userspace programs.
4+
Libbpf C-bindings for loading eBPF ELF files into the kernel.
55

66
Writing eBPF programs consist of two distinct parts. Implementing the
77
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
1212
handling your eBPF objects. As of now, the kernel part must still be
1313
written in [restricted
1414
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.
1616

1717
The full API set of Libbpf is quite large, see [supported](supported.json) for the list
1818
of currently bound API's. Contributions are welcome.
1919

20+
### External dependencies
21+
ocaml\_libbpf depends on the system package of `libbpf`. This is
22+
automatically installed along with ocaml_libbpf
23+
2024
# 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.
2532

2633
To run these examples, clone this repository and set up the package with
2734
```bash
@@ -31,17 +38,31 @@ opam install . --deps-only
3138
eval $(opam env)
3239
```
3340

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)
3644
repository and rewritten in OCaml.
3745

3846
### Open/Load/Link
3947
Now let's run through an example of how we would use
4048
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
4251
[resource](https://nakryiko.com/posts/libbpf-bootstrap/#the-bpf-side). ocaml\_libbpf
4352
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.
4566
4667
```c
4768
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
@@ -82,24 +103,49 @@ int handle_tp(void *ctx) {
82103

83104
```
84105
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".
88111
89112
```ocaml
90113
let obj_path = "minimal.bpf.o"
91114
let program_names = [ "handle_tp" ]
92-
let map = "globals"
93115
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" ]
99143
100144
let () =
101145
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 *)
103149
let exitting = ref true in
104150
let sig_handler = Sys.Signal_handle (fun _ -> exitting := false) in
105151
Sys.(set_signal sigint sig_handler);
@@ -109,26 +155,44 @@ let () =
109155
"Successfully started! Please run `sudo cat \
110156
/sys/kernel/debug/tracing/trace_pipe` to see output of the BPF \
111157
programs.\n\
112-
%!";
158+
%!"
113159
114-
(* Loop until Ctrl-C is called *)
160+
(* Loop until Ctrl-C is called *)
115161
while !exitting do
116162
Printf.eprintf ".%!";
117163
Unix.sleepf 1.0
118164
done)
119165
```
120166

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+
121189
### Maps
122190
`ocaml_libbpf_maps` is an optional convenience package that provides
123191
wrappers for BPF maps. Currently only Ringbuffer maps are added. An
124192
example usage of them can be found in
125193
[examples/bootstrap.ml](./examples/bootstrap.ml). This has been
126194
packaged separately since it drags in `libffi` dependency.
127195

128-
### External dependencies
129-
ocaml\_libbpf depends on the system package of `libbpf`. This is
130-
automatically installed along with ocaml_libbpf
131-
132196
## Notes on compatibility
133197
> Libbpf is designed to be kernel-agnostic and work across multitude
134198
> of kernel versions. It has built-in mechanisms to gracefully handle

0 commit comments

Comments
 (0)