تعمل بوابات الشبكات الخاصة الافتراضية عالية الأداء تحت وطأة معدلات تدفق هائلة لحزم البيانات. على واجهات الشبكة ذات السرعات المتعددة الجيجابت التي تعالج ملايين الحزم في الثانية عبر عشرات الآلاف من أنفاق العملاء المتزامنة، يجب على نواة نظام التشغيل مطابقة كل مخطط بيانات مشفر وارد بجلسة التشفير الخاصة به، وزوج المفاتيح المتماثل، ومدخل جدول التوجيه المقابل بدقة فورية.

في بروتوكول طبقة النقل غير المتصل UDP، لا تكشف ترويسات IP وUDP الخارجية سوى عن عنوان الطرف البعيد ومنفذ المصدر. ونظراً لأن أجهزة العملاء تتنقل باستمرار بين أبراج الهواتف المحمولة، وبوابات ترجمة عناوين الشبكة (NAT)، والشبكات المحلية، لا تستطيع النواة الاعتماد على عنوان IP أو منفذ المصدر لتحديد الجلسة النشطة. بالإضافة إلى ذلك، فإن التحقق من المفاتيح العامة غير المتماثلة عند وصول كل حزمة يتطلب عمليات حسابية باهظة تؤدي إلى تشبع ناقل الذاكرة ومسارات الذاكرة المؤقتة لوحدة المعالجة المركزية.

لحل معضلة توجيه الحزم هذه دون المساس بالأمان التشفيري، يطبق WireGuard جدول تجزئة فهارس عديم الأقفال داخل نواة النظام مباشرة (drivers/net/wireguard/index_hashtable.c). من خلال تخصيص فهارس عشوائية مشفرة غير قابلة للتخمين بطول 32 بت لعمليات المصافحة وأزواج مفاتيح نقل البيانات، يحقق البروتوكول سرعة مطابقة للجلسات في زمن ثابت $O(1)$ دون أي تنافس على أقفال الدوران (spinlocks) عبر المعالجات متعددة الأنوية.

+-----------------------------------------------------------------------------+
|              مسار توجيه الفهارس الداخلي لبروتوكول WIREGUARD في النواة       |
|                                                                             |
| إطار كبسولة UDP الوارد                                                      |
| [ ترويسة IP | ترويسة UDP | النوع (4B) | فهرس المستقبل (4B) | حمولة مشفرة ]  |
|                                            |                                |
|                                            v                                |
|                                 دخول منطقة rcu_read_lock()                  |
|                                            |                                |
|                                            v                                |
|                       +-----------------------------------+                 |
|                       |    دالة التجزئة index_hashtable   |                 |
|                       |   h = siphash_1u32(receiver_index,|                 |
|                       |                    secret_key)    |                 |
|                       +-----------------------------------+                 |
|                                            |                                |
|                                            v                                |
|                       +-----------------------------------+                 |
|                       |     بحث الحاوية وفك مرجع RCU      |                 |
|                       |     تكرار عناصر قائمة hlist_head  |                 |
|                       +-----------------------------------+                 |
|                                            |                                |
|                         +------------------+------------------+             |
|                         |                                     |             |
|                         v                                     v             |
|                [ تم العثور على المفتاح ]              [ الفهرس غير مطابق ]  |
|                         |                                     |             |
|                         v                                     v             |
|              فك تشفير ChaCha20-Poly1305             إسقاط صامت للحزمة       |
|              التحقق من العداد المتصاعد              (goto drop)             |
|              توجيه الحزمة عبر Cryptokey Routing               |             |
|                         |                                     v             |
|                         +----------------------------> rcu_read_unlock()   |
+-----------------------------------------------------------------------------+

معوقات تحديد الجلسات في هياكل VPN التقليدية

