عنق الزجاجة للطابور الأحادي في واجهات الشبكة الافتراضية

تتعامل مجمعات VPN المؤسسية وبوابات الشبكات المعرفة برمجياً وموجهات الحافة الحديثة مع كميات هائلة من البيانات عبر روابط شبكية فائقة السرعة تصل إلى 40 GbE و 100 GbE. وعلى الرغم من أن بطاقات واجهة الشبكة الفيزيائية (NIC) تدعم منذ سنوات طويلة آليات تعدد الطوابير العتادية (Receive Side Scaling / RSS)، إلا أن واجهات الشبكة الافتراضية في مساحة المستخدم غالباً ما تظل مقيدة بالإعدادات الأحادية التقليدية.

في نظام التشغيل لينكس، تعد آلية Universal TUN/TAP عبر المسار /dev/net/tun المعيار الأساسي لتبادل حزم بروتوكول IP من الطبقة الثالثة (Layer 3) بين برمجيات مساحة المستخدم ومكدس شبكة النواة. في التصاميم التقليدية السابقة، تفتح الخدمة ملف /dev/net/tun لمرة واحدة فقط، لتحصل على واصف ملف (file descriptor) وحيد وتنشئ واجهة شبكة واحدة مثل tun0.

عندما يتجاوز معدل تدفق البيانات عدة غيغابت في الثانية، تصطدم هذه المعمارية ذات الطابور الوحيد بعوائق هيكلية حادة:

  1. تسلسل المعالجة على نواة المعالج رقم 0: نظراً لوجود واصف ملف وحيد لقراءة وكتابة إطارات الواجهة الافتراضية، تنحصر معالجة الحزم بالكامل على نواة المعالج المركزية التي تنفذ خيط الإدخال والإخراج هذا. حتى في الخوادم الضخمة التي تحتوي على 64 نواة معالجة، تصل النواة 0 إلى نسبة تشغيل 100%، بينما تظل بقية الأنوية الـ 63 في حالة خمول تام.
  2. التنافس الحاد على أقفال spinlock في tun.c: في واجهة TUN ذات الطابور الواحد، تتزامن كافة عمليات الاستقبال والإرسال عبر قفل دوراني واحد في النواة (tun->lock). وعندما تحاول خيوط المعالجة المتعددة القراءة أو الكتابة بالتزامن عبر هذا الواصف، تؤدي عمليات تبديل السياق والتنافس على القفل إلى تدهور حاد في إنتاجية تعليمات المعالج.
  3. فقدان مكاسب التوزيع العتادي للبطاقة: تقوم بطاقات الشبكة الفيزيائية بتوزيع الحزم الواردة عبر حلقات استقبال عتادية متعددة (RX rings) باستخدام دالة التجزئة Toeplitz. ولكن عند حشر هذه التدفقات المتوازية في واجهة افتراضية أحادية الخيط، تتلاشى تماماً الفائدة من معالجة العتاد المتوازية.

للتغلب على هذا القيد الجذري، قدمت نواة لينكس بدءاً من الإصدار 3.8 دعم ميزة IFF_MULTI_QUEUE لنظام TUN/TAP الفرعي.

البنية الداخلية لنظام IFF_MULTI_QUEUE في نواة لينكس

تعيد معمارية تعدد الطوابير صياغة العلاقة الهيكلية بين أجهزة الشبكة وواصفات الملفات في النواة. فبدلاً من التقيد الصارم بربط واحد لواحد بين struct net_device وجهاز المحارف، يتيح علم IFF_MULTI_QUEUE ربط عدة واصفات ملفات مستقلة (struct tun_file) بالواجهة الشبكية الافتراضية الأساسية نفسها (struct tun_struct).

+-------------------------------------------------------------------------+
|                  محرك VPN متعدد الخيوط في مساحة المستخدم               |
+-------------------------------------------------------------------------+
|  خيط العمل 0       |  خيط العمل 1       |  ...   |  خيط العمل N-1       |
|  (تثبيت على نواة 0)|  (تثبيت على نواة 1)|        |  (تثبيت على نواة N-1)|
+--------------------+--------------------+--------+----------------------+
       |                    |                                 |
       | fd_0               | fd_1                            | fd_n-1
       v                    v                                 v
+-------------------------------------------------------------------------+
|                واصفات ملفات مستقلة ومنفصلة (/dev/net/tun)               |
+-------------------------------------------------------------------------+
       |                    |                                 |
       v قراءة/كتابة        v قراءة/كتابة                     v قراءة/كتابة
