گیت‌وی‌های سازمانی VPN و رله‌های لبه‌ای توزیع‌شده در محیط‌های شبکه‌ای بسیار پرفشار و پرتقاضا فعالیت می‌کنند. زیر بار ترافیکی مداوم شامل میلیون‌ها بسته در ثانیه (Mpps)، مهندسان امنیت به مشاهده‌پذیری عمیق و بلادرنگ نیاز دارند: ردیابی موج‌های غیرعادی دست‌دادن (Handshake)، حسابرسی رویدادهای امنیتی و ارزیابی نوسانات تأخیر به ازای هر جریان بدون کاستن از توان عملیاتی هدایت بسته‌ها. در معماری‌های نوین شبکه در لینوکس، فیلترهای بسته تعمیم‌یافته برکلی (eBPF) و مسیر داده پرسرعت (XDP) ستون‌های اصلی این لایه نظارتی هستند.

با این وجود، استخراج داده‌های تله‌متری از فضای هسته (kernel space) به برنامه‌های پس‌زمینه در فضای کاربر (user space) همواره با چالش‌های ساختاری چشمگیری روبه‌رو بوده است. برای سال‌های متوالی، سازوکار استاندارد بر پایه بافر رویداد perf در eBPF موسوم به (BPF_MAP_TYPE_PERF_EVENT_ARRAY) استوار بود. با اینکه این روش برای بارهای پردازشی متوسط کارآمد بود، بافرهای perf در سرورهای چند‌هسته‌ای امروزی دچار ناکارآمدی‌های حادی می‌شوند: تورم شدید حافظه رم فیزیکی، ناسازگاری حافظه پنهان میان حلقه‌های مستقل هر پردازنده و به هم ریختن ترتیب رویدادها که تحلیل توالی زمانی بسته‌ها را دشوار می‌سازد.

برای رفع این محدودیت‌های ریشه‌ای، هسته لینوکس ساختار BPF Ring Buffer (BPF_MAP_TYPE_RINGBUF) را معرفی کرد. این ساختار داده‌ای بدون قفل (lockless) و نگاشت‌شده در حافظه، به‌طور ویژه برای انتقال جریانی رویدادها در سرعت خط فیزیکی طراحی شده است. با ارائه یک استخر حافظه مشترک، سازوکار رزرو بدون کپی (zero-copy) و تضمین ترتیب زمانی دقیق در کل سیستم، بافر حلقه‌ای BPF امکان پایش عمیق رفتار هسته را با کمترین بار پردازشی ممکن می‌سازد.


بحران مقیاس‌پذیری تله‌متری: محدودیت‌های بافرهای سنتی BPF Perf

شناخت برتری‌های BPF Ring Buffer نیازمند بازبینی کاستی‌های معماری در مدل پیشین بافرهای perf است. هنگامی که یک برنامه eBPF به هوک‌های XDP، کنترل ترافیک (tc) یا نقاط ردیابی هسته متصل می‌شود، ساختارهای تله‌متری شامل وضعیت اتصالات را ایجاد می‌کند. در مدل سنتی، هسته یک حلقه حافظه دایره‌ای مجزا به ازای هر هسته فعال CPU روی میزبان تخصیص می‌داد.

این رویکرد جداسازی پردازنده‌ای به سه گلوگاه اساسی ختم می‌شد:

  1. مصرف بیش از حد حافظه: از آنجا که توزیع ترافیک روی هسته‌های CPU به‌ندرت کاملاً یکدست است، مدیران ناچارند ظرفیت هر حلقه جداگانه را برای مهار اوج بار ناگهانی تعیین کنند. در یک گیت‌وی ۱۲۸ هسته‌ای، در نظر گرفتن ۱۶ مگابایت بافر برای هر هسته به قفل شدن ۲ گیگابایت حافظه رم فیزیکی منجر می‌شود، حتی اگر ۹۰ درصد هسته‌ها در حالت بیکار باشند.
  2. برهم خوردن توالی زمانی رویدادها: چون هر هسته پردازشی یک حلقه کاملاً مستقل دارد، پردازش فضای کاربر هنگام خواندن رویدادها از طریق چند توصیف‌کننده epoll با رویدادهای درهم‌ریخته مواجه می‌شود. بازسازی توالی دقیق فرآیندهای دست‌دادن نیازمند الگوریتم‌های پیچیده و سنگین مرتب‌سازی در فضای کاربر است.
  3. هزینه مضاعف کپی داده در حافظه: ارسال رویداد از طریق bpf_perf_event_output() مستلزم تخصیص ساختار روی پشته BPF (یا در آرایه per-CPU)، پر کردن فیلدها و سپس کپی مجدد داده‌ها به درون بافر حلقه‌ای perf بود.
