تفرض إدارة بوابات شبكات VPN المركزية التي تخدم عشرات الآلاف من الأنفاق المتزامنة أعباء تشغيلية حادة على مكدس شبكات نواة لينكس. على الرغم من أن الخوادم متعددة المعالجات الحديثة تحتوي على ما بين 64 إلى 128 مسار معالجة عتادي، إلا أن حالات إسقاط الحزم تتكرر غالباً قبل الوصول إلى السعة القصوى لبطاقات الشبكة المادية. وفي البيئات ذات الكثافة العالية، لا يعود السبب الأساسي إلى العمليات التشفيرية أو حدود مسارات الذاكرة، بل إلى نظام تتبع الاتصالات التابع لـ Netfilter والمعروف باسم nf_conntrack.
صُمم نظام nf_conntrack في الأصل للجدران النارية ذات الحالة (Stateful Firewalls)، حيث يتولى الاحتفاظ بسجل الحالة لكل تدفق IP ثنائي الاتجاه يعبر نظام التشغيل. ومع ذلك، في بوابات VPN المركزة—حيث تتجدد ملايين جلسات UDP و TCP المؤقتة في كل دقيقة—يتسبب هذا التصميم في نزاع حاد على أقفال المغزل (spinlocks)، واستهلاك مفرط للذاكرة، وفقدان غير قابل للاسترجاع لحزم البيانات.
اختناق Conntrack: نزاع أقفال المغزل وتشبع الجداول
عند دخول كل حزمة IP إلى مكدس شبكة لينكس، يحسب Netfilter حاصل التجزئة (Hash) للقيم الخمس المميزة للحزمة (IP المصدر، IP الوجهة، منفذ المصدر، منفذ الوجهة، بروتوكول النقل) لتحديد أو إنشاء بنية struct nf_conn داخل جدول تجزئة عام:
[مسار التدفق الوارد الافتراضي مع Conntrack]
الحزمة الواردة ──> خطاف PRE_ROUTING ──> nf_conntrack_in()
│
حيازة قفل المغزل (nf_conntrack_locks) ◄── [نزاع حاد بين 64+ نواة معالجة]
│
البحث / حجز struct nf_conn (ذاكرة النواة: kmalloc-512)
│
تحرير القفل ──> التوجيه / فحص المسار ──> الخروج Egress
تحت وطأة حركة مرور VPN الكثيفة، يواجه هذا المسار ثلاث نقاط إخفاق رئيسية:
- تشبع الجداول وإسقاط الحزم الصامت: سعة الجدول مقيدة بالقيمة
net.netfilter.nf_conntrack_max. عندما يتجاوز عدد الاتصالات المتزامنة هذا السقف، تُطلق النواة التحذير الشهيرnf_conntrack: table full, dropping packet. وتُسقط حزم TCP SYN وحزم مصافحة WireGuard تلقائياً، مما يؤدي إلى انقطاع اتصالات المستخدمين. - النزاع الذري على أقفال المغزل بين الأنوية: يخضع جدول التجزئة لحماية مصفوفة من أقفال المغزل المجزأة (
nf_conntrack_locks). وعندما توزع بطاقات الشبكة الحزم على دزينات من أنوية المعالج عبر ميزة RSS، تتنافس مسارات المعالجة في وقت واحد لحيازة نفس القفل. يؤدي هذا إلى تذبذب خطوط الذاكرة المخبأة للمعالج، مما يرفع استهلاك مقاطعات البرمجيات (softirq) إلى 100% دون مردود فعلي. - تضخم ذاكرة النواة وتفتتها: يستهلك كل اتصال مسجل ما يقارب 320 إلى 384 بايتاً من ذاكرة النواة غير القابلة للترحيل (
kmalloc-512). وتتطلب إدارة 3 ملايين اتصال متزامن ما يزيد عن 1.2 جيجابايت من ذاكرة RAM الصافية للنواة، مسببة ضغطاً شديداً على جداول الصفحات وذاكرة TLB.
التخلص من أعباء Conntrack: قواعد NOTRACK التقريرية
يتمثل الحل العملي الأكثر فاعلية لحماية بوابات VPN في توجيه Netfilter لاستثناء حزم طبقة النقل الخاصة بالأنفاق من عمليات تتبع الحالة.
من خلال إضافة قواعد في جدول raw—الذي يعالج الحزم قبل بدء إجراءات تتبع الاتصال عند خطافي PREROUTING و OUTPUT—تتجنب النواة حجز سجلات الحالة لحزم النفق:
# ضبط nftables لاستثناء حركة 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 \; }
# وسم منفذ UDP رقم 51820 بقاعدة notrack
nft add rule inet raw prerouting udp dport 51820 notrack
nft add rule inet raw output udp sport 51820 notrack
وفي صيغة iptables التقليدية، يُطبق الضبط المطابق على جدول raw:
iptables -t raw -A PREROUTING -p udp --dport 51820 -j NOTRACK
iptables -t raw -A OUTPUT -p udp --sport 51820 -j NOTRACK
يسهم تطبيق NOTRACK في خفض استهلاك دورات المعالج بنسبة تصل إلى 28% لكل حزمة على طبقة UDP الخارجية. ومع إيقاف تتبع الاتصالات التقليدي، يُسند التدقيق الأمني إلى قواعد التصفية دون حالة أو إلى مسارات eBPF البرمجية المسرعة.
تتبع التدفقات داخل النواة دون أقفال باستخدام eBPF
للحفاظ على إدارة الجلسات وضبط معدلات التدفق وتوجيه المسارات دون التأثر بسلبيات nf_conntrack، تنقل المنظومات الحديثة جداول الحالات مباشرة إلى داخل النواة عبر خرائط BPF_MAP_TYPE_LRU_HASH في eBPF.
على عكس أقفال Netfilter العامة، تتولى جداول eBPF المستندة إلى خوارزمية الأقل استخداماً مؤخراً (LRU) تحرير الذاكرة دون أقفال تنافسية. وعند امتلاء الخريطة، يُستبدل أقدم تدفق غير نشط بالتدفقات الجديدة دون التضحية بأي حزمة بيانات مشروعة.
يوضح الكود التالي مصنف eBPF مرتبطاً بخطاف التحكم في حركة المرور (clsact) لتسجيل مؤشرات الجلسات:
#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;
};
/* جدول تجزئة LRU دون أقفال لحركة المرور العالية */
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 2097152); /* دعم مليوني تدفق متزامن */
__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;
/* فحص حدود ترويسة الإيثرنت */
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;
/* تصفية الحزم الموجهة لمنفذ VPN */
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";
نظراً لأن استعلام bpf_map_lookup_elem في خرائط LRU يعمل بآلية RCU دون أقفال متبادلة، تقوم أنوية المعالج بتحديث العدادات بالتوازي دون اختناق عبر مقابس المعالجات.
مقارنة معايير الأداء والإنتاجية
خلال اختبارات على خادم ثنائي المعالجات من طراز AMD EPYC 9654 (يحتوي على 192 نواة معالجة فعلية) محمل بـ 5 ملايين جلسة UDP متزامنة، تظهر المقارنة فارقاً بنيوياً واضحاً:
| مؤشر الأداء التقني | نظام nf_conntrack التقليدي | توليفة NOTRACK مع eBPF LRU |
|---|---|---|
| الحد الأقصى للجلسات | 1,048,576 (سقف ثابت) | 4,000,000+ (إدارة LRU ديناميكية) |
| معدل تمرير الحزم (Mpps) | 1.8 Mpps (عائق أقفال المغزل) | 21.4 Mpps (سرعة السلك القصوى) |
| إجهاد softirq على الأنوية | 82% تشبع كامل للمعالج | 7% تشغيل اعتيادي |
| ذاكرة النواة المستهلكة | ~1.4 جيجابايت تخصيص Slab | ~256 ميجابايت محجوزة مسبقاً |
| السلوك عند امتلاء الجدول | إسقاط الحزم (توقف الخدمة) | استبدال سلس لأقدم الجلسات |
التطبيق الميداني في بوابات شبكة RiceGuard Anycast
تعتمد شبكة RiceGuard على الاستغناء الكامل عن تتبع اتصالات النواة عبر خوادم Anycast الموزعة عالمياً:
- الحصانة ضد هجمات استنزاف الجداول: تستهدف هجمات حجب الخدمة الموزعة (DDoS) إغراق جداول conntrack بمنافذ عشوائية. تقوم بوابات RiceGuard بمعالجة الحزم دون حالة عبر eBPF قبل تدقيق هويات تشفير Noise في الذاكرة، مما يبطل مفعول هجمات الاستنزاف كلياً.
- زمن استجابة بمقياس الميكروثانية: من خلال تجنب خطافات Netfilter الثقيلة ونزاع الأقفال، يستقر زمن توجيه الحزم داخل الأنفاق عند مستوى أقل من 1.5 ميكروثانية.
- التوافق التام مع بيئة لينكس القياسية: تواصل برمجيات التوجيه وأنظمة BGP Anycast عملها المعتاد، مما يحافظ على وضوح المراقبة الشبكية دون التأثير على سرعة نقل البيانات.
يتيح استبدال آليات تتبع الاتصالات القديمة بقواعد NOTRACK وجداول eBPF الخالية من الأقفال توسعاً خطياً متكاملاً على الخوادم الحديثة، مما يوفر أداءً ثابتاً لخدمات VPN تحت أعتى أعباء التشغيل.



