پرش به مطلب اصلی

UdpStatelessSocket

UdpStatelessSocket یک adapter دوطرفه در لبه UDP است. یک UDP socket را bind می‌کند، datagramها را از peerهای مختلف می‌گیرد و برای flow ورودی هر peer یک line معمولی WaterWall می‌سازد.

کاربرد اصلی این نود کنار نودهایی مثل WireGuardDevice است؛ جایی که یک UDP socket مشترک باید هم برای peerهای تنظیم‌شده داده بفرستد و هم packetهای ورودی آن‌ها را بپذیرد. datagramهای ورودی بر اساس endpointهای peer و local گروه‌بندی می‌شوند. lineهایی که این نود نساخته است نیز می‌توانند با استفاده از routing context از همان socket داده بفرستند.

جایگاه رایج

WireGuardDevice -> UdpStatelessSocket
UdpStatelessSocket -> WireGuardDevice
TunDevice -> WireGuardDevice -> UdpStatelessSocket
UdpStatelessSocket -> WireGuardDevice -> TunDevice

در لبه UDP یک packet chain زمانی از این نود استفاده کنید که نود کناری برای send و receive به یک UDP socket مشترک نیاز دارد و هر peer ورودی باید line و lifetime جداگانه‌ای داشته باشد. نمونه اصلی WireGuardDevice است: پیش از ارسال datagram رمزنگاری‌شده، endpoint peer را در destination routing context می‌نویسد؛ datagramهای ورودی نیز با line مخصوص همان peer وارد chain می‌شوند.

نمونه تنظیم

{
"name": "wg-udp-socket",
"type": "UdpStatelessSocket",
"settings": {
"listen-address": "0.0.0.0",
"listen-port": 51820,
"interface": "eth0",
"fwmark": 10,
"large-send-buffer": true,
"large-recv-buffer": true,
"source-ip": "192.0.2.10",
"verbose": false
}
}

تنظیمات ضروری

فیلدنوعتوضیح
listen-addressstringآدرس IP محلی برای bind. باید IP معتبر باشد.
listen-portintegerپورت UDP محلی. بازه معتبر 0 تا 65535 است.

object مربوط به settings الزامی است و نباید خالی باشد.

تنظیمات اختیاری

فیلدپیش‌فرضتوضیح
interfaceندارددر صورت پشتیبانی پلتفرم، socket را به یک network device محلی محدود می‌کند.
fwmarkنداردsocket mark در پلتفرم‌هایی که SO_MARK دارند؛ عمدتاً Linux.
source-ipنداردآدرس مؤثر bind را تغییر می‌دهد و در این نود جای listen-address را می‌گیرد.
large-send-buffertrueمقدار true از socket buffer بزرگ پیش‌فرض WaterWall استفاده می‌کند، false تنظیم پیش‌فرض kernel را نگه می‌دارد و یک عدد مثبت همان اندازه را بر حسب byte درخواست می‌کند.
large-recv-buffertrueهمان رفتار large-send-buffer را برای SO_RCVBUF دارد؛ false یعنی استفاده از مقدار پیش‌فرض kernel.
verbosefalseجزئیات datagramهای ارسالی و دریافتی را در debug log می‌نویسد.

اندازه buffer بزرگ پیش‌فرض فعلی 4194304 بایت است.

رفتار interface و source-ip

interface پیش از bind اعمال می‌شود. در Linux معمولاً socket مستقیماً به device متصل می‌شود. اگر پلتفرم چنین قابلیتی نداشته باشد و source-ip هم تنظیم نشده باشد، WaterWall تلاش می‌کند socket را به آدرس IPv4 همان interface bind کند.

در این نود source-ip مستقیماً جای listen-address را می‌گیرد، چون یک socket مشترک هم ارسال را انجام می‌دهد و هم دریافت را.

اگر loop protection در TunDevice یک egress pin خودکار ایجاد کرده باشد، source-ip به‌تنهایی آن را تغییر نمی‌دهد. برای انتخاب interface دیگر، interface را صریحاً تنظیم کنید.

