sk_buff dialokasikan oleh kernel, sistem dapat membuang paket ilegal dengan latensi deterministik tanpa memicu saturasi ksoftirqd.
Hambatan Pemrosesan Paket di Kernel Stack Legasi
- NIC menghasilkan Direct Memory Access (DMA) transfer ke ring buffer host.
- Hardware interrupt (IRQ) dinaikkan, memicu interrupt handler yang menjadwalkan subsistem NAPI (New API).
- NAPI menjalankan loop pemrosesan dalam konteks
softirq(Software Interrupt). - Kernel mengeksekusi alokasi struktur metadata paket melalui
alloc_skb()atau__netdev_alloc_skb(). - Paket melintasi hook netfilter (
PREROUTING,conntrack,FORWARD/INPUT). - Evaluasi aturan iptables/nftables dilakukan secara linear atau tree-based sebelum keputusan routing FIB diambil.
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 Komponen | Lokasi Eksekusi | Alokasi Memori Per Paket | Batas Throughput Tipikal (Per Core) | Dampak Bottleneck |
|---|---|---|---|---|
| iptables / conntrack | Network Stack (netfilter) | Membutuhkan struct sk_buff komplit | 1.2 - 2.5 Mpps | Saturasi CPU softirq, packet drop di ring buffer |
| nftables (flowtable) | Network Stack bypass | Membutuhkan struct sk_buff parsial | 3.5 - 5.0 Mpps | Tekanan memory allocator, cache thrashing |
| eBPF XDP (Driver Mode) | Ring RX Driver NIC | Nol alokasi sk_buff (struct xdp_buff) | 14.0 - 28.0 Mpps | Minimal (dibatasi oleh clock PCIe / bus memory) |
Mekanisme eBPF XDP dan Struktur LPM Trie
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];
};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
};Desain Program XDP Ingress Filter
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";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.
Sinkronisasi Control Plane RPKI ke BPF Map
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;
}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.
Kompilasi, Loading, dan Pinning eBPF Map
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_mapxdpdrv) 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.
Telemetri Data Plane dan Validasi Performa
1. Inspeksi Peta BPF dan Statistik Drop
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_mapTEXT
[{ "key": 1,
"values": [{
"cpu": 0,
"value": 14820921
},{
"cpu": 1,
"value": 15201194
},{
"cpu": 2,
"value": 14991203
},{
"cpu": 3,
"value": 15110488
}
]
}]2. Verifikasi Profil Beban Kernel dan Interrupt
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- Menggunakan iptables konvensional: Utilisasi CPU
softirqpada core yang menangani NAPI (%soft) mencapai 100%, sistem freeze, dandmesgmencatat pesan drop pada antarmuka jaringan. - Menggunakan eBPF XDP: Penggunaan
%softbertahan di bawah 12% per core, dengan siklus CPU didominasi oleh instruksi driver NIC native (mlx5e_napi_poll) dan eksekusi XDP BPF JIT instruction tanpa alokasialloc_skb.
BASH
# Memeriksa RX drop pada level hardware ring buffer
ethtool -S eth0 | grep -E 'rx_dropped|rx_missed_errors|rx_discards'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.