انتقال دادههای رمزگذاریشده با پهنای باند بالا نیازمند هماهنگی دقیق میان سازوکارهای سوکت شبکه و صفهای پردازش بسته در پایینترین لایههای سیستم است. دیمنهای متداول شبکههای خصوصی مجازی عمدتاً در فضای کاربری (user space) اجرا میشوند و بستههای داده را از طریق رابطهای کاراکتری مجازی TUN/TAP منتقل میکنند. در چنین ساختاری، هر بسته رمزگذاریشده دستکم دو بار تغییر زمینه میان هسته و کاربر را تجربه میکند و دادهها مکرراً میان بافرهای برنامه و ساختارهای بافر شبکه هسته لینوکس (sk_buff) کپی میشوند.
پروتکل WireGuard با استقرار کامل درون پشته شبکه هسته لینوکس این موانع ساختاری را برطرف میکند. بخش محوری این معماری در فایل منبع drivers/net/wireguard/socket.c قرار دارد. این زیرسیستم اختصاصی وظیفه ایجاد، اتصال پورت، تسهیم چندگانه و مسیریابی سوکتهای UDP را مستقیماً در فضای هسته مدیریت میکند. به جای تلقی سوکت به عنوان نقاط انتهایی برنامههای کاربردی، WireGuard سوکتهای بومی هسته را به مسیر مستقیم دریافت دادههای شبکه متصل میسازد.
چرخه حیات سوکت هسته و راهاندازی پشته دوگانه (Dual-Stack)
هنگامی که یک رابط کاربری WireGuard (مانند wg0) ایجاد میشود، ماژول هسته ساختارهای رمزنگاری را تخصیص داده و تابع wg_socket_init() را فراخوانی میکند. برخلاف برنامههای معمولی که به فراخوانهای سیستمی استاندارد POSIX (socket() و bind()) وابستهاند، WireGuard با توابع داخلی هسته تعامل برقرار میکند:
int wg_socket_init(struct wg_device *wg, u16 port)
{
struct net *net = dev_net(wg->dev);
struct socket *new_4 = NULL, *new_6 = NULL;
struct udp_port_cfg port_4 = {
.family = AF_INET,
.local_ip.s_addr = htonl(INADDR_ANY),
.local_udp_port = htons(port),
.use_saddr = false
};
#if IS_ENABLED(CONFIG_IPV6)
struct udp_port_cfg port_6 = {
.family = AF_INET6,
.local_ip6 = IN6ADDR_ANY_INIT,
.local_udp_port = htons(port),
.use_saddr = false,
.ipv6_v6only = true
};
#endif
/* تخصیص سوکتهای کرنل IPv4 و IPv6 روی یک پورت شنود یکسان */
ret = udp_sock_create(net, &port_4, &new_4);
if (ret < 0)
return ret;
#if IS_ENABLED(CONFIG_IPV6)
ret = udp_sock_create(net, &port_6, &new_6);
if (ret < 0) {
sock_release(new_4);
return ret;
}
#endif
/* ثبت تابع رهگیری مستقیم کپسولهسازی */
setup_udp_tunnel_sock(net, new_4, &tunnel_cfg);
setup_udp_tunnel_sock(net, new_6, &tunnel_cfg);
return 0;
}
طراحی دوگانه دو اشارهگر مستقل struct socket را درون یک فضای نام شبکه یکسان اختصاص میدهد: یکی برای IPv4 (AF_INET) و دیگری برای IPv6 (AF_INET6). در هستههایی که از IPv6 پشتیبانی میکنند، پارامتر ipv6_v6only = true تعیین میشود. این پیکربندی از ابهام نگاشت آدرسهای IPv4 روی IPv6 جلوگیری کرده و تضمین میکند که جریانهای داده در مسیرهای مجزا بدون سربار تبدیل پروتکل پردازش شوند.
هر دو سوکت به یک پورت UDP معین متصل میشوند. در صورت مشخص شدن مقدار 0 برای پورت، هسته یک پورت موقت برای سوکت اول انتخاب کرده و دقیقاً همان پورت را به سوکت دوم اختصاص میدهد تا رفتار اتصال پایدار بماند.
رهگیری بستهها بدون کپی داده از طریق udp_tunnel_sock
در مدل سنتی، ورود یک بسته UDP باعث بیداری ریسه نظارتی epoll میشود تا دادهها در صف دریافت قرار گرفته و با recvmsg() خوانده شوند. در ترافیک سنگین، این رویه موجب تاخیر و کاهش کارایی کش پردازنده میگردد.
پروتکل WireGuard با ثبت یک مدیریتکننده کپسولهسازی از طریق setup_udp_tunnel_sock()، صف دریافت سنتی UDP را حذف میکند. در این ساختار اشارهگر بازخوانی encap_rcv = wg_receive مستقیماً تنظیم میشود.
درایور کارت شبکه فیزیکی (Ring Buffer)
│
▼ (NAPI poll / NET_RX_SOFTIRQ)
ip_rcv() / ip6_rcv()
│
▼
udp_rcv()
│
▼
┌───────────────────────────────────────┐
│ udp_tunnel_sock_cfg.encap_rcv │ <--- رهگیری مستقیم درون هسته
│ (wg_receive در drivers/net/wireguard) │ (بدون جابجایی فضای کاربری)
└───────────────────────────────────────┘
│
▼
اعتبارسنجی کوکی و مک (MAC)
│
▼
صفهای رمزگشایی موازی (padata)
│
▼
انتقال wg_xmit() به wg0
هنگامی که دیتگرام ورودی به udp_rcv() در هسته شبکه میرسد، پشته بررسی میکند که آیا قلاب کپسولهسازی وجود دارد یا خیر. با شناسایی encap_rcv، پشته بلافاصله تابع wg_receive(sk, skb) را در زمینه وقفه نرمافزاری (NET_RX_SOFTIRQ) اجرا میکند.
این سازوکار مزایای فنی بنیادینی به همراه دارد:
- عدم کپی دادهها (Zero-Copy): ساختار بافر شبکه (
skb) از لحظه دریافت توسط درایور سختافزاری تا پایان رمزگشایی در حافظه پیوسته هسته دستنخورده باقی میماند. - ارزیابی آنی بسته: هدر ۴ بایتی پیام فوراً بررسی شده و توکنهای کوکی و وضعیت همگامسازی Noise پیش از مصرف منابع پردازنده برای محاسبات ChaCha20-Poly1305 اعتبارسنجی میشوند.
- حذف قفلهای متداخل: عدم نگهداری بستهها در صفهای عمومی سوکت، احتمال مسدود شدن منابع و پدیده تورم بافر (bufferbloat) را از بین میبرد.
مهار حلقههای مسیریابی با استفاده از Fwmark
یکی از مخاطرات رایج در پیکربندی تونلهای امن، ایجاد حلقه مسیریابی (routing loop) است: اگر بستهای پس از رمزگذاری به کارت شبکه فیزیکی هدایت شود اما جدول مسیریابی پیشفرض (0.0.0.0/0) همان بسته UDP را مجدداً به داخل تونل بازگرداند، سیستم دچار بازگشت بیپایان شده و با اتمام حافظه متوقف میشود.
در فایل socket.c، این مسئله در لایه خروجی سوکت با برچسبگذاری بسته توسط فایروال (fwmark) و جستجوی محدود مسیر حل شده است:
static struct dst_entry *wg_socket_route4(struct wg_device *wg,
struct sockaddr_in *dst,
u32 fwmark)
{
struct flowi4 fl = {
.daddr = dst->sin_addr.s_addr,
.saddr = 0,
.flowi4_proto = IPPROTO_UDP,
.flowi4_mark = fwmark,
.flowi4_flags = FLOWI_FLAG_ANYSRC
};
struct rtable *rt = ip_route_output_key(dev_net(wg->dev), &fl);
return &rt->dst;
}
هنگام ارسال بستههای دستتکانی یا ترافیک رمزگذاریشده، wg_socket_send_skb_to_peer() برچسب incoming_handshake_fwmark را در فیلد مسیریابی درج میکند. مدیران سیستم با تعیین قوانین مسیریابی مبتنی بر خطمشی (ip rule add not fwmark 51820 table 51820) اطمینان حاصل میکنند که تمام بستههای UDP به صورت مستقیم به درگاه فیزیکی هدایت شده و به جدول تونل وارد نشوند.
علاوه بر این، زیرسیستم سوکت در صورت مشخص بودن مبدا خروجی، سوکت را به اینترفیس سختافزاری مشخصی مقید میسازد (sk_bound_dev_if).
مدیریت بهینه بافر sk_buff هنگام ارسال
زیرسیستم socket.c بازتخصیص ساختارهای sk_buff را در هنگام ارسال به حداقل میرساند. وقتی یک بسته IP خروجی از طریق wg_xmit() وارد wg0 میشود، فضای مورد نیاز برای سرآیندها از پیش محاسبه میگردد:
- ۱۶ بایت برای برچسب اصالت Poly1305.
- ۱۶ بایت برای سرآیند انتقال WireGuard.
- ۸ بایت برای هدر UDP.
- ۲۰ بایت برای سرآیند IPv4 (یا ۴۰ بایت برای IPv6).
با برآورد فضاهای بالاسری (headroom) و انتهایی (tailroom)، تابع skb_cow_head() اندازه بافر موجود را گسترش میدهد. دادهها در همان محل رمزگذاری شده و توسط udp_tunnel_xmit_skb() مستقیماً تحویل صف کارت شبکه فیزیکی میشوند.
بهینهسازیهای صنعتی در زیرساخت RiceGuard
ساختار پیشفرض WireGuard از یک جفت سوکت برای هر رابط استفاده میکند. در گرههای پرتراکم Anycast سیستم RiceGuard که صدها هزار اتصال همزمان را پردازش میکنند، اشتراک یک سوکت واحد میان هستهها باعث رقابت در خطوط کش حافظه میشود.
زیرساخت RiceGuard بهینهسازیهای زیر را در سطح کرنل اعمال کرده است:
- وابستگی سوکت با مکانیسم RSS/RPS: تقسیم جریانهای بسته ورودی بر اساس هش آدرس مبدا به هستههای پردازشی مستقل، جهت جلوگیری از تخریب کش در وقفههای نرمافزاری.
- پایش لحظهای PMTU با sk_error_report: دریافت مستقیم پیامهای ICMP مربوط به نیاز به قطعهبندی از پشته هسته، و تنظیم اندازه بسته بدون قطع ارتباط رمزنگاری.
- پایداری صف با SO_RCVBUFFORCE: تخصیص ظرفیتهای بافر ترجیحی برای نشستهای تاییدشده در زمان وقوع حملات سنگین UDP برای تداوم پایداری خدمات.
یکپارچگی عمیق چرخه حیات سوکت با هسته لینوکس و حذف لایههای زائد، بستری با تأخیر قطعی زیر میلیثانیه و ایزولاسیون کامل ترافیک برای زیرساخت امن ارتباطی فراهم میآورد.



