this repo has no description

Plenty of work

+2 -2
src/bin/main.ml
···
match Sys.argv.(1) with
| "shelter" ->
let dir = state_dir env#fs "shelter" in
-
Shelter.main env#clock env#process_mgr dir
+
Shelter.main env#fs env#clock env#process_mgr dir
| _ | (exception Invalid_argument _) ->
let dir = state_dir env#fs "passthrough" in
-
Pass.main env#clock env#process_mgr dir
+
Pass.main env#fs env#clock env#process_mgr dir
+10 -10
src/lib/cshell.ml
···
module Make (H : History.S) (Engine : Engine.S with type entry = H.t) = struct
module Store = Irmin_fs_unix.KV.Make (H)
-
let run clock proc store =
+
let run fs clock proc store =
let store = History.Store ((module Store), store) in
-
Engine.init store;
-
let rec loop store exit_code =
+
let initial_ctx = Engine.init fs proc store in
+
let rec loop store ctx exit_code =
let prompt = Engine.prompt exit_code store in
match LNoise.linenoise prompt with
| None -> ()
| Some input -> (
let action = Engine.action_of_command input in
-
match Engine.run clock proc store action with
+
match Engine.run fs clock proc (store, ctx) action with
| Error (Eio.Process.Child_error exit_code) ->
Fmt.epr "%a\n%!" Eio.Process.pp_status exit_code;
-
loop store exit_code
+
loop store ctx exit_code
| Error (Eio.Process.Executable_not_found m) ->
Fmt.epr "cshell: excutable not found %s\n%!" m;
-
loop store (`Exited 127)
-
| Ok store -> loop store (`Exited 0))
+
loop store ctx (`Exited 127)
+
| Ok (store, ctx) -> loop store ctx (`Exited 0))
in
-
loop store (`Exited 0)
+
loop store initial_ctx (`Exited 0)
-
let main clock proc directory =
+
let main fs clock proc directory =
Irmin_fs.run directory @@ fun () ->
let conf = Irmin_fs.config (Eio.Path.native_exn directory) in
let repo = Store.Repo.v conf in
let store = Store.main repo in
-
run clock proc store
+
run fs clock proc store
end
+12 -3
src/lib/engine.ml
···
type entry
-
val init : entry History.t -> unit
+
type ctx
+
(** A context that is not persisted, but is passed through each loop of the
+
shell *)
+
+
val init :
+
_ Eio.Path.t ->
+
Eio_unix.Process.mgr_ty Eio_unix.Process.mgr ->
+
entry History.t ->
+
ctx
(** [init store] will be called before entering the shell loop. You may wish
to setup history completions etc. with LNoise. *)
val run :
+
_ Eio.Path.t ->
_ Eio.Time.clock ->
Eio_unix.Process.mgr_ty Eio_unix.Process.mgr ->
-
entry History.t ->
+
entry History.t * ctx ->
action ->
-
(entry History.t, Eio.Process.error) result
+
(entry History.t * ctx, Eio.Process.error) result
(** [run history action] runs the action in [history]. Return a new [history]
that can be persisted *)
+9 -5
src/lib/passthrough/cshell_passthrough.ml
···
let history_key = [ "history" ]
let key () = history_key @ [ string_of_float @@ Unix.gettimeofday () ]
-
let init (Cshell.History.Store ((module S), store) : entry Cshell.History.t) =
+
type ctx = unit
+
+
let init _ _ (Cshell.History.Store ((module S), store) : entry Cshell.History.t)
+
=
match S.list store history_key with
| [] -> ()
| xs ->
···
in
List.iter (fun v -> LNoise.history_add v |> ignore) entries
-
let run clock proc
-
((Cshell.History.Store ((module S), store) : entry Cshell.History.t) as
-
full_store) (Exec command) =
+
let run _fs clock proc
+
( ((Cshell.History.Store ((module S), store) : entry Cshell.History.t) as
+
full_store),
+
() ) (Exec command) =
let info () =
S.Info.v ~message:"cshell" (Eio.Time.now clock |> Int64.of_float)
in
···
if res = `Exited 0 then (
S.set_exn ~info store (key ()) command;
let _ : (unit, string) result = LNoise.history_add command in
-
Ok full_store)
+
Ok (full_store, ()))
else Error (Eio.Process.Child_error res)
with Eio.Exn.Io (Eio.Process.E e, _) -> Error e
+39
src/lib/shelter/diff.ml
···
+
type diff =
+
| Modified of string
+
| Created of string
+
| Renamed of string * string
+
| Removed of string
+
[@@deriving repr]
+
+
type t = diff list [@@deriving repr]
+
+
let truncate_path s =
+
match Astring.String.cut ~sep:"rootfs" s with Some (_, p) -> p | None -> s
+
+
let parse_row = function
+
| [ "M"; s ] ->
+
let path = truncate_path s in
+
if String.equal path "" then None else Some (Modified path)
+
| [ "+"; s ] ->
+
let path = truncate_path s in
+
if String.equal path "" then None else Some (Created path)
+
| [ "R"; a; b ] ->
+
let a_path = truncate_path a in
+
let b_path = truncate_path b in
+
Some (Renamed (a_path, b_path))
+
| [ "-"; s ] ->
+
let path = truncate_path s in
+
if String.equal path "" then None else Some (Removed path)
+
| s ->
+
Fmt.invalid_arg "Unknown ZFS diff: %a"
+
(Fmt.list ~sep:Fmt.comma Fmt.string)
+
s
+
+
let of_zfs s : t =
+
let lines = String.split_on_char '\n' s in
+
let tsv =
+
List.map (String.split_on_char '\t') lines
+
|> List.map (List.filter (fun s -> not (String.equal "" s)))
+
|> List.filter (function [] -> false | _ -> true)
+
in
+
List.filter_map parse_row tsv
+1 -1
src/lib/shelter/dune
···
(public_name cshell.shelter)
(preprocess
(pps ppx_repr))
-
(libraries cshell cid void))
+
(libraries cshell cid void zfs))
+26
src/lib/shelter/fetch.ml
···
+
let ( / ) = Eio.Path.( / )
+
+
let get_image ~dir ~proc image =
+
let container_id =
+
Eio.Process.parse_out proc Eio.Buf_read.take_all
+
[ "docker"; "run"; "-d"; image ]
+
|> String.trim
+
in
+
let tar = image ^ ".tar.gz" in
+
let dir_s = Eio.Path.native_exn dir in
+
let () =
+
Eio.Process.run proc
+
[ "docker"; "export"; container_id; "-o"; Filename.concat dir_s tar ]
+
in
+
Eio.Path.mkdir ~perm:0o777 (dir / "rootfs");
+
let () =
+
Eio.Process.run proc
+
[
+
"tar";
+
"-xf";
+
Filename.concat dir_s "alpine.tar.gz";
+
"-C";
+
Filename.concat dir_s "rootfs";
+
]
+
in
+
Filename.concat dir_s "rootfs"
+173 -56
src/lib/shelter/shelter.ml
···
open Eio
+
module Store = Store
+
module H = Cshell.History
-
module Build = struct
-
type cid = Cid.t
+
module History = struct
+
type mode = Void.mode
-
let cid_of_string s =
-
match Cid.of_string s with
-
| Ok v -> v
-
| Error (`Msg m) -> failwith m
-
| Error (`Unsupported _) -> failwith "unsupported cid"
+
let mode_t =
+
Repr.map Repr.string
+
(function
+
| "R" -> Void.R | "RW" -> Void.RW | _ -> failwith "Malformed Void.mode")
+
(function Void.R -> "R" | Void.RW -> "RW")
-
let cid_t = Repr.map Repr.string cid_of_string Cid.to_string
-
-
type t = Image of string | Build of cid [@@deriving repr]
-
end
-
-
type mode = R | RW [@@deriving repr]
-
-
module History = struct
-
type t = { mode : mode; build : Build.t; args : string list }
+
type t = {
+
mode : mode;
+
build : Store.Build.t;
+
args : string list;
+
time : int64;
+
diff : Diff.t;
+
}
[@@deriving repr]
let merge = Irmin.Merge.(default (Repr.option t))
···
type entry = History.t
type action =
-
| Set_mode of mode
+
| Set_mode of History.mode
| Set_session of string
| Exec of string list
| Info
+
| Undo
+
| Fork of string
+
| Replay of string
| Unknown of string list
+
| History
[@@deriving repr]
let split_and_remove_empty s =
···
let action = action_t
let shelter_action = function
-
| "set" :: "mode" :: [ "r" ] -> Set_mode R
-
| "set" :: "mode" :: [ "rw" ] -> Set_mode R
+
| "mode" :: [ "r" ] -> Set_mode R
+
| "mode" :: [ "rw" ] -> Set_mode R
| "session" :: [ m ] -> Set_session m
+
| "fork" :: [ m ] -> Fork m
+
| "replay" :: [ onto ] -> Replay onto
| [ "info" ] -> Info
+
| [ "undo" ] -> Undo
+
| [ "history" ] -> History
| other -> Unknown other
let action_of_command cmd =
···
let history_key = [ "history" ]
let key clock = history_key @ [ string_of_float @@ Eio.Time.now clock ]
-
let list (Cshell.History.Store ((module S), store) : entry Cshell.History.t) =
+
let list (H.Store ((module S), store) : entry H.t) =
match S.list store history_key with
| [] -> []
| xs ->
···
| [] -> List.rev acc
in
loop [] (List.map (fun (v, tree) -> (v, S.Tree.to_concrete tree)) xs)
-
|> List.stable_sort (fun (s1, _) (s2, _) -> String.compare s1 s2)
+
|> List.stable_sort (fun (s1, _) (s2, _) ->
+
Float.compare (Float.of_string s1) (Float.of_string s2))
|> List.rev
let with_latest ~default s f =
···
let text c = Fmt.(styled (`Fg c) string)
-
let sessions (Cshell.History.Store ((module S), store) : entry Cshell.History.t)
-
=
+
let sessions (H.Store ((module S), store) : entry H.t) =
S.Branch.list (S.repo store)
-
let commit ~message clock
-
(Cshell.History.Store ((module S), store) : entry Cshell.History.t) v =
+
let commit ~message clock (H.Store ((module S), store) : entry H.t) v =
let info () = S.Info.v ~message (Eio.Time.now clock |> Int64.of_float) in
S.set_exn ~info store (key clock) v
-
let which_branch
-
((Cshell.History.Store ((module S), session) : entry Cshell.History.t) as s)
-
=
+
let which_branch ((H.Store ((module S), session) : entry H.t) as s) =
let branches = sessions s in
let repo = S.repo session in
let heads = List.map (fun b -> (S.Branch.find repo b, b)) branches in
let head = S.Head.find session in
List.assoc_opt head heads
-
let prompt status
-
((Cshell.History.Store ((module S), _session) : entry Cshell.History.t) as
-
store) =
+
(* Reset the head of the current session by one commit *)
+
let reset_hard ((H.Store ((module S), session) : entry H.t) as s) =
+
match
+
List.filter_map (S.Commit.of_hash (S.repo session))
+
@@ S.Commit.parents (S.Head.get session)
+
with
+
| [] -> s
+
| p :: _ ->
+
S.Head.set session p;
+
s
+
+
(* Fork a new session from an existing one *)
+
let fork (H.Store ((module S), session) : entry H.t) new_branch =
+
let repo = S.repo session in
+
match (S.Head.find session, S.Branch.find repo new_branch) with
+
| _, Some _ ->
+
Error (new_branch ^ " already exists, try @ session " ^ new_branch)
+
| None, _ -> Error "Current branch needs at least one commit"
+
| Some commit, None ->
+
let new_store = S.of_branch (S.repo session) new_branch in
+
S.Branch.set repo new_branch commit;
+
let store = H.Store ((module S), new_store) in
+
Ok store
+
+
(* Fork a new session from an existing one *)
+
let display_history (H.Store ((module S), session) : entry H.t) =
+
let history = S.history ~depth:max_int session in
+
let content c =
+
H.Store ((module S), S.of_commit c) |> list |> List.hd |> snd
+
in
+
let pp_diff fmt d =
+
if d = [] then () else Fmt.pf fmt "\n %a" (Repr.pp Diff.t) d
+
in
+
let pp_entry fmt (e : entry) =
+
Fmt.pf fmt "%-10s %s%a"
+
Fmt.(str "%a" (styled (`Fg `Yellow) uint64_ns_span) e.time)
+
(String.concat " " e.args) pp_diff e.diff
+
in
+
let linearize =
+
S.History.fold_vertex (fun c v -> content c :: v) history [] |> List.rev
+
in
+
List.iter (fun c -> Fmt.pr "%a\n%!" pp_entry c) linearize
+
+
let prompt status ((H.Store ((module S), _session) : entry H.t) as store) =
let sesh = Option.value ~default:"main" (which_branch store) in
let prompt () =
Fmt.(styled (`Fg `Yellow) string) Format.str_formatter "shelter> ";
···
in
with_latest store ~default:prompt prompt_entry
-
let init s =
+
type ctx = Store.t
+
+
let init fs proc s =
+
let store = Store.init fs proc "test-pool" in
List.iter
(fun (_, { History.args; _ }) ->
LNoise.history_add (String.concat " " args) |> ignore)
-
(list s)
+
(list s);
+
store
-
let run clock proc
-
((Cshell.History.Store ((module S), store) : entry Cshell.History.t) as s) =
-
function
+
let run _fs clock _proc (((H.Store ((module S), store) : entry H.t) as s), ctx)
+
= function
| Set_mode mode ->
-
with_latest ~default:(fun _ -> Ok s) s @@ fun (_, entry) ->
+
with_latest ~default:(fun _ -> Ok (s, ctx)) s @@ fun (_, entry) ->
commit ~message:"mode change" clock s { entry with mode };
-
Ok s
+
Ok (s, ctx)
| Set_session m ->
-
with_latest ~default:(fun _ -> Ok s) s @@ fun (_, entry) ->
+
with_latest ~default:(fun _ -> Ok (s, ctx)) s @@ fun (_, entry) ->
let new_store = S.of_branch (S.repo store) m in
-
let new_full_store = Cshell.History.Store ((module S), new_store) in
+
let new_full_store = H.Store ((module S), new_store) in
commit ~message:"new session" clock new_full_store entry;
-
Ok new_full_store
+
Ok (new_full_store, ctx)
| Unknown args ->
Fmt.epr "%a: %s\n%!" (text `Red) "Unknown Shelter Action"
(String.concat " " args);
-
Ok s
+
Ok (s, ctx)
| Info ->
let sessions = sessions s in
let sesh = Option.value ~default:"main" (which_branch s) in
···
in
let commits =
S.History.fold_vertex
-
(fun commit acc ->
+
(fun commit acc ->
let info = S.Commit.info commit |> S.Info.message in
let hash = S.Commit.hash commit |> Repr.to_string S.Hash.t in
-
((String.sub hash 0 7), info) :: acc)
+
(String.sub hash 0 7, info) :: acc)
history []
in
-
Fmt.pr "Sessions: %a\nCurrent: %a\nCommits:@. %a\n%!"
+
let latest =
+
with_latest
+
~default:(fun () -> None)
+
s
+
(fun (_, e) -> Some (Repr.to_string Store.Build.t e.build))
+
in
+
Fmt.pr "Sessions: %a\nCurrent: %a\nHash: %a\nCommits:@. %a\n%!"
Fmt.(list ~sep:(Fmt.any ", ") string)
sessions (text `Green) sesh
+
Fmt.(option string)
+
latest
Fmt.(vbox ~indent:2 @@ list pp_commit)
commits;
-
Ok s
-
| Exec [] -> Ok s
+
Ok (s, ctx)
+
| Exec [] -> Ok (s, ctx)
+
| Undo -> Ok (reset_hard s, ctx)
+
| Fork new_branch -> (
+
match fork s new_branch with
+
| Error err ->
+
Fmt.pr "[fork]: %a\n%!" (text `Red) err;
+
Ok (s, ctx)
+
| Ok store -> Ok (store, ctx))
+
| Replay _ -> Ok (s, ctx)
+
| History ->
+
display_history s;
+
Ok (s, ctx)
| Exec command -> (
-
Switch.run @@ fun sw ->
+
let entry =
+
with_latest
+
~default:(fun () ->
+
History.
+
{
+
mode = Void.RW;
+
build = Store.Build.Image "alpine";
+
args = command;
+
time = 0L;
+
diff = [];
+
})
+
s
+
@@ fun (_, e) -> e
+
in
+
let build =
+
match entry.build with
+
| Store.Build.Image img -> Store.fetch ctx img
+
| Store.Build.Build cid -> cid
+
in
+
let hash_entry = { entry with build = Build build; args = command } in
+
let new_cid = Store.cid (Repr.to_string History.t hash_entry) in
+
let with_rootfs fn =
+
if entry.mode = R then (Store.Run.with_build ctx build fn, [])
+
else Store.Run.with_clone ctx ~src:build new_cid fn
+
in
try
-
let proc = Eio.Process.spawn ~sw proc [ "bash"; "-c"; String.concat " " command ] in
-
let res = Eio.Process.await proc in
-
if res = `Exited 0 then (
-
let entry =
-
History.{ mode = RW; build = Image "TODO"; args = command }
+
let new_entry, diff =
+
with_rootfs @@ fun rootfs ->
+
let void =
+
Void.empty
+
|> Void.rootfs ~mode:entry.mode rootfs
+
|> Void.exec [ "/bin/ash"; "-c"; String.concat " " command ]
+
in
+
Switch.run @@ fun sw ->
+
let start = Mtime_clock.now () in
+
let proc = Void.spawn ~sw void in
+
let res =
+
Void.exit_status proc |> Eio.Promise.await |> Void.to_eio_status
in
-
commit ~message:("exec " ^ (String.concat " " command)) clock s entry;
+
let stop = Mtime_clock.now () in
+
let span = Mtime.span start stop in
+
let time = Mtime.Span.to_uint64_ns span in
+
(* Add command to history regardless of exit status *)
let _ : (unit, string) result =
LNoise.history_add (String.concat " " command)
in
-
Ok s)
-
else Error (Eio.Process.Child_error res)
+
if res = `Exited 0 then
+
if entry.mode = RW then
+
Ok { hash_entry with build = Build new_cid; time }
+
else Ok hash_entry
+
else Error (Eio.Process.Child_error res)
+
in
+
match new_entry with
+
| Error e -> Error e
+
| Ok entry ->
+
(* Set diff *)
+
let entry = { entry with diff } in
+
(* Commit if RW *)
+
if entry.mode = RW then
+
commit
+
~message:("exec " ^ String.concat " " command)
+
clock s entry;
+
Ok (s, ctx)
with Eio.Exn.Io (Eio.Process.E e, _) -> Error e)
+8 -6
src/lib/shelter/shelter.mli
···
-
module Build : sig
-
type t = Image of string | Build of Cid.t [@@deriving repr]
-
end
-
-
type mode = R | RW
+
module Store = Store
module History : sig
-
type t = { mode : mode; build : Build.t; args : string list }
+
type t = {
+
mode : Void.mode;
+
build : Store.Build.t;
+
args : string list;
+
time : int64;
+
diff : Diff.t;
+
}
[@@deriving repr]
include Irmin.Contents.S with type t := t
+158
src/lib/shelter/store.ml
···
+
(* A store a bit like OBuilder's but a little simplified
+
for our purposes *)
+
module Build = struct
+
type cid = Cid.t
+
+
let cid_of_string s =
+
match Cid.of_string s with
+
| Ok v -> v
+
| Error (`Msg m) -> failwith m
+
| Error (`Unsupported _) -> failwith "unsupported cid"
+
+
let cid_t = Repr.map Repr.string cid_of_string Cid.to_string
+
+
type t = Image of string | Build of cid [@@deriving repr]
+
end
+
+
type path = string list
+
+
type t = {
+
fs : Eio.Fs.dir_ty Eio.Path.t;
+
proc : Eio_unix.Process.mgr_ty Eio_unix.Process.mgr;
+
zfs : Zfs.Handle.t;
+
pool : string;
+
}
+
+
module Datasets : sig
+
type dataset = private string
+
type snapshot = private string
+
+
val builds : string -> dataset
+
val build : string -> string -> dataset
+
val snapshot : dataset -> snapshot
+
end = struct
+
type dataset = string
+
type snapshot = string
+
+
let ( / ) a b = a ^ "/" ^ b
+
let builds pool : dataset = pool / "builds"
+
let build pool path : dataset = builds pool / path
+
let snapshot ds = ds ^ "@snappy"
+
end
+
+
let with_dataset ?(typ = Zfs.Types.filesystem) t dataset f =
+
let exists = Zfs.exists t.zfs (dataset :> string) typ in
+
if not exists then Zfs.create t.zfs dataset typ;
+
let dataset = Zfs.open_ t.zfs dataset typ in
+
Fun.protect ~finally:(fun () -> Zfs.close dataset) (fun () -> f dataset)
+
+
let mount_dataset ?(typ = Zfs.Types.dataset) t (dataset : Datasets.dataset) =
+
match Zfs.is_mounted t.zfs (dataset :> string) with
+
| Some _ -> ()
+
| None -> with_dataset ~typ t (dataset :> string) @@ fun d -> Zfs.mount d
+
+
let unmount_dataset t (dataset : Datasets.dataset) =
+
match Zfs.is_mounted t.zfs (dataset :> string) with
+
| None -> ()
+
| Some _ ->
+
with_dataset t (dataset :> string) @@ fun d ->
+
let _todo () = Zfs.unmount d in
+
()
+
+
let create_dataset t (dataset : Datasets.dataset) =
+
with_dataset t (dataset :> string) (fun _ -> ())
+
+
let create_and_mount t (dataset : Datasets.dataset) =
+
create_dataset t dataset;
+
mount_dataset t dataset
+
+
let init fs proc pool =
+
let zfs = Zfs.init () in
+
Zfs.debug zfs true;
+
let t =
+
{
+
fs :> Eio.Fs.dir_ty Eio.Path.t;
+
proc :> Eio_unix.Process.mgr_ty Eio_unix.Process.mgr;
+
zfs;
+
pool;
+
}
+
in
+
create_and_mount t (Datasets.builds t.pool);
+
t
+
+
let snapshot t (snap : Datasets.snapshot) =
+
let exists = Zfs.exists t.zfs (snap :> string) Zfs.Types.snapshot in
+
if not exists then Zfs.snapshot t.zfs (snap :> string) true
+
+
let clone t (snap : Datasets.snapshot) (tgt : Datasets.dataset) =
+
with_dataset ~typ:Zfs.Types.snapshot t (snap :> string) @@ fun src ->
+
Zfs.clone src (tgt :> string)
+
+
let read_all fd =
+
let buf = Buffer.create 128 in
+
let bytes = Bytes.create 4096 in
+
let rec loop () =
+
match Unix.read fd bytes 0 4096 with
+
| 0 | (exception End_of_file) -> Buffer.contents buf
+
| n ->
+
Buffer.add_bytes buf (Bytes.sub bytes 0 n);
+
loop ()
+
in
+
loop ()
+
+
let diff t (data : Datasets.snapshot) (snap : Datasets.snapshot) =
+
let data_fs =
+
String.sub (data :> string) 0 (String.index (data :> string) '@')
+
in
+
let zh = Zfs.open_ t.zfs data_fs Zfs.Types.filesystem in
+
let diff =
+
let r, w = Unix.pipe ~cloexec:false () in
+
try
+
Zfs.show_diff zh ~from_:(data :> string) ~to_:(snap :> string) w;
+
let f = read_all r in
+
Unix.close r;
+
f
+
with e ->
+
Unix.close r;
+
raise e
+
in
+
Zfs.close zh;
+
Diff.of_zfs diff
+
+
let cid s =
+
let hash =
+
Multihash_digestif.of_cstruct `Sha2_256 (Cstruct.of_string s)
+
|> Result.get_ok
+
in
+
Cid.v ~version:`Cidv1 ~base:`Base32 ~codec:`Raw ~hash
+
+
let fetch t image =
+
let cid = cid image in
+
let cids = cid |> Cid.to_string in
+
let dataset = Datasets.build t.pool cids in
+
let dir = Eio.Path.(t.fs / ("/" ^ (Datasets.build t.pool cids :> string))) in
+
if Zfs.exists t.zfs (dataset :> string) Zfs.Types.filesystem then cid
+
else (
+
create_and_mount t dataset;
+
let _dir : string = Fetch.get_image ~dir ~proc:t.proc image in
+
snapshot t (Datasets.snapshot dataset);
+
cid)
+
+
module Run = struct
+
let with_build t cid fn =
+
let ds = Datasets.build t.pool (Cid.to_string cid) in
+
Fun.protect ~finally:(fun () -> unmount_dataset t ds) @@ fun () ->
+
mount_dataset t ds;
+
fn ("/" ^ (ds :> string) ^ "/rootfs")
+
+
let with_clone t ~src new_cid fn =
+
let ds = Datasets.build t.pool (Cid.to_string src) in
+
let tgt = Datasets.build t.pool (Cid.to_string new_cid) in
+
let src_snap = Datasets.snapshot ds in
+
let tgt_snap = Datasets.snapshot tgt in
+
clone t src_snap tgt;
+
let v = with_build t new_cid fn in
+
snapshot t tgt_snap;
+
let d = diff t src_snap tgt_snap in
+
(v, d)
+
end
+33
vendor/ocaml-libbpf/.gitignore
···
+
*.annot
+
*.cmo
+
*.cma
+
*.cmi
+
*.a
+
*.o
+
*.cmx
+
*.cmxs
+
*.cmxa
+
+
# ocamlbuild working directory
+
_build/
+
+
# ocamlbuild targets
+
*.byte
+
*.native
+
+
# oasis generated files
+
setup.data
+
setup.log
+
+
# Merlin configuring file for Vim and Emacs
+
.merlin
+
+
# Dune generated files
+
*.install
+
+
# Local OPAM switch
+
_opam/
+
+
# generated files
+
*.txt
+
*.bin
+2
vendor/ocaml-libbpf/.ocamlformat
···
+
version=0.26.2
+
profile=default
+12
vendor/ocaml-libbpf/CHANGES.md
···
+
## v0.1.1 (2024-07-05)
+
- Improve documentation
+
+
## v0.1.0 (2024-07-01)
+
- Initial release.
+
+
`ocaml_libbpf`:
+
- [supported](./supported.json) bindings
+
- high level API's for open/load/attach/teardown
+
+
`ocaml_libbpf_maps`:
+
- high level API's for BPF ring_buffer map
+15
vendor/ocaml-libbpf/LICENSE.md
···
+
/*
+
* Copyright (C) 2024 Lee Koon Wen
+
*
+
* Permission to use, copy, modify, and distribute this software for any
+
* purpose with or without fee is hereby granted, provided that the above
+
* copyright notice and this permission notice appear in all copies.
+
*
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
*/
+220
vendor/ocaml-libbpf/README.md
···
+
[![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)
+
- [API documentation](https://koonwen.github.io/ocaml-libbpf/)
+
+
# ocaml-libbpf
+
Libbpf C-bindings for loading eBPF ELF files into the kernel with OCaml.
+
+
Writing eBPF programs consist of two distinct parts. Implementing the
+
code that executes in-kernel **and** user-level code responsible for
+
loading/initializing/linking/teardown of the in-kernel code. This
+
OCaml library provides the latter via binding the C
+
[libbpf](https://github.com/libbpf/libbpf) library. It exposes both
+
the raw low-level bindings as well as a set of high-level API's for
+
handling your eBPF objects. As of now, the kernel part must still be
+
written in [restricted
+
C](https://stackoverflow.com/questions/57688344/what-is-not-allowed-in-restricted-c-for-ebpf)
+
and compiled with llvm to eBPF bytecode.
+
+
The full API set of Libbpf is quite large, see [supported](supported.json) for the list
+
of currently bound API's. Contributions are welcome.
+
+
### External dependencies
+
ocaml-libbpf depends on the system package of `libbpf`.
+
+
# Usage
+
> ⚠️ **Disambiguation:** The name of this repository and
+
> references to it will be "ocaml-libbpf". However, the library's
+
> entry module and package name is **Libbpf**. To install it, you
+
> would use `opam install libbpf`. To access it's High-level API's use
+
> `Libbpf.<api>`. To use the raw bindings, they are exposed in
+
> `Libbpf.C.<api>` namespace.
+
+
See `examples` directory on how ocaml-libbpf can be used to load eBPF
+
ELF files into the kernel and interact with the loaded kernel program.
+
The eBPF kernel programs are defined in *.bpf.c source files and are
+
compiled with clang as specified in the `dune` rules. ocaml-libbpf
+
exposes some high-level API's exposed by the toplevel `Libbpf` module
+
to make it easy to perform repetitive tasks such as
+
open/load/linking/initializing/teardown of bpf programs.
+
+
To run these examples, clone this repository and set up the package with
+
```bash
+
git clone git@github.com:koonwen/ocaml-libbpf.git
+
cd ocaml-libbpf
+
opam install . --deps-only
+
eval $(opam env)
+
```
+
+
then run `make < minimal | kprobe | bootstrap | tc >` to try out the
+
different bpf programs. These examples are all taken from
+
[libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap)
+
repository and rewritten in OCaml.
+
+
### Open/Load/Link
+
Now let's run through an example of how we would use
+
ocaml-libbpf. This usage tutorial assumes some knowledge of how to
+
write eBPF kernel programs in C compile them to ELF files. If not, you
+
can check out this
+
[resource](https://nakryiko.com/posts/libbpf-bootstrap/#the-bpf-side). ocaml-libbpf
+
provides an easy API to install your eBPF program into the kernel. Say
+
your eBPF kernel program looks like this where we print something
+
whenever the syscall `write` event occurs. We also want to implement a
+
filtering mechanism to only print on `write` calls for our process. To
+
do this, we initialize a BPF array map with a single entry that works
+
like a holder for our global variable. The BPF map is neccessary to
+
because it allows us to communicate values between user and kernel
+
space.
+
+
> The libbpf C library in fact already supports declarations of global
+
> variables in the usual form with the ability to manage them in user
+
> space. However for various technical reasons, ocaml-libbpf does not
+
> enable that feature yet. So we use the old style of working with
+
> global variables here.
+
+
```c
+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
/* Copyright (c) 2020 Facebook */
+
#include <linux/bpf.h>
+
#include "bpf/bpf_helpers.h" /* This is from our libbpf library */
+
+
char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+
/* Globals implemented as an array */
+
struct {
+
__uint(type, BPF_MAP_TYPE_ARRAY);
+
__uint(max_entries, 1);
+
__type(key, int);
+
__type(value, long);
+
} globals SEC(".maps");
+
+
int my_pid_index = 0;
+
+
SEC("tp/syscalls/sys_enter_write")
+
int handle_tp(void *ctx) {
+
int pid = bpf_get_current_pid_tgid() >> 32;
+
+
long *my_pid;
+
my_pid = bpf_map_lookup_elem(&globals, &my_pid_index);
+
if (my_pid == NULL) {
+
bpf_printk("Error got NULL");
+
return 1;
+
};
+
+
if (pid != *my_pid)
+
return 0;
+
+
bpf_printk("Hello, BPF triggered from PID %d", pid);
+
+
return 0;
+
}
+
+
```
+
+
After compilation to eBPF ELF file as `minimal.o`. Users just need to
+
provide the path to this ELF file along with the name of the program
+
and optionally an initialization function. Note that the name of the
+
program refers to the function identifier under the SEC(...)
+
attribute, in this case it is "handle_tp".
+
+
```ocaml
+
open Libbpf
+
+
let obj_path = "minimal.bpf.o"
+
let program_names = [ "handle_tp" ]
+
+
let () =
+
with_bpf_object_open_load_link ~obj_path ~program_names ~before_link
+
(fun obj link ->
+
+
< user code to interact with bpf program running in kernel >
+
+
)
+
```
+
+
The API provided by ocaml-libbpf `with_bpf_object_open_load_link` is
+
a context manager that ensures the proper cleanup of resources if a
+
failure is encountered. Right now our loaded kernel program is
+
attached to the kernel and then immediately unloaded, users are
+
responsible for keeping the bpf program alive by looping within the
+
function block.
+
+
> Users may also pin the bpf program to persist after user code
+
> exits. Do note that if pinning is desired, users should not use the
+
> `with_bpf_object_open_load_link` API and instead manually load and
+
> attach their bpf program since the context manager shutdowns all
+
> resources on exit.
+
+
Now let's add some looping logic to keep the loaded bpf program alive.
+
+
```ocaml
+
let obj_path = "minimal.bpf.o"
+
let program_names = [ "handle_tp" ]
+
+
let () =
+
with_bpf_object_open_load_link ~obj_path ~program_names ~before_link
+
(fun obj link ->
+
+
(* Set up signal handlers *)
+
let exitting = ref true in
+
let sig_handler = Sys.Signal_handle (fun _ -> exitting := false) in
+
Sys.(set_signal sigint sig_handler);
+
Sys.(set_signal sigterm sig_handler);
+
+
Printf.printf
+
"Successfully started! Please run `sudo cat \
+
/sys/kernel/debug/tracing/trace_pipe` to see output of the BPF \
+
programs.\n\
+
%!"
+
+
(* Loop until Ctrl-C is called *)
+
while !exitting do
+
Printf.eprintf ".%!";
+
Unix.sleepf 1.0
+
done)
+
```
+
+
Our bpf program is now running in the kernel until we decide to
+
interrupt it. However, it doesn't do exactly what we want. In
+
particular, it doesn't filter for our process PID. This is because we
+
haven't loaded our process PID into the BPF map. To do this, we need
+
the name of the map we declared in the `minimal.bpf.c` program. In
+
this case, our BPF array map was named `globals`.
+
+
```ocaml
+
let map = "globals"
+
+
(* Load PID into BPF map *)
+
let before_link obj =
+
let pid = Unix.getpid () |> Signed.Long.of_int in
+
let global_map = bpf_object_find_map_by_name obj map in
+
(* When updating an element, users need to specify the type of the key and value
+
declared by the map which checks that the key and value size are consistent. *)
+
bpf_map_update_elem ~key_ty:Ctypes.int ~val_ty:Ctypes.long global_map 0 pid
+
```
+
+
Put together in [minimal.ml](./examples/minimal.ml), your bpf program
+
runs in kernel and print to the trace pipe every second.
+
+
### Maps
+
`libbpf_maps` is an optional convenience package that provides
+
wrappers for BPF maps. Currently only Ringbuffer maps are added. An
+
example usage of them can be found in
+
[examples/bootstrap.ml](./examples/bootstrap.ml). This has been
+
packaged separately since it drags in `libffi` dependency.
+
+
## Notes on compatibility
+
> The libbpf C library is designed to be kernel-agnostic and work
+
> across multitude of kernel versions. It has built-in mechanisms to
+
> gracefully handle older kernels, that are missing some of the
+
> features, by working around or gracefully degrading functionality.
+
+
Vendoring libbpf was a option. However, since bpf programs require
+
writing the kernel components that may use libbpf, we made the choice
+
to use the system's package versioned instead. This avoids users from
+
knowingly/unknowingly using libbpf API's from two different
+
versions. As a consequence, this library support operating systems
+
that package libbpf.v.1.1 and up. Check ocaml-ci for the list of
+
operating systems that successfully builds.
+
+
If so desired, you can also checkout the `vendored` branch in this
+
repo which builds ocaml-libbpf with the latest libbpf package.
+16
vendor/ocaml-libbpf/conf-bpftool.opam
···
+
opam-version: "2.0"
+
synopsis: "Virtual package for system installation of bpftool"
+
maintainer: ["Lee Koon Wen"]
+
authors: ["Lee Koon Wen"]
+
license: ["ISC" "BSD-3-Clause"]
+
homepage: "https://github.com/koonwen/ocaml-libbpf"
+
doc: "https://koonwen.github.io/ocaml-libbpf"
+
bug-reports: "https://github.com/koonwen/ocaml-libbpf/issues"
+
available: [ os = "linux" ]
+
depexts: [
+
[ "linux-tools-common" ] {os-distribution = "ubuntu"}
+
[ "bpftool" ] {os-distribution = "debian"}
+
[ "bpftool" ] {os-distribution = "fedora"}
+
]
+
flags: conf
+
x-commit-hash: "c7ac4c7ff9f2aa23c374a619990c0bdd78976102"
+17
vendor/ocaml-libbpf/conf-libbpf.opam
···
+
opam-version: "2.0"
+
synopsis: "Virtual package for system installation of libbpf"
+
maintainer: ["Lee Koon Wen"]
+
authors: ["Lee Koon Wen"]
+
license: ["ISC" "BSD-3-Clause"]
+
homepage: "https://github.com/koonwen/ocaml-libbpf"
+
doc: "https://koonwen.github.io/ocaml-libbpf"
+
bug-reports: "https://github.com/koonwen/ocaml-libbpf/issues"
+
available: [ os = "linux" ]
+
+
depexts: [
+
["libbpf-dev"] { os-distribution = "ubuntu" & os-version >= "18.04" }
+
["libbpf-dev"] { os-distribution = "debian" & os-version >= "9.0" }
+
["libbpf-devel"] { os-distribution = "fedora" & os-version >= "38" }
+
]
+
flags: conf
+
x-commit-hash: "c7ac4c7ff9f2aa23c374a619990c0bdd78976102"
+55
vendor/ocaml-libbpf/dune-project
···
+
(lang dune 3.13)
+
+
(name libbpf)
+
(source
+
(github koonwen/ocaml-libbpf))
+
(authors "Lee Koon Wen")
+
(maintainers "Lee Koon Wen")
+
(license ISC BSD-3-Clause)
+
(documentation https://koonwen.github.io/ocaml-libbpf)
+
+
(package
+
(allow_empty)
+
(name conf-libbpf)
+
(synopsis "Virtual package for system installation of libbpf"))
+
+
(package
+
(allow_empty)
+
(name conf-bpftool)
+
(synopsis "Virtual package for system installation of bpftool"))
+
+
(package
+
(name libbpf)
+
(synopsis "Libbpf bindings")
+
(description "Wrapped libbpf api's for writing BPF user programs in OCaml")
+
(depends
+
(ocaml
+
(>= 4.08))
+
dune
+
(ctypes
+
(>= 0.22.0))
+
ppx_deriving
+
ppx_expect
+
conf-libbpf
+
conf-bpftool
+
conf-clang)
+
; This is only a dependency for the examples directory
+
; to show how to use dune to build bpf programs, it is
+
; not part of the library bindings, however, we can't
+
; remove it since it builds alongside the library
+
(tags
+
(bindings bpf libbpf)))
+
+
(package
+
(name libbpf_maps)
+
(synopsis "Libbpf maps API")
+
(description "High level API's for interacting with BPF maps in OCaml")
+
(depends
+
(ctypes
+
(>= 0.22.0))
+
(ctypes-foreign
+
(>= 0.22.0))
+
(libbpf
+
(= :version)))
+
(tags
+
(bindings bpf libbpf)))
+29
vendor/ocaml-libbpf/examples/LICENSE.md
···
+
BSD 3-Clause License
+
+
Copyright (c) 2020, Andrii Nakryiko
+
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.
+
+
3. Neither the name of the copyright holder nor the names of its
+
contributors may be used to endorse or promote products derived from
+
this software without specific prior written permission.
+
+
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.
+23
vendor/ocaml-libbpf/examples/Makefile
···
+
CWD=../_build/default/examples
+
+
build:
+
dune build
+
+
minimal: build
+
sudo $(CWD)/minimal.exe
+
+
kprobe: build
+
sudo $(CWD)/kprobe.exe
+
+
tc: build
+
sudo $(CWD)/tc.exe
+
+
bootstrap: build
+
sudo $(CWD)/bootstrap.exe
+
+
bootstrap_c: build
+
sudo $(CWD)/bootstrap_c.exe
+
+
clean:
+
dune clean
+
rm vmlinux.h
+111
vendor/ocaml-libbpf/examples/bootstrap.bpf.c
···
+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
/* Copyright (c) 2020 Facebook */
+
#include "vmlinux.h"
+
#include <bpf/bpf_helpers.h>
+
#include <bpf/bpf_tracing.h>
+
#include <bpf/bpf_core_read.h>
+
#include "bootstrap.h"
+
+
char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+
struct {
+
__uint(type, BPF_MAP_TYPE_HASH);
+
__uint(max_entries, 8192);
+
__type(key, pid_t);
+
__type(value, u64);
+
} exec_start SEC(".maps");
+
+
struct {
+
__uint(type, BPF_MAP_TYPE_RINGBUF);
+
__uint(max_entries, 256 * 1024);
+
} rb SEC(".maps");
+
+
const volatile unsigned long long min_duration_ns = 0;
+
+
SEC("tp/sched/sched_process_exec")
+
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
+
{
+
struct task_struct *task;
+
unsigned fname_off;
+
struct event *e;
+
pid_t pid;
+
u64 ts;
+
+
/* remember time exec() was executed for this PID */
+
pid = bpf_get_current_pid_tgid() >> 32;
+
ts = bpf_ktime_get_ns();
+
bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY);
+
+
/* don't emit exec events when minimum duration is specified */
+
if (min_duration_ns)
+
return 0;
+
+
/* reserve sample from BPF ringbuf */
+
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
+
if (!e)
+
return 0;
+
+
/* fill out the sample with data */
+
task = (struct task_struct *)bpf_get_current_task();
+
+
e->exit_event = false;
+
e->pid = pid;
+
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
+
bpf_get_current_comm(&e->comm, sizeof(e->comm));
+
+
fname_off = ctx->__data_loc_filename & 0xFFFF;
+
bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off);
+
+
/* successfully submit it to user-space for post-processing */
+
bpf_ringbuf_submit(e, 0);
+
return 0;
+
}
+
+
SEC("tp/sched/sched_process_exit")
+
int handle_exit(struct trace_event_raw_sched_process_template *ctx)
+
{
+
struct task_struct *task;
+
struct event *e;
+
pid_t pid, tid;
+
u64 id, ts, *start_ts, duration_ns = 0;
+
+
/* get PID and TID of exiting thread/process */
+
id = bpf_get_current_pid_tgid();
+
pid = id >> 32;
+
tid = (u32)id;
+
+
/* ignore thread exits */
+
if (pid != tid)
+
return 0;
+
+
/* if we recorded start of the process, calculate lifetime duration */
+
start_ts = bpf_map_lookup_elem(&exec_start, &pid);
+
if (start_ts)
+
duration_ns = bpf_ktime_get_ns() - *start_ts;
+
else if (min_duration_ns)
+
return 0;
+
bpf_map_delete_elem(&exec_start, &pid);
+
+
/* if process didn't live long enough, return early */
+
if (min_duration_ns && duration_ns < min_duration_ns)
+
return 0;
+
+
/* reserve sample from BPF ringbuf */
+
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
+
if (!e)
+
return 0;
+
+
/* fill out the sample with data */
+
task = (struct task_struct *)bpf_get_current_task();
+
+
e->exit_event = true;
+
e->duration_ns = duration_ns;
+
e->pid = pid;
+
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
+
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
+
bpf_get_current_comm(&e->comm, sizeof(e->comm));
+
+
/* send data to user-space for post-processing */
+
bpf_ringbuf_submit(e, 0);
+
return 0;
+
}
+19
vendor/ocaml-libbpf/examples/bootstrap.h
···
+
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
/* Copyright (c) 2020 Facebook */
+
#ifndef __BOOTSTRAP_H
+
#define __BOOTSTRAP_H
+
+
#define TASK_COMM_LEN 16
+
#define MAX_FILENAME_LEN 127
+
+
struct event {
+
int pid;
+
int ppid;
+
unsigned exit_code;
+
unsigned long long duration_ns;
+
char comm[TASK_COMM_LEN];
+
char filename[MAX_FILENAME_LEN];
+
bool exit_event;
+
};
+
+
#endif /* __BOOTSTRAP_H */
+78
vendor/ocaml-libbpf/examples/bootstrap.ml
···
+
open Libbpf
+
open Libbpf_maps
+
open Ctypes
+
+
let obj_path = "bootstrap.bpf.o"
+
let program_names = [ "handle_exec"; "handle_exit" ]
+
let rb_name = "rb"
+
+
(* event structure layout from bootstrap.h *)
+
let event : [ `Event ] structure typ = Ctypes.structure "event"
+
let ( -: ) ty label = field event label ty
+
let pid = int -: "pid"
+
let ppid = int -: "ppid"
+
let exit_code = uint -: "exit_code"
+
let duration = ullong -: "duration_ns"
+
let comm = array 16 char -: "comm"
+
let filename = array 127 char -: "filename"
+
let exit_event = bool -: "exit_event"
+
let _ = seal event
+
+
let char_array_as_string a =
+
let len = CArray.length a in
+
let b = Buffer.create len in
+
try
+
for i = 0 to len - 1 do
+
let c = CArray.get a i in
+
if c = '\x00' then raise Exit else Buffer.add_char b c
+
done;
+
Buffer.contents b
+
with Exit -> Buffer.contents b
+
+
(* Describe User callback event handler *)
+
let handle_event _ctx data _sz =
+
let ev = !@(from_voidp event data) in
+
let pid = getf ev pid in
+
let ppid = getf ev ppid in
+
let exit_code = getf ev exit_code |> Unsigned.UInt.to_string in
+
let duration = getf ev duration |> Unsigned.ULLong.to_int64 in
+
let comm = getf ev comm |> char_array_as_string in
+
let filename = getf ev filename |> char_array_as_string in
+
let exit_event = getf ev exit_event in
+
let tm = Unix.time () |> Unix.localtime in
+
let ts = Printf.sprintf "%d:%d:%d" tm.tm_hour tm.tm_min tm.tm_sec in
+
if exit_event then (
+
Printf.printf "%-8s %-5s %-16s %-7d %-7d [%s]" ts "EXIT" comm pid ppid
+
exit_code;
+
if duration >= 0L then
+
Printf.printf " (%Lums)" (Int64.div duration 1000000L);
+
print_newline ())
+
else
+
Printf.printf "%-8s %-5s %-16s %-7d %-7d %s\n" ts "EXEC" comm pid ppid
+
filename;
+
0
+
+
let () =
+
(* Set signal handlers *)
+
let exitting = ref true in
+
let sig_handler = Sys.Signal_handle (fun _ -> exitting := false) in
+
Sys.(set_signal sigint sig_handler);
+
Sys.(set_signal sigterm sig_handler);
+
+
(* Use auto open/load/link helper *)
+
with_bpf_object_open_load_link ~obj_path ~program_names (fun obj _links ->
+
(* Load ringbuffer map *)
+
let map = bpf_object_find_map_by_name obj rb_name in
+
+
(* Set up ring buffer *)
+
RingBuffer.init map ~callback:handle_event (fun rb ->
+
Printf.printf "%-8s %-5s %-16s %-7s %-7s %s\n%!" "TIME" "EVENT" "COMM"
+
"PID" "PPID" "FILENAME/EXIT CODE";
+
+
while !exitting do
+
ignore
+
(try RingBuffer.poll rb ~timeout:100
+
with _ ->
+
exitting := false;
+
-1)
+
done))
+109
vendor/ocaml-libbpf/examples/bootstrap_c.ml
···
+
module F = Libbpf.C.Functions
+
module T = Libbpf.C.Types
+
+
let bpf_obj_path = "bootstrap.bpf.o"
+
let program_names = [ "handle_exec"; "handle_exit" ]
+
let rb_name = "rb"
+
+
exception Exit of int
+
+
let main () =
+
(* Set signal handlers *)
+
let exitting = ref true in
+
let sig_handler = Sys.Signal_handle (fun _ -> exitting := false) in
+
Sys.(set_signal sigint sig_handler);
+
Sys.(set_signal sigterm sig_handler);
+
+
(* Read BPF object *)
+
let obj =
+
match F.bpf_object__open bpf_obj_path with
+
| None ->
+
Printf.eprintf "Failed to open BPF object\n";
+
raise (Exit 1)
+
| Some obj -> obj
+
in
+
+
at_exit (fun () -> F.bpf_object__close obj);
+
+
(* Load BPF object *)
+
if F.bpf_object__load obj = 1 then (
+
Printf.eprintf "Failed to load BPF object\n";
+
raise (Exit 1));
+
+
let progs =
+
let find_exn name =
+
match F.bpf_object__find_program_by_name obj name with
+
| None ->
+
Printf.eprintf "Failed to find bpf program: %s\n" name;
+
raise (Exit 1)
+
| Some p -> p
+
in
+
List.map find_exn program_names
+
in
+
+
(* Attach tracepoint *)
+
let links =
+
let attach_exn prog =
+
match F.bpf_program__attach prog with
+
| Some linkp -> linkp
+
| None ->
+
Printf.eprintf "Failed to attach BPF program\n";
+
raise (Exit 1)
+
in
+
List.map attach_exn progs
+
in
+
+
at_exit (fun () ->
+
List.iter (fun link -> F.bpf_link__destroy link |> ignore) links);
+
+
(* Load maps *)
+
let map =
+
match F.bpf_object__find_map_by_name obj rb_name with
+
| None ->
+
Printf.eprintf "Failed to find map\n";
+
raise (Exit 1)
+
| Some m -> m
+
in
+
let rb_fd = F.bpf_map__fd map in
+
+
(* Describe event handler *)
+
let handle_event _ctx _data _sz =
+
Printf.printf "Handle_event called\n%!";
+
0
+
in
+
+
(* Coerce it to the static_funptr *)
+
let handle_event_f =
+
Ctypes.(
+
coerce
+
(Foreign.funptr ~runtime_lock:false ~check_errno:true
+
(ptr void @-> ptr void @-> size_t @-> returning int))
+
T.ring_buffer_sample_fn handle_event)
+
in
+
+
(* Set up ring buffer polling *)
+
let rb =
+
match
+
F.ring_buffer__new rb_fd handle_event_f Ctypes.null
+
Ctypes.(from_voidp T.ring_buffer_opts null)
+
with
+
| None ->
+
Printf.eprintf "Failed to create ring buffer\n";
+
raise (Exit 1)
+
| Some rb -> rb
+
in
+
+
at_exit (fun () -> F.ring_buffer__free rb);
+
+
while !exitting do
+
Printf.printf "polling\n%!";
+
let err = F.ring_buffer__poll rb 100 in
+
match err with
+
| e when e = Sys.sighup -> raise (Exit 0)
+
| e when e < 0 ->
+
Printf.eprintf "Error polling ring buffer, %d\n" e;
+
raise (Exit 1)
+
| _ -> ()
+
done
+
+
let () = try main () with Exit i when i <> 0 -> Printf.eprintf "[Exit %d]" i
+57
vendor/ocaml-libbpf/examples/dune
···
+
(executables
+
(names tc bootstrap bootstrap_c kprobe minimal)
+
(libraries libbpf libbpf_maps))
+
+
; Below is repetitive build rules to compile *.bpf.c eBPF C source code that runs in the kernel
+
+
(rule
+
(mode
+
(promote (until-clean)))
+
(targets minimal.bpf.o)
+
(deps arch minimal.bpf.c)
+
(action
+
(system
+
"clang -g -O2 -target bpf -I/usr/include/%{architecture}-linux-gnu/ -c minimal.bpf.c -D__TARGET_ARCH_%{read:arch}")))
+
+
(rule
+
(mode
+
(promote (until-clean)))
+
(targets kprobe.bpf.o)
+
(deps arch vmlinux.h kprobe.bpf.c)
+
(action
+
(system
+
"clang -g -O2 -target bpf -I/usr/include/%{architecture}-linux-gnu/ -c kprobe.bpf.c -D__TARGET_ARCH_%{read:arch}")))
+
+
(rule
+
(mode
+
(promote (until-clean)))
+
(targets tc.bpf.o)
+
(deps arch vmlinux.h tc.bpf.c)
+
(action
+
(system
+
"clang -g -O2 -target bpf -I/usr/include/%{architecture}-linux-gnu/ -c tc.bpf.c -D__TARGET_ARCH_%{read:arch}")))
+
+
(rule
+
(mode
+
(promote (until-clean)))
+
(targets bootstrap.bpf.o)
+
(deps arch vmlinux.h bootstrap.bpf.c bootstrap.h)
+
(action
+
(system
+
"clang -g -O2 -target bpf -I/usr/include/%{architecture}-linux-gnu/ -c bootstrap.bpf.c -D__TARGET_ARCH_%{read:arch}")))
+
+
(rule
+
(mode
+
(promote (until-clean)))
+
(targets vmlinux.h arch)
+
(action
+
(progn
+
(with-stdout-to
+
vmlinux.h
+
(run /usr/sbin/bpftool btf dump file /sys/kernel/btf/vmlinux format c))
+
(with-stdout-to
+
arch
+
(bash
+
"uname -m | sed 's/x86_64/x86/' | sed 's/arm.*/arm/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/' | sed 's/riscv64/riscv/' | sed 's/loongarch64/loongarch/'")))))
+
+
; /usr/include/%{architecture}-linux-gnu/ Find asm/types.h for eBPF code
+30
vendor/ocaml-libbpf/examples/kprobe.bpf.c
···
+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
/* Copyright (c) 2021 Sartura */
+
#include "vmlinux.h"
+
#include <bpf/bpf_helpers.h>
+
#include <bpf/bpf_tracing.h>
+
#include <bpf/bpf_core_read.h>
+
+
char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+
SEC("kprobe/do_unlinkat")
+
int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name)
+
{
+
pid_t pid;
+
const char *filename;
+
+
pid = bpf_get_current_pid_tgid() >> 32;
+
filename = BPF_CORE_READ(name, name);
+
bpf_printk("KPROBE ENTRY pid = %d, filename = %s\n", pid, filename);
+
return 0;
+
}
+
+
SEC("kretprobe/do_unlinkat")
+
int BPF_KRETPROBE(do_unlinkat_exit, long ret)
+
{
+
pid_t pid;
+
+
pid = bpf_get_current_pid_tgid() >> 32;
+
bpf_printk("KPROBE EXIT: pid = %d, ret = %ld\n", pid, ret);
+
return 0;
+
}
+23
vendor/ocaml-libbpf/examples/kprobe.ml
···
+
open Libbpf
+
+
let obj_path = "kprobe.bpf.o"
+
let program_names = [ "do_unlinkat"; "do_unlinkat_exit" ]
+
+
let () =
+
with_bpf_object_open_load_link ~obj_path ~program_names (fun _obj _links ->
+
(* Set signal handlers *)
+
let exitting = ref true in
+
let sig_handler = Sys.Signal_handle (fun _ -> exitting := false) in
+
Sys.(set_signal sigint sig_handler);
+
Sys.(set_signal sigterm sig_handler);
+
+
Printf.printf
+
"Successfully started! Please run `sudo cat \
+
/sys/kernel/debug/tracing/trace_pipe` to see output of the BPF \
+
programs.\n\
+
%!";
+
+
while !exitting do
+
Unix.sleepf 1.0;
+
Printf.eprintf ".%!"
+
done)
+35
vendor/ocaml-libbpf/examples/minimal.bpf.c
···
+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
/* Copyright (c) 2020 Facebook */
+
#include <linux/bpf.h>
+
#include "bpf/bpf_helpers.h" /* This is from our libbpf library */
+
+
char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+
/* Globals implemented as an array */
+
struct {
+
__uint(type, BPF_MAP_TYPE_ARRAY);
+
__uint(max_entries, 5);
+
__type(key, int);
+
__type(value, long);
+
} globals SEC(".maps");
+
+
int my_pid_index = 0;
+
+
SEC("tp/syscalls/sys_enter_write")
+
int handle_tp(void *ctx) {
+
int pid = bpf_get_current_pid_tgid() >> 32;
+
+
long *my_pid;
+
my_pid = bpf_map_lookup_elem(&globals, &my_pid_index);
+
if (my_pid == NULL) {
+
bpf_printk("Error got NULL");
+
return 1;
+
};
+
+
if (pid != *my_pid)
+
return 0;
+
+
bpf_printk("Hello, BPF triggered from PID %d", pid);
+
+
return 0;
+
}
+31
vendor/ocaml-libbpf/examples/minimal.ml
···
+
open Libbpf
+
+
let obj_path = "minimal.bpf.o"
+
let program_names = [ "handle_tp" ]
+
let map = "globals"
+
+
(* Load PID into BPF map*)
+
let before_link obj =
+
let pid = Unix.getpid () |> Signed.Long.of_int in
+
let global_map = bpf_object_find_map_by_name obj map in
+
bpf_map_update_elem ~key_ty:Ctypes.int ~val_ty:Ctypes.long global_map 0 pid
+
+
let () =
+
with_bpf_object_open_load_link ~obj_path ~program_names ~before_link
+
(fun _obj _link ->
+
let exitting = ref true in
+
let sig_handler = Sys.Signal_handle (fun _ -> exitting := false) in
+
Sys.(set_signal sigint sig_handler);
+
Sys.(set_signal sigterm sig_handler);
+
+
Printf.printf
+
"Successfully started! Please run `sudo cat \
+
/sys/kernel/debug/tracing/trace_pipe` to see output of the BPF \
+
programs.\n\
+
%!";
+
+
(* Loop until Ctrl-C is called *)
+
while !exitting do
+
Printf.eprintf ".%!";
+
Unix.sleepf 1.0
+
done)
+34
vendor/ocaml-libbpf/examples/tc.bpf.c
···
+
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
/* Copyright (c) 2022 Hengqi Chen */
+
#include "vmlinux.h"
+
#include <bpf/bpf_endian.h>
+
#include <bpf/bpf_helpers.h>
+
#include <bpf/bpf_tracing.h>
+
+
#define TC_ACT_OK 0
+
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+
+
SEC("tc")
+
int tc_ingress(struct __sk_buff *ctx)
+
{
+
void *data_end = (void *)(__u64)ctx->data_end;
+
void *data = (void *)(__u64)ctx->data;
+
struct ethhdr *l2;
+
struct iphdr *l3;
+
+
if (ctx->protocol != bpf_htons(ETH_P_IP))
+
return TC_ACT_OK;
+
+
l2 = data;
+
if ((void *)(l2 + 1) > data_end)
+
return TC_ACT_OK;
+
+
l3 = (struct iphdr *)(l2 + 1);
+
if ((void *)(l3 + 1) > data_end)
+
return TC_ACT_OK;
+
+
bpf_printk("Got IP packet: tot_len: %d, ttl: %d", bpf_ntohs(l3->tot_len), l3->ttl);
+
return TC_ACT_OK;
+
}
+
+
char __license[] SEC("license") = "GPL";
+70
vendor/ocaml-libbpf/examples/tc.ml
···
+
(* This program monitors the traffic going through your loopback
+
interface, once this program is run, check your trace pipe with
+
`sudo cat /sys/kernel/debug/tracing/trace_pipe` and run `ping
+
127.0.0.1` to see the output *)
+
open Ctypes
+
open Libbpf
+
+
let obj_path = "tc.bpf.o"
+
let program_name = "tc_ingress"
+
+
let () =
+
(* Set signal handlers *)
+
let exitting = ref true in
+
let sig_handler = Sys.Signal_handle (fun _ -> exitting := false) in
+
Sys.(set_signal sigint sig_handler);
+
Sys.(set_signal sigterm sig_handler);
+
+
let hook_created = ref false in
+
+
let tc_hook = make C.Types.Bpf_tc.hook in
+
setf tc_hook C.Types.Bpf_tc.ifindex 1;
+
setf tc_hook C.Types.Bpf_tc.attach_point `INGRESS;
+
let sz = Ctypes.sizeof C.Types.Bpf_tc.hook in
+
setf tc_hook C.Types.Bpf_tc.sz (Unsigned.Size_t.of_int sz);
+
+
let tc_opts = make C.Types.Bpf_tc.Opts.t in
+
setf tc_opts C.Types.Bpf_tc.Opts.handle (Unsigned.UInt32.of_int 1);
+
setf tc_opts C.Types.Bpf_tc.Opts.priority (Unsigned.UInt32.of_int 1);
+
let sz = Ctypes.sizeof C.Types.Bpf_tc.Opts.t in
+
setf tc_opts C.Types.Bpf_tc.Opts.sz (Unsigned.Size_t.of_int sz);
+
+
(* Open and load bpf object *)
+
let obj = bpf_object_open obj_path in
+
bpf_object_load obj;
+
let prog = bpf_object_find_program_by_name obj program_name in
+
+
(* Try to create hook *)
+
(* The hook (i.e. qdisc) may already exists because: *)
+
(* 1. it is created by other processes or users *)
+
(* 2. or since we are attaching to the TC ingress ONLY, *)
+
(* bpf_tc_hook_destroy does NOT really remove the qdisc, *)
+
(* there may be an egress filter on the qdisc *)
+
let err = C.Functions.bpf_tc_hook_create (addr tc_hook) in
+
if err = 0 then hook_created := true;
+
+
if err <> 0 && err <> -17 (*EEXIST*) then (
+
Printf.eprintf "Failed to create tc hook: %d\n" err;
+
exit 1);
+
+
setf tc_opts C.Types.Bpf_tc.Opts.prog_fd prog.fd;
+
let err = C.Functions.bpf_tc_attach (addr tc_hook) (addr tc_opts) in
+
if err = 1 then (
+
Printf.eprintf "Failed to attach TC: %d\n" err;
+
C.Functions.bpf_tc_hook_destroy (addr tc_hook) |> ignore;
+
exit 1);
+
+
Printf.printf
+
"Successfully started! Please run `sudo cat \
+
/sys/kernel/debug/tracing/trace_pipe` to see output of the BPF program.\n\
+
%!";
+
+
while !exitting do
+
Printf.eprintf ".%!";
+
Unix.sleepf 1.0
+
done;
+
+
let err = C.Functions.bpf_tc_detach (addr tc_hook) (addr tc_opts) in
+
if err = 1 then Printf.eprintf "Failed to detach TC: %d\n" err;
+
C.Functions.bpf_tc_hook_destroy (addr tc_hook) |> ignore;
+
bpf_object_close obj
+44
vendor/ocaml-libbpf/libbpf.opam
···
+
# This file is generated by dune, edit dune-project instead
+
opam-version: "2.0"
+
synopsis: "Libbpf bindings"
+
description: "Wrapped libbpf api's for writing BPF user programs in OCaml"
+
maintainer: ["Lee Koon Wen"]
+
authors: ["Lee Koon Wen"]
+
license: ["ISC" "BSD-3-Clause"]
+
tags: ["bindings" "bpf" "libbpf"]
+
homepage: "https://github.com/koonwen/ocaml-libbpf"
+
doc: "https://koonwen.github.io/ocaml-libbpf"
+
bug-reports: "https://github.com/koonwen/ocaml-libbpf/issues"
+
depends: [
+
"ocaml" {>= "4.08"}
+
"dune" {>= "3.13"}
+
"ctypes" {>= "0.22.0"}
+
"ppx_deriving"
+
"ppx_expect"
+
"conf-libbpf"
+
"conf-bpftool"
+
"conf-clang"
+
"odoc" {with-doc}
+
]
+
build: [
+
["dune" "subst"] {dev}
+
[
+
"dune"
+
"build"
+
"-p"
+
name
+
"-j"
+
jobs
+
"@install"
+
"@runtest" {with-test}
+
"@doc" {with-doc}
+
]
+
]
+
dev-repo: "git+https://github.com/koonwen/ocaml-libbpf.git"
+
# eBPF features by kernel version https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md
+
# Fix to kernel >= 6.1 to provide bound BPF map types
+
available: [ os = "linux" &
+
(( os-distribution = "debian" & os-version >= "12" ) # Linux 6.1 & Libbpf 1.1.0
+
|( os-distribution = "ubuntu" & os-version >= "23.04" ) # Linux 6.2 & Libbpf 1.1.0
+
|( os-distribution = "fedora" & os-version >= "38" )) # Linux 6.2 & Libbpf 1.1.0
+
]
+7
vendor/ocaml-libbpf/libbpf.opam.template
···
+
# eBPF features by kernel version https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md
+
# Fix to kernel >= 6.1 to provide bound BPF map types
+
available: [ os = "linux" &
+
(( os-distribution = "debian" & os-version >= "12" ) # Linux 6.1 & Libbpf 1.1.0
+
|( os-distribution = "ubuntu" & os-version >= "23.04" ) # Linux 6.2 & Libbpf 1.1.0
+
|( os-distribution = "fedora" & os-version >= "38" )) # Linux 6.2 & Libbpf 1.1.0
+
]
+32
vendor/ocaml-libbpf/libbpf_maps.opam
···
+
opam-version: "2.0"
+
synopsis: "Libbpf maps API"
+
description: "High level API's for interacting with BPF maps in OCaml"
+
maintainer: ["Lee Koon Wen"]
+
authors: ["Lee Koon Wen"]
+
license: ["ISC" "BSD-3-Clause"]
+
tags: ["bindings" "bpf" "libbpf"]
+
homepage: "https://github.com/koonwen/ocaml-libbpf"
+
doc: "https://koonwen.github.io/ocaml-libbpf"
+
bug-reports: "https://github.com/koonwen/ocaml-libbpf/issues"
+
depends: [
+
"dune" {>= "3.13"}
+
"ctypes" {>= "0.22.0"}
+
"ctypes-foreign" {>= "0.22.0"}
+
"libbpf" {= version}
+
"odoc" {with-doc}
+
]
+
build: [
+
["dune" "subst"] {dev}
+
[
+
"dune"
+
"build"
+
"-p"
+
name
+
"-j"
+
jobs
+
"@install"
+
"@runtest" {with-test}
+
"@doc" {with-doc}
+
]
+
]
+
dev-repo: "git+https://github.com/koonwen/ocaml-libbpf.git"
+5
vendor/ocaml-libbpf/src/bindings/c.ml
···
+
module Types = C_function_description.Types
+
(** Low-level C bindings of libbpf types *)
+
+
module Functions = C_function_description.Functions (C_function_generated)
+
(** Low-level C bindings of libbpf functions *)
+284
vendor/ocaml-libbpf/src/bindings/c_function_description.ml
···
+
open Ctypes
+
+
(** You probably don't mean to be looking into this section, it is
+
part of the stub generation process of the bindings.*)
+
+
(* The C_types_generated module is generated by the build system to
+
grab the type definitions from the header files in C to ensure
+
that offsets and structs are aligned. *)
+
module Types = C_type_description.Types (C_types_generated)
+
+
module Functions (F : Ctypes.FOREIGN) = struct
+
open F
+
+
(* ======================================== Generics ======================================== *)
+
let libbpf_major_version =
+
foreign "libbpf_major_version" (void @-> returning uint32_t)
+
+
let libbpf_minor_version =
+
foreign "libbpf_minor_version" (void @-> returning uint32_t)
+
+
let libbpf_version_string =
+
foreign "libbpf_version_string" (void @-> returning string)
+
+
let libbpf_strerror =
+
foreign "libbpf_strerror" (int @-> ptr char @-> size_t @-> returning int)
+
+
let libbpf_bpf_attach_type_str =
+
foreign "libbpf_bpf_attach_type_str"
+
(Types.Bpf_attach_type.t @-> returning string)
+
+
let libbpf_bpf_link_type_str =
+
foreign "libbpf_bpf_link_type_str"
+
(Types.Bpf_link_type.t @-> returning string)
+
+
let libbpf_bpf_map_type_str =
+
foreign "libbpf_bpf_map_type_str" (Types.Bpf_map_type.t @-> returning string)
+
+
let libbpf_bpf_prog_type_str =
+
foreign "libbpf_bpf_prog_type_str"
+
(Types.Bpf_prog_type.t @-> returning string)
+
+
let libbpf_set_print =
+
foreign "libbpf_set_print"
+
(Types.libbpf_print_fn_t @-> returning Types.libbpf_print_fn_t)
+
+
(* ================================= Open / Load / Close =================================== *)
+
+
(** [bpf_object__open path] creates a bpf_object by opening the BPF
+
ELF object file pointed to by the passed [path] and loading it
+
into memory.
+
+
Returns pointer to the new bpf_object; or NULL is returned on
+
error, error code is stored in errno. *)
+
let bpf_object__open =
+
foreign "bpf_object__open" (string @-> returning (ptr_opt Types.bpf_object))
+
+
(** [bpf_object__load obj_ptr] loads the BPF object into the
+
kernel. [obj_ptr] must be a valid BPF object instance returned
+
by a successful call to [bpf_object__open].
+
+
Returns 0, on success; negative error code, otherwise, error code is stored in errno *)
+
let bpf_object__load =
+
foreign "bpf_object__load" (ptr Types.bpf_object @-> returning int)
+
+
(** [bpf_object__find_program_by_name name] returns the BPF program
+
of the given [name], if it exists within the passed BPF object
+
+
Returns the pointer to the BPF program instance, if such program
+
exists within the BPF object; or NULL otherwise. *)
+
let bpf_object__find_program_by_name =
+
foreign "bpf_object__find_program_by_name"
+
(ptr Types.bpf_object @-> string @-> returning (ptr_opt Types.bpf_program))
+
+
(** [bpf_object__next_program obj_ptr prog_ptr] returns the next
+
program after [prog_ptr] found in the passed BPF object *)
+
let bpf_object__next_program =
+
foreign "bpf_object__next_program"
+
(ptr Types.bpf_object @-> ptr Types.bpf_program
+
@-> returning (ptr Types.bpf_program))
+
+
(** [bpf_program__pin prog path] pins the BPF program to a file in
+
the BPF FS specified by a [path]. This increments the programs
+
reference count, allowing it to stay loaded after the process
+
which loaded it has exited.
+
+
@param prog BPF program to pin, must already be loaded
+
@param path file path in a BPF file system
+
@return 0, on success; negative error code, otherwise *)
+
let bpf_program__pin =
+
foreign "bpf_program__pin"
+
(ptr Types.bpf_program @-> ptr char @-> returning int)
+
+
(** [bpf_program__unpin prog path] unpins the BPF program from a file in the
+
BPFFS specified by a path. This decrements the programs
+
reference count. The file pinning the BPF program can also be
+
unlinked by a different process in which case this function will
+
return an error.
+
+
@param prog BPF program to unpin
+
@param path file path to the pin in a BPF file system
+
@return 0, on success; negative error code, otherwise *)
+
let bpf_program__unpin =
+
foreign "bpf_program__unpin"
+
(ptr Types.bpf_program @-> ptr char @-> returning int)
+
+
(** [bpf_program__attach prog] is a generic function for
+
attaching a BPF program based on auto-detection of program type,
+
attach type, and extra paremeters, where applicable.
+
+
This is supported for:
+
- kprobe/kretprobe (depends on SEC() definition)
+
- uprobe/uretprobe (depends on SEC() definition)
+
- tracepoint
+
- raw tracepoint
+
- tracing programs (typed raw TP/fentry/fexit/fmod_ret)
+
+
Returns pointer to the newly created BPF link; or NULL is
+
returned on error, error code is stored in errno *)
+
let bpf_program__attach =
+
foreign "bpf_program__attach"
+
(ptr Types.bpf_program @-> returning (ptr_opt Types.bpf_link))
+
+
let bpf_program__fd =
+
foreign "bpf_program__fd" (ptr Types.bpf_program @-> returning int)
+
+
(** [bpf_link__pin link path] pins the BPF link to a file in the
+
BPF FS specified by a path. This increments the links reference
+
count, allowing it to stay loaded after the process which loaded
+
it has exited.
+
+
@param link BPF link to pin, must already be loaded
+
@param path file path in a BPF file system
+
@return 0, on success; negative error code, otherwise *)
+
let bpf_link__pin =
+
foreign "bpf_link__pin" (ptr Types.bpf_link @-> ptr char @-> returning int)
+
+
(** [bpf_link__unpin link path] unpins the BPF link from a file in
+
the BPFFS specified by a path. This decrements the links
+
reference count. The file pinning the BPF link can also be
+
unlinked by a different process in which case this function will
+
return an error.
+
+
@param prog BPF program to unpin
+
@param path file path to the pin in a BPF file system
+
@return 0, on success; negative error code, otherwise *)
+
let bpf_link__unpin =
+
foreign "bpf_link__unpin" (ptr Types.bpf_link @-> returning int)
+
+
(** [bpf_link__destroy link_ptr] Removes the link to the BPF program.
+
Returns 0 on success or -errno *)
+
let bpf_link__destroy =
+
foreign "bpf_link__destroy" (ptr Types.bpf_link @-> returning int)
+
+
(** [bpf_object__close obj_ptr] closes a BPF object and releases all
+
resources. *)
+
let bpf_object__close =
+
foreign "bpf_object__close" (ptr Types.bpf_object @-> returning void)
+
+
(* ======================================== Maps ======================================== *)
+
(* Not explicitly mentioned in the documentation but keys and values look
+
like they're copied into the internal bpf map structure, so we
+
don't need to be worried about keeping references around. *)
+
+
(** [bpf_object__find_map_by_name obj_ptr name] returns BPF map of the given
+
[name], if it exists within the passed BPF object.
+
+
Returns the pointer to the BPF map instance, if such map exists
+
within the BPF object; or NULL otherwise. *)
+
let bpf_object__find_map_by_name =
+
foreign "bpf_object__find_map_by_name"
+
(ptr Types.bpf_object @-> string @-> returning (ptr_opt Types.bpf_map))
+
+
(** [bpf_map__fd map_ptr] gets the file descriptor of the passed BPF
+
map
+
+
Returns the file descriptor; or -EINVAL in case of an error *)
+
let bpf_map__fd = foreign "bpf_map__fd" (ptr Types.bpf_map @-> returning int)
+
+
(** [bpf_map__lookup_elem map_ptr key_ptr key_sz val_ptr val_sz
+
flags] allows to lookup BPF map value corresponding to provided
+
key.
+
+
[bpf_map__lookup_elem] is high-level equivalent of
+
[bpf_map_lookup_elem] API with added check for key and value
+
size.
+
+
sizes are in bytes of key and value data. For per-CPU BPF maps
+
value size has to be a product of BPF map value size and number
+
of possible CPUs in the system (could be fetched with
+
libbpf_num_possible_cpus()). Note also that for per-CPU values
+
value size has to be aligned up to closest 8 bytes for alignment
+
reasons, so expected size is: round_up(value_size, 8)
+
+
Returns 0, on success; negative error, otherwise *)
+
let bpf_map__lookup_elem =
+
foreign "bpf_map__lookup_elem"
+
(ptr Types.bpf_map @-> ptr void @-> size_t @-> ptr void @-> size_t
+
@-> uint64_t @-> returning int)
+
+
(** [bpf_map__update_elem map_ptr key_ptr key_sz val_ptr val_sz
+
flags] allows to insert or update value in BPF map that
+
corresponds to provided key.
+
+
[bpf_map__update_elem] is high-level equivalent of
+
[bpf_map_update_elem] API with added check for key and value
+
size.
+
+
Check [bpf_map__lookup_elem] for details on sizes.
+
Returns 0, on success; negative error, otherwise *)
+
let bpf_map__update_elem =
+
foreign "bpf_map__update_elem"
+
(ptr Types.bpf_map @-> ptr void @-> size_t @-> ptr void @-> size_t
+
@-> uint64_t @-> returning int)
+
+
(** [bpf_map__delete_elem map_ptr key_ptr key_sz flags] allows to
+
delete element in BPF map that corresponds to provided key.
+
+
[bpf_map__delete_elem] is high-level equivalent of
+
[bpf_map_delete_elem] API with added check for key size.
+
+
Returns 0, on success; negative error, otherwise *)
+
let bpf_map__delete_elem =
+
foreign "bpf_map__delete_elem"
+
(ptr Types.bpf_map @-> ptr void @-> size_t @-> uint64_t @-> returning int)
+
+
(* ================================== Traffic control ================================== *)
+
+
let bpf_tc_hook_create =
+
foreign "bpf_tc_hook_create" (ptr Types.Bpf_tc.hook @-> returning int)
+
+
let bpf_tc_hook_destroy =
+
foreign "bpf_tc_hook_destroy" (ptr Types.Bpf_tc.hook @-> returning int)
+
+
let bpf_tc_attach =
+
foreign "bpf_tc_attach"
+
(ptr Types.Bpf_tc.hook @-> ptr Types.Bpf_tc.Opts.t @-> returning int)
+
+
let bpf_tc_detach =
+
foreign "bpf_tc_detach"
+
(ptr Types.Bpf_tc.hook @-> ptr Types.Bpf_tc.Opts.t @-> returning int)
+
+
(* ====================================== RingBuf ===================================== *)
+
+
(** [ring_buffer__new map_fd fn ctx_ptr opts] loads the callback
+
function [fn] into the ring buffer map provided by the file
+
descriptor [map_fd]. [ctx_ptr] allows the callback function to
+
access user provided context.
+
+
Returns pointer to the ring_buffer manager instance or NULL
+
otherwise *)
+
let ring_buffer__new =
+
foreign "ring_buffer__new"
+
(int @-> Types.ring_buffer_sample_fn @-> ptr void
+
@-> ptr Types.ring_buffer_opts
+
@-> returning (ptr_opt Types.ring_buffer))
+
+
(** [ring_buffer__poll ring_buf_ptr timeout] poll for available
+
data and consume records, if any are available.
+
+
Returns number of records consumed (or INT_MAX, whichever is
+
less), or negative number, if any of the registered callbacks
+
returned error. *)
+
let ring_buffer__poll =
+
foreign "ring_buffer__poll" (ptr Types.ring_buffer @-> int @-> returning int)
+
+
(** [ring_buffer__free ring_buf_ptr] Frees resources of the ring
+
buffer manager *)
+
let ring_buffer__free =
+
foreign "ring_buffer__free" (ptr Types.ring_buffer @-> returning void)
+
+
(** [ring_buffer__consume ring_buf_ptr] Consume available ring
+
buffer(s) data without event polling.
+
+
Returns number of records consumed across all registered ring
+
buffers (or INT_MAX, whichever is less), or negative number if
+
any of the callbacks return error. *)
+
let ring_buffer__consume =
+
foreign "ring_buffer__consume" (ptr Types.ring_buffer @-> returning int)
+
+
(** [ring_buffer__epoll_fd ring_buf_ptr] Gets an fd that can be used
+
to sleep until data is available in the ring(s) *)
+
let ring_buffer__epoll_fd =
+
foreign "ring_buffer__epoll_fd" (ptr Types.ring_buffer @-> returning int)
+
end
+409
vendor/ocaml-libbpf/src/bindings/c_type_description.ml
···
+
open Ctypes
+
+
(** You probably don't mean to be looking into this section, it is
+
part of the stub generation process of the bindings.*)
+
+
module Types (F : Ctypes.TYPE) = struct
+
open F
+
+
let c ?(prefix = "") label = constant (prefix ^ label) int64_t
+
+
let libbpf_print_level : [ `WARN | `INFO | `DEBUG | `UNEXPECTED ] typ =
+
let def = c ~prefix:"LIBBPF_" in
+
enum "libbpf_print_level"
+
~unexpected:(fun _ -> `UNEXPECTED)
+
[ (`WARN, def "WARN"); (`INFO, def "INFO"); (`DEBUG, def "DEBUG") ]
+
+
let libbpf_print_fn_t :
+
([ `WARN | `INFO | `DEBUG | `UNEXPECTED ] -> string -> int) static_funptr
+
typ =
+
typedef
+
(static_funptr (libbpf_print_level @-> string @-> returning int))
+
"libbpf_print_fn_t"
+
+
module Errno = struct
+
type t =
+
[ `LIBELF (* Something wrong in libelf *)
+
| `FORMAT (* BPF object format invalid *)
+
| `KVERSION (* Incorrect or no 'version' section *)
+
| `ENDIAN (* Endian mismatch *)
+
| `INTERNAL (* Internal error in libbpf *)
+
| `RELOC (* Relocation failed *)
+
| `LOAD (* Load program failure for unknown reason *)
+
| `VERIFY (* Kernel verifier blocks program loading *)
+
| `PROG2BIG (* Program too big *)
+
| `KVER (* Incorrect kernel version *)
+
| `PROGTYPE (* Kernel doesn't support this program type *)
+
| `WRNGPID (* Wrong pid in netlink message *)
+
| `INVSEQ (* Invalid netlink sequence *)
+
| `NLPARSE (* netlink parsing error *)
+
| `UNKNOWN ]
+
+
let t : t typ =
+
let def = c ~prefix:"LIBBPF_ERRNO__" in
+
enum "libbpf_errno"
+
~unexpected:(fun _ -> `UNKNOWN)
+
[
+
(`LIBELF, def "LIBELF");
+
(`FORMAT, def "FORMAT");
+
(`KVERSION, def "KVERSION");
+
(`ENDIAN, def "ENDIAN");
+
(`INTERNAL, def "INTERNAL");
+
(`RELOC, def "RELOC");
+
(`LOAD, def "LOAD");
+
(`VERIFY, def "VERIFY");
+
(`PROG2BIG, def "PROG2BIG");
+
(`KVER, def "KVER");
+
(`PROGTYPE, def "PROGTYPE");
+
(`WRNGPID, def "WRNGPID");
+
(`INVSEQ, def "INVSEQ");
+
(`NLPARSE, def "NLPARSE");
+
]
+
end
+
+
module Bpf_attach_type = struct
+
type cgroup =
+
[ `BPF_CGROUP_INET_INGRESS
+
| `BPF_CGROUP_INET_EGRESS
+
| `BPF_CGROUP_INET_SOCK_CREATE
+
| `BPF_CGROUP_SOCK_OPS
+
| `BPF_CGROUP_DEVICE
+
| `BPF_CGROUP_INET4_BIND
+
| `BPF_CGROUP_INET6_BIND
+
| `BPF_CGROUP_INET4_CONNECT
+
| `BPF_CGROUP_INET6_CONNECT
+
| `BPF_CGROUP_INET4_POST_BIND
+
| `BPF_CGROUP_INET6_POST_BIND
+
| `BPF_CGROUP_UDP4_SENDMSG
+
| `BPF_CGROUP_UDP6_SENDMSG
+
| `BPF_CGROUP_SYSCTL
+
| `BPF_CGROUP_UDP4_RECVMSG
+
| `BPF_CGROUP_UDP6_RECVMSG
+
| `BPF_CGROUP_GETSOCKOPT
+
| `BPF_CGROUP_SETSOCKOPT
+
| `BPF_CGROUP_INET4_GETPEERNAME
+
| `BPF_CGROUP_INET6_GETPEERNAME
+
| `BPF_CGROUP_INET4_GETSOCKNAME
+
| `BPF_CGROUP_INET6_GETSOCKNAME
+
| `BPF_CGROUP_INET_SOCK_RELEASE ]
+
+
type sk =
+
[ `BPF_SK_SKB_STREAM_PARSER
+
| `BPF_SK_SKB_STREAM_VERDICT
+
| `BPF_SK_MSG_VERDICT
+
| `BPF_SK_LOOKUP
+
| `BPF_SK_SKB_VERDICT
+
| `BPF_SK_REUSEPORT_SELECT
+
| `BPF_SK_REUSEPORT_SELECT_OR_MIGRATE ]
+
+
type trace =
+
[ `BPF_TRACE_RAW_TP
+
| `BPF_TRACE_FENTRY
+
| `BPF_TRACE_FEXIT
+
| `BPF_TRACE_ITER
+
| `BPF_TRACE_KPROBE_MULTI ]
+
+
type xdp = [ `BPF_XDP_DEVMAP | `BPF_XDP_CPUMAP | `BPF_XDP ]
+
+
type other =
+
[ `BPF_LIRC_MODE2
+
| `BPF_FLOW_DISSECTOR
+
| `BPF_MODIFY_RETURN
+
| `BPF_PERF_EVENT
+
| `BPF_LSM_MAC
+
| `BPF_LSM_CGROUP ]
+
+
type t = [ cgroup | sk | trace | xdp | other | `UNKNOWN ]
+
+
let t : t typ =
+
let cgroup = c ~prefix:"BPF_CGROUP_" in
+
let sk = c ~prefix:"BPF_SK_" in
+
let trace = c ~prefix:"BPF_TRACE_" in
+
let xdp = c ~prefix:"BPF_XDP_" in
+
enum "bpf_attach_type"
+
~unexpected:(fun _ -> `UNKNOWN)
+
[
+
(`BPF_CGROUP_INET_INGRESS, cgroup "INET_INGRESS");
+
(`BPF_CGROUP_INET_EGRESS, cgroup "INET_EGRESS");
+
(`BPF_CGROUP_INET_SOCK_CREATE, cgroup "INET_SOCK_CREATE");
+
(`BPF_CGROUP_SOCK_OPS, cgroup "SOCK_OPS");
+
(`BPF_CGROUP_DEVICE, cgroup "DEVICE");
+
(`BPF_CGROUP_INET4_BIND, cgroup "INET4_BIND");
+
(`BPF_CGROUP_INET6_BIND, cgroup "INET6_BIND");
+
(`BPF_CGROUP_INET4_CONNECT, cgroup "INET4_CONNECT");
+
(`BPF_CGROUP_INET6_CONNECT, cgroup "INET6_CONNECT");
+
(`BPF_CGROUP_INET4_POST_BIND, cgroup "INET4_POST_BIND");
+
(`BPF_CGROUP_INET6_POST_BIND, cgroup "INET6_POST_BIND");
+
(`BPF_CGROUP_UDP4_SENDMSG, cgroup "UDP4_SENDMSG");
+
(`BPF_CGROUP_UDP6_SENDMSG, cgroup "UDP6_SENDMSG");
+
(`BPF_CGROUP_SYSCTL, cgroup "SYSCTL");
+
(`BPF_CGROUP_UDP4_RECVMSG, cgroup "UDP4_RECVMSG");
+
(`BPF_CGROUP_UDP6_RECVMSG, cgroup "UDP6_RECVMSG");
+
(`BPF_CGROUP_GETSOCKOPT, cgroup "GETSOCKOPT");
+
(`BPF_CGROUP_SETSOCKOPT, cgroup "SETSOCKOPT");
+
(`BPF_CGROUP_INET4_GETPEERNAME, cgroup "INET4_GETPEERNAME");
+
(`BPF_CGROUP_INET6_GETPEERNAME, cgroup "INET6_GETPEERNAME");
+
(`BPF_CGROUP_INET4_GETSOCKNAME, cgroup "INET4_GETSOCKNAME");
+
(`BPF_CGROUP_INET6_GETSOCKNAME, cgroup "INET6_GETSOCKNAME");
+
(`BPF_CGROUP_INET_SOCK_RELEASE, cgroup "INET_SOCK_RELEASE");
+
(`BPF_SK_SKB_STREAM_PARSER, sk "SKB_STREAM_PARSER");
+
(`BPF_SK_SKB_STREAM_VERDICT, sk "SKB_STREAM_VERDICT");
+
(`BPF_SK_MSG_VERDICT, sk "MSG_VERDICT");
+
(`BPF_SK_LOOKUP, sk "LOOKUP");
+
(`BPF_SK_SKB_VERDICT, sk "SKB_VERDICT");
+
(`BPF_SK_REUSEPORT_SELECT, sk "REUSEPORT_SELECT");
+
(`BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, sk "REUSEPORT_SELECT_OR_MIGRATE");
+
(`BPF_TRACE_RAW_TP, trace "RAW_TP");
+
(`BPF_TRACE_FENTRY, trace "FENTRY");
+
(`BPF_TRACE_FEXIT, trace "FEXIT");
+
(`BPF_TRACE_ITER, trace "ITER");
+
(`BPF_TRACE_KPROBE_MULTI, trace "KPROBE_MULTI");
+
(`BPF_XDP_DEVMAP, xdp "DEVMAP");
+
(`BPF_XDP_CPUMAP, xdp "CPUMAP");
+
(`BPF_XDP, c "BPF_XDP");
+
(`BPF_LIRC_MODE2, c "BPF_LIRC_MODE2");
+
(`BPF_FLOW_DISSECTOR, c "BPF_FLOW_DISSECTOR");
+
(`BPF_MODIFY_RETURN, c "BPF_MODIFY_RETURN");
+
(`BPF_PERF_EVENT, c "BPF_PERF_EVENT");
+
(`BPF_LSM_MAC, c "BPF_LSM_MAC");
+
(`BPF_LSM_CGROUP, c "BPF_LSM_CGROUP");
+
]
+
end
+
+
module Bpf_link_type = struct
+
type t =
+
[ `UNSPEC
+
| `RAW_TRACEPOINT
+
| `TRACING
+
| `CGROUP
+
| `ITER
+
| `NETNS
+
| `XDP
+
| `PERF_EVENT
+
| `KPROBE_MULTI
+
| `STRUCT_OPS
+
| `UNKNOWN ]
+
+
let t : t typ =
+
let def = c ~prefix:"BPF_LINK_TYPE_" in
+
enum "bpf_link_type"
+
~unexpected:(fun _ -> `UNKNOWN)
+
[
+
(`UNSPEC, def "UNSPEC");
+
(`RAW_TRACEPOINT, def "RAW_TRACEPOINT");
+
(`TRACING, def "TRACING");
+
(`CGROUP, def "CGROUP");
+
(`ITER, def "ITER");
+
(`NETNS, def "NETNS");
+
(`XDP, def "XDP");
+
(`PERF_EVENT, def "PERF_EVENT");
+
(`KPROBE_MULTI, def "KPROBE_MULTI");
+
(`STRUCT_OPS, def "STRUCT_OPS");
+
]
+
end
+
+
module Bpf_map_type = struct
+
type t =
+
[ `UNSPEC
+
| `HASH
+
| `ARRAY
+
| `PROG_ARRAY
+
| `PERF_EVENT_ARRAY
+
| `PERCPU_HASH
+
| `PERCPU_ARRAY
+
| `STACK_TRACE
+
| `CGROUP_ARRAY
+
| `LRU_HASH
+
| `LRU_PERCPU_HASH
+
| `LPM_TRIE
+
| `ARRAY_OF_MAPS
+
| `HASH_OF_MAPS
+
| `DEVMAP
+
| `SOCKMAP
+
| `CPUMAP
+
| `XSKMAP
+
| `SOCKHASH
+
| `CGROUP_STORAGE
+
| `REUSEPORT_SOCKARRAY
+
| `PERCPU_CGROUP_STORAGE
+
| `QUEUE
+
| `STACK
+
| `SK_STORAGE
+
| `DEVMAP_HASH
+
| `STRUCT_OPS
+
| `RINGBUF
+
| `INODE_STORAGE
+
| `TASK_STORAGE
+
| `BLOOM_FILTER
+
| `USER_RINGBUF
+
| `UNKNOWN ]
+
+
let def = c ~prefix:"BPF_MAP_TYPE_"
+
+
let t : t typ =
+
enum "bpf_map_type"
+
~unexpected:(fun _ -> `UNKNOWN)
+
[
+
(`UNSPEC, def "UNSPEC");
+
(`HASH, def "HASH");
+
(`ARRAY, def "ARRAY");
+
(`PROG_ARRAY, def "PROG_ARRAY");
+
(`PERF_EVENT_ARRAY, def "PERF_EVENT_ARRAY");
+
(`PERCPU_HASH, def "PERCPU_HASH");
+
(`PERCPU_ARRAY, def "PERCPU_ARRAY");
+
(`STACK_TRACE, def "STACK_TRACE");
+
(`CGROUP_ARRAY, def "CGROUP_ARRAY");
+
(`LRU_HASH, def "LRU_HASH");
+
(`LRU_PERCPU_HASH, def "LRU_PERCPU_HASH");
+
(`LPM_TRIE, def "LPM_TRIE");
+
(`ARRAY_OF_MAPS, def "ARRAY_OF_MAPS");
+
(`HASH_OF_MAPS, def "HASH_OF_MAPS");
+
(`DEVMAP, def "DEVMAP");
+
(`SOCKMAP, def "SOCKMAP");
+
(`CPUMAP, def "CPUMAP");
+
(`XSKMAP, def "XSKMAP");
+
(`SOCKHASH, def "SOCKHASH");
+
(`CGROUP_STORAGE, def "CGROUP_STORAGE");
+
(`REUSEPORT_SOCKARRAY, def "REUSEPORT_SOCKARRAY");
+
(`PERCPU_CGROUP_STORAGE, def "PERCPU_CGROUP_STORAGE");
+
(`QUEUE, def "QUEUE");
+
(`STACK, def "STACK");
+
(`SK_STORAGE, def "SK_STORAGE");
+
(`DEVMAP_HASH, def "DEVMAP_HASH");
+
(`STRUCT_OPS, def "STRUCT_OPS");
+
(`RINGBUF, def "RINGBUF");
+
(`INODE_STORAGE, def "INODE_STORAGE");
+
(`TASK_STORAGE, def "TASK_STORAGE");
+
(`BLOOM_FILTER, def "BLOOM_FILTER");
+
(`USER_RINGBUF, def "USER_RINGBUF");
+
]
+
end
+
+
module Bpf_prog_type = struct
+
type t =
+
[ `UNSPEC
+
| `SOCKET_FILTER
+
| `KPROBE
+
| `SCHED_CLS
+
| `SCHED_ACT
+
| `TRACEPOINT
+
| `XDP
+
| `PERF_EVENT
+
| `CGROUP_SKB
+
| `CGROUP_SOCK
+
| `LWT_IN
+
| `LWT_OUT
+
| `LWT_XMIT
+
| `SOCK_OPS
+
| `SK_SKB
+
| `CGROUP_DEVICE
+
| `SK_MSG
+
| `RAW_TRACEPOINT
+
| `CGROUP_SOCK_ADDR
+
| `LWT_SEG6LOCAL
+
| `LIRC_MODE2
+
| `SK_REUSEPORT
+
| `FLOW_DISSECTOR
+
| `CGROUP_SYSCTL
+
| `RAW_TRACEPOINT_WRITABLE
+
| `CGROUP_SOCKOPT
+
| `TRACING
+
| `STRUCT_OPS
+
| `EXT
+
| `LSM
+
| `SK_LOOKUP
+
| `SYSCALL
+
| `UNKNOWN ]
+
+
let t : t typ =
+
let def = c ~prefix:"BPF_PROG_TYPE_" in
+
enum "bpf_prog_type"
+
~unexpected:(fun _ -> `UNKNOWN)
+
[
+
(`UNSPEC, def "UNSPEC");
+
(`SOCKET_FILTER, def "SOCKET_FILTER");
+
(`KPROBE, def "KPROBE");
+
(`SCHED_CLS, def "SCHED_CLS");
+
(`SCHED_ACT, def "SCHED_ACT");
+
(`TRACEPOINT, def "TRACEPOINT");
+
(`XDP, def "XDP");
+
(`PERF_EVENT, def "PERF_EVENT");
+
(`CGROUP_SKB, def "CGROUP_SKB");
+
(`CGROUP_SOCK, def "CGROUP_SOCK");
+
(`LWT_IN, def "LWT_IN");
+
(`LWT_OUT, def "LWT_OUT");
+
(`LWT_XMIT, def "LWT_XMIT");
+
(`SOCK_OPS, def "SOCK_OPS");
+
(`SK_SKB, def "SK_SKB");
+
(`CGROUP_DEVICE, def "CGROUP_DEVICE");
+
(`SK_MSG, def "SK_MSG");
+
(`RAW_TRACEPOINT, def "RAW_TRACEPOINT");
+
(`CGROUP_SOCK_ADDR, def "CGROUP_SOCK_ADDR");
+
(`LWT_SEG6LOCAL, def "LWT_SEG6LOCAL");
+
(`LIRC_MODE2, def "LIRC_MODE2");
+
(`SK_REUSEPORT, def "SK_REUSEPORT");
+
(`FLOW_DISSECTOR, def "FLOW_DISSECTOR");
+
(`CGROUP_SYSCTL, def "CGROUP_SYSCTL");
+
(`RAW_TRACEPOINT_WRITABLE, def "RAW_TRACEPOINT_WRITABLE");
+
(`CGROUP_SOCKOPT, def "CGROUP_SOCKOPT");
+
(`TRACING, def "TRACING");
+
(`STRUCT_OPS, def "STRUCT_OPS");
+
(`EXT, def "EXT");
+
(`LSM, def "LSM");
+
(`SK_LOOKUP, def "SK_LOOKUP");
+
(`SYSCALL, def "SYSCALL");
+
]
+
end
+
+
type bpf_object
+
type bpf_program
+
type bpf_link
+
type bpf_map
+
+
let bpf_object : bpf_object structure typ = structure "bpf_object"
+
let bpf_program : bpf_program structure typ = structure "bpf_program"
+
let bpf_link : bpf_link structure typ = structure "bpf_link"
+
let bpf_map : bpf_map structure typ = structure "bpf_map"
+
+
let ring_buffer_sample_fn :
+
(unit ptr -> unit ptr -> Unsigned.size_t -> int) static_funptr typ =
+
typedef
+
(static_funptr (ptr void @-> ptr void @-> size_t @-> returning int))
+
"ring_buffer_sample_fn"
+
+
let ring_buffer : [ `Ring_buffer ] structure typ = structure "ring_buffer"
+
+
let ring_buffer_opts : [ `Ring_buffer_opts ] structure typ =
+
structure "ring_buffer_opts"
+
+
module Bpf_tc = struct
+
let attach_point : [ `INGRESS | `EGRESS | `CUSTOM ] typ =
+
let def = c ~prefix:"BPF_TC_" in
+
enum "bpf_tc_attach_point"
+
[
+
(`INGRESS, def "INGRESS");
+
(`EGRESS, def "EGRESS");
+
(`CUSTOM, def "CUSTOM");
+
]
+
+
module Opts = struct
+
let t : [ `Opts ] structure typ = structure "bpf_tc_opts"
+
let ( -: ) ty label = field t label ty
+
let sz = size_t -: "sz"
+
let prog_fd = int -: "prog_fd"
+
let flags = uint32_t -: "flags"
+
let prog_id = uint32_t -: "prog_id"
+
let handle = uint32_t -: "handle"
+
let priority = uint32_t -: "priority"
+
let () = seal t
+
end
+
+
let hook : [ `Hook ] structure typ = structure "bpf_tc_hook"
+
let ( -: ) ty label = field hook label ty
+
let sz = size_t -: "sz"
+
let ifindex = int -: "ifindex"
+
let attach_point = attach_point -: "attach_point"
+
let parent = uint32_t -: "parent"
+
let () = seal hook
+
end
+
end
+47
vendor/ocaml-libbpf/src/bindings/dune
···
+
(library
+
(name c_type_description)
+
(public_name libbpf.c_type_description)
+
(libraries ctypes)
+
(modules c_type_description))
+
+
(rule
+
(target c_types_generated.ml)
+
(deps stubgen/gen_type_bindings_from_c.exe)
+
(action
+
(with-stdout-to
+
%{target}
+
(run %{deps}))))
+
+
(library
+
(name c_function_description)
+
(public_name libbpf.c_function_description)
+
(libraries ctypes c_type_description)
+
(modules c_types_generated c_function_description))
+
+
(rule
+
(target c_function_generated.ml)
+
(deps stubgen/gen_function_bindings.exe)
+
(action
+
(with-stdout-to
+
%{target}
+
(run %{deps} ml))))
+
+
(rule
+
(target bpf_stubs.c)
+
(deps stubgen/gen_function_bindings.exe)
+
(action
+
(with-stdout-to
+
%{target}
+
(run %{deps} c))))
+
+
; This is just a level of indirection to organize functions and type bindings
+
+
(library
+
(name c)
+
(public_name libbpf.c)
+
(c_library_flags -lbpf)
+
(foreign_stubs
+
(language c)
+
(names bpf_stubs))
+
(libraries c_function_description)
+
(modules c c_function_generated))
+23
vendor/ocaml-libbpf/src/bindings/stubgen/dune
···
+
(executable
+
(name gen_type_bindings)
+
(modules gen_type_bindings)
+
(libraries ctypes.stubs c_type_description))
+
+
(rule
+
(target gen_type_bindings.c)
+
(action
+
(with-stdout-to
+
%{target}
+
(run ./gen_type_bindings.exe))))
+
+
(rule
+
(targets gen_type_bindings_from_c.exe)
+
(deps gen_type_bindings.c)
+
(action
+
(bash
+
"%{cc} %{deps} -I `dirname %{lib:ctypes:ctypes_cstubs_internals.h}` -I %{ocaml_where} -o %{targets}")))
+
+
(executable
+
(name gen_function_bindings)
+
(modules gen_function_bindings)
+
(libraries ctypes.stubs c_function_description))
+12
vendor/ocaml-libbpf/src/bindings/stubgen/gen_function_bindings.ml
···
+
let () =
+
let concurrency = Cstubs.unlocked in
+
let errno = Cstubs.ignore_errno in
+
match Sys.argv.(1) with
+
| "ml" ->
+
Cstubs.write_ml ~concurrency Format.std_formatter ~prefix:"" ~errno
+
(module C_function_description.Functions)
+
| "c" ->
+
print_endline "#include <bpf/libbpf.h>";
+
Cstubs.write_c ~concurrency Format.std_formatter ~prefix:"" ~errno
+
(module C_function_description.Functions)
+
| s -> failwith ("unknown functions " ^ s)
+3
vendor/ocaml-libbpf/src/bindings/stubgen/gen_type_bindings.ml
···
+
let () =
+
print_endline "#include <bpf/libbpf.h>";
+
Cstubs_structs.write_c Format.std_formatter (module C_type_description.Types)
+7
vendor/ocaml-libbpf/src/dune
···
+
(library
+
(name libbpf)
+
(public_name libbpf)
+
(libraries c))
+
+
(documentation
+
(package libbpf))
+151
vendor/ocaml-libbpf/src/index.mld
···
+
{0 ocaml-libbpf}
+
+
OCaml bindings to {{:https://github.com/libbpf/libbpf}libbpf} C
+
library for loading eBPF programs into the linux kernel.
+
+
{1 Introduction}
+
+
Writing eBPF programs consist of two distinct parts. Implementing the
+
code that executes in-kernel and user-level code responsible for
+
loading/initializing/linking/teardown of the in-kernel code. This
+
OCaml library provides the latter via binding the C libbpf library. It
+
exposes both the raw low-level bindings as well as a set of high-level
+
API's for handling your eBPF objects. As of now, the kernel part must
+
still be written in
+
{{:https://stackoverflow.com/questions/57688344/what-is-not-allowed-in-restricted-c-for-ebpf}
+
restricted C} and compiled with llvm to eBPF bytecode.
+
+
For the high-level APIs: {!Libbpf}
+
+
For the low-level bindings: {!Libbpf.C}.
+
+
{1:Tutorial Tutorial}
+
+
This example assumes the user has knowledge of how to implement the
+
kernel part of a eBPF program. If not, you can check out this
+
{{:https://nakryiko.com/posts/libbpf-bootstrap/#the-bpf-side}
+
resource} first. Consider the following kernel eBPF program named {b
+
minimal.bpf.c}:
+
+
{@c[
+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
/* Copyright (c) 2020 Facebook */
+
#include <linux/bpf.h>
+
#include "bpf/bpf_helpers.h" /* This is from our libbpf library */
+
+
char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+
/* Globals implemented as an array */
+
struct {
+
__uint(type, BPF_MAP_TYPE_ARRAY);
+
__uint(max_entries, 1);
+
__type(key, int);
+
__type(value, long);
+
} globals SEC(".maps");
+
+
int my_pid_index = 0;
+
+
SEC("tp/syscalls/sys_enter_write")
+
int handle_tp(void *ctx) {
+
int pid = bpf_get_current_pid_tgid() >> 32;
+
+
long *my_pid;
+
my_pid = bpf_map_lookup_elem(&globals, &my_pid_index);
+
if (my_pid == NULL) {
+
bpf_printk("Error got NULL");
+
return 1;
+
};
+
+
if (pid != *my_pid)
+
return 0;
+
+
bpf_printk("Hello, BPF triggered from PID %d", pid);
+
+
return 0;
+
}
+
]}
+
+
After compilation to eBPF ELF file as {b "minimal.o"}. Users just need
+
to provide the path to this ELF file along with the name of the
+
program and optionally an initialization function. Note that the name
+
of the program refers to the function identifier under the SEC(...)
+
attribute, in this case it is {b "handle_tp"}.
+
+
{@ocaml[
+
open Libbpf
+
+
let obj_path = "minimal.bpf.o"
+
let program_names = [ "handle_tp" ]
+
+
let () =
+
with_bpf_object_open_load_link ~obj_path ~program_names ~before_link
+
(fun obj link -> (* Do something *))
+
]}
+
+
The context manager {{!Libbpf.with_bpf_object_open_load_link}
+
with_bpf_object_open_load_link} is a convenience wrapper for all the
+
neccessary steps to load up your eBPF program into the kernel.
+
+
If we don't specify anything in the body of the function marked with
+
{b (* Do something *)}, our loaded kernel program will be unloaded
+
immediately. In this case, we will add some looping logic to keep the
+
program running in the kernel and add a set of signal handlers to
+
escape the loop.
+
+
{@ocaml[
+
let obj_path = "minimal.bpf.o"
+
let program_names = [ "handle_tp" ]
+
+
let () =
+
with_bpf_object_open_load_link ~obj_path ~program_names ~before_link
+
(fun obj link ->
+
+
(* Set up signal handlers *)
+
let exitting = ref true in
+
let sig_handler = Sys.Signal_handle (fun _ -> exitting := false) in
+
Sys.(set_signal sigint sig_handler);
+
Sys.(set_signal sigterm sig_handler);
+
+
Printf.printf
+
"Successfully started! Please run `sudo cat \
+
/sys/kernel/debug/tracing/trace_pipe` to see output of the BPF \
+
programs.\n\
+
%!"
+
+
(* Loop until Ctrl-C is called *)
+
while !exitting do
+
Printf.eprintf ".%!";
+
Unix.sleepf 1.0
+
done)
+
]}
+
+
Our bpf program is now running in the kernel until we decide to
+
interrupt it. However, it doesn't do exactly what we want. In
+
particular, it doesn't filter for our process PID. This is because we
+
haven't loaded our process PID into the BPF map. To do this, we need
+
the name of the map we declared by our {b minimal.bpf.c} program. In
+
this case, our BPF array map was named {b globals}.
+
+
{@ocaml[
+
let map = "globals"
+
+
(* Load PID into BPF map *)
+
let before_link obj =
+
let pid = Unix.getpid () |> Signed.Long.of_int in
+
let global_map = bpf_object_find_map_by_name obj map in
+
(* When updating an element, users need to specify the type of the key and value
+
declared by the map which checks that the key and value size are consistent. *)
+
bpf_map_update_elem ~key_ty:Ctypes.int ~val_ty:Ctypes.long global_map 0 pid
+
]}
+
+
Now if we combine the two, we can run this program and see the output
+
interactively being printed to the trace pipe.
+
+
{1 Notice!}
+
+
root permissions are required when you run eBPF programs. This is a
+
consequence of the fact that they are loaded into the kernel. To offer
+
some assurance though, eBPF programs always have to pass through a
+
verifier before they can be loaded. This ensures that eBPF programs
+
aren't able crash to crash the kernel. For more information, read
+
{{:https://ebpf.io/what-is-ebpf/#ebpf-safety} here}.
+153
vendor/ocaml-libbpf/src/libbpf.ml
···
+
open Ctypes
+
module C = C
+
+
let major_version =
+
C.Functions.libbpf_major_version () |> Unsigned.UInt32.to_int
+
+
let minor_version =
+
C.Functions.libbpf_minor_version () |> Unsigned.UInt32.to_int
+
+
let version_string = C.Functions.libbpf_version_string ()
+
+
let bpf_attach_type_str attach_type =
+
C.Functions.libbpf_bpf_attach_type_str attach_type
+
+
let bpf_link_type_str link_type = C.Functions.libbpf_bpf_link_type_str link_type
+
let bpf_map_type_str map_type = C.Functions.libbpf_bpf_map_type_str map_type
+
let bpf_prog_type_str prog_type = C.Functions.libbpf_bpf_prog_type_str prog_type
+
+
type bpf_object = C.Types.bpf_object structure ptr
+
+
type bpf_program = {
+
name : string;
+
fd : int;
+
ptr : C.Types.bpf_program structure ptr;
+
}
+
+
type bpf_map = { fd : int; ptr : C.Types.bpf_map structure ptr }
+
type bpf_link = C.Types.bpf_link structure ptr
+
+
let failwith_f fmt =
+
let fails s = failwith s in
+
Printf.ksprintf fails fmt
+
+
let bpf_object_open obj_file =
+
match C.Functions.bpf_object__open obj_file with
+
| Some obj -> obj
+
| None -> failwith_f "Error opening object file at %s" obj_file
+
+
let bpf_object_load bpf_object =
+
let ret = C.Functions.bpf_object__load bpf_object in
+
if ret = 0 then ()
+
else failwith_f "Could not load bpf_object, got exit %d" ret
+
+
let bpf_object_find_program_by_name bpf_object name =
+
match C.Functions.bpf_object__find_program_by_name bpf_object name with
+
| Some prog -> { name; fd = C.Functions.bpf_program__fd prog; ptr = prog }
+
| None -> failwith_f "Program name %s not found" name
+
+
let bpf_program_attach ({ name; ptr; _ } : bpf_program) =
+
match C.Functions.bpf_program__attach ptr with
+
| Some link -> link
+
| None -> failwith_f "Error attaching program %s" name
+
+
let bpf_program_fd (prog : bpf_program) = prog.fd
+
+
let bpf_object_find_map_by_name bpf_object name =
+
match C.Functions.bpf_object__find_map_by_name bpf_object name with
+
| Some ptr -> { fd = C.Functions.bpf_map__fd ptr; ptr }
+
| None -> failwith_f "Map %s not found" name
+
+
let bpf_map_fd (map : bpf_map) = map.fd
+
+
let bpf_link_destroy bpf_link =
+
match C.Functions.bpf_link__destroy bpf_link with
+
| e when e <> 0 -> Printf.eprintf "Failed to destroy link %d\n" e
+
| _ -> ()
+
+
let bpf_object_close bpf_object = C.Functions.bpf_object__close bpf_object
+
+
let with_bpf_object_open_load_link ~obj_path ~program_names
+
?(before_link = Stdlib.ignore) fn =
+
let obj = bpf_object_open obj_path in
+
bpf_object_load obj;
+
+
let cleanup ?links obj =
+
Option.iter (List.iter bpf_link_destroy) links;
+
bpf_object_close obj
+
in
+
+
(* Programs to load cannot be zero *)
+
if program_names = [] then (
+
cleanup obj;
+
failwith "Need to specify at least one program to load");
+
+
(* Get list of programs *)
+
let programs, not_found =
+
List.fold_left
+
(fun (succ, fail) name ->
+
match C.Functions.bpf_object__find_program_by_name obj name with
+
| None -> (succ, name :: fail)
+
| Some prog -> ((prog, name) :: succ, fail))
+
([], []) program_names
+
in
+
if not_found <> [] then (
+
cleanup obj;
+
failwith_f "Failed to find %s programs" (String.concat "," not_found));
+
+
(* Run before_link user initialization code *)
+
(try before_link obj
+
with e ->
+
bpf_object_close obj;
+
raise e);
+
+
(* Get list of links *)
+
let links, not_attached =
+
List.fold_left
+
(fun (succ, fail) (prog, name) ->
+
match C.Functions.bpf_program__attach prog with
+
| None -> (succ, name :: fail)
+
| Some prog -> (prog :: succ, fail))
+
([], []) programs
+
in
+
if not_attached <> [] then (
+
(* Detached successfully attached before shutdown *)
+
cleanup ~links obj;
+
failwith_f "Failed to link %s programs" (String.concat "," not_attached));
+
+
(* Run user program *)
+
(try fn obj links
+
with e ->
+
cleanup ~links obj;
+
raise e);
+
+
(* Ensure proper shutdown *)
+
cleanup ~links obj
+
+
let bpf_map_lookup_value ~key_ty ~val_ty ~val_zero bpf_map key =
+
let key = allocate key_ty key in
+
let sz_key = sizeof key_ty |> Unsigned.Size_t.of_int in
+
let value = allocate val_ty val_zero in
+
let sz_val = sizeof val_ty |> Unsigned.Size_t.of_int in
+
let err =
+
C.Functions.bpf_map__lookup_elem bpf_map.ptr (to_voidp key) sz_key
+
(to_voidp value) sz_val Unsigned.UInt64.zero
+
in
+
if err = 0 then !@value
+
else
+
let err = Printf.sprintf "bpf_map_lookup_value got %d" err in
+
raise (Sys_error err)
+
+
let bpf_map_update_elem ~key_ty ~val_ty bpf_map key value =
+
let key = allocate key_ty key in
+
let sz_key = sizeof key_ty |> Unsigned.Size_t.of_int in
+
let value = allocate val_ty value in
+
let sz_val = sizeof val_ty |> Unsigned.Size_t.of_int in
+
let err =
+
C.Functions.bpf_map__update_elem bpf_map.ptr (to_voidp key) sz_key
+
(to_voidp value) sz_val Unsigned.UInt64.zero
+
in
+
if err = 0 then ()
+
else
+
let err = Printf.sprintf "bpf_map_update_value got %d" err in
+
raise (Sys_error err)
+114
vendor/ocaml-libbpf/src/libbpf.mli
···
+
(** See {!page-index} for example usage *)
+
+
open Ctypes
+
+
module C : module type of C
+
(** Entry point for the underlying C primatives *)
+
+
val major_version : int
+
val minor_version : int
+
val version_string : string
+
val bpf_attach_type_str : C.Types.Bpf_attach_type.t -> string
+
val bpf_link_type_str : C.Types.Bpf_link_type.t -> string
+
val bpf_map_type_str : C.Types.Bpf_map_type.t -> string
+
val bpf_prog_type_str : C.Types.Bpf_prog_type.t -> string
+
+
type bpf_object = C.Types.bpf_object structure ptr
+
+
type bpf_program = {
+
name : string;
+
fd : int;
+
ptr : C.Types.bpf_program structure ptr;
+
}
+
+
type bpf_map = { fd : int; ptr : C.Types.bpf_map structure ptr }
+
type bpf_link = C.Types.bpf_link structure ptr
+
+
val bpf_object_open : string -> bpf_object
+
(** [bpf_object_open path] opens and tries to read the bpf_object
+
found at path [path] in the filesystem. Libbpf parses the BPF
+
object file and discovers BPF maps, BPF programs, and global
+
variables. After a BPF app is opened, user space apps can make
+
additional adjustments (setting BPF program types, if necessary;
+
pre-setting initial values for global variables, etc.) before all
+
the entities are created and loaded.
+
+
Fails if object file is in invalid format or path does not exist *)
+
+
val bpf_object_load : bpf_object -> unit
+
(** [bpf_object_load obj] tries to load [obj]. Libbpf parses
+
the BPF object file and discovers BPF maps, BPF programs, and
+
global variables. After a BPF app is opened, user space apps can
+
make additional adjustments (setting BPF program types, if
+
necessary; pre-setting initial values for global variables, etc.)
+
before all the entities are created and loaded. *)
+
+
val bpf_object_find_program_by_name : bpf_object -> string -> bpf_program
+
(** [bpf_object_find_program_by_name obj name] locates the
+
program identifier [name] within the [obj].
+
+
Fails if [name] is not found *)
+
+
val bpf_program_attach : bpf_program -> bpf_link
+
(** [bpf_program_attach prog] attaches the [prog] in the
+
kernel to start respond to events. Libbpf attaches BPF programs to
+
various BPF hook points (e.g., tracepoints, kprobes, cgroup hooks,
+
network packet processing pipeline, etc.). During this phase, BPF
+
programs perform useful work such as processing packets, or
+
updating BPF maps and global variables that can be read from user
+
space
+
+
Fails if link could not be attached *)
+
+
val bpf_program_fd : bpf_program -> int
+
(** [bpf_map_fd prog] returns the fd of the [prog] *)
+
+
val bpf_object_find_map_by_name : bpf_object -> string -> bpf_map
+
(** [bpf_object_find_map_by_name obj name] locates the bpf_map
+
identifier [name] within [obj].
+
+
Fails if map is not found *)
+
+
val bpf_map_fd : bpf_map -> int
+
(** [bpf_map_fd map] returns the fd of the [map] *)
+
+
val bpf_link_destroy : bpf_link -> unit
+
(** [bpf_link_destroy link] detaches and unloads the bpf program
+
associated to [link] from the kernel *)
+
+
val bpf_object_close : bpf_object -> unit
+
(** [bpf_object_close obj] tearsdown [obj]. BPF maps are destroyed,
+
and all the resources used by the BPF app are freed. *)
+
+
val with_bpf_object_open_load_link :
+
obj_path:string ->
+
program_names:string list ->
+
?before_link:(bpf_object -> unit) ->
+
(bpf_object -> bpf_link list -> unit) ->
+
unit
+
(** [with_bpf_object_open_load_link obj_path program_names
+
?before_link fn] performs opening and loading of the provided
+
filesystem path to the bpf_object [obj_path]. This helper runs
+
[before_link] before the program attaches the bpf programs
+
specified in [program_names]. Initialization code should go
+
here. [fn] is passed the bpf_object and the list of program links
+
if all steps were successful. Ensures all the proper shutdown and
+
cleanup of bpf_object resources and links *)
+
+
val bpf_map_lookup_value :
+
key_ty:'a typ -> val_ty:'b typ -> val_zero:'b -> bpf_map -> 'a -> 'b
+
(** [bpf_map_lookup_value key_ty val_ty val_zero map k flags] Looks
+
up the value associated with the key [k]. If key is invalid, no
+
value is found or the size of key/value is not in sync, it will
+
return an error. [bpf_map_lookup_value] expects [key_ty] and
+
[val_ty] to verify the types are coherent in your bpf map
+
declaration. [val_zero] is merely an initialization value that
+
will be overwritten. *)
+
+
val bpf_map_update_elem :
+
key_ty:'a typ -> val_ty:'b typ -> bpf_map -> 'a -> 'b (* -> flags *) -> unit
+
(** [bpf_map_update_elem key_ty val_ty map k v flags] updates the
+
value associated the key [k] to [v]. If key is invalid or the
+
size of key/value is not in sync, it will return an
+
error. [bpf_map_update_elem] expects [key_ty] and [val_ty] to
+
verify the types are coherent in your bpf map declaration. *)
+4
vendor/ocaml-libbpf/src/libbpf_maps/dune
···
+
(library
+
(public_name libbpf_maps)
+
(name libbpf_maps)
+
(libraries libbpf ctypes.foreign))
+34
vendor/ocaml-libbpf/src/libbpf_maps/libbpf_maps.ml
···
+
open Libbpf
+
open Ctypes
+
+
module RingBuffer = struct
+
type t = [ `Ring_buffer ] structure ptr
+
+
type callback =
+
unit Ctypes_static.ptr -> unit Ctypes_static.ptr -> Unsigned.size_t -> int
+
+
let init bpf_map ~callback f =
+
(* Coerce it to the static_funptr so it can be passed to the C function *)
+
let callback_c =
+
coerce
+
(Foreign.funptr ~runtime_lock:true ~check_errno:true
+
(ptr void @-> ptr void @-> size_t @-> returning int))
+
C.Types.ring_buffer_sample_fn callback
+
in
+
let rb =
+
match
+
C.Functions.ring_buffer__new bpf_map.fd callback_c null
+
(from_voidp C.Types.ring_buffer_opts null)
+
with
+
| None -> failwith "Failed to create ring buffer\n"
+
| Some rb -> rb
+
in
+
try f rb
+
with e ->
+
C.Functions.ring_buffer__free rb;
+
raise e
+
+
let poll t ~timeout = C.Functions.ring_buffer__poll t timeout
+
let consume t = C.Functions.ring_buffer__consume t
+
let get_epoll_fd t = C.Functions.ring_buffer__epoll_fd t
+
end
+38
vendor/ocaml-libbpf/src/libbpf_maps/libbpf_maps.mli
···
+
open Libbpf
+
(** Libbpf_maps provide a convenient API's for handling maps,
+
currently only Ringbuffers are supported *)
+
+
module RingBuffer : sig
+
type t
+
+
type callback =
+
unit Ctypes_static.ptr -> unit Ctypes_static.ptr -> Unsigned.size_t -> int
+
+
val init : bpf_map -> callback:callback -> (t -> unit) -> unit
+
(** [init bpf_map callback] loads [callback] into the ring buffer
+
map provided by [bpf_map]. bpf map is freed by default when
+
the OCaml process exits
+
+
TO BE ADDED [ctx_ptr] allows the callback function to access
+
user provided context. *)
+
+
val poll : t -> timeout:int -> int
+
(** [poll t timeout] polls the ringbuffer to execute the loaded
+
callbacks on any pending entries, The function returns if
+
there are no entries in the given timeout,
+
+
Error code is returned if something went wrong, Ctrl-C will
+
cause -EINTR *)
+
+
val consume : t -> int
+
(** [consume t] runs callbacks on all entries in the ringbuffer
+
without event polling. Use this only if trying to squeeze
+
extra performance with busy-waiting.
+
+
Error code is returned if something went wrong Ctrl-C will
+
cause -EINTR *)
+
+
val get_epoll_fd : t -> int
+
(** [get_epoll_fd t] returns a file descriptor that can be used
+
to sleep until data is available in the ring(s) *)
+
end
+39
vendor/ocaml-libbpf/supported.json
···
+
{
+
"ocaml_libbpf.1.0": [
+
"libbpf_major_version",
+
"libbpf_minor_version",
+
"libbpf_version_string",
+
"libbpf_strerror",
+
"libbpf_bpf_attach_type_str",
+
"libbpf_bpf_link_type_str",
+
"libbpf_bpf_map_type_str",
+
"libbpf_bpf_prog_type_str",
+
"libbpf_set_print",
+
"bpf_object__open",
+
"bpf_object__load",
+
"bpf_object__find_program_by_name",
+
"bpf_object__next_program",
+
"bpf_program__pin",
+
"bpf_program__unpin",
+
"bpf_program_attach",
+
"bpf_program__fd",
+
"bpf_link__pin",
+
"bpf_link__unpin",
+
"bpf_link__destroy",
+
"bpf_object__close",
+
"bpf_object__find_map_by_name",
+
"bpf_map__fd",
+
"bpf_map_lookup_elem",
+
"bpf_map_update_elem",
+
"bpf_map_delete_elem",
+
"bpf_tc_hook_create",
+
"bpf_tc_hook_destroy",
+
"bpf_tc_attach",
+
"bpf_tc_detach",
+
"ring_buffer__new",
+
"ring_buffer__poll",
+
"ring_buffer__free",
+
"ring_buffer__consume",
+
"ring_buffer__epoll_fd"
+
]
+
}
+3
vendor/ocaml-libbpf/test/dune
···
+
(test
+
(name test_version)
+
(libraries libbpf unix))
+4
vendor/ocaml-libbpf/test/test_version.ml
···
+
let () =
+
let open Libbpf in
+
Printf.printf "Major:%d, Minor:%d, Libbpf.%s" major_version minor_version
+
version_string