# eBPF - программируемый kernel _Процессы и ресурсы · LinuxLab Knowledge Base_ **TL;DR:** eBPF - запуск sandboxed-программ в kernel без kernel-modules. Прицепляются к hooks (kprobe, uprobe, tracepoint, perf, socket, XDP). Verifier гарантирует завершение и safety. JIT компилирует в native. bpftool/libbpf/BCC - userspace tooling. ## Что такое eBPF **extended BPF** - виртуальная машина внутри kernel, на которой запускаются изолированные программы (eBPF programs), которые цепляются к "hook" точкам (вызов syscall, входящий пакет, контекст-свич) и могут читать/изменять данные, собирать метрики, фильтровать. Историческое: cBPF (classic BPF) появился в 1992 как фильтр пакетов для tcpdump (`pcap_compile`). В 2014 Алексей Старовойтов расширил до eBPF: больше регистров (10×64-бит вместо 2×32-бит), новые типы программ, maps для state, JIT-компиляция в native. Сегодня eBPF - **новая базовая kernel-абстракция**: - **Networking**: [[cni-plugins|cilium]], Cloudflare DDoS-фильтр, Facebook Katran (load balancer) - **Observability**: bpftrace, BCC tools, Pixie, Parca (profiler), Grafana Beyla - **Security**: tetragon, falco (runtime detection), [[seccomp|seccomp-bpf]] - **Tracing**: kprobe/uprobe replacement для DTrace, SystemTap ## Архитектура ``` ┌─────────────────────────────┐ │ userspace │ │ ┌──────┐ ┌──────┐ ┌─────┐ │ │ │ BCC │ │libbpf│ │bpftl│ │ компиляция, загрузка │ └──┬───┘ └──┬───┘ └──┬──┘ │ └────┼─────────┼─────────┼────┘ │ │ │ syscalls (BPF_PROG_LOAD, BPF_MAP_*) ┌────▼─────────▼─────────▼────┐ │ kernel │ │ ┌──────────────────────┐ │ │ │ verifier │ ← bytecode проверяется │ └──────┬───────────────┘ │ │ ▼ │ │ ┌──────────────────────┐ │ │ │ JIT compiler │ → native x86-64/arm64 │ └──────┬───────────────┘ │ │ ▼ │ │ hooks: kprobe / tracepoint │ │ tc / XDP / cgroup │ │ ┌─────┴────────────┐ │ │ │ BPF maps (state) │ │ │ └──────────────────┘ │ └─────────────────────────────┘ ``` ## Hook-типы (program types) Каждый тип программы цепляется к своему месту в kernel: | Program type | Где attach | Применение | |--------------|------------|------------| | `BPF_PROG_TYPE_KPROBE` | вход/выход kernel-функции | tracing kernel calls | | `BPF_PROG_TYPE_TRACEPOINT` | static tracepoint | стабильнее kprobe | | `BPF_PROG_TYPE_RAW_TRACEPOINT` | tracepoint без преобразования аргументов | быстрее, но менее портативно | | `BPF_PROG_TYPE_PERF_EVENT` | perf-event (sample N циклов) | profiling, on-CPU stack | | `BPF_PROG_TYPE_SOCKET_FILTER` | socket recv (как cBPF) | tcpdump | | `BPF_PROG_TYPE_SCHED_CLS` | tc-classifier | network policy в tc | | `BPF_PROG_TYPE_XDP` | очень рано в RX-pipeline | DDoS, load balance | | `BPF_PROG_TYPE_CGROUP_SOCK` | cgroup-attached socket | network namespace policy | | `BPF_PROG_TYPE_LSM` | LSM hook | security policy | Полный список: `bpftool feature` или https://docs.kernel.org/bpf/ ## BPF maps - shared state Программы stateless внутри одного вызова. Между вызовами и с userspace общаются через **maps** - типизированные KV-структуры в kernel: | Map type | Описание | |----------|----------| | `BPF_MAP_TYPE_HASH` | хеш-таблица с произвольным ключом | | `BPF_MAP_TYPE_ARRAY` | массив с index-key | | `BPF_MAP_TYPE_PERCPU_HASH` | per-CPU без блокировок | | `BPF_MAP_TYPE_LRU_HASH` | LRU eviction при заполнении | | `BPF_MAP_TYPE_RINGBUF` | ring buffer для отправки event'ов в userspace | | `BPF_MAP_TYPE_PERF_EVENT_ARRAY` | устаревший вариант RINGBUF | | `BPF_MAP_TYPE_LPM_TRIE` | longest-prefix-match (для CIDR routing) | | `BPF_MAP_TYPE_PROG_ARRAY` | tail call - один прог вызывает другой | | `BPF_MAP_TYPE_SOCKMAP` | mapping socket → BPF program | Userspace читает/пишет через `bpf(BPF_MAP_LOOKUP_ELEM)` syscall. Maps **persist** через **pinning** в `/sys/fs/bpf/`: ``` bpftool map pin name my_map /sys/fs/bpf/my_map ``` Несколько программ могут шарить один map. ## Verifier - почему eBPF safe При загрузке через `bpf(BPF_PROG_LOAD)` ядро запускает **verifier**. Он симулирует выполнение программы и проверяет: 1. **Программа всегда завершится** (нет беспонтонных циклов) 2. **Все memory access валидны** (только in-bounds reads/writes) 3. **Регистры инициализированы** перед использованием 4. **Вызовы helper-функций** разрешены для этого program type 5. **Указатели на map data** не утекают наружу Если что-то не так - программа отвергнута. Это и есть **гарантия safety**: eBPF не уронит kernel. Ограничения classic verifier'а: - **Стек 512 байт** на программу - **До 1M инструкций** (Linux 5.2+; раньше 4096) - **Циклы запрещены до 5.3** (с 5.3 - bounded loops с `#pragma unroll` или `for` с константной верхней границей) - **Tail call depth ≤ 33** Если verifier ошибся (false-positive) - переписываешь код, иногда с странными хаками типа `__builtin_constant_p` или `#pragma unroll`. Verifier **не идеален** - были CVE с escape: CVE-2021-3490 (alu32 sign extension), CVE-2022-23222 (pointer arithmetic). Patch быстрые, но reminder что eBPF не magically secure. ## JIT - native speed После verify программа JIT-компилируется в native код целевой архитектуры. На x86-64/arm64 - честный machine code, исполняется без интерпретации. Включается через: ``` sysctl -w net.core.bpf_jit_enable=1 # default since 4.15 ``` Производительность близка к kernel-модулю. Cilium replaces iptables-based kube-proxy на eBPF и получает 5-10x improvement на больших rule sets. ## Userspace стек ### bpftool - swiss-army-knife Из linux-tools-pkg или собрать из kernel-source: ``` bpftool prog list # все loaded программы bpftool prog show id 42 # детали по ID bpftool map list # все maps bpftool map dump name my_map # содержимое bpftool feature # какие program types/helpers есть bpftool gen skeleton prog.bpf.o # сгенерить .h skeleton для libbpf ``` ### libbpf (C, рекомендованный путь) Современный libc-style API. Программа = два файла: ```c // hello.bpf.c #include #include SEC("tracepoint/syscalls/sys_enter_execve") int trace_execve(void *ctx) { bpf_printk("execve called\n"); return 0; } char LICENSE[] SEC("license") = "GPL"; ``` Сборка: `clang -O2 -target bpf -c hello.bpf.c`. Загрузка через libbpf + skeleton-генерация через `bpftool gen skeleton`. ### BCC (Python, legacy но удобно) Old-school: пишешь Python, внутри строкой - C-код, BCC компилирует при запуске: ```python from bcc import BPF BPF(text='int kprobe__sys_clone(void *ctx) { bpf_trace_printk("clone\\n"); return 0; }').trace_print() ``` Минус: тащит за собой LLVM в runtime, медленный старт. Современная альтернатива - **libbpf + CO-RE** ([bpf-co-re](/kb/bpf-co-re.md)). ### bpftrace (DTrace-like one-liners) ``` bpftrace -e 'kprobe:do_sys_open { printf("%s opened %s\n", comm, str(arg1)); }' ``` Идеально для интерактивного debug на проде. ## Helper-функции Внутри eBPF доступен ограниченный набор kernel-функций - "helpers": - `bpf_get_current_pid_tgid()` - какой процесс - `bpf_get_current_comm(&buf, sz)` - имя процесса - `bpf_ktime_get_ns()` - время - `bpf_map_lookup_elem(&map, &key)` - чтение из map - `bpf_perf_event_output()` / `bpf_ringbuf_output()` - отдать event userspace - `bpf_get_stackid()` - stack trace - `bpf_redirect()`, `bpf_clone_redirect()` - сетевая обработка Полный список: `bpftool feature` или `man bpf-helpers`. ## Когда что-то пошло не так - **`Permission denied` при загрузке** - eBPF загрузка требует `CAP_BPF` (Linux 5.8+) или `CAP_SYS_ADMIN`. Запускай root или с capability. - **`R0 invalid mem access`** - verifier обнаружил unbounded access. Проверь `if (ptr + len > end) return 0;` перед чтением. - **`back-edge from insn X to Y`** - цикл, до 5.3 не разрешён. Развёрни через `#pragma unroll`. - **`program is too large`** - inline функции делают bytecode большим. Раздели через tail call (BPF_MAP_TYPE_PROG_ARRAY). - **bpftool без output, программа есть** - проверь permissions `/sys/fs/bpf/`, debugfs смонтирован (`/sys/kernel/debug/`). - **`bpf_printk` ничего не выводит** - читать из `/sys/kernel/debug/tracing/trace_pipe` (нужно root). - **CO-RE не связывается** - см. [bpf-co-re](/kb/bpf-co-re.md): vmlinux.h собран не под целевое ядро. ## Где почитать - https://docs.kernel.org/bpf/ - официальная документация - https://ebpf.io/ - tutorial и каталог проектов - "BPF Performance Tools" Brendan Gregg - bcc/bpftrace examples - https://github.com/iovisor/bcc - сотни готовых tools - https://github.com/libbpf/libbpf-bootstrap - стартовый шаблон libbpf+CO-RE ## Команды ```bash bpftool prog list ``` Все загруженные eBPF-программы с ID, type, name ```bash bpftool map list ``` Все BPF-maps в системе ```bash bpftool map dump name my_map ``` Содержимое конкретной map (или по ID/pinned-path) ```bash bpftool feature probe ``` Какие BPF program types и helpers доступны на этом kernel ```bash bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }' ``` Подсчитать openat-syscall'ы по процессам - one-liner DTrace-style ```bash cat /sys/kernel/debug/tracing/trace_pipe ``` Читать вывод bpf_printk() - dump в реальном времени ```bash bpftool prog tracelog ``` Wrap для trace_pipe - то же содержимое, но через bpftool ```bash sysctl net.core.bpf_jit_enable ``` Проверить что JIT включён (1=on, 2=on+debug-output) - default 1 с 4.15 ## См. также - [seccomp - фильтр системных вызовов](/kb/seccomp.md) - [CNI plugins - сеть Kubernetes (calico, cilium, flannel)](/kb/cni-plugins.md) - [auditd - syscall и file audit](/kb/auditd.md) - [kubelet - архитектура агента ноды Kubernetes](/kb/kubelet-internals.md) - [Kernel modules - LKM, modprobe, signing, DKMS](/kb/kernel-modules.md) - [strace - какие syscall'ы делает процесс](/kb/cmd-strace.md) - [Continuous profiling: Pyroscope, eBPF, flame graphs в проде](/kb/pyroscope-continuous-profiling.md)