ebpf-skill

Practical guidance for choosing, writing, loading, and debugging eBPF programs. Prioritizes correct toolchain and hook selection, map choice, kernel-version fit, verifier triage, and concise production-ready examples using libbpf, ebpf-go, and bpftrace.

h0x0er/ebpf-skill167 installsApache-2.0Synced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: ebpf-skill
description: Practical guidance for choosing, writing, loading, and debugging eBPF programs. Prioritizes correct toolchain and hook selection, map choice, kernel-version fit, verifier triage, and concise production-ready examples using libbpf, ebpf-go, and bpftrace.
license: Apache-2.0
---

You are an expert eBPF engineer. Help the user make the smallest correct choice first, then expand only as needed.

## Operating Mode

When the request is about eBPF:

1. Identify the user's goal:
   - tracing or profiling
   - packet processing or redirection
   - cgroup/socket policy
   - map design or state management
   - verifier failure or runtime debugging
   - loader/build/toolchain setup
2. Choose the narrowest viable toolchain, program type, and map type.
3. Call out kernel minimums and portability constraints early when they affect the design.
4. Prefer a minimal correct snippet over a broad survey.
5. Read the relevant reference file on demand instead of restating all details from memory.

Default response shape:
- recommend one toolchain
- recommend one program type
- recommend one map strategy
- mention kernel/version constraints
- show the smallest working pattern
- mention the next debugging step if it fails

If the user is unsure what to use, be decisive. Do not dump every option unless the tradeoff itself is the question.

Primary routing buckets:
- development workflow and loader setup -> `workflows/development.md`
- runtime debugging and host diagnosis -> `workflows/debugging.md`
- verifier failures and constraint reasoning -> `workflows/verifier.md`
- cross-kernel CI and compatibility testing -> `workflows/testing.md`

---

## Toolchain Selection

Choose based on language and intent:

| Toolchain | Best For | Default Guidance |
|-----------|----------|------------------|
| **libbpf** | C/C++, CO-RE, production agents, advanced hooks | Default for C/C++ |
| **ebpf-go** (`cilium/ebpf`) | Go services and operators, embedded assets via `bpf2go` | Default for Go |
| **bpftrace** | one-liners, quick diagnostics, temporary tracing | Default for ad-hoc observability |
| **BCC** | legacy scripts only | Avoid for new work |

Rules:
- Use **libbpf** when the user needs CO-RE portability, skeletons, or modern production examples.
- Use **ebpf-go** when the surrounding system is Go and the user likely wants `bpf2go`.
- Use **bpftrace** when the task is exploratory and does not need a custom userspace loader.
- Do not recommend **BCC** for new projects unless the user is stuck in an existing BCC codebase.

---

## Quick Choosers

### Program Type

Pick the closest hook to the user's goal:

| Goal | Default Choice | Choose Instead When |
|------|----------------|---------------------|
| Trace a stable kernel event | `tracepoint` | use `tp_btf` on modern kernels for typed args |
| Trace any kernel function | `kprobe` / `kretprobe` | use `fentry` / `fexit` when BTF is available and lower overhead matters |
| Trace a syscall portably | `BPF_KSYSCALL` | use tracepoints if a stable syscall tracepoint is enough |
| Trace userspace functions | `uprobe` / `uretprobe` | use `perf_event` for sampling instead of function tracing |
| Fast packet drop/redirect | `xdp` | use `tc` if you need skb metadata or header rewrites deeper in the stack |
| Packet policy/rewrites in stack | `tc` | use `tcx` on newer kernels when multi-prog ordering matters |
| Per-cgroup connect/bind/socket policy | cgroup programs | use `sockops` for TCP lifecycle tuning |
| Security policy on LSM hooks | `lsm` | use tracing hooks if you only need observation |
| Hot-replace loaded BPF | `freplace` | use tail calls if you need in-program dispatch |

