Tại sao iptables không còn hiệu quả ở quy mô lớn
Các kỹ sư Linux đã dựa vào iptables và nftables trong nhiều thập kỷ. Chúng rất đáng tin cậy, nhưng không hề miễn phí. Mỗi gói tin đi vào hệ thống đều phải len lỏi qua một mạng lưới dày đặc các Netfilter hook. Trên các đường truyền 10Gbps hoặc 40Gbps, chi phí vận hành này trở thành một gánh nặng CPU khổng lồ. Thực tế, trong các môi trường mật độ cao, Netfilter có thể tiêu tốn tới 20% tổng chu kỳ CPU chỉ để quản lý các quy tắc lọc cơ bản.
eBPF (Extended Berkeley Packet Filter) đã thay đổi hoàn toàn mô hình này. Bằng cách sử dụng eBPF tại lớp TC (Traffic Control), chúng ta chạy mã nguồn đã được biên dịch JIT trực tiếp trong đường dẫn mạng của nhân (kernel). Khác với XDP vốn chỉ giới hạn ở lưu lượng ingress tại cấp độ driver, TC nằm ở vị trí cao hơn một chút trong stack. Vị trí này mang lại một lợi thế then chốt: nó xử lý được cả ingress và egress. Nó cũng cung cấp quyền truy cập đầy đủ vào cấu trúc sk_buff, giúp đơn giản hóa việc xử lý các metadata phức tạp của gói tin.
Gần đây, tôi đã giúp một đội ngũ gặp khó khăn với việc giới hạn băng thông (rate limiting) giữa các container. Các tc qdiscs tiêu chuẩn của họ quá cứng nhắc, và việc biến đổi gói tin bằng iptables đã gây ra các đỉnh trễ lên tới 150ms trong giờ cao điểm. Bằng cách chuyển sang eBPF TC, chúng tôi đã giảm được 15 micro giây thời gian xử lý mỗi gói tin và giảm tải CPU gần 35%.
Ưu điểm của TC
- Kiểm soát hai chiều: XDP là đường một chiều. TC lọc cả những gì đi vào và đi ra.
- Không phụ thuộc phần cứng: XDP thường yêu cầu driver NIC cụ thể cho “Native mode”. TC hoạt động trên bất kỳ interface nào mà nhân Linux nhận diện, từ NIC vật lý đến các bridge ảo.
- Ngữ cảnh phong phú: TC chạy sau khi nhân hệ điều hành đã phân tích cú pháp gói tin. Bạn có thể dễ dàng truy cập các trường giao thức mà không cần tính toán độ lệch byte (byte offset) thủ công.
Thiết lập môi trường
Bạn cần một bộ công cụ hiện đại để xây dựng các bộ lọc này. Chúng ta sử dụng clang để biên dịch C thành bytecode BPF và iproute2 để tải mã đó vào nhân. Hãy đảm bảo bạn đang chạy nhân Linux 5.10 hoặc mới hơn để sử dụng các tính năng BPF ổn định nhất.
Cài đặt các gói phụ thuộc trên Ubuntu hoặc Debian:
sudo apt update
sudo apt install -y clang llvm libelf-dev libbpf-dev gcc-multilib build-essential iproute2 bpftool
Xây dựng bộ lọc gói tin tốc độ cao
Hãy cùng tạo một bộ phân loại (classifier) để ngay lập tức loại bỏ lưu lượng từ một IP cụ thể. Điều này đóng vai trò như một sự thay thế có khả năng lập trình cho quy tắc DROP của iptables thông thường, nhưng hoạt động với tốc độ cao hơn nhiều.
1. Viết chương trình eBPF bằng ngôn ngữ C
Tạo một tệp có tên tc_filter.c. Chúng ta sử dụng macro SEC("classifier") để xác định điểm khởi đầu (entry point).
#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#define TARGET_IP 0x0101A8C0 // 192.168.1.1 ở dạng hex (little endian)
SEC("classifier")
int handle_ingress(struct __sk_buff *skb) {
void *data_end = (void *)(long)skb->data_end;
void *data = (void *)(long)skb->data;
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return TC_ACT_OK;
if (eth->h_proto != __constant_htons(ETH_P_IP))
return TC_ACT_OK;
struct iphdr *ip = data + sizeof(struct ethhdr);
if ((void *)(ip + 1) > data_end)
return TC_ACT_OK;
if (ip->saddr == TARGET_IP) {
bpf_printk("Đang chặn lưu lượng từ: %pI4\n", &ip->saddr);
return TC_ACT_SHOT;
}
return TC_ACT_OK;
}
char _license[] SEC("license") = "GPL";
2. Biên dịch sang Bytecode
Các trình biên dịch C thông thường sẽ không hoạt động ở đây. Chúng ta phải nhắm mục tiêu cụ thể vào kiến trúc bpf.
clang -O2 -target bpf -c tc_filter.c -o tc_filter.o
3. Gắn vào Interface
TC eBPF yêu cầu một qdisc clsact (classifier action). Hàng đợi ảo này cung cấp các hook cần thiết để chương trình của chúng ta chặn lưu lượng.
# 1. Tạo clsact qdisc trên eth0
sudo tc qdisc add dev eth0 clsact
# 2. Tải chương trình vào ingress hook
sudo tc filter add dev eth0 ingress bpf da obj tc_filter.o sec classifier
Cờ da (Direct Action) là cực kỳ quan trọng. Nó chỉ dẫn nhân hệ điều hành tin tưởng vào mã trả về của chương trình (như TC_ACT_SHOT) như phán quyết cuối cùng cho gói tin.
Giám sát và kiểm tra
Không giống như các tường lửa truyền thống, eBPF không hiển thị trong iptables -L. Bạn cần một bộ công cụ khác để quan sát những gì đang diễn ra bên trong nhân.
Xác minh bộ lọc
Chạy lệnh này để xác nhận bộ lọc đang hoạt động trên interface của bạn:
tc filter show dev eth0 ingress
Đọc Kernel Trace
Hàm bpf_printk gửi các thông điệp đến đường ống trace của nhân. Đây là người bạn tốt nhất để gỡ lỗi. Hãy mở một terminal mới và chạy:
sudo cat /sys/kernel/debug/tracing/trace_pipe
Khi một gói tin từ 192.168.1.1 đi vào interface, bạn sẽ thấy thông báo “Đang chặn lưu lượng từ…” xuất hiện ngay lập tức.
Thống kê hiệu suất
Để xem bộ lọc của bạn đã xử lý bao nhiêu gói tin, hãy sử dụng cờ thống kê:
tc -s filter show dev eth0 ingress
Nâng cao: QoS có khả năng lập trình
Loại bỏ gói tin mới chỉ là bắt đầu. Bạn có thể sử dụng eBPF TC cho Chất lượng dịch vụ (QoS) chi tiết. Bằng cách tận dụng BPF Maps, bạn có thể theo dõi số lượng byte cho hàng nghìn IP riêng lẻ. Thay vì chỉ DROP đơn giản, mã của bạn có thể tính toán băng thông trong thời gian thực. Mức độ kiểm soát này là không thể đối với các lớp tc tiêu chuẩn, vốn thường gặp khó khăn với các tập luật động và có độ đa dạng cao. Làm chủ vòng đời này cho phép bạn xây dựng logic mạng vừa linh hoạt vừa cực kỳ nhanh chóng.

