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-address | string | آدرس IP محلی برای bind. باید IP معتبر باشد. |
listen-port | integer | پورت UDP محلی. بازه معتبر 0 تا 65535 است. |
object مربوط به settings الزامی است و نباید خالی باشد.
تنظیمات اختیاری
| فیلد | پیشفرض | توضیح |
|---|---|---|
interface | ندارد | در صورت پشتیبانی پلتفرم، socket را به یک network device محلی محدود میکند. |
fwmark | ندارد | socket mark در پلتفرمهایی که SO_MARK دارند؛ عمدتاً Linux. |
source-ip | ندارد | آدرس مؤثر bind را تغییر میدهد و در این نود جای listen-address را میگیرد. |
large-send-buffer | true | مقدار true از socket buffer بزرگ پیشفرض WaterWall استفاده میکند، false تنظیم پیشفرض kernel را نگه میدارد و یک عدد مثبت همان اندازه را بر حسب byte درخواست میکند. |
large-recv-buffer | true | همان رفتار large-send-buffer را برای SO_RCVBUF دارد؛ false یعنی استفاده از مقدار پیشفرض kernel. |
verbose | false | جزئیات 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 برسد:
- worker مالک socket، datagram را دریافت میکند
UdpStatelessSocketاز endpoint peer، endpoint محلی، و هویت tunnel یک flow key میسازد- اگر flow متناظری وجود نداشته باشد، یک line معمولی میسازد و
Initرا به سمت متصل chain میفرستد - آدرس و پورت فرستنده داخل
routing_context.src_ctxنوشته میشود و local listener port هم ثبت میشود - بدنه 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 از هر جهت به این نود برسد:
- اگر line مربوط به یک peer ورودی و ساخته همین tunnel باشد، datagram به endpoint ذخیرهشده همان peer ارسال میشود
- در غیر این صورت مقصد از
line->routing_context.dest_ctxخوانده میشود - اگر مقصد یک domain حلنشده باشد، async DNS آغاز میشود
- اگر مقصد IP و port آماده باشد، به socket address تبدیل میشود
- 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 model | UDP edge adapter با inbound lineهای per-peer |
| Socket count | یک UDP socket bound بهازای هر node instance |
| Per-peer state | line معمولی بهازای هر inbound peer flow |
| Packet-line use | سازگاری outbound با routing context |
| Required left padding | 0 |
| DNS cache freshness | 30 minutes |
خطاهای رایج
- ارسال payload قبل از اینکه
dest_ctxمقصد IP/domain و port داشته باشد. - در نظر گرفتن lineهای peer ورودی بهعنوان packet line.
- استفاده از این نود مثل stream adapter عمومی.
- تکیه به
source-ipبرای override کردن loop-protection؛ در این حالتinterfaceرا صریح تنظیم کنید. - فراموش کردن وابستگی
fwmarkو interface binding به پلتفرم.