تعتمد بروتوكولات الأنفاق التقليدية في مطابقة الحزم الواردة على آلات حالات معقدة أو استعلامات قواعد بيانات تستهلك موارد مكثفة. في بنية IPsec القياسية (RFC 4301)، يعتمد البحث عن رابطة الأمان (SA) على فهرس معلمات الأمان (SPI) بطول 32 بت. ومع ذلك، يتطلب IPsec مطابقة الحزم مع قاعدتي بيانات في آن واحد: قاعدة بيانات روابط الأمان (SAD) وقاعدة بيانات سياسات الأمان (SPD). مع توسع عدد الأنفاق، يؤدي التقييم التسلسلي لمرشحات تصنيف الحزم إلى زمن تأخير ملموس.

أما تطبيقات OpenVPN التاريخية فتقوم بتجميع اتصالات العملاء عبر مقبس UDP فردي في مساحة المستخدم. عند وصول حزمة مشفرة، يقرأ التطبيق الأحادي المخزن المؤقت عبر استدعاء نظام POSIX ثم يجري بحثاً في جدول تجزئة بذاكرة المستخدم. وفي الأنظمة متعددة الأنوية، تتنافس خيوط المعالجة على أقفال الاستبعاد المتبادل (mutex) التي تحمي جدول الاتصالات. يؤدي هذا التنافس إلى هدر دورات المعالج في تبديل السياق وجدولة الخيوط، مما يفرض سقفاً منخفضاً للسرعة لا يبلغ الطاقة الاستيعابية للرابط الفيزيائي.

كما أن هياكل البحث البسيطة تكون عرضة لهجمات التعقيد الخوارزمي، المعروفة بهجمات Hash-DoS. فإذا استخدم نفق الشبكة دالة تجزئة غير مقترنة بمفتاح سري (مثل خوارزميات Jenkins أو CRC32)، يستطيع المهاجم توليد آلاف المعرفات المزيفة المصممة عمداً لتصطدم في نفس حاوية التجزئة. وعندما تتراكم آلاف المدخلات في قائمة مرتبطة فردية، تنحدر كفاءة البحث من $O(1)$ إلى $O(N)$. ومع التدفق الكثيف للحزم، تستهلك النواة كامل طاقة المعالجة في المرور على القوائم المتشابكة، مما يسبب إسقاطاً للحزم وتعطلاً في خدمة المستخدمين المصرح لهم.

تحليل ملف index_hashtable.c: تقنية RCU وحماية خوارزمية SipHash

يتجاوز بروتوكول WireGuard كلاً من اختناقات القفل وهجمات التعقيد الخوارزمي عبر فصل تخزين الجلسات في جدول تجزئة مخصص داخل النواة. في ملف drivers/net/wireguard/index_hashtable.c، يتم تعريف بنية البيانات بضمانات عزل واضحة:

struct index_hashtable {
    struct hlist_head *handshake_hash;
    struct hlist_head *keypair_hash;
    u32 mask;
    siphash_key_t key;
    spinlock_t lock;
};

يفصل هذا الجدول بصرامة بين تتبع مصافحات التأسيس ونقل بيانات الجلسات الجارية، مانعاً أي تداخل بين عمليات تبادل المفاتيح وفك تشفير حمولات البيانات.

1. توليد فهارس الجلسات العشوائية تشفيرياً

أثناء بدء المصافحة، يولد الطرف البادئ عدداً صحيحاً شبه عشوائي بطول 32 بت يُدعى sender_index. ويولد الطرف المجيب فهرس receiver_index المستقل الخاص به. وعند إنشاء حزم البيانات اللاحقة، يدرج كل طرف الفهرس المخصص له من الطرف الآخر مباشرة في ترويسة الرسالة عند الإزاحة الرابعة:

+---------------+---------------------+------------------------+
|   النوع (4B)  |   فهرس المستقبل     | عداد الحزم (8 بايت)    |
|   بايت واحد   |       4 بايت        | تصاعدي بشكل رتيب صارم  |
+---------------+---------------------+------------------------+
|             الحمولة المشفرة ووسم التحقق Poly1305             |
+---------------------------------------------------------------+

ونظراً لاختيار الفهارس بتوزيع متجانس تماماً ضمن مساحة $2^{32}$، فإن التخمين بطريقة القوة الغاشمة مستحيل رياضياً، مما يمنع الأطراف الخارجية من حقن حزم مزيفة في الجلسات الحية.