مقایسه معماری BPF Perf Buffer و BPF Ring Buffer مدرن:

معماری سنتی (بافرهای مستقل به ازای هر پردازنده):
[ هسته 0 ] ──► [ حلقه Perf 0 (۱۶ مگابایت) ] ──► (رویدادها با ترتیب نامرتب)
[ هسته 1 ] ──► [ حلقه Perf 1 (۱۶ مگابایت) ] ──► (مرتب‌سازی سنگین در user space)
[ هسته N ] ──► [ حلقه Perf N (۱۶ مگابایت) ] ──► (اتلاف بسیار بالای رم فیزیکی)

معماری مدرن BPF Ring Buffer:
[ هسته 0 ] ──┐
[ هسته 1 ] ──┼──► [ یک بافر حلقه‌ای مشترک BPF ] ──► [ دیمن ناظر در فضای کاربر ]
[ هسته N ] ──┘    (نگاشت Mmap بدون قفل، ترتیب دقیق،   (خواندن سریع Zero-Copy)
                   استخر پویای حافظه مشترک)

معماری BPF Ring Buffer در لینوکس: حافظه یکپارچه و جریان بدون قفل

ساختار BPF_MAP_TYPE_RINGBUF که توسط آندری ناکریایکو در لینوکس 5.8 عرضه شد، حلقه‌های اختصاصی هر CPU را با یک صف حافظه دایره‌ای یکپارچه از نوع چندتولیدکننده تک‌مصرف‌کننده (MPSC) جایگزین می‌کند. برنامه‌های چندگانه BPF که هم‌زمان روی هسته‌های مختلف اجرا می‌شوند، می‌توانند رویدادها را به‌صورت امن و بدون نیاز به قفل‌های سراسری به این بافر مشترک بفرستند.

این بافر از آرایش حافظه نوآورانه‌ای بهره می‌برد که از طریق فراخوانی سیستم mmap() به فضای کاربر نگاشت می‌شود. با دو بار نگاشت پی‌درپی صفحات داده در فضای آدرس مجازی، هسته به‌کلی مسئله انتهای حلقه (wrap-around) را حل کرده است؛ بدین ترتیب پردازش مصرف‌کننده می‌تواند رکوردهای با طول متغیر را به‌طور پیوسته و بدون محاسبات دشوار اشاره‌گر حلقه‌ای بخواند.

آرایش صفحات مجازی دوگانه (Double Virtual Page):

فضای آدرس مجازی:
┌───────────────────────┬───────────────────────┐
│ صفحات فیزیکی داده     │ صفحات آینه‌ای داده    │ (خوانش پیوسته از مرز گردش حلقه
│ (طول = N)             │ (همان صفحات فیزیکی)   │  بدون ایجاد بریدگی در رکورد)
└───────────────────────┴───────────────────────┘
0                       N                       2N

1. صف حلقه‌ای نگاشت‌شده در حافظه و ثبات دقیق ترتیب زمانی

به دلیل اینکه تمامی هسته‌ها داده‌های خود را در یک توالی واحد درج می‌کنند، BPF Ring Buffer ترتیب زمانی سراسری رویدادها را تضمین می‌نماید. چنانچه هسته ۴ بسته‌ای از WireGuard را در لحظه $t_1$ پردازش کند و هسته ۱۲ رویداد مرتبط با آن را در زمان $t_2$ ثبت نماید، این دو رکورد دقیقاً پشت سر هم در بافر مشترک جای می‌گیرند.

این پایداری زمانی به موتورهای تشخیص نفوذ و تحلیلگران ترافیک در فضای کاربر امکان می‌دهد تا جریان‌های رویداد را به‌صورت خطی و بدون نیاز به صف‌های اولویت یا بافرهای مرتب‌سازی مجدد پردازش کنند.