+-------------------------------------------------------------------------+
|                         نظام TUN الفرعي في النواة                       |
|                                                                         |
|  +--------------+  +--------------+              +--------------+       |
|  | tun_file [0] |  | tun_file [1] |     ...      | tun_file[n-1]|       |
|  | طابور المقبس |  | طابور المقبس |              | طابور المقبس |       |
|  +--------------+  +--------------+              +--------------+       |
|         \                 |                             /               |
|          \                |                            /                |
|           v               v                           v                 |
|   +-----------------------------------------------------------------+   |
|   |             struct tun_struct (net_device: tun0)                |   |
|   |         - توجيه التدفقات عبر دالة skb_get_hash()                |   |
|   |         - توزيع الطوابير بدون أقفال عبر مصفوفة RCU              |   |
|   +-----------------------------------------------------------------+   |
+-------------------------------------------------------------------------+
       |                                                    ^
       v إرسال الحزم للشبكة                                 | استقبال الحزم
+-------------------------------------------------------------------------+
|                    مكدس توجيه IP و Netfilter في نواة لينكس              |
+-------------------------------------------------------------------------+

1. تحديد الطابور بدون أقفال باستخدام تقنية RCU

في الملف المصدري للنواة drivers/net/tun.c، تتم إدارة الطوابير النشطة عبر مصفوفة مؤشرات محمية بآلية Read-Copy-Update (RCU): struct tun_file __rcu *tfiles[MAX_TAP_QUEUES]. وعند توجيه حزمة صادرة نحو tun0، تحدد النواة الطابور المستهدف دون الحاجة لحجز أقفال متبادلة عامة:

// منطق اختيار الطابور داخل ملف tun.c في نواة لينكس
static struct tun_file *tun_get_queue(struct tun_struct *tun, struct sk_buff *skb)
{
    u32 hash = skb_get_hash(skb);
    u16 numqueues = READ_ONCE(tun->numqueues);
    u16 index = reciprocal_scale(hash, numqueues);
    return rcu_dereference(tun->tfiles[index]);
}

بفضل فهرسة RCU الخالية من الأقفال، يتوسع توجيه الحزم بشكل خطي متناسق مع عدد أنوية المعالج المتاحة.

2. التوجيه المتناظر لتدفقات الحزم

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

التنفيذ البرمجي لتخصيص الطوابير المتعددة بلغة C

لتهيئة واجهة TUN متعددة الطوابير، ينبغي لبرمجيات مساحة المستخدم فتح المسار /dev/net/tun بشكل مستقل لكل خيط عمل مخصص. وبعدها ينفذ كل خيط استدعاء ioctl(TUNSETIFF) بتمرير اسم الجهاز نفسه مع تضمين علم IFF_MULTI_QUEUE.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <errno.h>

/**
 * ربط واصف ملف مستقل بواجهة TUN متعددة الطوابير
 * @param dev اسم الواجهة الشبكية (مثل "tun0")
 * @return واصف الملف المخصص عند النجاح، أو -1 عند حدوث خطأ
 */
int tun_alloc_mq(const char *dev)
{
    struct ifreq ifr;
    int fd;

    // فتح نسخة مستقلة من جهاز المحارف /dev/net/tun
    if ((fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK)) < 0) {
        perror("فشل فتح /dev/net/tun");
        return -1;
    }

    memset(&ifr, 0, sizeof(ifr));

    // طلب نمط TUN للطبقة 3، ودعم تعدد الطوابير وتجريد الترويسات الزائدة
    ifr.ifr_flags = IFF_TUN | IFF_MULTI_QUEUE | IFF_NO_PI;

    if (dev && *dev) {
        strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
    }

    // تسجيل واصف الملف لدى واجهة النواة الافتراضية
    if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
        perror("فشل استدعاء ioctl(TUNSETIFF)");
        close(fd);
        return -1;
    }

    return fd;
}

الأعلام التكميلية الأساسية: IFF_NO_PI و IFF_VNET_HDR

لتحقيق أقصى قدر من سرعة المعالجة عبر خيوط العمل، يدمج علم IFF_MULTI_QUEUE مع علمين رئيسيين:

  • IFF_NO_PI (بدون معلومات الحزمة الإضافية): يضيف مشغل TUN افتراضياً 4 بايتات كترويسة زائدة أمام كل إطار. وتفعيل IFF_NO_PI يحذف هذه الترويسة، مما يتيح محاذاة حزم IP الخام مباشرة داخل مخازن الذاكرة للتشفير الشعاعي SIMD.
  • IFF_VNET_HDR (ترويسة الشبكات الافتراضية Virtio): يفعل آليات تقسيم وتجميع الحزم العامة (GSO و GRO) بين مساحة المستخدم والنواة. وبدلاً من تداول حزم مفردة بحجم MTU 1500 بايت، يمكن للتطبيق تداول حزم عملاقة مجمعة تصل إلى 64 كيلوبايت، مما يخفض عبء استدعاءات النظام بنسبة تصل إلى 75%.

