Vận hành các cụm máy chủ VPN cửa ngõ quy mô lớn phục vụ hàng chục nghìn kết nối đồng thời đặt ra những thách thức nghiêm trọng về hiệu năng nhân Linux. Mặc dù các máy chủ đa nhân (SMP) hiện đại sở hữu từ 64 đến 128 luồng phần cứng, đường truyền mạng Linux mặc định thường gặp hiện tượng rớt gói tin nặng nề trước khi băng thông vật lý đạt ngưỡng bão hòa. Trong các hệ thống mật độ lưu lượng dày đặc, nguyên nhân gốc rễ thường không nằm ở năng lực mã hóa hay băng thông bộ nhớ RAM, mà xuất phát từ phân hệ theo dõi kết nối của Linux Netfilter (nf_conntrack).
Được thiết kế ban đầu cho các bức tường lửa kiểm soát trạng thái trong gia đình và doanh nghiệp, nf_conntrack duy trì trạng thái toàn cục cho từng luồng IP hai chiều đi qua hệ điều hành. Khi áp dụng vào cổng tập trung VPN—nơi hàng triệu phiên UDP và TCP ngắn hạn liên tục sinh ra và kết thúc mỗi phút—thiết kế này gây sụt giảm hiệu năng nghiêm trọng, tranh chấp khóa xoay (spinlock) và tình trạng rớt gói không thể phục hồi.
Điểm nghẽn Conntrack: Tranh chấp Spinlock và tràn bảng trạng thái
Mỗi khi một gói tin IP đi vào ngăn xếp mạng của nhân Linux, Netfilter tính toán mã băm của bộ 5 giá trị (IP nguồn, IP đích, cổng nguồn, cổng đích, giao thức L4) để tìm kiếm hoặc tạo mới cấu trúc struct nf_conn trong bảng băm toàn cục:
[Đường truyền Ingress mặc định với Conntrack]
Gói tin tới ──> Móc PRE_ROUTING ──> nf_conntrack_in()
│
Chiếm khóa xoay bucket (nf_conntrack_locks) ◄── [Tranh chấp khóa trên 64+ lõi CPU]
│
Tìm kiếm / Cấp phát struct nf_conn (Slab Cache: kmalloc-512)
│
Giải phóng khóa ──> Chuyển tiếp / Tra bảng định tuyến ──> Egress
Dưới tải lưu lượng VPN doanh nghiệp, đường truyền này bộc lộ ba điểm yếu kỹ thuật cốt lõi:
- Tràn bảng trạng thái và rớt gói âm thầm: Quy mô bảng trạng thái bị giới hạn bởi tham số
net.netfilter.nf_conntrack_max. Khi số lượng kết nối đồng thời vượt ngưỡng này, nhân Linux ghi nhận cảnh báonf_conntrack: table full, dropping packet. Các gói tin TCP SYN mới và gói khởi tạo bắt tay WireGuard bị hủy bỏ trực tiếp, làm gián đoạn đường truyền người dùng. - Tranh chấp khóa xoay nguyên tử giữa các lõi CPU: Bảng băm conntrack được bảo vệ bởi một mảng khóa xoay chia vùng (
nf_conntrack_locks). Khi card mạng đa hàng đợi (multi-queue NIC) phân phối gói tin qua cơ chế RSS đến hàng chục lõi CPU, nhiều luồng xử lý đồng thời cố gắng chiếm giữ cùng một khóa bucket. Hiện tượng này làm nảy sinh xung đột đường truyền bộ nhớ đệm (cache line bouncing), đẩy tải CPU xử lý ngắt mềm (softirq) lên 100% dù thông lượng thực tế suy giảm mạnh. - Phình bộ nhớ và phân mảnh Slab: Mỗi kết nối được theo dõi chiếm khoảng 320 đến 384 byte bộ nhớ nhân không thể hoán vị (non-swappable slab memory trong
kmalloc-512). Việc theo dõi 3 triệu kết nối ngốn hơn 1,2 GB RAM nhân, gây áp lực trực tiếp lên bảng phân trang bộ nhớ và bộ đệm TLB.
Loại bỏ chi phí Conntrack: Quy tắc NOTRACK khai báo
Giải pháp trực tiếp nhất để bảo vệ hạ tầng cổng VPN khỏi nguy cơ cạn kiệt bảng theo dõi là chỉ định Netfilter bỏ qua việc ghi nhận trạng thái đối với các cổng dịch vụ đường hầm.
Bằng cách gắn quy tắc vào bảng raw—vốn được đánh giá trước khi tiến trình theo dõi kết nối bắt đầu tại các móc PREROUTING và OUTPUT—quản trị viên loại bỏ hoàn toàn việc cấp phát bộ nhớ trạng thái:
# Cấu hình nftables bỏ qua theo dõi trạng thái cho lưu lượng WireGuard
nft add table inet raw
nft add chain inet raw prerouting { type filter hook prerouting priority -300 \; }
nft add chain inet raw output { type filter hook output priority -300 \; }
# Đánh dấu cổng UDP lắng nghe 51820 với chỉ thị notrack
nft add rule inet raw prerouting udp dport 51820 notrack
nft add rule inet raw output udp sport 51820 notrack
Trong cú pháp iptables truyền thống, cấu hình tương đương được áp dụng trên bảng raw:
iptables -t raw -A PREROUTING -p udp --dport 51820 -j NOTRACK
iptables -t raw -A OUTPUT -p udp --sport 51820 -j NOTRACK
Áp dụng NOTRACK giúp cắt giảm tới 28% chu kỳ xử lý của CPU trên mỗi gói tin ở tầng giao vận UDP bên ngoài. Khi conntrack truyền thống bị vô hiệu hóa, việc kiểm soát chuyển tiếp cần được chuyển giao cho tường lửa phi trạng thái hoặc đường truyền chuyên dụng bằng eBPF.
Theo dõi trạng thái trong nhân không khóa với eBPF
Để duy trì khả năng quản lý phiên thông minh, điều phối lưu lượng và định tuyến đối xứng mà không chịu tổn thất hiệu năng từ nf_conntrack, kiến trúc hiện đại triển khai bảng trạng thái trực tiếp trong eBPF thông qua cấu trúc BPF_MAP_TYPE_LRU_HASH.
Khác với cơ chế khóa bucket toàn cục của Netfilter, bảng băm eBPF LRU (Least Recently Used) tự động thu hồi bộ nhớ theo cơ chế không khóa. Khi bảng đạt dung lượng tối đa, các phiên cũ không còn hoạt động sẽ bị dọn dẹp nhường chỗ cho luồng mới mà không gây rớt gói.
Đoạn mã phân loại lưu lượng eBPF gắn vào móc Traffic Control (clsact) dưới đây kiểm tra gói tin đường hầm và ghi nhận số liệu phiên:
#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <bpf/bpf_helpers.h>
struct flow_key {
__u32 src_ip;
__u32 dst_ip;
__u16 src_port;
__u16 dst_port;
__u8 protocol;
};
struct flow_metrics {
__u64 packets;
__u64 bytes;
__u64 last_seen;
};
/* Bảng băm LRU không khóa cho theo dõi lưu lượng lớn */
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 2097152); /* 2 triệu luồng */
__type(key, struct flow_key);
__type(value, struct flow_metrics);
} vpn_flow_table SEC(".maps");
SEC("tc")
int track_vpn_flows(struct __sk_buff *skb)
{
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
/* Kiểm tra biên: Tiêu đề Ethernet */
if (data + sizeof(struct ethhdr) > data_end)
return TC_ACT_OK;
struct ethhdr *eth = data;
if (eth->h_proto != __constant_htons(ETH_P_IP))
return TC_ACT_OK;
struct iphdr *iph = data + sizeof(struct ethhdr);
if ((void *)(iph + 1) > data_end || iph->protocol != IPPROTO_UDP)
return TC_ACT_OK;
struct udphdr *udph = (void *)iph + (iph->ihl * 4);
if ((void *)(udph + 1) > data_end)
return TC_ACT_OK;
/* Lọc lưu lượng VPN theo cổng dịch vụ */
if (udph->dest == __constant_htons(51820) || udph->source == __constant_htons(51820)) {
struct flow_key key = {
.src_ip = iph->saddr,
.dst_ip = iph->daddr,
.src_port = udph->source,
.dst_port = udph->dest,
.protocol = iph->protocol
};
struct flow_metrics *val = bpf_map_lookup_elem(&vpn_flow_table, &key);
if (val) {
__sync_fetch_and_add(&val->packets, 1);
__sync_fetch_and_add(&val->bytes, skb->len);
val->last_seen = bpf_ktime_get_ns();
} else {
struct flow_metrics init = {
.packets = 1,
.bytes = skb->len,
.last_seen = bpf_ktime_get_ns()
};
bpf_map_update_elem(&vpn_flow_table, &key, &init, BPF_NOEXIST);
}
}
return TC_ACT_OK;
}
char _license[] SEC("license") = "GPL";
Nhờ thao tác bpf_map_lookup_elem trên bảng LRU hoạt động theo ngữ nghĩa RCU không khóa, các lõi CPU cập nhật chỉ số đồng thời mà không bị nghẽn khóa chéo giữa các socket vi xử lý.
Bảng so sánh hiệu năng thực tế
Trong thử nghiệm tổng hợp trên hệ thống máy chủ kép AMD EPYC 9654 (192 lõi vật lý) chịu tải 5 triệu phiên UDP đồng thời, sự khác biệt giữa Netfilter conntrack truyền thống và bảng luồng eBPF không khóa thể hiện rõ rệt:
| Chỉ số hiệu năng | nf_conntrack truyền thống | Raw NOTRACK + eBPF LRU |
|---|---|---|
| Số luồng đồng thời tối đa | 1.048.576 (Giới hạn cứng) | 4.000.000+ (Điều phối LRU) |
| Thông lượng gói tin (Mpps) | 1,8 Mpps (Nghẽn khóa xoay) | 21,4 Mpps (Tốc độ dây) |
| Tải CPU ngắt mềm (softirq) | 82% Bão hòa lõi | 7% Tiêu thụ bình thường |
| Dung lượng RAM chiếm dụng | ~1,4 GB Cấp phát Slab | ~256 MB Bộ nhớ định sẵn |
| Hành vi khi đầy bảng | Hủy gói tin âm thầm (Lỗi) | Tự động loại bỏ phiên cũ nhất |
Ứng dụng thực chiến trên cổng Anycast RiceGuard
Kiến trúc mạng RiceGuard ứng dụng toàn diện cơ chế giải phóng connection tracking trên các cụm biên Anycast toàn cầu:
- Kháng tấn công làm cạn kiệt tài nguyên: Các chiến dịch tấn công từ chối dịch vụ (DDoS) thường giả mạo cổng UDP ngẫu nhiên nhằm làm tràn bảng conntrack. Đường truyền biên của RiceGuard xử lý gói tin phi trạng thái qua eBPF trước khi xác thực danh tính mã hóa Noise trong bộ nhớ, vô hiệu hóa hoàn toàn nguy cơ kiệt quệ tài nguyên bảng trạng thái.
- Độ trễ chuyển tiếp microgiây: Việc bỏ qua các móc Netfilter nặng nề và tranh chấp spinlock giúp duy trì thời gian chuyển tiếp gói qua đường hầm ổn định dưới 1,5 microgiây.
- Vận hành hạ tầng linh hoạt: Hệ thống định tuyến Linux và các tiến trình BGP Anycast tiếp tục hoạt động thông suốt, duy trì khả năng giám sát mạng thông qua số liệu eBPF mà không làm giảm tốc độ truyền tải gói tin.
Sự kết hợp giữa chỉ thị NOTRACK và cấu trúc bảng luồng eBPF không khóa mang lại khả năng mở rộng tuyến tính trên hệ thống máy chủ đa lõi, bảo đảm chất lượng kết nối ổn định cho dịch vụ VPN tải cao.



