درگاه‌های شبکه خصوصی مجازی با پهنای باند گیگابیتی تحت فشار نرخ ورودی بسیار بالای بسته‌های داده کار می‌کنند. روی رابط‌های چندگیگابیتی که میلیون‌ها بسته را در هر ثانیه میان ده‌ها هزار تونل فعال پردازش می‌کنند، هسته سیستم‌عامل باید هر دیتاگرام رمزشده ورودی را با کمترین تاخیر به نشست رمزنگاری، جفت‌کلید متقارن و ورودی جدول مسیریابی مربوطه متصل کند.

در پروتکل لایه انتقال بدون اتصال UDP، سرآیندهای بیرونی IP و UDP تنها نشانی نقطه دوردست و درگاه مبدا را نشان می‌دهند. از آنجا که دستگاه‌های کلاینت به طور مداوم میان دکل‌های مخابراتی، درگاه‌های NAT و شبکه‌های محلی جابه‌جا می‌شوند، هسته سیستم‌عامل نمی‌تواند برای تشخیص هویت نشست به IP یا درگاه مبدا اتکا کند. افزون بر این، بررسی کلید عمومی در هر بسته ورودی نیازمند پردازش‌های نامتقارن سنگینی است که گذرگاه حافظه و خطوط حافظه نهان پردازنده را به اشباع می‌رساند.

برای برطرف کردن این چالش هدایت بسته‌ها بدون کاهش امنیت رمزنگاری، وایرگارد یک جدول درهم‌سازی شاخص بدون قفل را مستقیما درون هسته پیاده‌سازی کرده است (drivers/net/wireguard/index_hashtable.c). با اختصاص شاخص‌های ۳۲ بیتی شبه‌تصادفی و غیرقابل پیش‌بینی به مراحل دست‌تکانی و جفت‌کلیدهای انتقال داده، وایرگارد به زمان جستجوی ثابت $O(1)$ دست می‌یابد؛ بدون اینکه در پردازنده‌های چند‌هسته‌ای نیازی به قفل‌های چرخشی (spinlock) داشته باشد.

+-----------------------------------------------------------------------------+
|             خط لوله هدایت شاخص درون هسته‌ای پروتکل وایرگارد                 |
|                                                                             |
| فریم کپسوله‌شده ورودی 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()   |
+-----------------------------------------------------------------------------+

چالش‌های شناسایی نشست در ساختارهای سنتی شبکه

پروتکل‌های تونل‌سازی سنتی بسته‌های ورودی را از طریق ماشین‌های حالت پیچیده یا پایگاه‌های داده سنگین شناسایی می‌کنند. در ساختار IPsec مطابق استاندارد RFC 4301، جستجوی توافقنامه امنیتی بر پایه شناسه ۳۲ بیتی شاخص پارامترهای امنیتی (SPI) است. اما IPsec نیازمند تطبیق بسته‌ها با دو پایگاه داده مستقل است: پایگاه توافقنامه‌های امنیتی (SAD) و پایگاه سیاست‌های امنیتی (SPD). با افزایش تعداد ارتباطات، بررسی ترتیبی فیلترهای مسیریابی تاخیرهای شدیدی بر ترافیک تحمیل می‌کند.

در ساختارهای متداول OpenVPN، اتصال‌های کلاینت اغلب روی یک سوکت منفرد UDP در فضای کاربری نگهداری می‌شوند. با ورود هر بسته، فرایند برنامه باید با فراخوانی سیستمی داده را دریافت کرده و در جدول فضای کاربری به جستجوی نشست بپردازد. در سرورهای چند‌هسته‌ای، رشته‌های پردازشی بر سر قفل‌های همگام‌سازی (mutex) رقابت می‌کنند. این رقابت منجر به اتلاف توان پردازشی در تعویض زمینه و افت شدید ظرفیت عبور ترافیک می‌گردد.

ساختارهای ابتدایی درهم‌سازی در برابر حملات پیچیدگی الگوریتمی (Hash-DoS) نیز آسیب‌پذیرند. اگر تونل شبکه از توابع درهم‌سازی فاقد کلید امنیتی (مانند Jenkins یا CRC32) استفاده کند، نفوذگر می‌تواند هزاران شناسه ساختگی تولید کند که همگی به یک باکت مشترک برخورد می‌کنند. هنگامی که زنجیره یک باکت طولانی شود، مرتبه زمانی جستجو از $O(1)$ به $O(N)$ سقوط می‌کند. با ارسال ترافیک سنگین، هسته ناچار می‌شود تمام زمان خود را صرف پیمایش زنجیره‌ها کند و بسته‌های معتبر کاربران دچار افت شدید خواهند شد.

کالبدشکافی index_hashtable.c: فناوری RCU و دفاع SipHash

پروتکل وایرگارد چالش‌های رقابت قفل و حملات الگوریتمی را با جداسازی نشست‌ها در جدول اختصاصی هسته حل می‌کند. در کد منبع 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;
};

این ساختار پیگیری دست‌تکانی‌ها را از جفت‌کلیدهای تبادل ترافیک تفکیک می‌کند تا ایجاد ارتباط‌های جدید هیچ تداخلی با رمزگشایی سریع داده‌های در جریان نداشته باشد.

۱. تولید شناسه‌های ۳۲ بیتی تصادفی