2. توزيع الحاويات الآمن ضد التصادم باستخدام SipHash

لمنع هجمات Hash-DoS، يحسب WireGuard مواقع حاويات التجزئة باستخدام الدالة شبه العشوائية SipHash (siphash_1u32). وأثناء تهيئة واجهة الشبكة، يولد برنامج التشغيل مفتاحاً سرياً عشوائياً بطول 128 بت (key) من مجمع العشوائية في النواة:

static inline struct hlist_head *
keypair_bucket(struct index_hashtable *table, u32 index)
{
    u32 hash = siphash_1u32(index, &table->key);
    return &table->keypair_hash[hash & table->mask];
}

بما أن مفتاح SipHash معزول في ذاكرة النواة المحمية ومخصص لكل واجهة افتراضية، لا يمكن لأي مهاجم خارجي معرفة الفهارس التي ستؤدي إلى تصادم في حاوية واحدة. ويبقى توزيع المدخلات خاضعاً لمتوسط توزيع بواسون، مما يضمن أداء بحث ثابت في زمن $O(1)$ بصرف النظر عن حجم حركة المرور.

3. مسار القراءة عديم الأقفال تماماً عبر RCU

يكمن الإنجاز الهندسي الأبرز لبروتوكول WireGuard في نموذج التزامن القائم على آلية Read-Copy Update (RCU). يتم توجيه ومطابقة كل حزمة واردة بالكامل داخل المقاطع الحرجة لقراءة RCU:

struct noise_keypair *
wg_index_hashtable_lookup_keypair(struct index_hashtable *table,
                                  enum noise_keypair_side side, u32 index)
{
    struct noise_keypair *keypair;
    rcu_read_lock();
    hlist_for_each_entry_rcu(keypair, keypair_bucket(table, index), entry) {
        if (keypair->entry[side].index == index) {
            if (unlikely(!kref_get_unless_zero(&keypair->refcount)))
                keypair = NULL;
            rcu_read_unlock();
            return keypair;
        }
    }
    rcu_read_unlock();
    return NULL;
}

في مسار القراءة، لا يكلف استدعاء rcu_read_lock() أي استهلاك ملحوظ لدورات المعالج (حيث يقوم فقط بتعطيل استباق النواة مؤقتاً على النواة المحلية). ولا يتم استخدام أي أقفال دوران عمومية أو إشارات حجب. وتستطيع عشرات الأنوية في وحدة المعالجة المركزية قراءة جدول الفهارس في نفس الوقت دون إحداث ارتداد في خطوط الذاكرة المخبأة.

تقتصر عمليات القفل الداخلي table->lock على إجراءات التعديل فقط — مثل إدراج زوج مفاتيح جديد أو حذف جلسة منتهية. وبما أن توليد الجلسات يحدث فقط أثناء المصافحات الدورية (كل بضع دقائق)، فإن عمليات الكتابة لا تمثل سوى أقل من 0.01% من إجمالي حركة الشبكة.

دورة حياة المفاتيح واسترجاع الذاكرة الآمن

يجب أن تدير بروتوكولات الأنفاق تجديد المفاتيح (rekeying) دون فقدان للحزم أو أخطاء في الذاكرة. وفي WireGuard، يحتفظ كل طرف بما يصل إلى ثلاثة أزواج مفاتيح متزامنة أثناء فترات الانتقال: المفتاح السابق (previous)، والمفتاح الحالي (current)، والمفتاح القادم (next).

معيار الحالة جدول فهارس المصافحة جدول فهارس مفاتيح النقل
هيكل C المرتبط struct noise_handshake struct noise_keypair
طريقة توليد المعرف رمز عشوائي تشفيري 32 بت رمز عشوائي تشفيري 32 بت
دالة استعلام الحاوية handshake_bucket(table, index) keypair_bucket(table, index)
تزامن القراءة rcu_read_lock() / بدون أقفال rcu_read_lock() / بدون أقفال
الحماية من التصادم دالة SipHash بمفتاح الواجهة السري دالة SipHash بمفتاح الواجهة السري
آلية تحرير الذاكرة إزالة فورية عند اكتمال الربط استرجاع مؤجل وآمن (kfree_rcu)