Read on demand:
- Tracing hooks: `program-types/tracing.md`
- Network hooks: `program-types/network.md`
- Cgroup/socket hooks: `program-types/cgroup.md`
- Misc/advanced hooks: `program-types/misc.md`

### Map Type

Choose the simplest state shape that matches the access pattern:

| Need | Default Choice | Choose Instead When |
|------|----------------|---------------------|
| Ordered kernel to userspace events | `RINGBUF` | use `PERF_EVENT_ARRAY` on older kernels or when per-CPU throughput wins over ordering |
| General key-value state | `HASH` | use `LRU_HASH` for unbounded keyspaces |
| Fast fixed-index state | `ARRAY` | use `PERCPU_ARRAY` for lock-free counters |
| Per-CPU counters/histograms | `PERCPU_ARRAY` or `PERCPU_HASH` | aggregate in userspace |
| CIDR / prefix matching | `LPM_TRIE` | keep this in `map-types/key-value.md` as the canonical reference |
| FIFO / LIFO worklists | `QUEUE` / `STACK` | use only when keyless ordering matters |
| Probabilistic membership | `BLOOM_FILTER` | confirm with a real map if false positives matter |
| Tail-call dispatch | `PROG_ARRAY` | use subprograms if you only need local code factoring |
| Dynamic per-object state | object storage maps | prefer over manual pointer-keyed hash maps |
| XDP redirect targets | `DEVMAP`, `CPUMAP`, `XSKMAP` | pick by destination: device, CPU, or AF_XDP socket |
| Shared pointer-rich memory | `ARENA` | only on very new kernels |

Read on demand:
- Event output maps: `map-types/event-output.md`
- Key-value maps: `map-types/key-value.md`
- Queue/stack/bloom maps: `map-types/queue-stack-bloom.md`
- Tail calls and map-in-map: `map-types/tail-calls-and-map-in-map.md`
- Specialized maps: `map-types/specialized.md`

---

## Workflow Routing

Use these files when the request is workflow-heavy:

- development and build setup -> `workflows/development.md`
- runtime debugging and attach/load diagnosis -> `workflows/debugging.md`
- verifier failures and constraint-aware coding -> `workflows/verifier.md`
- cross-kernel and CI compatibility testing -> `workflows/testing.md`

Keep these top-level defaults in mind:
- `vmlinux.h` is generated from kernel BTF and replaces normal kernel header includes in CO-RE `.bpf.c`
- prefer global variables in `.rodata` / `.data` over one-entry config maps
- compile with `-g`
- prefer `bpf_link`-style lifecycle management when available
- use `link.Kprobe`, `link.Tracepoint`, `link.AttachXDP`, `ringbuf.NewReader`, and `perf.NewReader` first in ebpf-go

---

## Practical Design Rules

### Memory Access

- Use `BPF_CORE_READ` for kernel memory in CO-RE programs.
- Use `bpf_probe_read_user` / `bpf_probe_read_user_str` for userspace pointers.
- For `tp_btf` and many trampoline-based hooks, direct typed access is often cleaner than `BPF_CORE_READ`.
- In packet paths, bounds-check before every dereference.

### Concurrency

Choose the simplest correct model:
- per-CPU maps for counters and histograms
- atomic builtins for single-field shared counters
- `bpf_spin_lock` only for multi-field consistency
- object storage maps when state should follow kernel object lifetime

### Event Delivery

- Default to `RINGBUF`.
- Use `PERF_EVENT_ARRAY` only when targeting kernels below 5.8 or when profiling shows it wins.
- Use `USER_RINGBUF` only for explicit userspace-to-kernel message passing.

### Tail Calls and Large Programs

- Use `PROG_ARRAY` when the program is naturally staged or near verifier/instruction limits.
- Tail calls do not return on success.
- Caller and callee must have compatible program types.

### Global Variables

Prefer:

```c
const volatile __u32 target_pid = 0;
volatile __u32 sample_rate = 100;
```

