خطرات نشت داده در روش‌های سنتی بررسی دوره‌ای مسیرها

در روترهای دارای چند اتصال اینترنت (Multi-WAN) و گیت‌وی‌های سازمانی VPN، پایداری ارتباط به سرعت شناسایی دگرگونی‌های فیزیکی بستگی دارد. هنگامی که یک لینک فیبر نوری قطع می‌شود، مودم ارتباطی سیگنال را از دست می‌دهد یا مهلت آدرس DHCP به پایان می‌رسد، جدول هدایت بسته‌ها در هسته لینوکس (Forwarding Information Base یا FIB) دچار نوسان می‌گردد.

در گذشته، بسیاری از اسکریپت‌ها از شیوه ارزیابی دوره‌ای (Polling) استفاده می‌کردند: فراخوانی دستور ip route show از طریق یک پردازه جانبی هر ۲ تا ۵ ثانیه یک‌بار، تجزیه متن خروجی یا ارسال بسته‌های پینگ دوره‌ای. این رویکرد در بسترهای امنیتی چالش‌های بزرگی به وجود می‌آورد:

  1. روزنه نشت ترافیک رمزنگاری‌نشده: در فاصله چند ثانیه‌ای میان قطع فیزیکی اتصال تا نوبت بعدی بررسی، برنامه‌ها همچنان بسته‌های خود را ارسال می‌کنند. چنانچه مسیر پیش‌فرض VPN ناگهان حذف شود یا سیستم به کارت شبکه ثانویه محافظت‌نشده سوییچ کند، بسته‌های شفاف و بدون رمزنگاری مستقیماً به بستر عمومی اینترنت ارسال می‌شوند.
  2. هزینه پردازشی تغییر مداوم کانتکست: اجرای مداوم پردازه‌های شل و بررسی رشته‌های متنی، توان پردازنده را هدر داده و در زمان ترافیک سنگین به نوسان تاخیر (Jitter) می‌انجامد.
  3. تداخل وضعیت هنگام قطعی‌های مقطعی: اگر وضعیت یک درگاه به سرعت قطع و وصل شود (Flapping)، سامانه بررسی دوره‌ای قوانین کهنه را ثبت کرده و سبب ایجاد حلقه‌های مسیریابی و قطع تونل‌ها می‌شود.

برای دستیابی به پاسخی آنی و بدون کوچک‌ترین نشت داده، سرویس‌های پیشرفته VPN مستقیماً با گذرگاه رویدادهای ناهمگام در هسته لینوکس تبادل داده می‌کنند: RTNetlink (NETLINK_ROUTE).

پروتکل Netlink یک سازوکار ارتباط میان‌پردارشی (IPC) در لینوکس است که به منظور تبادل وضعیت شبکه میان فضای هسته و برنامه‌های کاربردی از طریق سوکت‌های استاندارد BSD طراحی شده است (AF_NETLINK). بر خلاف رابط‌های قدیمی ioctl یا فایل‌های شاخه /proc، ساختار Netlink از ارسال رویدادهای چندپخشی (Multicast) به شکل بلادرنگ پشتیبانی می‌کند.

زیرپروتکل تخصصی RTNetlink، مدیریت کامل جدول‌های مسیریابی، وضعیت فیزیکی اینترفیس‌ها، آدرس‌های IP، جدول همسایگی (ARP/NDP) و صف‌های کنترل ترافیک (Traffic Control qdisc) را بر عهده دارد.