2. سازوکار رزرو بدون کپی: bpf_ringbuf_reserve و Submit

بارزترین نوآوری در BPF Ring Buffer رابط برنامه‌نویسی رزرو بدون کپی دو مرحله‌ای است. به‌جای ساخت رویداد در پشته BPF و انتقال مجدد آن به بافر، برنامه BPF مستقیماً فضای مورد نیاز را درون حلقه رزرو می‌کند:

/* رزرو حافظه بدون کپی در فضای هسته */
struct vpn_event *event;

event = bpf_ringbuf_reserve(&telemetry_ring, sizeof(*event), 0);
if (!event) {
    /* بافر تکمیل است؛ رد محتاطانه رویداد */
    return 0;
}

/* تکمیل داده‌ها مستقیماً در حافظه رزرو شده */
event->src_ip = iph->saddr;
event->dst_ip = iph->daddr;
event->bytes  = skb->len;
event->timestamp = bpf_ktime_get_ns();

/* ثبت نهایی رکورد و مشاهده‌پذیری آنی در فضای کاربر */
bpf_ringbuf_submit(event, 0);

با تخصیص مستقیم فضا در حلقه از طریق bpf_ringbuf_reserve()، کپی‌های واسط حافظه کاملاً منتفی می‌شوند. همچنین اگر اعتبارسنجی پس از مرحله رزرو ناموفق باشد، برنامه BPF می‌تواند با فراخوانی bpf_ringbuf_discard() بدون آسیب به یکپارچگی صف خواندن، رکورد را باطل کند.


مقایسه Perf Buffer با BPF Ring Buffer: بنچمارک‌های خرد و مصرف منابع

در مسیریاب‌های لبه شبکه که ترافیک پایداری معادل 10 Mpps را مدیریت می‌کنند، تفاوت‌های عملکردی بافرهای perf و BPF Ring Buffer در بهره‌وری حافظه و توان کلی به وضوح نمایان است.

مقایسه شاخص‌های کلیدی عملکرد:

شاخص                    BPF Perf Buffer             BPF Ring Buffer
────────────────────────────────────────────────────────────────────────
مدل حافظه               حلقه‌های جداگانه هر هسته     استخر مشترک و یکپارچه MPSC
میزان اشغال رم          تعداد هسته‌ها * اندازه بافر  استخر ثابت سراسری (مثلاً ۶۴ مگابایت)
کپی حافظه               کپی دوگانه (پشته به حلقه)   Zero-copy (تخصیص بدون کپی)
توالی زمانی رویدادها    تنها در سطح هر هسته         ترتیب زمانی قطعی و سراسری
سربار سیگنال‌دهی         تکرار بالای وقفه‌های پردازنده بیداری تطبیقی هوشمند (Adaptive epoll)

1. مصرف حافظه رم و محلی‌سازی حافظه پنهان در سرورهای چند‌هسته‌ای

در سرورهای ۶۴ و ۱۲۸ هسته‌ای نوین AMD EPYC و Intel Xeon، صرفه‌جویی در رم بلافاصله خود را نشان می‌دهد. رله‌ای که از BPF Ring Buffer با حجم ۶۴ مگابایت استفاده می‌کند، در مهار جهش‌های ناگهانی ترافیک توانمندتر از راه‌حل بافر perf با مصرف ۲ گیگابایت حافظه فیزیکی عمل می‌کند.

به دلیل اشتراک پویای حافظه، اگر هجوم ناگهانی بسته‌ها روی هسته ۳ متمرکز شود، این هسته می‌تواند از کل ظرفیت ۶۴ مگابایتی بهره گیرد. در مقابل، در سامانه perf، هسته ۳ بلافاصله پس از پر شدن سقف ۱۶ مگابایتی خود بسته‌ها را رها می‌کرد، حتی اگر سایر هسته‌ها در بی‌کاری کامل به سر می‌بردند.

2. سیگنال‌دهی بیداری تطبیقی: تعادل میان تأخیر Epoll و بار پردازنده

تحت بار ترافیکی شدید، ارسال وقفه از هسته به فضای کاربر برای تک‌تک رویدادها سبب تعویض مداوم متن اجرا (context switch) و اتلاف توان پردازشی می‌شود. BPF Ring Buffer این معضل را با سازوکار سرکوب تطبیقی هشدار برطرف ساخته است:

  • در حین تخلیه فعال داده‌ها توسط پردازش فضای کاربر، bpf_ringbuf_submit() درمی‌یابد که مصرف‌کننده بیدار است و سیگنال‌های وقفه را مهار می‌کند.
  • زمانی که مصرف‌کننده تمام داده‌ها را خواند و در epoll_wait() به خواب رفت، بافر حلقه‌ای به صورت خودکار سیگنال‌دهی را فعال می‌کند تا با اولین ورودی جدید پردازش را بیدار نماید.

طرح عملیاتی: استقرار BPF Ring Buffer در رله‌های پرسرعت

به‌کارگیری BPF Ring Buffer در پروکسی‌های فعال VPN نیازمند هماهنگی میان نگاشت‌های BPF و چرخه مصرف در فضای کاربر است:

  1. تعریف نگاشت BPF: تعریف نقشه در کد منبع C با ابعاد متناسب (الزاماً توانی از ۲ در تعداد صفحات حافظه):
struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 64 * 1024 * 1024); /* ۶۴ مگابایت بافر مشترک */
} telemetry_ring SEC(".maps");
  1. مصرف‌کننده بافر حلقه‌ای در فضای کاربر: بهره‌گیری از توابع مدیریت بافر در کتابخانه libbpf مانند (ring_buffer__new() و ring_buffer__poll()) جهت پردازش مستقیم و بدون کپی رویدادها.
  2. همگام‌سازی رویدادها: اتصال توصیف‌کننده فایل حلقه به حلقه epoll اصلی سرویس پروکسی، تا پردازش ورودی/خروجی سوکت‌های شبکه و تله‌متری به طور هماهنگ انجام پذیرد.
مسیر جریان پردازش تله‌متری:

[ هوک هسته XDP / TC ] ──► bpf_ringbuf_reserve() ──► درج داده در ساختار
                                                              │
                                                              ▼
                                                     bpf_ringbuf_submit()
                                                              │
                                                              ▼
                                               [ حافظه بافر حلقه‌ای مشترک ]
                                                              │
                                                              ▼ (epoll / mmap)
                                               [ دیمن مصرف‌کننده فضای کاربر ]
                                                              │
                                                              ▼
                                               [ متریک‌های زنده امنیت شبکه ]

رقابت چندهسته‌ای و کاهش فشار معکوس

با اینکه BPF Ring Buffer بر پایه دستورات اتمی و بدون قفل (atomic_add بر روی اشاره‌گرهای نوشتن) ساخته شده است، در سرورهای ۱۲۸ هسته‌ای با فشار کاری بسیار شدید، چنانچه در هر میکروثانیه ده‌ها هزار رویداد ثبت شوند، امکان بروز تداخل در خطوط حافظه پنهان (cache-line bouncing) وجود دارد.

تیم‌های فنی این چالش را با استراتژی‌های فیلتر هدفمند برطرف می‌نمایند:

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

معماری RiceGuard: تله‌متری بلادرنگ تهدیدات و مشاهده‌پذیری شبکه

سرویس VPNRice ساختار Linux BPF Ring Buffer را به عنوان عنصری بنیادین در سیستم پایش و مهار تهدیدات RiceGuard به کار گرفته است. در کلاسترهای لبه‌ای بین‌المللی که میزبان هزاران تونل پرسرعت چندگیگابیتی هستند، تشخیص زودهنگام حملات تخلیه منابع، سیل تقاضاهای دست‌دادن غیرواقعی و انحرافات مسیریابی نقشی تعیین‌کننده در تداوم خدمات ایفا می‌کند.

با استقرار نقاط سنجش در مسیر عبور داده هسته از طریق BPF Ring Buffer، رله‌های RiceGuard تله‌متری را بدون افت بسته و بدون تحمیل بار مضاعف بر پردازنده با نرخ کامل خط فیزیکی ثبت می‌کنند. این جریان داده هماهنگ و منسجم مستقیماً به سامانه‌های خودکار دفاعی ارسال می‌شود تا پایداری ارتباط کاربران را بدون خلل در حریم خصوصی دیجیتال آنان حفظ نماید.