Bali, Indonesia
Insights

Validasi BGP Route Origin dan Filtrasi Prefix Menggunakan eBPF XDP pada Gateway Hosting Multi-Tenant

October 15, 2026
0:00
Pada arsitektur jaringan multi-tenant edge gateway, kebocoran rute BGP (route leak) sering terjadi ketika autonomous system (AS) perantara secara keliru meneruskan prefix yang dipelajari dari satu upstream transit atau peer ke transit provider lain. Insiden seperti kebocoran prefix Google (AS15169) oleh MainOne (AS37282) ke China Telecom (AS4809) menunjukkan bahwa ketiadaan validasi prefix yang ketat pada sisi ingress menyebabkan anomali penyebaran rute global. Ketika rute yang salah masuk ke Forwarding Information Base (FIB) kernel Linux, gateway multi-tenant harus memproses lonjakan lalu lintas yang tidak sah. Pemfilteran paket liar pada layer kontrol menggunakan route-map statis atau pada layer data plane menggunakan aturan iptables/nftables konvensional menimbulkan hambatan komputasi yang signifikan pada kernel stack. Pendekatan modern untuk mengamankan data plane gateway edge Linux melibatkan integrasi Resource Public Key Infrastructure (RPKI) Route Origin Validation (ROV) langsung ke network driver ingress melalui eBPF (Extended Berkeley Packet Filter) XDP (eXpress Data Path). Dengan memvalidasi integritas origin prefix pada cincin buffer Network Interface Card (NIC) sebelum struktur sk_buff dialokasikan oleh kernel, sistem dapat membuang paket ilegal dengan latensi deterministik tanpa memicu saturasi ksoftirqd.
Dalam skenario kebocoran rute skala besar, gateway multi-tenant menerima jutaan paket per detik yang ditujukan ke prefix yang seharusnya tidak diarahkan ke node tersebut. Pada pipeline jaringan Linux standar, setiap frame Ethernet yang tiba pada cincin RX ring buffer driver NIC melewati alur alokasi memori yang intensif:
  1. NIC menghasilkan Direct Memory Access (DMA) transfer ke ring buffer host.
  2. Hardware interrupt (IRQ) dinaikkan, memicu interrupt handler yang menjadwalkan subsistem NAPI (New API).
  3. NAPI menjalankan loop pemrosesan dalam konteks softirq (Software Interrupt).
  4. Kernel mengeksekusi alokasi struktur metadata paket melalui alloc_skb() atau __netdev_alloc_skb().
  5. Paket melintasi hook netfilter (PREROUTING, conntrack, FORWARD/INPUT).
  6. Evaluasi aturan iptables/nftables dilakukan secara linear atau tree-based sebelum keputusan routing FIB diambil.