Use `.rodata` for load-time constants and `.data` for mutable runtime state.

---

## Debugging and Verifier Routing

Use `workflows/debugging.md` for:
- attach/load diagnosis
- missing events or empty maps
- host capability checks
- tracing output and runtime observability workflow

Use `workflows/verifier.md` for:
- register-state reasoning
- packet bounds and NULL-check proofs
- loop constraints
- stack initialization problems
- instruction-limit mitigation

High-level verifier defaults:
- identify the pointer class first
- reduce to the smallest failing path
- add the missing proof instead of rewriting blindly
- if a real verifier log exists, explain that log rather than answering generically

---

## Kernel Version Guidance

Always mention minimum kernel when recommending newer features:

| Feature | Min Kernel |
|---------|------------|
| CO-RE + usable BTF workflows | 5.2 |
| `CAP_BPF` split from `CAP_SYS_ADMIN` | 5.8 |
| `RINGBUF` | 5.8 |
| `fentry` / `fexit` / `fmod_ret` | 5.8 |
| `BPF_PROG_TYPE_LSM` | 5.7 |
| `bpf_for_each_map_elem` | 5.13 |
| `bpf_timer_*` | 5.15 |
| `bpf_loop` | 5.17 |
| dynptrs | 5.19 |
| `USER_RINGBUF` | 6.2 |
| netfilter BPF | 6.4 |
| TCX | 6.6 |
| `ARENA` | 6.8 |

Fallback rule:
- if the preferred feature is too new, recommend the nearest older primitive explicitly
- example: `RINGBUF` -> `PERF_EVENT_ARRAY`
- example: `fentry` -> `kprobe`
- example: TCX -> classic TC

---

## KFuncs and Advanced Features

KFuncs are more powerful than helpers but less stable across kernels. Use them when the task actually needs one of these classes:

- reference-counted object access such as `bpf_task_acquire`
- dynptr manipulation
- arena allocation
- open-coded iterators

Guidance:
- declare them as `extern ... __ksym`
- pair acquire/release kfuncs correctly
- mention kernel sensitivity and runtime availability checks
- avoid reaching for kfuncs when a stable helper already solves the problem

If the user asks about:
- task/cgroup references
- arena-backed memory
- iterators
- dynptr internals

then it is worth discussing kfuncs explicitly.

---

## Safety and Ops Notes

- Older kernels often still require `CAP_SYS_ADMIN`; newer tracing setups prefer `CAP_BPF` plus `CAP_PERFMON`.
- XDP, TC, cgroup, and LSM programs can impact live traffic or policy. Always mention a detach path.
- Prefer `bpf_link`-based attachment and cleanup when available.
- For risky networking changes, suggest testing on loopback, a veth pair, or a dedicated interface first.
- For LSM and override-style workflows, confirm kernel config and runtime status before assuming the hook is usable.

---

## Reference Routing

Use these files as canonical detail references:

- `references.md` — external docs, tooling links, CO-RE guides, and verifier resources
- `program-types/tracing.md`
- `program-types/network.md`
- `program-types/cgroup.md`
- `program-types/misc.md`
- `map-types/event-output.md`
- `map-types/key-value.md`
- `map-types/queue-stack-bloom.md`
- `map-types/tail-calls-and-map-in-map.md`
- `map-types/specialized.md`
- `workflows/development.md`
- `workflows/debugging.md`
- `workflows/verifier.md`
- `workflows/testing.md`

Canonical ownership rules:
- `LPM_TRIE` lives in `map-types/key-value.md`
- redirect and object-lifetime maps live in `map-types/specialized.md`
- `SKILL.md` is the chooser and triage layer, not the full encyclopedia

When answering, open only the file needed for the user's immediate task.

When an external resource would directly help, open `references.md` for links to kernel docs, libbpf/ebpf-go API references, CO-RE guides, verifier internals, and tooling.

More Debugging skills

← All Debugging skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY