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

TcpUdpConnector

TcpUdpConnector یک wrapper در انتهای chain است که TcpConnector و UdpConnector را در یک node ترکیب می‌کند. با تنظیمات یکسان، یک connector داخلی برای TCP و یکی برای UDP می‌سازد و سپس بر اساس metadata مسیریابی WaterWall، connector مناسب هر line را انتخاب می‌کند.

این node TCP را داخل UDP کپسوله نمی‌کند و UDP را هم از TCP عبور نمی‌دهد؛ فقط یک wrapper دوپروتکلی برای connectorهاست.

جایگاه رایج​

وقتی node خروجی باید هم TCP و هم UDP را پشتیبانی کند، از TcpUdpConnector استفاده کنید:

Socks5Server -> TcpUdpConnector
TcpUdpListener -> TcpUdpConnector
TcpUdpListener -> SniffRouter -> TcpUdpConnector
PacketsToConnection -> TcpUdpConnector

node قبلی باید اطلاعات protocol را در destination context یا source context قرار دهد. TcpUdpConnector با همین metadata تصمیم می‌گیرد line را به TcpConnector داخلی بسپارد یا به UdpConnector داخلی.

نمونه ساده​

{
"name": "mixed-out",
"type": "TcpUdpConnector",
"settings": {
"address": "198.51.100.10",
"port": 443,
"domain-strategy": "prefer-ipv4",
"nodelay": true,
"fastopen": false,
"large-send-buffer": true,
"large-recv-buffer": true,
"interface": "eth0",
"fwmark": 10,
"balance-mode": "connection"
}
}

هم line‌های TCP و هم line‌های مربوط به peer‌های UDP از destination تنظیم‌شده استفاده می‌کنند. TcpConnector داخلی line‌های TCP را مدیریت می‌کند و line‌های UDP به UdpConnector داخلی سپرده می‌شوند.

نمونه dynamic destination​

{
"name": "mixed-out",
"type": "TcpUdpConnector",
"settings": {
"address": "dest_context->address",
"port": "dest_context->port",
"domain-strategy": "prefer-ipv4",
"large-send-buffer": true,
"large-recv-buffer": true
}
}

این حالت بعد از node‌هایی مثل Socks5Server و SniffRouter مفید است؛ یعنی node‌هایی که address، port و protocol مقصد را در routing context قرار می‌دهند.

نمونه weighted destination​

{
"name": "mixed-out",
"type": "TcpUdpConnector",
"settings": {
"addresses": [
{
"address": "198.51.100.10",
"port": 443,
"weight": 3
},
{
"address": "198.51.100.20",
"port": 443,
"weight": 1
}
],
"domain-strategy": "prefer-ipv4",
"large-send-buffer": true,
"large-recv-buffer": true,
"balance-mode": "connection"
}
}

آرایه addresses بدون تغییر به هر دو connector داخلی داده می‌شود. اگر امکان انتخاب هر دو protocol وجود دارد، syntax مقصد باید هم برای TcpConnector و هم برای UdpConnector معتبر باشد.

فیلدهای ضروری​

فیلدهای سطح بالا:

فیلدنوعتوضیح
namestringنام دلخواه node. باید داخل فایل config یکتا باشد.
typestringباید دقیقاً "TcpUdpConnector" باشد.
settingsobjectتنظیمات مشترک هر دو connector داخلی؛ باید objectی غیرخالی باشد.

TcpUdpConnector یک chain end است. نباید next داشته باشد.

تنظیمات destination لازم:

سبکفیلدهای لازمتوضیح
مقصد تکیaddress و portهر connector داخلی که انتخاب شود از همین مقصد استفاده می‌کند.
مقصدهای weightedaddressesمقصد هر line از یک فهرست وزن‌دار انتخاب می‌شود. connector‌های داخلی spelling قدیمی adresses را هم می‌پذیرند، اما در config‌های جدید از addresses استفاده کنید.

addresses را با address یا port top-level ترکیب نکنید.

تنظیمات مشترک connector​

object مربوط به settings باید هنگام راه‌اندازی برای هر دو connector داخلی معتبر باشد. حتی اگر قرار است در runtime فقط از یک protocol استفاده کنید، این نکته همچنان مهم است؛ چون wrapper موقع خواندن config هر دو connector را می‌سازد.

گزینهروی کدام child اثر داردتوضیح
addressTCP و UDPآدرس ثابت، domain، "src_context->address" یا "dest_context->address".
portTCP و UDPپورت ثابت، "src_context->port" یا "dest_context->port".
addressesTCP و UDPلیست weighted destination.
domain-strategyTCP و UDPاستراتژی انتخاب نتیجه DNS برای مقصدهای domain.
large-send-bufferTCP و UDPتنظیم SO_SNDBUF در صورت پشتیبانی.
large-recv-bufferTCP و UDPتنظیم SO_RCVBUF در صورت پشتیبانی.
fwmarkTCP و UDPاعمال socket mark به سبک Linux در صورت پشتیبانی.
interfaceTCP و UDPمحدود کردن socket‌های خروجی به یک network device در صورت پشتیبانی.
source-ipTCP و UDPbind کردن socket‌های خروجی به یک source IP محلی مشخص.
nodelayTCP childفعال‌سازی TCP_NODELAY روی TCP socket‌های خروجی.
fastopenTCP childدرخواست TCP_FASTOPEN در صورت پشتیبانی.
balance-modeUDP childانتخاب weighted destination در UDP: "connection" یا "packet".

برای جزئیات انتخاب destination، DNS، گزینه‌های socket و balance mode در UDP، به صفحه‌های TcpConnector و UdpConnector مراجعه کنید.

هشدار

UdpConnector از syntax مخصوص UDP مثل "random(x,y)" برای port پشتیبانی می‌کند، اما TcpConnector نه. چون TcpUdpConnector هر دو child connector را می‌سازد، از syntax مخصوص UDP اینجا خودداری کنید مگر اینکه در کد فعلی برای TcpConnector هم معتبر شده باشد.

انتخاب protocol​

با دریافت upstream Init، TcpUdpConnector یکی از connector‌های داخلی را انتخاب و آن را در per-line state ذخیره می‌کند. callback‌های بعدی upstream هم به همان connector می‌روند؛ بنابراین حتی اگر node دیگری بعداً routing context را تغییر دهد، انتخاب اولیه عوض نمی‌شود.

ترتیب انتخاب:

مرحلهرفتار
1line->routing_context.dest_ctx بررسی می‌شود.
2اگر destination context دقیقاً flag مربوط به TCP را داشته باشد، TcpConnector داخلی انتخاب می‌شود.
3اگر destination context دقیقاً flag مربوط به UDP را داشته باشد، UdpConnector داخلی انتخاب می‌شود.
4اگر destination context هیچ protocol flag نداشته باشد، به line->routing_context.src_ctx برمی‌گردد.
5اگر source context دقیقاً flag مربوط به TCP یا UDP را داشته باشد، connector متناظر انتخاب می‌شود.
6اگر protocol مشخصی پیدا نشود، برنامه آن را خطایی fatal در config یا routing در نظر می‌گیرد.

protocol مقصد بر protocol مبدأ اولویت دارد. به این ترتیب، node‌های آگاه از مسیریابی می‌توانند ترافیک inbound را با یک transport بگیرند و transport خروجی را بر اساس مقصد درخواستی انتخاب کنند.

جریان runtime​

هنگام راه‌اندازی، TcpUdpConnector دو tunnel داخلی می‌سازد:

  • TcpConnector با نامی شبیه <node-name>.tcp-connector
  • UdpConnector با نامی شبیه <node-name>.udp-connector