Ketika volume traffic akibat route leak mencapai jutaan paket per detik (Mpps), alokasi sk_buff dan overhead traversal tabel conntrack (nf_conntrack) memicu saturasi total pada core CPU yang menangani ksoftirqd. Masalah ini diperparah oleh pembacaan lock read-write pada rantai aturan iptables yang besar, yang menyebabkan perebutan lock (lock contention) antar core CPU.
Pipeline KomponenLokasi EksekusiAlokasi Memori Per PaketBatas Throughput Tipikal (Per Core)Dampak Bottleneck
iptables / conntrackNetwork Stack (netfilter)Membutuhkan struct sk_buff komplit1.2 - 2.5 MppsSaturasi CPU softirq, packet drop di ring buffer
nftables (flowtable)Network Stack bypassMembutuhkan struct sk_buff parsial3.5 - 5.0 MppsTekanan memory allocator, cache thrashing
eBPF XDP (Driver Mode)Ring RX Driver NICNol alokasi sk_buff (struct xdp_buff)14.0 - 28.0 MppsMinimal (dibatasi oleh clock PCIe / bus memory)
Ketika netfilter kehabisan siklus CPU untuk memproses paket yang salah arah, ring buffer RX NIC akan terisi penuh (overflow), mengakibatkan buffer overrun dan jatuhnya paket legitimate untuk tenant hosting lain pada gateway yang sama.
eBPF XDP memindahkan eksekusi logika verifikasi paket ke lapisan paling bawah dari driver jaringan, tepat setelah NIC menulis frame ke memori host melalui DMA dan sebelum alloc_skb() dipanggil. Program XDP beroperasi pada struktur data struct xdp_buff, yang hanya berisi pointer ringan ke raw packet memory (data, data_end, data_meta, dan data_hard_start). Untuk memvalidasi apakah paket IP ingress sesuai dengan rute yang valid dan diotorisasi, XDP memanfaatkan tipe BPF map khusus: BPF_MAP_TYPE_LPM_TRIE (Longest Prefix Match Trie). Struktur data ini dioptimalkan di tingkat kernel untuk pencarian subnet IP dengan kompleksitas waktu proporsional terhadap panjang bit prefix (maksimal 32 iterasi untuk IPv4 dan 128 iterasi untuk IPv6), bukan jumlah entri prefix yang tersimpan. Struktur key untuk LPM trie map pada kernel didefinisikan sebagai berikut:
C
struct bpf_lpm_trie_key {
    __u32 prefixlen;
    __u8  data[0];
};
Dalam konteks validasi prefix, key tersebut dikapsulasi ke dalam struktur kustom yang menyimpan prefix IP target, sedangkan nilai (value) dari map menyimpan status validasi origin dan atribut keamanan rute (misalnya ASN asal yang sah dan flag status RPKI: VALID, INVALID, atau NOT_FOUND).
C
struct route_key {
    __u32 prefixlen;
    __u32 ipv4_addr; // Network byte order
};

struct route_val {
    __u32 origin_asn;
    __u8  rpki_status; // 0 = Valid, 1 = Invalid, 2 = Unknown
    __u8  action;      // 0 = Pass, 1 = Drop, 2 = Trap to Control Plane
};
Penggunaan LPM trie berbasis RPKI ini melengkapi mitigasi di tingkat routing daemon yang dibahas pada panduan Hardening Linux Border Routers Against BGP Route Leaks Using RPKI Route Origin Validation in BIRD, namun XDP bertindak langsung pada bidang data (data plane) untuk menolak lalu lintas gelap seketika.
Program C berikut di-compile menggunakan Clang/LLVM ke dalam arsitektur target BPF bytecode. Program ini mem-parse header Ethernet, mengekstrak header IPv4, mencari destination IP pada BPF_MAP_TYPE_LPM_TRIE, dan segera mengembalikan kode vonis XDP_DROP jika prefix tujuan atau jalur rutenya ditandai sebagai RPKI_INVALID atau rute terlarang yang bocor.
C
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

#define RPKI_VALID       0
#define RPKI_INVALID     1
#define RPKI_NOT_FOUND   2

#define ACTION_PASS      0
#define ACTION_DROP      1

struct lpm_key_ipv4 {
    __u32 prefixlen;
    __u32 addr;
};

struct lpm_val {
    __u32 origin_asn;
    __u8  rpki_status;
    __u8  action;
};

struct {
    __uint(type, BPF_MAP_TYPE_LPM_TRIE);
    __type(key, struct lpm_key_ipv4);
    __type(value, struct lpm_val);
    __uint(map_flags, BPF_F_NO_PREALLOC);
    __uint(max_entries, 1000000);
} prefix_validation_map SEC(".maps");

struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __type(key, __u32);
    __type(value, __u64);
    __uint(max_entries, 4);
} drop_stats_map SEC(".maps");

static __always_inline void increment_stat(__u32 reason) {
    __u64 *counter = bpf_map_lookup_elem(&drop_stats_map, &reason);
    if (counter) {
        (*counter)++;
    }
}