+-------------------------------------------------------------------+
|                     برنامه VPN در فضای کاربری                     |
|    - حلقه مدیریت رویدادها (epoll / kqueue)                        |
|    - تحلیل‌گر ساختار RTNetlink و ماشین وضعیت                      |
+-------------------------------------------------------------------+
                  ^                                    |
      جریان رویداد| گروه‌های چندپخشی      دستورات      | به‌روزرسانی
      ناهمگام     | (RTMGRP_LINK,         همگام        | اتمیک مسیر
     (RTM_NEWLINK,|  RTMGRP_IPV4_ROUTE)                | (NLM_F_REPLACE,
      RTM_NEWROUTE|                                    |  RTM_NEWROUTE)
                  |                                    v
+-------------------------------------------------------------------+
|                    هسته اصلی Netlink در لینوکس                    |
|                    (AF_NETLINK / NETLINK_ROUTE)                   |
+-------------------------------------------------------------------+
        ^                                            |
        | رویدادهای وضعیت سخت‌افزار                   | اعمال در جدول FIB
        |                                            v
+---------------------------+        +------------------------------+
| تغییر وضعیت کارت شبکه     |        | جدول هدایت بسته هسته         |
| (carrier on/off, netif_*) |        | (FIB IPv4 / IPv6, ip rules)  |
+---------------------------+        +------------------------------+

گروه‌های چندپخشی کلیدی برای نظارت بر شبکه

برای دریافت لحظه‌ای پیام‌ها بدون نیاز به ایجاد حلقه پردازشی مداوم، برنامه VPN یک سوکت Netlink ایجاد کرده و آن را به گروه‌های چندپخشی مورد نظر پیوند می‌دهد:

struct sockaddr_nl sa;
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;

int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
bind(fd, (struct sockaddr *)&sa, sizeof(sa));

پیام‌های اصلی دریافتی عبارتند از:

  • RTM_NEWLINK / RTM_DELLINK: بلافاصله پس از تغییر وضعیت مدیریتی کارت (IFF_UP)، قطع یا وصل سیگنال فیزیکی (IFF_RUNNING / IFLA_CARRIER) یا تغییر MTU مخابره می‌شود.
  • RTM_NEWADDR / RTM_DELADDR: هنگام اختصاص یا حذف آدرس IP توسط سرویس‌های DHCP، SLAAC یا به شکل دستی فرستاده می‌شود.
  • RTM_NEWROUTE / RTM_DELROUTE: با هر گونه افزودن، ویرایش یا حذف مسیر در جدول FIB هسته برای سوکت ارسال می‌گردد.

هنگامی که رخدادی در شبکه پیش می‌آید، داده‌ها در قالب ساختارهای پیاپی nlmsghdr در بافر سوکت قرار می‌گیرند. هر بسته شامل یک هدر اولیه و به دنبال آن زنجیره‌ای از ویژگی‌های مسیریابی (RtAttr) است:

+-------------------------+-------------------------+------------------------+
|  nlmsghdr (۱۶ بایت)     |   rtmsg (۱۲ بایت)       |  داده‌های rtattr...    |
|  - nlmsg_len            |   - rtm_family          |  - RTA_DST (مقصد)      |
|  - nlmsg_type           |   - rtm_table           |  - RTA_GATEWAY (گیت‌وی)|
|  - nlmsg_flags          |   - rtm_protocol        |  - RTA_OIF (درگاه خروج)|
|  - nlmsg_seq / pid      |   - rtm_scope           |  - RTA_PRIORITY (متریک)|
+-------------------------+-------------------------+------------------------+

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

ماکروهای استاندارد هسته (NLMSG_OK، NLMSG_NEXT، RTA_OK، RTA_NEXT) پیمایش بایت‌ها را بدون خطر خطای سرریز بافر تضمین می‌کنند:

char buffer[8192];
ssize_t len = recv(fd, buffer, sizeof(buffer), 0);

for (struct nlmsghdr *nh = (struct nlmsghdr *)buffer; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
    if (nh->nlmsg_type == NLMSG_DONE) break;
    if (nh->nlmsg_type == NLMSG_ERROR) { /* مدیریت خطا */ continue; }

    if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE) {
        struct rtmsg *rt = (struct rtmsg *)NLMSG_DATA(nh);
        struct rtattr *rta = (struct rtattr *)RTM_RTA(rt);
        int rta_len = RTM_PAYLOAD(nh);

        for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
            if (rta->rta_type == RTA_GATEWAY) {
                // دریافت آدرس گیت‌وی
            } else if (rta->rta_type == RTA_OIF) {
                int ifindex = *(int *)RTA_DATA(rta);
                // شناسه عددی کارت شبکه خروجی
            }
        }
    }
}

مدیریت سرریز بافر با خطای ENOBUFS

در زمان ناپایداری گسترده (مانند تغییر همزمان هزاران مسیر پروتکل BGP)، هسته ممکن است در کسری از ثانیه هزاران رویداد صادر کند. اگر برنامه برای چند میلی‌ثانیه پردازش را به تاخیر بیندازد، بافر سوکت پر شده و پیام‌های تازه حذف می‌شوند که پیامد آن بازگشت خطای -ENOBUFS خواهد بود.

طراحی استاندارد شامل دو مرحله است:

  1. افزایش حافظه سوکت: فعال‌سازی SO_RCVBUFFORCE (با مجوز CAP_NET_ADMIN) برای رساندن بافر به ۱۶ یا ۳۲ مگابایت:
    int rcvbuf = 16 * 1024 * 1024;
    setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf, sizeof(rcvbuf));
    
  2. همگام‌سازی دوباره از طریق NLM_F_DUMP: پس از رخ دادن خطای ENOBUFS، رشته تغییرات پیوسته دچار شکاف شده است. برنامه فوراً یک پیام درخواست کامل جدول (NLM_F_REQUEST | NLM_F_DUMP) با دستور RTM_GETROUTE می‌فرستد تا جدول FIB در حافظه بدون قطع شدن تونل‌ها بازسازی شود.

پیاده‌سازی سوییچینگ Multi-WAN بدون کوچک‌ترین نشت داده

برای اطمینان از اینکه هیچ داده‌ای در زمان بروز مشکل به صورت رمزنشده خارج نمی‌شود، فرآیند تغییر مسیر باید کاملاً اتمیک و تحت نظارت قوانین مسیریابی هسته (ip rule) صورت پذیرد.

