Mango.Libbpf 0.0.5

dotnet add package Mango.Libbpf --version 0.0.5
                    
NuGet\Install-Package Mango.Libbpf -Version 0.0.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Mango.Libbpf" Version="0.0.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Mango.Libbpf" Version="0.0.5" />
                    
Directory.Packages.props
<PackageReference Include="Mango.Libbpf" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Mango.Libbpf --version 0.0.5
                    
#r "nuget: Mango.Libbpf, 0.0.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Mango.Libbpf@0.0.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Mango.Libbpf&version=0.0.5
                    
Install as a Cake Addin
#tool nuget:?package=Mango.Libbpf&version=0.0.5
                    
Install as a Cake Tool

                                     __  ___                     
                                    /  |/  /___ _____  ____ _____ 
                                   / /|_/ / __ `/ __ \/ __ `/ __ \
                                  / /  / / /_/ / / / / /_/ / /_/ /
                                 /_/  /_/\__,_/_/ /_/\__, /\____/ 
                                                    /____/         
                
                                    ----- C# bindings for libbpf -----

NuGet Version NuGet Downloads .NET 10 Linux only MIT License

Mango is a strongly-typed C# wrapper around the native libbpf C library. It exposes eBPF object/program/map/ring-buffer loading through P/Invoke — open a compiled .bpf.o, load it into the kernel, attach its programs, and read its maps/ring buffers — behind a small, Result-returning API, so you don't have to hand-roll the native bindings yourself.

This is a personal project built to learn the libbpf C API and P/Invoke internals from the ground up. It's shared as-is — feel free to use it, fork it, or build on top of it.


Table of contents


Features

Domain Type Covers
Objects BpfObject Open a .bpf.o ELF file, prepare/load it into the kernel, pin/unpin it or its maps/programs, enumerate/find programs and maps
Programs BpfProgram Inspect name/fd/type, toggle autoload, attach via libbpf's generic auto-detection (kprobe, uprobe, tracepoint, raw tracepoint, typed tracing)
Maps BpfMap Inspect name/fd/type/sizes, iterate keys, pin/unpin, lookup/update/delete elements by raw byte span
Links BpfLink The live attachment returned by Attach() — detaches on dispose
Ring buffers BpfRingBuffer Poll a BPF_MAP_TYPE_RINGBUF map with a managed callback per record

Every public type:

  • targets .NET 10 and ships with full nullable-reference-type annotations
  • returns BpfResult<T> instead of throwing for expected native-call failures
  • wraps its native handle in a SafeHandle and implements IDisposable where the underlying libbpf object owns kernel resources
  • keeps public naming aligned with the bpf_*__* libbpf call it wraps, so the native docs stay a direct reference

Installation

dotnet add package Mango.Libbpf

Or via the NuGet Package Manager:

Install-Package Mango.Libbpf

Quickstart

using Mango;

using var obj = BpfObject.Open("probe.bpf.o").Value!;
obj.Load();

var program = obj.FindProgram("kprobe_sys_kill")!;
using var link = program.Attach().Value!;

var map = obj.FindMap("event_output")!;
using var ringBuffer = BpfRingBuffer.Create(map, data =>
{
    // data is a ReadOnlySpan<byte>, valid only for the duration of this call
    Console.WriteLine($"{data.Length} bytes received");
}).Value!;

while (true)
    ringBuffer.Poll(timeoutMs: 200);

Every domain follows the same pattern: open/find the object you need, check the returned BpfResult<T>.IsSuccess (or unwrap .Value! once you trust the call), and dispose whatever owns a native handle (BpfObject, BpfLink, BpfRingBuffer) when you're done.


Project structure

Mango/
├── Interops/              # Raw P/Invoke bindings (NativeMethods) onto libbpf
│   ├── NativeBpfMethods.cs # [DllImport] extern declarations, grouped by #region
│   ├── NativeEnums.cs      # Enums mirroring kernel enums (bpf_prog_type, bpf_map_type, ...)
│   └── NativeHelpers.cs    # Delegate types for native callbacks (libbpf_set_print, ring buffer sample fn)
├── Handles/                # SafeHandle wrappers for native BPF resources
├── Models/                 # BpfError / BpfResult<T> — the Result<T, BpfError> vocabulary
├── BpfObject.cs             # Public API: Open/Prepare/Load/pin-unpin/Programs/Maps
├── BpfProgram.cs             # Public API: wraps BpfProgramHandle (Type/Autoload/Attach)
├── BpfMap.cs                  # Public API: wraps BpfMapHandle (CRUD/Keys/pin-unpin)
├── BpfLink.cs                  # Public API: wraps BpfLinkHandle (IDisposable)
└── BpfRingBuffer.cs              # Public API: wraps BpfRingBufferHandle (Create/Poll)

Alongside the library:

Ebpf/         # Sample BPF probe (sys_kill kprobe) compiled to main.bpf.o via `make`
Mango.Poc/    # Runnable console app that loads/attaches the sample probe and prints its events

Every native call funnels through Interops/NativeBpfMethods.cs's NativeMethods — the only place [DllImport] declarations live — and errors are rendered through libbpf's own libbpf_strerror().


