Chapter tracing dan profiling ditutup dengan satu keterbatasan: ftrace hanya menjalankan tracer yang disediakan kernel, dan perf hanya menjawab lewat sampel periodik. eBPF membalik posisinya — alih-alih memilih alat bawaan, kita menulis program kecil sendiri dan menjalankannya di dalam kernel, tepat di titik kejadian. Kedengarannya berbahaya, dan memang akan demikian bila tidak ada satu komponen yang menjadi syarat mutlak: verifier yang memeriksa program secara matematis sebelum diizinkan berjalan. Chapter ini membedah jaminan keamanan itu, titik-titik tempat program menempel, cara data mengalir ke user space, lalu menutup dengan perbandingannya terhadap modul kernel — mekanisme “kode di kernel” yang lama.
Program Kecil yang Diperiksa Sebelum Dijalankan #
Nama lengkapnya extended Berkeley Packet Filter — perpanjangan dari mesin filter paket klasik yang sudah lama hidup di kernel. Bila Anda pernah memakai filter tcp dst port 80 pada tcpdump, Anda sudah pernah menjalankan program BPF klasik: filter itu dikompilasi menjadi instruksi kecil yang diperiksa kernel sebelum dijalankan. eBPF memperluas mesin itu menjadi komputer kecil serbaguna: program berjalan pada mesin register virtual (r0 sebagai nilai balik, r1–r5 untuk argumen), dimuat lewat syscall bpf(2)
, lalu dikompilasi JIT menjadi instruksi mesin asli sehingga eksekusinya nyaris secepat kode kernel biasa.
Yang membuat eBPF aman dipakai di sistem produksi adalah verifier: sebelum program dimuat, kernel menelusuri setiap jalur eksekusi yang mungkin dan membuktikan tiga hal — semua akses memori berada di dalam batas, loop tidak berjalan tanpa batas (dahulu loop dilarang total; kernel 5.3 ke atas menerima loop yang terbukti berbatas), dan program pasti berhenti karena jumlah instruksinya dibatasi. Program yang gagal diperiksa ditolak saat pemuatan: kernel tidak pernah tersentuh kode yang berpotensi mengacaukannya. Skrip verifier-mini.sh pada bundle meniru pemeriksaan itu atas tiga program kecil berformat ringkas:
for p in program-aman.txt program-loop.txt program-luar-batas.txt; do
printf '== %s\n' "$p"
bash verifier-mini.sh "$p"
doneOutput yang Dihasilkan:
== program-aman.txt
DITERIMA: 7 instruksi lolos pemeriksaan
== program-loop.txt
DITOLAK (baris 2): goto 1 melompat mundur (loop tanpa batas)
== program-luar-batas.txt
DITOLAK (baris 0): akses mem[r1+100] melewati batas 64 byteBedah hasilnya satu per satu:
program-aman.txtditerima: membaca dua field pada offset 0 dan 32 (dalam batas 64 byte), melompat maju dengan syarat, dan ditutupexit— semua aturan terpenuhi.program-loop.txtditolak:goto 1melompat mundur tanpa bukti batas iterasi; simulasi kita menolak semua lompatan mundur, sementara verifier sungguhan menelusuri nilai penghitungnya terlebih dahulu.program-luar-batas.txtditolak: aksesmem[r1+100]melewati batas 64 byte data yang dijamin ada — inilah pemeriksaan yang mencegah program eBPF membaca memori kernel sembarangan.
Titik Kait Tempat Program Menempel #
Program eBPF tidak berjalan begitu saja — ia menempel pada hook, yaitu titik di jalur eksekusi kernel yang memanggilnya. Titik-titik utamanya:
| Hook | Menempel di | Kegunaan khas |
|---|---|---|
| kprobe | alamat fungsi mana pun, dinamis | mengukur fungsi yang tak punya tracepoint |
| tracepoint | penanda statis milik kernel | statistik event yang stabil lintas versi |
| perf event | penghitung/interupsi periodik | profiling sampling ala perf |
| XDP | driver kartu jaringan, saat paket tiba | filter dan load balancing paket tercepat |
| tc | traffic control pada antarmuka | shaping dan marking paket |
| LSM | kait kebijakan keamanan Linux | audit dan kebijakan per proses |
Pilihan hook menentukan pertanyaan yang terjawab — statistik syscall lebih aman lewat tracepoint karena namanya stabil, sementara paket yang harus disaring secepat mungkin menempuh XDP.
Peta Data: Jembatan Kernel dan User Space #
Program eBPF berjalan singkat: menempel di hook, mengolah satu event, lalu selesai. Agar hasilnya bisa dibaca dunia luar, kernel menyediakan map — struktur data bersama (bpf_map) yang ditulis program eBPF dan dibaca proses user space lewat syscall bpf(). Bentuknya beragam: hash untuk pasangan kunci-nilai, array untuk pencacah cepat, dan ring buffer untuk mengalirkan event. Alur lengkapnya:
Baca diagramnya begini: sebuah event kernel memicu program eBPF di hook-nya; program memutuskan sendiri (misalnya membuang paket) atau menulis temuan ke map; tool di user space membaca map kapan pun diperlukan. Dokumentasi resmi lengkapnya ada di dokumentasi BPF kernel .
Demo Konsep: Filter Paket Gaya XDP #
XDP (eXpress Data Path) menempatkan hook di driver kartu jaringan — sebelum paket dijadikan struktur sk_buff, jauh lebih awal daripada titik penyaringan iptables/nftables. Karena itu filter DDoS berbasis XDP mampu membuang paket bermasalah hingga jutaan per detik per core tanpa membebani tumpukan jaringan. Berkas paket-masuk.txt pada bundle berisi aliran paket — satu paket per baris dengan empat kolom — dan saringan-paket.sh menilai setiap paket dengan dua aturan yang dimuat lebih dulu:
head -n 3 paket-masuk.txt
bash saringan-paket.sh paket-masuk.txtOutput yang Dihasilkan:
# aliran paket: src_ip dst_ip proto dport
203.0.113.7 192.0.2.10 tcp 22
198.51.100.23 192.0.2.10 tcp 443
DROP 203.0.113.7 192.0.2.10 tcp 22
ALLOW 198.51.100.23 192.0.2.10 tcp 443
DROP 203.0.113.8 192.0.2.10 udp 53
ALLOW 198.51.100.31 192.0.2.10 tcp 443
DROP 203.0.113.7 192.0.2.10 tcp 22
ALLOW 192.0.2.55 192.0.2.10 icmp 0
ALLOW 198.51.100.44 192.0.2.10 tcp 80
DROP 203.0.113.9 192.0.2.10 tcp 443
DROP 198.51.100.99 192.0.2.10 tcp 23
ALLOW 198.51.100.23 192.0.2.10 tcp 443
DROP 203.0.113.8 192.0.2.10 udp 53
ALLOW 192.0.2.77 192.0.2.10 tcp 443
rekap: 6 diizinkan, 6 diblok, 12 totalPemetaan simulasi ini ke eBPF sungguhan:
- Aturan prefix
203.0.113.0/24menjadi keputusanDROP— di XDP asli, daftar prefix hidup di map khusus bernama LPM trie, dan pencocokannya hanya menempuh beberapa langkah. - Aturan port 23 (telnet) adalah logika programnya sendiri — dua aturan digabung dengan logika berurutan, persis urutan pemeriksaan pada skrip awk di balik
saringan-paket.sh. - Pencacah
blokdanlewatadalah pasangan stats map aslinya — user space membacanya berkala untuk menggambar grafik laju paket. - Keputusan diambil di jalur cepat: tidak ada proses user space yang berpartisipasi per paket — semua keputusan
DROP/ALLOWdiambil di dalam “kernel” simulasi kita, dan hanya rekapnya yang keluar.
bpftrace: One-liner untuk Pertanyaan Sehari-hari #
Menulis program eBPF mentah dalam C itu panjang; karena itu lahir dua frontend. BCC menyusun program dari C plus antarmuka Python, cocok untuk aplikasi lengkap. bpftrace menyasar pertanyaan sehari-hari lewat bahasa mini satu baris — probe { aksi }. Berikut contoh tampilan one-liner yang menghitung pemanggil openat per nama proses:
$ sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'
Attaching 1 probe...
^C
@[cat]: 41
@[nginx]: 12
@[python3]: 7
Satu baris itu mengandung seluruh alur diagram di atas: probe tracepoint adalah hook, @[comm] = count() adalah map hash yang dikunci nama proses (comm), dan pencetakan hasilnya dilakukan bpftrace dari user space ketika sesi dihentikan dengan Ctrl+C.
eBPF versus Modul Kernel #
Kernel modules — yang kita bahas pada chapter kernel dan kernel modules
— adalah mekanisme lama untuk memasang kode ke kernel: kode C penuh akses, dimuat dengan modprobe. Keduanya kini hidup berdampingan dengan karakter yang bertolak belakang:
| Aspek | Modul kernel | Program eBPF |
|---|---|---|
| Kode yang diizinkan | C arbitrer, akses penuh ke seluruh kernel | instruksi terbatas, diperiksa verifier |
| Bila kodenya berbug | panic kernel atau celah keamanan sistem | ditolak saat dimuat, kernel tak tersentuh |
| Pemuatan | modprobe, idealnya sejak awal boot | dinamis kapan pun, tanpa reboot |
| Wilayah kerja | driver, filesystem, subsistem apa pun | hanya di hook yang tersedia |
| Alat khas | driver perangkat, kmod | observabilitas, jaringan, keamanan |
Praktisnya: driver Wi-Fi tetap wilayah modul kernel, sementara pertanyaan operasional — siapa yang memanggil syscall ini, berapa lama fungsi itu — wilayah eBPF.
Penerapan di Dunia Nyata #
Tiga wilayah penerapan paling terasa. Observabilitas: alat-alat bpftrace/BCC menggantikan kebutuhan menulis tracer khusus — histogram latensi syscall per proses sampai profil I/O per container, semuanya tanpa menghentikan sistem. Jaringan: penyedia CDN besar memakai XDP untuk menyerap serangan DDoS di level driver, dan Cilium membangun seluruh jaringan Kubernetes di atas program eBPF untuk policy per pod. Keamanan: container modern membatasi syscall yang boleh dipanggil lewat filter seccomp — pendahulu eBPF yang menyaring gerbang syscall yang kita bedah pada chapter syscall — sementara hook LSM versi eBPF memungkinkan kebijakan audit yang jauh lebih kaya tanpa memasang modul kernel.
Dengan ini penjelajahan internal Linux kita genap: dari syscall di gerbang kernel sampai program yang berjalan di dalamnya. Ilmu ini bukan sekadar kosakata — kemampuan membedah sistem dari dalam adalah bekal ketika Linux berpindah rumah ke cloud, tempat server lahir dan mati dalam hitungan menit. Section berikutnya, Cloud dan Production , dimulai dari langkah pertamanya: menjalankan Linux di cloud provider.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
saringan-paket.sh
Unduh berkas#!/bin/bash
# saringan-paket.sh -- simulasi konsep filter paket gaya XDP.
# Aturan dimuat sekali ke array awk (analogi bpf_map), lalu setiap paket
# dinilai langsung tanpa keluar dari jalur cepat:
# 1. IP sumber dari jaringan 203.0.113.0/24 -> DROP
# 2. TCP dengan port tujuan 23 (telnet) -> DROP
# 3. selain itu -> ALLOW
# Pemakaian: bash saringan-paket.sh <berkas-paket>
if [ $# -lt 1 ]; then
exit 0
fi
berkas=$1
if [ ! -f "$berkas" ]; then
echo "berkas paket tidak ditemukan: $berkas"
exit 0
fi
awk '
$1 ~ /^#/ || NF < 4 { next }
$1 ~ /^203\.0\.113\./ { printf "DROP %s\n", $0; blok++; next }
$3 == "tcp" && $4 == 23 { printf "DROP %s\n", $0; blok++; next }
{ printf "ALLOW %s\n", $0; lewat++ }
END {
printf "rekap: %d diizinkan, %d diblok, %d total\n", lewat, blok, lewat + blok
}' "$berkas"
verifier-mini.sh
Unduh berkas#!/bin/bash
# verifier-mini.sh -- simulasi verifier eBPF: pemeriksaan statis sebelum muat.
# Program ditulis satu instruksi per baris dengan format "<indeks>: <isi>".
# Aturan pemeriksaan (versi sederhana):
# 1. Akses memori mem[r1+N] hanya sah untuk N < BATAS (64 byte).
# 2. Lompatan goto harus menunjuk instruksi yang ada.
# 3. Lompatan mundur ditolak: batas iterasinya tidak bisa dibuktikan
# dengan sederhana. Verifier sungguhan menerima loop yang terbukti
# berbatas, misalnya penghitung yang dibandingkan konstanta.
# 4. Instruksi terakhir harus exit agar program pasti berhenti.
# Pemakaian: bash verifier-mini.sh <program.txt>
BATAS=64
if [ $# -lt 1 ]; then
exit 0
fi
program=$1
if [ ! -f "$program" ]; then
echo "program tidak ditemukan: $program"
exit 0
fi
total=$(grep -c ':' "$program")
terakhir=-1
while IFS= read -r baris; do
nomor=${baris%%:*}
isi=${baris#*:}
for akses in $(printf '%s\n' "$isi" | grep -oE 'mem\[r1\+[0-9]+\]'); do
offset=${akses#*+}
offset=${offset%]}
if [ "$offset" -ge "$BATAS" ]; then
echo "DITOLAK (baris $nomor): akses $akses melewati batas $BATAS byte"
exit 0
fi
done
case $isi in
*"goto "*)
target=$(printf '%s\n' "$isi" | grep -oE '[0-9]+$')
if [ "$target" -ge "$total" ]; then
echo "DITOLAK (baris $nomor): goto $target menunjuk instruksi yang tidak ada"
exit 0
fi
if [ "$target" -lt "$nomor" ]; then
echo "DITOLAK (baris $nomor): goto $target melompat mundur (loop tanpa batas)"
exit 0
fi
;;
*exit) terakhir=$nomor ;;
esac
done < "$program"
if [ "$terakhir" -ne "$(( total - 1 ))" ]; then
echo "DITOLAK: instruksi terakhir bukan exit"
exit 0
fi
echo "DITERIMA: $total instruksi lolos pemeriksaan"