[ ترافیک برنامه‌های سیستم ]
              |
              v
+-------------------------------------------------------------+
| قوانین مسیریابی سیاستی (ip rule):                          |
| - ترافیک نشان‌دار تونل (fwmark 0x51820) -> جدول اصلی 'main'|
| - سایر ترافیک‌های معمولی               -> جدول امن 'vpn'   |
+-------------------------------------------------------------+
                               |
               +---------------+---------------+
               |                               |
               v                               v
+-------------------------------+   +-------------------------+
| جدول ایزوله 'vpn':            |   | جدول عمومی 'main':      |
| - default dev wg0 با متریک ۵۰ |   | - default از خط اول     |
| - default blackhole متریک ۹۹۹ |   |   (eth0, 192.168.1.1)   |
+-------------------------------+   | - default از خط دوم     |
                                    |   (eth1, 10.0.0.1)      |
                                    +-------------------------+

۱. مسیر سیاهچاله پشتیبان (Blackhole Anchor)

پیش از بالا آوردن درگاه‌ها، برنامه یک مسیر سیاهچاله (blackhole) با اولویت پایین در جدول اختصاصی VPN ایجاد می‌کند:

# ایجاد مسیر پشتیبان سیاهچاله در جدول vpn
ip route add blackhole default table vpn metric 999

اگر درگاه تونل اصلی (wg0 یا riceguard0) قطع شود، هسته بی‌درنگ بسته‌های پس از آن را به این مسیر سیاهچاله می‌فرستد و بدون مصرف پردازنده آنها را از بین می‌برد تا هیچ داده‌ای از طریق درگاه‌های عمومی به بیرون نشت نکند.

۲. تعویض اتمیک مسیر با NLM_F_REPLACE

به جای استفاده از روش دو مرحله‌ای حذف مسیر کهنه و سپس ثبت مسیر نو (که شکاف زمانی خطرناکی پدید می‌آورد)، RTNetlink امکان تعویض در یک فراخوانی را می‌دهد. با فرستادن پیام RTM_NEWROUTE همراه با پرچم‌های NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE، اشاره‌گر مسیر در جدول FIB در کسری از میکروثانیه جابجا می‌شود.

۳. مدیریت پویای چندخطی در RiceGuard

در گیت‌وی‌های مجهز به پروتکل RiceGuard که روی دو اتصال همزمان کار می‌کنند، برنامه با بهره‌گیری از پایش سیگنال در RTNetlink فرآیند جابجایی را مدیریت می‌کند.

با صدور رویداد RTM_NEWLINK مبنی بر قطع سیگنال فیبر (IFLA_CARRIER برابر با صفر)، برنامه RiceGuard سوکت‌های UDP خارجی را ظرف ۴ میلی‌ثانیه از طریق SO_BINDTOIFINDEX به خط رادیویی پشتیبان منتقل می‌سازد. از آنجا که تونل‌های کاربران در لایه مجازی قرار دارند، نشست‌های TCP و مکالمات صوتی بدون افت کیفیت پابرجا می‌مانند.

مدیران شبکه می‌توانند رویدادهای زنده هسته را مستقیماً از طریق ابزارهای بومی لینوکس مشاهده کنند:

# پایش زنده تمام تغییرات لینک‌ها، آدرس‌ها و مسیرها
ip monitor all

# پایش تخصصی تغییرات جدول مسیرها با برچسب زمان دقیق
ip -ts monitor route

# بررسی صف پیام‌های Netlink و میزان بسته‌های از دست رفته
ss -u -a --netlink
cat /proc/net/netlink

در خروجی دستور /proc/net/netlink ستون Drops در ردیف پروتکل NETLINK_ROUTE (عدد صفر) را زیر نظر بگیرید. هر عدد بزرگتر از صفر نشان‌دهنده نیاز به افزایش حجم بافر سوکت است.

نکات اساسی در راه‌اندازی

  • جداسازی سوکت کنترل از سوکت رویداد: یک سوکت را تنها برای دریافت پیام‌های چندپخشی و سوکت دیگر را برای ارسال دستورات اختصاص دهید تا تداخلی در انتقال داده‌ها پیش نیاید.
  • تکیه بر شناسه عددی درگاه (ifindex): اینترفیس‌ها را همواره بر اساس عدد ثابت هسته رصد کنید نه نام‌های متنی نظیر eth0 تا تغییر نام کارت شبکه اختلالی ایجاد نکند.
  • ایزوله‌سازی جدول‌های مسیریابی (ip rule): هرگز مسیر پیش‌فرض VPN را در جدول عمومی main وارد نکنید و برای جلوگیری از خطاهای تصادفی از جدول‌های مجزا بهره بگیرید.

بهره‌گیری از مدل رویدادمحور RTNetlink، گیت‌وی‌های لینوکسی VPN را قادر می‌سازد در برابر هرگونه اختلال فیزیکی واکنشی فوری نشان داده و امنیت ترافیک را در بالاترین سطح نگه دارند.