this repo has no description
at fix-build 5.0 kB view raw
1{0 ocaml-libbpf} 2 3OCaml bindings to {{:https://github.com/libbpf/libbpf}libbpf} C 4library for loading eBPF programs into the linux kernel. 5 6{1 Introduction} 7 8Writing eBPF programs consist of two distinct parts. Implementing the 9code that executes in-kernel and user-level code responsible for 10loading/initializing/linking/teardown of the in-kernel code. This 11OCaml library provides the latter via binding the C libbpf library. It 12exposes both the raw low-level bindings as well as a set of high-level 13API's for handling your eBPF objects. As of now, the kernel part must 14still be written in 15{{:https://stackoverflow.com/questions/57688344/what-is-not-allowed-in-restricted-c-for-ebpf} 16restricted C} and compiled with llvm to eBPF bytecode. 17 18For the high-level APIs: {!Libbpf} 19 20For the low-level bindings: {!Libbpf.C}. 21 22{1:Tutorial Tutorial} 23 24This example assumes the user has knowledge of how to implement the 25kernel part of a eBPF program. If not, you can check out this 26{{:https://nakryiko.com/posts/libbpf-bootstrap/#the-bpf-side} 27resource} first. Consider the following kernel eBPF program named {b 28minimal.bpf.c}: 29 30{@c[ 31// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 32/* Copyright (c) 2020 Facebook */ 33#include <linux/bpf.h> 34#include "bpf/bpf_helpers.h" /* This is from our libbpf library */ 35 36char LICENSE[] SEC("license") = "Dual BSD/GPL"; 37 38/* Globals implemented as an array */ 39struct { 40 __uint(type, BPF_MAP_TYPE_ARRAY); 41 __uint(max_entries, 1); 42 __type(key, int); 43 __type(value, long); 44} globals SEC(".maps"); 45 46int my_pid_index = 0; 47 48SEC("tp/syscalls/sys_enter_write") 49int handle_tp(void *ctx) { 50 int pid = bpf_get_current_pid_tgid() >> 32; 51 52 long *my_pid; 53 my_pid = bpf_map_lookup_elem(&globals, &my_pid_index); 54 if (my_pid == NULL) { 55 bpf_printk("Error got NULL"); 56 return 1; 57 }; 58 59 if (pid != *my_pid) 60 return 0; 61 62 bpf_printk("Hello, BPF triggered from PID %d", pid); 63 64 return 0; 65} 66]} 67 68After compilation to eBPF ELF file as {b "minimal.o"}. Users just need 69to provide the path to this ELF file along with the name of the 70program and optionally an initialization function. Note that the name 71of the program refers to the function identifier under the SEC(...) 72attribute, in this case it is {b "handle_tp"}. 73 74{@ocaml[ 75open Libbpf 76 77let obj_path = "minimal.bpf.o" 78let program_names = [ "handle_tp" ] 79 80let () = 81 with_bpf_object_open_load_link ~obj_path ~program_names ~before_link 82 (fun obj link -> (* Do something *)) 83]} 84 85The context manager {{!Libbpf.with_bpf_object_open_load_link} 86with_bpf_object_open_load_link} is a convenience wrapper for all the 87neccessary steps to load up your eBPF program into the kernel. 88 89If we don't specify anything in the body of the function marked with 90{b (* Do something *)}, our loaded kernel program will be unloaded 91immediately. In this case, we will add some looping logic to keep the 92program running in the kernel and add a set of signal handlers to 93escape the loop. 94 95{@ocaml[ 96let obj_path = "minimal.bpf.o" 97let program_names = [ "handle_tp" ] 98 99let () = 100 with_bpf_object_open_load_link ~obj_path ~program_names ~before_link 101 (fun obj link -> 102 103 (* Set up signal handlers *) 104 let exitting = ref true in 105 let sig_handler = Sys.Signal_handle (fun _ -> exitting := false) in 106 Sys.(set_signal sigint sig_handler); 107 Sys.(set_signal sigterm sig_handler); 108 109 Printf.printf 110 "Successfully started! Please run `sudo cat \ 111 /sys/kernel/debug/tracing/trace_pipe` to see output of the BPF \ 112 programs.\n\ 113 %!" 114 115 (* Loop until Ctrl-C is called *) 116 while !exitting do 117 Printf.eprintf ".%!"; 118 Unix.sleepf 1.0 119 done) 120]} 121 122Our bpf program is now running in the kernel until we decide to 123interrupt it. However, it doesn't do exactly what we want. In 124particular, it doesn't filter for our process PID. This is because we 125haven't loaded our process PID into the BPF map. To do this, we need 126the name of the map we declared by our {b minimal.bpf.c} program. In 127this case, our BPF array map was named {b globals}. 128 129{@ocaml[ 130let map = "globals" 131 132(* Load PID into BPF map *) 133let before_link obj = 134 let pid = Unix.getpid () |> Signed.Long.of_int in 135 let global_map = bpf_object_find_map_by_name obj map in 136 (* When updating an element, users need to specify the type of the key and value 137 declared by the map which checks that the key and value size are consistent. *) 138 bpf_map_update_elem ~key_ty:Ctypes.int ~val_ty:Ctypes.long global_map 0 pid 139]} 140 141Now if we combine the two, we can run this program and see the output 142interactively being printed to the trace pipe. 143 144{1 Notice!} 145 146root permissions are required when you run eBPF programs. This is a 147consequence of the fact that they are loaded into the kernel. To offer 148some assurance though, eBPF programs always have to pass through a 149verifier before they can be loaded. This ensures that eBPF programs 150aren't able crash to crash the kernel. For more information, read 151{{:https://ebpf.io/what-is-ebpf/#ebpf-safety} here}.