مسیر دریافت

وقتی UDP datagram برسد:

  1. worker مالک socket، datagram را دریافت می‌کند
  2. UdpStatelessSocket از endpoint peer، endpoint محلی، و هویت tunnel یک flow key می‌سازد
  3. اگر flow متناظری وجود نداشته باشد، یک line معمولی می‌سازد و Init را به سمت متصل chain می‌فرستد
  4. آدرس و پورت فرستنده داخل routing_context.src_ctx نوشته می‌شود و local listener port هم ثبت می‌شود
  5. بدنه datagram روی line همان peer فرستاده می‌شود

اگر نود در انتهای chain باشد، datagramهای دریافتی از سمت next/tail وارد می‌شوند و در جهت downstream به previous می‌روند. در غیر این صورت ورود از سمت previous/head است و داده در جهت upstream به next می‌رود.

lineهای idle peer برای init timeout برابر 30 seconds و keepalive timeout برابر 300 seconds استفاده می‌کنند.

مسیر ارسال

وقتی payload از هر جهت به این نود برسد:

  1. اگر line مربوط به یک peer ورودی و ساخته همین tunnel باشد، datagram به endpoint ذخیره‌شده همان peer ارسال می‌شود
  2. در غیر این صورت مقصد از line->routing_context.dest_ctx خوانده می‌شود
  3. اگر مقصد یک domain حل‌نشده باشد، async DNS آغاز می‌شود
  4. اگر مقصد IP و port آماده باشد، به socket address تبدیل می‌شود
  5. datagram با همان UDP socket ارسال می‌شود

اگر destination context آماده نباشد یا port نداشته باشد، datagram دور ریخته می‌شود و warning یا error در log ثبت خواهد شد.

نتیجه DNS برای domainهای حل‌نشده بر اساس domain، port و domain strategy به مدت 30 minutes cache می‌شود. این نکته مهم است، چون routing context روی packet line موقتی است و packet بعدی ممکن است آن را بازنویسی کند.

Worker Ownership

UDP socket تنها در اختیار یک worker loop است. اگر payload از worker دیگری برای ارسال برسد، عملیات روی worker مالک socket زمان‌بندی می‌شود و sendto() همان‌جا اجرا خواهد شد.

چرا UdpListener یا UdpConnector نه؟

در WireGuard و سناریوهای مشابه، همان socket باید هم برای peer داده بفرستد و هم از آن دریافت کند. UdpListener برای ورودی و UdpConnector برای خروجی طراحی شده‌اند. UdpStatelessSocket هر دو جهت را با یک socket پوشش می‌دهد، برای datagramهای ورودی line مخصوص هر peer می‌سازد و lineهای دیگر را بر اساس routing context ارسال می‌کند.

Packet-Line Semantics

UdpStatelessSocket adapterی برای UDP datagramهاست. هر peer ورودی یک line معمولی می‌گیرد؛ datagramها مستقیماً وارد payload callback مربوط به packet line نمی‌شوند.

packet lineها همچنان می‌توانند بر اساس routing context داده outbound بفرستند و این tunnel هیچ packet lineای را در زمان اجرا از بین نمی‌برد.

متادیتای نود

ویژگیمقدار
Runtime modelUDP edge adapter با inbound lineهای per-peer
Socket countیک UDP socket bound به‌ازای هر node instance
Per-peer stateline معمولی به‌ازای هر inbound peer flow
Packet-line useسازگاری outbound با routing context
Required left padding0
DNS cache freshness30 minutes

خطاهای رایج

  • ارسال payload قبل از اینکه dest_ctx مقصد IP/domain و port داشته باشد.
  • در نظر گرفتن lineهای peer ورودی به‌عنوان packet line.
  • استفاده از این نود مثل stream adapter عمومی.
  • تکیه به source-ip برای override کردن loop-protection؛ در این حالت interface را صریح تنظیم کنید.
  • فراموش کردن وابستگی fwmark و interface binding به پلتفرم.