eBPF/XDP adalah layer pertahanan paling bawah di jarsWAF — beroperasi di L3 (network layer) sebelum TCP stack kernel. XDP program di-load ke interface via aya-rs runtime, dan menggunakan BLOCKLIST map (HashMap<u32, u8>) yang di-populate oleh agent secara dinamis dari threat intel feeds. Catatan ini mendokumentasikan setup, attachment, dan E2E verification XDP drop via veth pair + namespace.

eBPF/XDP Layer — Network-Level BLOCK via XDP_DROP

Daftar Isi

  1. 1. eBPF Architecture
  2. 2. eBPF Binary Analysis
  3. 3. Path Bug Fix
  4. 4. XDP Attachment Verification
  5. 5. E2E XDP Drop Test
  6. 6. Limitations
  7. 7. Mitigation & Recommendations

1. eBPF Architecture

jarsWAF menggunakan dua program eBPF:

┌──────────────────────────────────────────────────┐
│                  Userspace                        │
│  ┌──────────┐           ┌──────────────┐        │
│  │ Controller│           │ Agent        │        │
│  │ (gRPC)   │           │ (Pingora)    │        │
│  └──────────┘           └──────┬───────┘        │
│                                │                 │
│                    ┌───────────▼───────────┐     │
│                    │  XDPManager           │     │
│                    │  - load Ebpf::load()  │     │
│                    │  - attach Xdp::attach()│    │
│                    │  - block_ip()         │     │
│                    │  - unblock_ip()       │     │
│                    └───────────┬───────────┘     │
│                                │ libbpf syscall   │
├────────────────────────────────┼──────────────────┤
│                  Kernel Space  │                  │
│                   ┌────────────▼───────────┐     │
│                   │  XDP Program            │     │
│                   │  (attached to veth0)    │     │
│                   │                          │     │
│  Packet in ──────►│ ethhdr → Ipv4?          │     │
│                   │  └─ yes → IP src in     │     │
│                   │           BLOCKLIST?    │     │
│                   │             ├─ yes ──►  │     │
│                   │             │  XDP_DROP │     │
│                   │             └─ no ────► │     │
│                   │               XDP_PASS  │     │
│                   └──────────────────────────┘     │
│                                                    │
│  ┌──────────────────────────┐                      │
│  │ kprobe / RASP            │                      │
│  │ sys_execve → ring buffer │                      │
│  └──────────────────────────┘                      │
└─────────────────────────────────────────────────────┘

eBPF Program Objects

AspekDetail
Runtimeaya-rs (aya 0.12+) — pure Rust eBPF library
Compilerrustc via bpfel-unknown-none target
ModeXdpMode::Generic (SKB-based, compatible with veth)
Map TypeHashMap<u32, u8> — key=IPv4 (u32 BE), value=block flag
Max Entries10,240 IP addresses

2. eBPF Binary Analysis

$ readelf -a jarswaf-ebpf/target/bpfel-unknown-none/release/jarswaf-ebpf

2 programs:

ProgramTypeSizeSectionNama
XDPxdp216 bytes.textjarswaf_ebpf
kprobekprobe288 bytes.textjarswaf_rasp_exec

2 maps:

MapTypeKeyValueMaxKoneksi
BLOCKLISTHashMapu32 (BE)u810,240XDP drop decision
RASP_EVENTSPerfEventArrayExecveEventringkprobe → userspace

Relocations:

  • xdp: references BLOCKLIST map via R_BPF_64_64 relocation
  • kprobe: references RASP_EVENTS map + memset helper call

Source Logic

File: jarswaf-ebpf/src/main.rs

fn try_jarswaf_ebpf(ctx: XdpContext) -> Result<u32, ()> {
    // 1. Parse Ethernet header
    let ethhdr = unsafe { ptr_at::<EthHdr>(&ctx, 0)? };
 
    // 2. Skip non-IPv4
    if ethhdr.ether_type != ETH_P_IP {
        return Ok(XDP_PASS);
    }
 
    // 3. Parse IPv4 header
    let ipv4hdr = unsafe { ptr_at::<Ipv4Hdr>(&ctx, EthHdr::LEN)? };
    let source_ip = u32::from_be(ipv4hdr.src_addr);
 
    // 4. Check blocklist
    let blocklist = HashMap::<u32, u8>::try_from(&*BLOCKLIST)?;
    match blocklist.get(&source_ip, 0) {
        Some(_) => Ok(XDP_DROP),     // DROP at network layer
        None => Ok(XDP_PASS),        // Forward
    }
}

Kprobe Source Logic

fn try_jarswaf_rasp_exec(ctx: TracePointContext) -> Result<u32, ()> {
    // Monitor execve syscall → detect process injection
    // Sends event to RASP_EVENTS PerfEventArray
    // (Not yet tested in this session)
}

3. Path Bug Fix

Sebelum fix:

// src/xdp.rs:33
match Ebpf::load_file("target/bpfel-unknown-none/release/jarswaf-ebpf") {

Error:

WARN: Failed to load eBPF object.
Error 2: FileError { path: "target/bpfel-unknown-none/release/jarswaf-ebpf",
                      error: Os { code: 2, kind: NotFound } }

Sesudah fix:

// src/xdp.rs:33
match Ebpf::load_file("jarswaf-ebpf/target/bpfel-unknown-none/release/jarswaf-ebpf") {

Root cause: Di development (non-Docker), eBPF Cargo workspace menghasilkan binary di jarswaf-ebpf/target/... — relative path dari project root. Tapi xdp.rs nyari di target/... yang adalah root target Docker container.

Kenapa gak kena sebelumnya? Karena fitur XDP di-agent bersifat opsional — kalau path gak ketemu, warn! + skip. Gak ada hard error atau crash.


4. XDP Attachment Verification

CAP_BPF Grant

Karena loading eBPF butuh CAP_BPF dan CAP_NET_ADMIN, agent binary perlu capabilities:

sudo setcap cap_bpf,cap_net_admin+ep target/debug/agent
sudo setcap cap_bpf,cap_net_admin+ep target/debug/controller

Build Agent & Load

$ cargo run --bin agent
INFO: "Attaching eBPF XDP to interface: veth0"
INFO: "XDP program successfully attached to interface: veth0"

Verify via bpftool

$ sudo bpftool net
xdp:
veth0(3) driver id 185

driver id 185 = driver mode — XDP di-attach langsung ke veth driver, bukan generic SKB mode.

BLOCKLIST Auto-Population

Agent otomatis populate BLOCKLIST dari threat intel feeds (Spamhaus DROP list, FireHOL, Tor exit nodes):

$ process log | grep "added to XDP blocklist"
INFO: "IP 207.45.56.0 added to XDP blocklist"
INFO: "IP 208.75.88.0 added to XDP blocklist"
INFO: "IP 209.17.192.0 added to XDP blocklist"
... 300+ IPs loaded ...

5. E2E XDP Drop Test

Setup

# Create veth pair + namespace
sudo ip netns add testns
sudo ip link add veth0 type veth peer name veth1 netns testns
sudo ip addr add 10.0.0.1/24 dev veth0
sudo ip netns exec testns ip addr add 10.0.0.2/24 dev veth1
# Start agent with xdp_interface = "veth0"

Test Flow

Step 1: Verify connectivity (baseline)
  namespace → ping 10.0.0.1 → 2/2 ✅

Step 2: Inject IP ke XDP BLOCKLIST via bpftool
  sudo bpftool map update id 33 key 0x0a 0x00 0x00 0x02 value 0x01
  (key = 10.0.0.2 in network byte order)

Step 3: Verify XDP DROP
  namespace → ping 10.0.0.1 → 0/2 (100% loss) 🔥🔥

Step 4: Recovery
  sudo bpftool map delete id 33 key 0x0a 0x00 0x00 0x02
  namespace → ping 10.0.0.1 → 1/1 ✅

Hasil

Before (no blocklist):
  3 packets transmitted, 3 received, 0% packet loss

After (10.0.0.2 in BLOCKLIST):
  2 packets transmitted, 0 received, 100% packet loss   ← XDP_DROP!

After (removed from BLOCKLIST):
  1 packets transmitted, 1 received, 0% packet loss     ← Recovery OK

Key Finding

XDP program melakukan DROP di L3 — sebelum TCP stack, sebelum iptables, sebelum semua L7 processing. Ini artinya:

LayerSebelum XDPSesudah XDP
NIC driver rx queue
XDP hookDROP here
SKB allocation❌ (saved)
TCP/IP stack❌ (saved)
iptables/nftables❌ (saved)
Application (WAF)❌ (saved)

Ini extreme efficiency — attacker yang IP-nya sudah dikenal gak perlu consume CPU WAF sama sekali.


6. Limitations

XDP Tidak Bekerja di WiFi

Wireless interface (iwlwifi, ath, Broadcom) tidak support XDP karena driver tidak memiliki native XDP hook. Testing hanya bisa via:

MethodDapat?Keterangan
veth pair (namespace)XdpMode::Generic
VM with virtio NICDriver native XDP
Loopback (lo)Generic mode
Real Ethernet (mlx5, i40e, ixgbe)Driver mode tercepat
WiFi (wlp2s0)Driver tidak support

BLOCKLIST Hanya Exact Match

BLOCKLIST map adalah HashMap<u32, u8>exact IP match, bukan CIDR. IP 10.0.0.2 adalah entry berbeda dengan 10.0.0.0/24 subnet. Jika threat intel feeds memasukkan subnet (CIDR), hanya IP pertama yang di-block.

Ini limitasi structural di eBPF — HashMap gak support prefix lookup. Solusi: gunakan LPMTrie (Longest Prefix Match Trie) map type.

kprobe / RASP Belum Diuji

Program jarswaf_rasp_exec sudah tercompile tapi belum di-test karena:

  • Butuh CAP_SYS_ADMIN untuk kprobe attach
  • kprobe butuh /proc/kallsyms yang bisa di-restrict

7. Mitigation & Recommendations

Immediate Fix Sudah di-Apply

FixFileStatus
Path eBPF target/jarswaf-ebpf/target/src/xdp.rs:33
Config xdp_interface di config.tomlconfig.toml
#ImprovementImpact
1LPMTrie map untuk CIDR support di XDP BLOCKLISTBlock full subnet, bukan per-IP
2XDP program update tanpa restart agentMap pinning via bpffs
3kprobe testing di VM dengan virtio NICValidasi RASP layer
4Re-attach on interface changeAuto-detect interface down/up
5XDP statistics (packets dropped vs passed)Grafana metrics

References

  1. src/xdp.rs — XdpManager implementation
  2. jarswaf-ebpf/src/main.rs — eBPF program source
  3. aya-rs — Rust eBPF library
  4. XDP Project — eXpress Data Path
  5. Cilium BPF and XDP Reference Guide
  6. kernel.org: XDP documentation
  7. Fedora Wiki: BPF

Koneksi ke Vault

CatatanKoneksi
sql-comment-injection-waf-bypassL7 rule bypass testing — complement XDP at L3
waf-reverse-proxy-deepdiveWAF architecture overview
purple-team-osi-killchainPurple team killchain — L3 defense
web-hacking-exploitationWeb app exploitation — XDP tidak relevan di L3