ebpf-go Documentation

repository·main·Indexed 27 days ago

https://github.com/cilium/ebpf

A pure Go library for loading, compiling, and debugging eBPF programs, designed for integration into long-running processes. It includes the bpf2go tool for compiling C source files into eBPF bytecode and generating Go glue code, as well as specialized packages for assembly (asm), linking (link), perf events (perf), ring buffers (ringbuf), BTF data (btf), and BPF filesystem interaction (pin).

Tokens
29.2K
Snippets
21
Records
147
Agent score
93%

What's inside ebpf-go

  1. Overview of ebpf-go

    main
    ebpf-go is a pure Go library designed for loading, compiling, and debugging eBPF programs. It is intended for use in long-running processes and maintains minimal external dependencies. It provides tools for working with eBPF programs written in either C or assembly.
  2. Understand the eBPF Object Loading Workflow

    main

    The library provides an eBPF object (ELF) loader compatible with upstream libbpf and iproute2. The workflow follows these stages:

    1. ELF: The compiled eBPF C program (via clang).
    2. CollectionSpec: An intermediate Go representation of the ELF containing ProgramSpec, MapSpec, and Types.
    3. Collection: The actual resources (Maps and Programs) loaded into the kernel.
    4. Links: Connections between Maps, Programs, and kernel resources.

    You typically obtain a CollectionSpec by calling LoadCollectionSpec and then load it into the kernel to create a Collection.

  3. Understanding Compile Once - Run Everywhere (CO-RE)

    main

    While bpf2go produces standalone binaries, they are not automatically compatible with all kernel versions or distributions due to changes in kernel internal data structures and compile-time configurations.

    To achieve universal interoperability, use Compile Once - Run Everywhere (CO-RE) techniques. CO-RE relies on BPF Type Format (BTF) information provided by the kernel, which allows memory accesses to be adjusted dynamically right before the eBPF program is loaded into the kernel.

  4. Understand eBPF object lifecycle and Go Garbage Collection

    main

    In cilium/ebpf, eBPF resources like Map, Program, and link/Link are modeled around underlying Linux file descriptors.

    Because Go is a garbage-collected language, the runtime will automatically call Close() on the underlying file descriptor when the Go object is no longer reachable.

    Warning: If you create an object inside a function but do not return it to the caller, the garbage collector may reclaim it and close the file descriptor unexpectedly, detaching the eBPF program or destroying the resource.

  5. Manage eBPF object lifetimes via Pinning

    main

    To prevent eBPF objects (Maps, Programs, or Links) from being destroyed when your Go process exits, you can use pinning. This associates the resource with a file in the BPF File System (bpffs).

    • Persistence: Pins allow objects to persist after the Go process exits, enabling sharing between processes (e.g., inspecting a map with bpftool).
    • Removal: To remove a pin, use the standard rm command on the pin path. If the object was previously pinned and you are holding it in Go, you can call Map.Unpin, Program.Unpin, or Link.Unpin.
    • Limitation: Pins do not persist through a system reboot.
  6. Generate Go scaffolding from eBPF C code using bpf2go

    main

    The bpf2go tool automates the compilation of eBPF C code and generates Go scaffolding to interact with Maps and Programs.

    1. Create a C file (e.g., counter.c) for your eBPF program. Ensure C files are excluded from standard Go builds by using appropriate build tags if necessary.
    2. Create a Go file (e.g., gen.go) containing a //go:generate directive that calls bpf2go.
    3. Initialize your Go module and add bpf2go as a tool dependency.
    4. Run go generate to produce the .o (object) and .go (scaffolding) files.
    //go:generate bpf2go -type counter counter.c
    go mod init ebpf-test
    go mod tidy
    go get -tool github.com/cilium/ebpf/cmd/bpf2go
    go generate
  7. Prerequisites for eBPF development in Go

    main

    To develop eBPF applications using this library, ensure your environment meets the following requirements:

    • Linux kernel: Version 5.7 or later (required for bpf_link support).
    • LLVM: Version 11 or later (includes clang and llvm-strip).
    • libbpf headers:
      • Debian/Ubuntu: libbpf-dev
      • Fedora: libbpf-devel
    • Linux kernel headers:
      • AMD64 Debian/Ubuntu: linux-headers-amd64
      • Fedora: kernel-devel
      • Note for Debian: You may need to run ln -sf /usr/include/asm-generic/ /usr/include/asm to ensure <asm/types.h> is discoverable.
    • Go compiler: A version compatible with the project's Go module.
  8. Use eBPF for Windows with source compatibility

    main
    The library provides preliminary support for the [eBPF for Windows] runtime. While it offers source compatibility (allowing you to use the same Go APIs as on Linux), it does not provide feature parity or binary compatibility. Many APIs will return ErrNotSupported on Windows, and eBPF programs compiled for Linux cannot be used on Windows.
  9. Declare and use Global Variables in BPF

    main

    Non-const global variables are mutable and can be modified by both the BPF program and the user space application. They are typically used for stateful data like metrics, counters, or rate limiting.

    Best Practice: Like constants, declare global variables as volatile in BPF C to ensure the compiler reliably allocates them in the ELF data section, making them accessible to user space.

    Interaction Patterns

    1. Before Loading (Initialization)

    To ensure a variable is populated before the BPF program executes, use the VariableSpec methods found in CollectionSpec.Variables or injected via LoadAndAssign.

    2. After Loading (Runtime Access)

    Once the program is loaded, use the Variable abstraction to interact with the data:

    • Read: Use Variable.Get() to retrieve the current value.
    • Write: Use Variable.Set(value) to modify the value at runtime.

    Variables can be found in the Collection.Variables field or injected using LoadAndAssign.

  10. Install the ebpf Go library

    main

    To add github.com/cilium/ebpf as a dependency to your existing Go module, run the go get command from within your module's directory. This library is self-contained and does not depend on C, libbpf, or other non-standard Go libraries, making it suitable for portable tools across various architectures.

    go get github.com/cilium/ebpf
  11. Use bpf2go with go generate

    main

    Invoke bpf2go using the //go:generate directive. The tool compiles a C source file into eBPF bytecode and emits Go files containing the bytecode for both little-endian (_bpfel.go) and big-endian (_bpfeb.go) systems. You can pass additional arguments to the underlying compiler using the -- separator.

    Syntax: //go:generate go tool bpf2go <stem> <path/to/src.c> -- <compiler_flags>

    //go:generate go tool bpf2go foo path/to/src.c -- -I/path/to/include
  12. Declare and use Runtime Constants in BPF

    main

    Runtime constants are used for configuration values (e.g., network addresses, timeouts) that influence BPF program functionality. In BPF C, these are declared as const. The BPF verifier performs dead code analysis on these constants, which can improve performance and allow for portable code by removing unused paths.

    Important: When declaring global variables that need to be accessed from user space, it is common practice to use the volatile qualifier. This prevents the compiler from optimizing the variable away, ensuring it is correctly allocated in the ELF data section so user space can modify it.

    To modify a constant from Go before loading the program, use VariableSpec.Set on the variable found in CollectionSpec.Variables.