الإرشادات المعمارية الأساسية لبوابات VPN متعددة الطوابير

يتطلب تشغيل IFF_MULTI_QUEUE في البيئات الإنتاجية الكبرى تكاملاً دقيقاً بين إعدادات النواة، وجدولة المعالجات، وبنية الشبكة.

حركة المرور المشفرة الواردة (بطاقة شبكة 100GbE)
                      |
                      v التوزيع العتادي RSS
+-------------------------------------------------------------------+
|               فك التشفير وتوجيه التدفقات داخل النواة              |
+-------------------------------------------------------------------+
       |                      |                      |
       v نواة 0               v نواة 1               v نواة 2
+---------------+      +---------------+      +---------------+
| tun0 طابور 0  |      | tun0 طابور 1  |      | tun0 طابور 2  |
| fd_0          |      | fd_1          |      | fd_2          |
+---------------+      +---------------+      +---------------+
       |                      |                      |
       v                      v                      v
+---------------+      +---------------+      +---------------+
| تثبيت نواة 0  |      | تثبيت نواة 1  |      | تثبيت نواة 2  |
| خيط RiceGuard |      | خيط RiceGuard |      | خيط RiceGuard |
+---------------+      +---------------+      +---------------+

1. التثبيت الدقيق لكل خيط على نواة مخصصة

لضمان زمن استجابة مستقر يقل عن جزء من الميلي ثانية، ينبغي إسناد كل واصف طابور إلى خيط معالجة مستقل ومثبت على نواة معالج محددة باستخدام pthread_setaffinity_np(). يمنع ذلك تنقل الخيوط بين الأنوية، ويحافظ على كفاءة الذاكرة المخبئية L1/L2.

2. ضبط سعة طابور الإرسال في النواة

يحافظ كل واصف tun_file على مخزن استقبال مؤقت خاص به داخل النواة. وفي أوقات الذروة المفاجئة، قد يؤدي الحجم الافتراضي إلى فقدان صامت للحزم. لذلك يُوصى برفع طول طابور الإرسال (txqueuelen):

# زيادة سعة طابور إرسال الحزم للواجهة متعددة الطوابير
ip link set dev tun0 txqueuelen 10000

3. التكامل مع مضاعفة الإدخال والإخراج غير المتزامنة

يراقب كل خيط عمل واصف الملف المخصص له عبر آليات إشعار الأحداث المستندة إلى الحافة (epoll مع EPOLLET) أو حلقات io_uring. وتقوم الخيوط بقراءة الحزم على دفعات عبر القراءة الشعاعية (readv أو recvmmsg)، مما يضمن تفريغ المخازن المؤقتة فوراً أثناء فترات التدفق المكثف.

توسيع مسارات البيانات متعددة الأنوية في RiceGuard

في البنية التحتية الموزعة لشبكة VPNRice، يعتمد محرك RiceGuard على IFF_MULTI_QUEUE لإنشاء مسارات معالجة غير حاجبة وخالية من الأقفال عبر مجموعات الخوادم عالية الكثافة.

  1. طوبولوجيا الطوابير المتوافقة مع بنية NUMA: يكتشف محرك RiceGuard حدود عقد NUMA وقنوات الذاكرة العشوائية تلقائياً. ويتم تخصيص الطوابير بتناظر وربطها بالأنوية التي تشترك في ناقل PCIe المحلي، متفادياً الاختناقات في ممرات الربط البيني QuickPath / UltraPath.
  2. المعالجة التجميعية الشعاعية دون نسخ مضاعف: عبر دمج واصفات TUN متعددة الطوابير مع علم IFF_VNET_HDR والخوارزميات المشفرة الموجهة بتعليمات AVX-512 / NEON، يحقق RiceGuard معدل معالجة يتجاوز 25 مليون حزمة في الثانية على عتاد الخوادم القياسي.
  3. العزل الأمني الكامل عبر فضاءات أسماء الشبكة: يتم إنشاء كل واجهة TUN متعددة الطوابير مباشرة داخل فضاء أسماء شبكي مستقل (Network Namespace) في لينكس. ويمنع هذا التصميم أي تسرب للبيانات غير المشفرة نحو جداول التوجيه الافتراضية أو الواجهات العامة، مما يضمن أعلى معايير الخصوصية إلى جانب الأداء التشغيلي الفائق.