SEC("xdp_bgp_filter")
int filter_ingress(struct xdp_buff *ctx) {
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;

    struct ethhdr *eth = data;
    if ((void *)(eth + 1) > data_end) {
        return XDP_PASS;
    }

    if (eth->h_proto != bpf_htons(ETH_P_IP)) {
        return XDP_PASS;
    }

    struct iphdr *iph = (void *)(eth + 1);
    if ((void *)(iph + 1) > data_end) {
        return XDP_PASS;
    }

    // Ekstrak destination address untuk verifikasi keabsahan prefix tenant hosting
    struct lpm_key_ipv4 search_key = {
        .prefixlen = 32,
        .addr = iph->daddr
    };

    struct lpm_val *match = bpf_map_lookup_elem(&prefix_validation_map, &search_key);
    if (!match) {
        // Default action: prefix tidak berada di alokasi lokal gateway edge
        increment_stat(RPKI_NOT_FOUND);
        return XDP_PASS;
    }

    if (match->rpki_status == RPKI_INVALID || match->action == ACTION_DROP) {
        increment_stat(RPKI_INVALID);
        return XDP_DROP;
    }

    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
Instruksi if ((void *)(iph + 1) > data_end) merupakan syarat mutlak verifier kernel Linux. Verifier secara statis memvalidasi bahwa setiap dereferensi pointer berada di dalam batas fisik memori paket yang tersedia (data sampai data_end) untuk mencegah memory fault di level driver.
Data plane eBPF membutuhkan populasi data validasi secara real-time dari control plane routing. Gateway multi-tenant umumnya mengeksekusi routing daemon seperti BIRD atau FRRouting (FRR) yang terhubung ke RPKI Validator (misalnya Routinator, OctoRPKI, atau GoRTR) menggunakan protokol RTR (RPKI-to-Router). Daemon pengguna (userspace synchronization agent) memantau tabel BGP dan cache RPKI, lalu memetakan status validasi ke dalam eBPF map melalui pemanggilan system call bpf(BPF_MAP_UPDATE_ELEM). Implementasi userspace loader dalam C menggunakan libbpf mengeksekusi pembaruan tabel secara atomik:
C
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>

struct lpm_key_ipv4 {
    uint32_t prefixlen;
    uint32_t addr;
};

struct lpm_val {
    uint32_t origin_asn;
    uint8_t  rpki_status;
    uint8_t  action;
};

int update_prefix_validation(int map_fd, const char *ip_str, uint32_t cidr, uint32_t asn, uint8_t status, uint8_t action) {
    struct lpm_key_ipv4 key;
    struct lpm_val val;

    key.prefixlen = cidr;
    if (inet_pton(AF_INET, ip_str, &key.addr) != 1) {
        return -1;
    }

    val.origin_asn = asn;
    val.rpki_status = status;
    val.action = action;

    // BPF_ANY: Perbarui jika sudah ada, atau buat baru jika belum ada
    return bpf_map_update_elem(map_fd, &key, &val, BPF_ANY);
}

int main(int argc, char **argv) {
    int map_fd = bpf_obj_get("/sys/fs/bpf/xdp_prefix_validation_map");
    if (map_fd < 0) {
        perror("Gagal membuka pinned bpf map");
        return 1;
    }

    // Inisialisasi rute prefix tenant sah (misal 198.51.100.0/24, AS64496, VALID)
    if (update_prefix_validation(map_fd, "198.51.100.0", 24, 64496, 0, 0) == 0) {
        printf("Berhasil memuat rute valid 198.51.100.0/24
");
    }

    // Blokir rentang prefix yang terdeteksi leak/hijack (misal 203.0.113.0/24, INVALID, DROP)
    if (update_prefix_validation(map_fd, "203.0.113.0", 24, 64511, 1, 1) == 0) {
        printf("Berhasil menyuntikkan rule drop untuk prefix bocor 203.0.113.0/24
");
    }

    return 0;
}
Untuk menghindari memory allocations overhead secara dinamis di dalam kernel saat map diperbarui, atribut BPF_F_NO_PREALLOC dapat disesuaikan. Namun, untuk stabilitas sistem edge yang menerima tabel routing penuh (full routing table > 950.000 prefix), alokasi memori awal (preallocated trie nodes) harus dipertimbangkan untuk mencegah latensi write saat terjadi routing churn global.
Eksekusi kompilasi dan loading program eBPF ke antarmuka gateway multi-tenant menggunakan toolchain standar LLVM dan iproute2:
BASH
# Kompilasi C source code menjadi target BPF ELF object
clang -O2 -g -Wall -target bpf -c xdp_bgp_filter.c -o xdp_bgp_filter.o

# Mount BPF Virtual Filesystem jika belum terpasang
mount -t bpf bpf /sys/fs/bpf

# Attach program XDP ke network interface fisik (misal: eth0) pada mode native driver
ip link set dev eth0 xdpgeneric off
ip link set dev eth0 xdpdrv object xdp_bgp_filter.o section xdp_bgp_filter

# Pinning map agar dapat dimanipulasi oleh userspace synchronization agent
bpftool prog show name filter_ingress
bpftool map pin name prefix_validation_map /sys/fs/bpf/xdp_prefix_validation_map
Mode native driver (xdpdrv) mewajibkan driver NIC (seperti ixgbe, i40e, atau mlx5) mendukung native hook XDP. Mode ini memproses frame langsung saat descriptor ring dievaluasi. Jika NIC tidak mendukung mode ini, fallback ke generic mode (xdpgeneric) dapat digunakan, namun generic mode mengeksekusi XDP setelah alloc_skb() dipanggil, sehingga menghilangkan keuntungan performa reduksi overhead memori.
Verifikasi operasional eBPF XDP dilakukan dengan memeriksa inspeksi register CPU, status hardware ring buffer, dan isi BPF map secara interaktif.
Status prefix yang termuat dan jumlah paket yang terkena filter dapat dibaca melalui utilitas bpftool:
BASH
# Memeriksa isi LPM Trie Map yang terpasang di filesystem BPF
bpftool map dump pinned /sys/fs/bpf/xdp_prefix_validation_map

# Membaca per-CPU array statistik drop akibat RPKI invalid
bpftool map dump name drop_stats_map
Output tipikal pembacaan counter drop per CPU:
TEXT
[{   "key": 1,
    "values": [{
            "cpu": 0,
            "value": 14820921
        },{
            "cpu": 1,
            "value": 15201194
        },{
            "cpu": 2,
            "value": 14991203
        },{
            "cpu": 3,
            "value": 15110488
        }
    ]
}]
Untuk membuktikan pembebasan beban CPU dari alokasi memori sk_buff, jalankan profiling instruksi kernel saat gateway dibebani pengujian simulasi paket liar:
BASH
# Evaluasi alokasi waktu siklus CPU pada kernel
perf top --sort comm,dso,symbol

# Pantau konsumsi CPU softirq per core secara real-time
mpstat -P ALL 1 5
Di bawah serangan volumetrik lalu lintas akibat kebocoran BGP dengan rate 12 Mpps:
  • Menggunakan iptables konvensional: Utilisasi CPU softirq pada core yang menangani NAPI (%soft) mencapai 100%, sistem freeze, dan dmesg mencatat pesan drop pada antarmuka jaringan.
  • Menggunakan eBPF XDP: Penggunaan %soft bertahan di bawah 12% per core, dengan siklus CPU didominasi oleh instruksi driver NIC native (mlx5e_napi_poll) dan eksekusi XDP BPF JIT instruction tanpa alokasi alloc_skb.
BASH
# Memeriksa RX drop pada level hardware ring buffer
ethtool -S eth0 | grep -E 'rx_dropped|rx_missed_errors|rx_discards'
Jika nilai rx_dropped pada ring buffer berhenti bertambah sementara counter drop_stats_map di BPF bertambah secara linier, ini membuktikan bahwa paket yang tidak sah dieliminasi tepat di batas masuk interface hardware tanpa membebani komponen kernel stack gateway edge hosting multi-tenant.