Domain reference

BpfObject — open, load, pin, enumerate

using var obj = BpfObject.Open("probe.bpf.o").Value!;
Member libbpf call
Open(path) bpf_object__open
Prepare() bpf_object__prepare
Load() bpf_object__load
Name bpf_object__name
Programs / FindProgram(name) bpf_object__next_program / bpf_object__find_program_by_name
Maps / FindMap(name) bpf_object__next_map / bpf_object__find_map_by_name
Pin(path) / Unpin(path) bpf_object__pin / bpf_object__unpin
PinMaps(path?) / UnpinMaps(path?) bpf_object__pin_maps / bpf_object__unpin_maps
PinPrograms(path) / UnpinPrograms(path) bpf_object__pin_programs / bpf_object__unpin_programs
Dispose() bpf_object__close

Load() implicitly performs Prepare() if it wasn't called first.

BpfProgram — inspect and attach

var program = obj.FindProgram("kprobe_sys_kill")!;
using var link = program.Attach().Value!;
Member libbpf call
Name bpf_program__name
Fd bpf_program__fd
Type bpf_program__type
Autoload (get/set) bpf_program__autoload / bpf_program__set_autoload
Attach() bpf_program__attach

Autoload must be set before the parent object is loaded. Its setter throws InvalidOperationException on failure — that's a programmer error, not an expected outcome. Attach() uses libbpf's generic auto-detection: kprobe, uprobe, tracepoint, raw tracepoint, and typed tracing programs.

BpfMap — inspect and CRUD elements

var map = obj.FindMap("event_output")!;
map.TryLookup(key, value);
Member libbpf call
Name / Fd / Type bpf_map__name / bpf_map__fd / bpf_map__type
KeySize / ValueSize / MaxEntries bpf_map__key_size / bpf_map__value_size / bpf_map__max_entries
Keys bpf_map__get_next_key
Pin(path?) / Unpin(path?) bpf_map__pin / bpf_map__unpin
TryLookup / TryUpdate / TryDelete bpf_map__lookup_elem / bpf_map__update_elem / bpf_map__delete_elem

The Try* methods return bool instead of BpfResult<T> — "not found" is a normal outcome for map element CRUD, matching the Dictionary.TryGetValue idiom.

using var link = program.Attach().Value!;
Member libbpf call
Dispose() bpf_link__destroy

Returned by BpfProgram.Attach(). Disposing detaches the program from its hook.

BpfRingBuffer — poll BPF_MAP_TYPE_RINGBUF

using var rb = BpfRingBuffer.Create(map, data => Console.WriteLine(data.Length)).Value!;
while (true) rb.Poll(timeoutMs: 200);
Member libbpf call
Create(map, onEvent) ring_buffer__new
Poll(timeoutMs) ring_buffer__poll
Dispose() ring_buffer__free

onEvent is invoked with each record's raw bytes during Poll; the ReadOnlySpan<byte> is only valid for the duration of that call. Poll returns the number of records consumed.


Error handling

Mango follows the shape of the native call it wraps, rather than one uniform rule:

  • Int-returning calls (0/negative error code) — like Load, Pin, TryUpdate's underlying call — build a BpfResult<T> via BpfError.FromCode(rc).
  • Pointer/handle-returning calls with no other numeric signal — like Open, Attach — build a BpfResult<T> via BpfError.FromLastError(), reading Marshal.GetLastPInvokeError().
  • Map element CRUD exposes Try*(...) : bool instead of BpfResult<T> — "not found" is a normal outcome, not a failure.
public readonly record struct BpfError(int Code, string Message);

public sealed record BpfResult<T>
{
    public bool IsSuccess { get; }
    public T? Value { get; }
    public BpfError? Error { get; }
}

BpfError.Message is rendered by libbpf's own libbpf_strerror(), so it matches what a native libbpf/bpftool user would see. Always check IsSuccess before reading Value:

var result = BpfObject.Open(path);
if (!result.IsSuccess)
{
    Console.Error.WriteLine(result.Error);
    return;
}

Requirements

  • .NET 10.0 SDK or later
  • Linux with libbpf installed (the native library Mango P/Invokes into)
  • Root, or CAP_BPF/CAP_PERFMON, to load BPF programs into the kernel

Building from source

git clone https://github.com/Yekuuun/Mango.git
cd Mango

# Build the library
dotnet build Mango/Mango.csproj

# Build + run the sample: loads Ebpf/out/main.bpf.o, attaches its sys_kill
# kprobe, and prints every kill(pid, 64) it observes. Building auto-runs
# `make` in Ebpf/ and copies main.bpf.o next to the app's own output.
dotnet build Mango.Poc
sudo dotnet Mango.Poc/bin/Debug/net10.0/Mango.Poc

Packed and published to NuGet as Mango.Libbpf on pushing a v* tag (see .github/workflows/publish-nuget.yml).


Resources


License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.5 80 9/13/2026
0.0.4 86 9/13/2026
0.0.3 85 9/12/2026
0.0.2 113 8/19/2026
0.0.1 104 8/19/2026