هنگام شروع دست‌تکانی، فرستنده یک شناسه ۳۲ بیتی شبه‌تصادفی به نام sender_index ایجاد می‌کند. طرف مقابل نیز به نوبه خود یک شناسه ۳۲ بیتی receiver_index می‌سازد. در تمام بسته‌های داده بعدی، هر دو طرف شاخص اعلام‌شده توسط طرف مقابل را در بخش سرآیند بسته در موقعیت بایت چهارم قرار می‌دهند:

+---------------+---------------------+------------------------+
|   نوع (0x04)  |     شاخص گیرنده     | شمارنده داده (۸ بایت)  |
|    یک بایت    |       ۴ بایت        | یکنواخت اکیداً افزایشی |
+---------------+---------------------+------------------------+
|           بار مفید رمزشده و شناسه اعتبارسنجی Poly1305          |
+---------------------------------------------------------------+

از آنجا که این شاخص‌ها به طور یکنواخت از فضای $2^{32}$ تولید می‌شوند، حدس زدن آنها توسط مهاجم غیرممکن است و امکان تزریق داده ساختگی به نشست‌های فعال سلب می‌گردد.

۲. توزیع بدون تصادم با تابع SipHash

برای خنثی‌سازی حملات Hash-DoS، وایرگارد با استفاده از تابع SipHash (siphash_1u32) موقعیت باکت را محاسبه می‌کند. در زمان راه‌اندازی هر رابط شبکه مجازی، درایور هسته یک کلید ۱۲۸ بیتی کاملاً سری (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)$ تحت ترافیک انبوه تضمین می‌گردد.

۳. مسیر خواندن داده بدون هیچ‌گونه قفل با RCU

دلیل بازدهی بی‌نظیر وایرگارد در هدایت داده‌ها، بهره‌گیری از الگوی همزمانی 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 هستند. از آنجا که این عملیات فقط هر چند دقیقه یک‌بار در فواصل دست‌تکانی رخ می‌دهد، بخش نوشتن کمتر از ۰.۰۱ درصد از کل فرآیند شبکه را شامل می‌شود.

چرخه حیات نشست و آزادسازی حافظه با امنیت بالا

پروتکل‌های مدرن باید تعویض کلید (rekeying) را بدون از دست رفتن بسته‌ها و تداخل حافظه مدیریت کنند. در وایرگارد هر گره در طول دوره گذار تا سه جفت‌کلید را به صورت همزمان حفظ می‌کند: کلید پیشین (previous)، کلید جاری (current) و کلید آینده (next).

بعد وضعیت جدول شاخص دست‌تکانی جدول شاخص کلیدهای انتقال
ساختار زبان C struct noise_handshake struct noise_keypair
روش تولید شناسه توکن ۳۲ بیتی شبه‌تصادفی توکن ۳۲ بیتی شبه‌تصادفی
تابع جستجوی باکت 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() از جدول جدا می‌شود.

حافظه این ساختار را نمی‌توان فوراً آزاد کرد زیرا هسته‌های دیگر پردازنده ممکن است همزمان در حال پردازش بسته‌های در جریان باشند. وایرگارد این وظیفه را به kfree_rcu() می‌سپارد. هسته لینوکس تا سپری شدن دوره آرامش (quiescent state) تمام هسته‌ها صبر می‌کند و سپس حافظه را آزاد می‌سازد، که خطر آسیب‌پذیری‌های استفاده پس از آزادسازی (Use-After-Free) را کاملا ریشه‌کن می‌کند.

پایش کارایی و پیکربندی بافرهای هسته در سیستم

مدیران لینوکس می‌توانند عملکرد سوکت‌ها و صف بسته‌ها را در سامانه وایرگارد بررسی و بهینه‌سازی نمایند.

بررسی وضعیت بافرهای دریافت UDP و اطمینان از عدم ریزش بسته‌ها:

# بررسی حافظه سوکت و صف بسته‌های ورودی پورت وایرگارد
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 در تمامی گره‌های لبه‌ای خود از هدایت بدون قفل شاخص‌ها بهره می‌برد:

۱. مسیربندی بدون قفل در RiceGuard: با به‌کارگیری جداول درهم‌سازی RCU در هسته، گره‌های VPNRice میلیون‌ها بسته رمزشده را بدون درگیری قفل‌های پردازنده و با سرعت سخت‌افزاری شبکه هدایت می‌کنند.

۲. ایمنی در برابر حملات Hash-DoS: هر گره اختصاصی VPNRice کلیدهای نمک‌گذاری SipHash را در سطوح ایزوله تولید کرده و مانع از اختلال الگوریتمی در جداول ارتباطی می‌گردد.

۳. تعویض کلید مداوم بدون افت بسته: مدیریت همزمان شاخص‌های چندگانه تضمین می‌کند که در جریان تعویض کلیدهای پنهان‌داری پیشرو (PFS)، بسته‌های در حال انتقال بدون نوسان و ریزش پردازش شوند.

ترکیب شاخص‌های ۳۲ بیتی تصادفی، تابع محافظتی SipHash و مکانیزم بدون قفل RCU در هسته، عملکردی پایدار، کم‌تاخیر و قابل اعتماد را در زیرساخت‌های نوین شبکه مهیا می‌سازد.