هر دو node داخلی زیر wrapper در chain قرار می‌گیرند. callback‌ها در runtime این مسیر را طی می‌کنند:

  1. node قبلی upstream Init را به TcpUdpConnector می‌فرستد
  2. wrapper یکی از connector‌های داخلی را انتخاب و مقداردهی اولیه می‌کند
  3. callback‌های upstream مربوط به payload، pause، resume، establishment و finish به همان connector فرستاده می‌شوند
  4. callback‌های downstream آن connector به TcpUdpConnector برمی‌گردند
  5. wrapper آن‌ها را در جهت downstream به node قبلی می‌فرستد

مالکیت line​

TcpUdpConnector هیچ connection line‌ای نمی‌سازد و از بین نمی‌برد. مالک line همان adapter یا bridge قبلی است که آن را ساخته است.

این wrapper فقط connector انتخاب‌شده را در یک per-line state کوچک نگه می‌دارد و پیش از فرستادن Finish واقعی در هر جهت، آن state را پاک می‌کند. lineDestroy() هیچ‌وقت از این node فراخوانی نمی‌شود.

اشتباه‌های رایج​

  • از TcpUdpConnector به‌عنوان protocol converter استفاده نکنید؛ کار آن فقط انتخاب بین connector داخلی TCP و UDP است.
  • برای آن next قرار ندهید؛ chain end است.
  • نمی‌توانید برای TCP و UDP دو destination جدا تنظیم کنید؛ settings میان هر دو مشترک است.
  • در config‌هایی که internal TcpConnector هم باید ساخته شود از syntax مخصوص UDP مثل "random(x,y)" استفاده نکنید.
  • آن را بعد از node‌ای نگذارید که protocol دقیق TCP یا UDP را در metadata مشخص نمی‌کند.

متادیتای node​

متادیتای برگرفته از کد منبع:

ویژگیمقدار
node flagskNodeFlagChainEnd | kNodeFlagSupportsSplice
can_have_prevtrue
can_have_nextfalse
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayer4
layer_group_next_nodekNodeLayerNone
required_padding_left0 بایت

ارسال UDP با splice​

دریافت UDP همچنان از buffer عادی استفاده می‌کند. در chain واجد شرایط روی Linux، ارسال UDP دادهٔ عادی یا prefix واقعی همراه با بدنهٔ pipe خصوصی را به‌صورت یک datagram به مقصد مشخص می‌پذیرد؛ datagram خالی نیز معتبر است. چیدمان‌های پشتیبانی‌شده از splice بین pipe و socket استفاده می‌کنند. برای pipe با fragment‌های زیاد، یا وقتی پیش از ارسال نتوان حد امن fragment‌ها را اثبات کرد، کل datagram پیش از شروع تجمیع روی socket به buffer عادی تبدیل می‌شود. دادهٔ بیش‌ازحد بزرگ بدون تقسیم رد می‌شود.

فشار پیش از شروع تجمیع همان سیاست drop فعلی را حفظ می‌کند و صف retry برای UDP ایجاد نمی‌شود. splice ناقص یا شکست commit پس از priming فقط socket مربوط را بازنشسته می‌کند و line‌های عادی وابسته از مسیر مالکشان بسته می‌شوند. بنابراین peer‌های یک socket مشترک ممکن است با هم بسته شوند. باقی‌ماندهٔ ناقص دوباره ارسال نمی‌شود و socket‌های دیگر فعال می‌مانند. سیاست خطای ارسال عادی تغییر نمی‌کند.

این پرچم بررسی پشتیبانی پلتفرم، misc.splice، ممنوعیت chain packet و پشتیبانی همهٔ node‌های chain نهایی را دور نمی‌زند. zero-copy سرتاسری یا بهبود کاراییِ اندازه‌گیری‌شده تضمین نمی‌شود.

هر دو connector داخلی پرچم قابلیت خود را حفظ می‌کنند. بررسی واجد شرایط بودن، حتی برای line مربوط به TCP، هر دو شاخه و helper‌های درج‌شده را شامل می‌شود. انتخاب پروتکل و مسیریابی peer در UDP تغییر نمی‌کند.