عندما يبدأ زوج مفاتيح جديد، تُدرج النواة فهرس receiver_index الجديد في keypair_hash. وتستمر الحزم المشفرة بالمفتاح السابق بالوصول وفك التشفير بسلاسة عبر الفهرس القديم. وبعد انقضاء فترة الانتقال، يتم فصل زوج المفاتيح القديم من الجدول عبر hlist_del_rcu().

لا يمكن تحرير الذاكرة فوراً لأن خيوط المعالجة على الأنوية الأخرى قد تكون في نفس اللحظة بصدد فك تشفير حزم متدفقة تستند إلى ذلك الفهرس. لذلك يسند WireGuard مهمة الإلغاء إلى kfree_rcu(). وتنتظر نواة لينكس حتى تمر جميع أنوية المعالج بفترة سكون كاملة (quiescent state) قبل تحرير الذاكرة الفعلي، مما يقضي تماماً على ثغرات الاستخدام بعد التحرير (Use-After-Free).

المراقبة التشغيلية وضبط ذاكرة النواة المؤقتة

يمكن لمهندسي الشبكات ومسؤولي أنظمة لينكس التحقق من أداء مقابس WireGuard وكفاءة جداول النواة باستخدام أدوات ss وperf.

مراقبة سعة المخازن المؤقتة لمقبس UDP وحركة الحزم على منفذ WireGuard:

# فحص طوابير الاستقبال لمقبس UDP على منفذ WireGuard
ss -u -a -m -p | grep 51820

متابعة معالجة مقاطعات الشبكة البرمجية وتوزيعها عبر أنوية المعالج:

# تحليل استهلاك الأنوية أثناء معالجة مقاطعات شبكة الاتصال
perf top -s comm,symbol -C 0-3

تعديل معلمات ذاكرة النواة لاستيعاب التدفقات الشبكية الضخمة:

# رفع الحد الأقصى للمخازن المؤقتة لاستقبال وإرسال الحزم
sysctl -w net.core.rmem_max=67108864
sysctl -w net.core.wmem_max=67108864
sysctl -w net.ipv4.udp_rmem_min=16384

قابلية التوسع والأداء في بنية شبكة VPNRice

تتطلب إدارة بنية تحتية عالمية لحماية الخصوصية الرقمية تدعم مئات الآلاف من الاتصالات المتزامنة سرعة استجابة حتمية وكفاءة تدفق عالية. وتعتمد شبكة VPNRice في جميع محطاتها الطرفية على آليات التوجيه بلا أقفال:

  1. توجيه جلسات RiceGuard بلا أقفال: بالاعتماد على جداول تجزئة RCU داخل النواة، تعالج بوابات VPNRice ملايين الحزم في الثانية دون أي تأخير ناشئ عن تنافس الخيوط، موفرة سرعة مطابقة لسرعة العتاد الفيزيائي.

  2. مناعة ذاتية ضد هجمات Hash-DoS: تُنشئ كل عقدة في شبكة VPNRice مفاتيح تشفير SipHash فريدة لكل مساحة أسماء شبكية، مما يمنع محاولات إغراق الجداول بالاصطدامات الحسابية.

  3. تجديد سلس للمفاتيح دون انقطاع الحزم: تضمن إدارة الفهارس المزدوجة أثناء تدوير مفاتيح السرية التوجيهية التامة (PFS) فك تشفير الحزم المتدفقة دون أي تقطع أو فقدان في البيانات.

بفضل الفهارس العشوائية 32 بت، وحماية SipHash، وقدرات RCU الخالية من الأقفال في النواة، توفر شبكات VPN الحديثة استقراراً فائقاً وإنتاجية عالية لمواجهة متطلبات الشبكات المؤسسية المتطورة.