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

IpManipulator

IpManipulator یک packet tunnel برای دست‌کاری packetهای خام IPv4 در chainهای لایه ۳ WaterWall است.

این نود برای مسیرهایی است که payload آن‌ها یک packet خام IP است؛ مثلاً در کنار TunDevice، RawSocket، WireGuardDevice یا نودهای packet دیگر. این نود را نباید آن را در chain معمولی بایت‌های stream یا TCP قرار داد.

هشدار

بیشتر قابلیت‌های این نود برای packet shaping و آزمایش رفتار middleboxها طراحی شده‌اند. نتیجه کاملاً به مسیر شبکه بستگی دارد؛ ممکن است یک trick در یک شبکه کار کند، اما در مسیری دیگر باعث حذف packet یا رفتاری نامطلوب شود.

عملکرد کلی

  • روی upstream و downstream packet payload کار می‌کند.
  • trickهای فعال را روی packetهای IPv4 اجرا می‌کند.
  • می‌تواند شماره پروتکل IP مربوط به TCP و UDP را تغییر دهد و در سمت مقابل بازیابی کند.
  • می‌تواند flagهای TCP را بازنویسی کند.
  • می‌تواند source/destination port اصلی را انتهای packet حمل کند و port زنده را تغییر دهد.
  • می‌تواند از packet نهایی کپی‌های اضافه بسازد.
  • می‌تواند شکل packetهای TLS ClientHello را تغییر دهد.
  • می‌تواند TLS ClientHello را با SNI Blender به fragmentهای IPv4 تبدیل کند و با ترتیب shuffle شده بفرستد.
  • برای protocol swap و تغییر ساده flagهای TCP، checksumهای headerِ IPv4 و TCP را درجا به‌روز می‌کند؛ trickهایی که اندازه packet را تغییر می‌دهند یا payload می‌سازند، درخواست محاسبه کامل checksum می‌دهند.

این نود با API تابع packettunnelCreate() ساخته می‌شود؛ بنابراین callbackهای اتصال مانند Finish، Pause، Resume و Est در مسیر عادی runtime آن قرار ندارند.

جایگاه رایج

نمونه chain packet:

TunDevice -> IpManipulator -> RawSocket
TunDevice -> WireGuardDevice -> IpManipulator -> UdpStatelessSocket

نمونه استقرار جفت‌شده برای protocol swap:

edge A packet chain -> IpManipulator -> packet transport
edge B packet chain -> IpManipulator -> packet output

برای protocol swap دوطرفه، معمولاً باید در سمت مقابل نیز یک IpManipulator با تنظیمات سازگار قرار دهید تا packetها به پروتکل اصلی خود برگردند.

اگر trickهایی مثل real-sni-upstream-node، crafted-server-hello-upstream-node یا real-fin-upstream-node استفاده می‌کنید، branch کمکی آن را جدا از مسیر معمول next تعریف کنید.

نمونه تنظیم

Protocol swap ساده:

{
"name": "ip-manipulator",
"type": "IpManipulator",
"settings": {
"protoswap-tcp": 253,
"protoswap-udp": 252
},
"next": "next-packet-node"
}

SNI Blender:

{
"name": "ip-manipulator",
"type": "IpManipulator",
"settings": {
"sni-blender": true,
"sni-blender-packets": 4
},
"next": "next-packet-node"
}

نمونه ترکیبی:

{
"name": "ip-manipulator",
"type": "IpManipulator",
"settings": {
"protoswap-tcp": 253,
"protoswap-udp": 252,
"sni-blender": true,
"sni-blender-packets": 4,
"packet-duplicate": 1,
"source-port-ghost": true,
"dest-port-ghost": true
},
"next": "next-packet-node"
}

فیلدهای لازم

فیلدنوعتوضیح
namestringنام دلخواه و یکتای نود.
typestringباید دقیقاً "IpManipulator" باشد.
settingsobjectباید حداقل یک trick فعال داشته باشد.
nextstringبرای عبور عادی packet لازم است. بعضی trickها صریحاً به next در سطح اصلی نیاز دارند.

اگر هیچ trickای فعال نباشد، ساخت نود شکست می‌خورد:

IpManipulator: no tricks are enabled, nothing to do

خلاصه trickها

خانوادهتنظیمات اصلیجهتکاربرد
Protocol swapprotoswap-tcp, protoswap-udpدو طرفتغییر شماره پروتکل IPv4 و بازیابی آن در سمت مقابل.
SNI Blendersni-blender, sni-blender-packetsupstreamقطعه‌قطعه کردن IPv4/TCP TLS ClientHello و فرستادن قطعه‌ها با ترتیب درهم.
Packet duplicatepacket-duplicateمسیر نهایی ارسالارسال چند کپی اضافه از packet نهایی و سپس نسخه اصلی.
first-snifirst-sniupstreamفرستادن یک ClientHello جعلی پیش از نسخه اصلی.
smuggle-snismuggle-sni, real-sni-upstream-nodeupstreamفرستادن ClientHello واقعی روی branch جدا و نسخه جعلی با تأخیر روی مسیر عادی.
overlap-snioverlap-sni, crafted-server-hello-upstream-nodeupstream با ردیابی downstreamهم‌پوشانی ClientHello ساخته‌شده با بایت‌های واقعی TLS.
ech-sni-trickech-sni-trickupstreamفرستادن ClientHello جعلی جاسازی‌شده توسط TlsClient در payloadِ ECH، به شکل TCP segment خارج از ترتیب.
synfin-snisynfin-sniupstreamترکیب نخستین بخش واقعی، packet بستن، SYN جعلی، ClientHello جعلی، filler و بقیه بایت‌های واقعی.
smuggle-finsmuggle-fin, real-fin-upstream-nodeupstream با ردیابی echo در downstreamتزریق `FIN
TCP flag rewriteup-tcp-bit-*, dw-tcp-bit-*دو طرفاجبار، toggle یا نگاشت دوباره flagهای TCP.
Port ghostsource-port-ghost, dest-port-ghostاعمال در upstream، بازیابی در downstreamحمل port اصلی در انتهای packet و تغییر port فعال transport.

Protocol Swap

در IPv4، فیلد Protocol مشخص می‌کند payload از چه نوعی است. برای مثال TCP عدد 6 و UDP عدد 17 دارد.

گزینهنوعتوضیح
protoswapintegerنام مستعار قدیمی protoswap-tcp.
protoswap-tcpintegerprotocol number جایگزین برای packetهای TCP.
protoswap-udpintegerprotocol number جایگزین برای packetهای UDP.

بازه معتبر شماره پروتکل جایگزین از 0 تا 255 است.

اگر شماره پروتکل packet برگشتی با یکی از مقادیر جایگزین منطبق باشد، نود آن را به TCP یا UDP عادی برمی‌گرداند. برای تونل دوطرفه، سمت مقابل هم باید تنظیمات سازگار داشته باشد.

مقدار جایگزین نباید برابر شماره پروتکل واقعی TCP یعنی 6 یا UDP یعنی 17 باشد؛ حتی اگر فقط یکی از این دو خانواده تنظیم شده باشد. در غیر این صورت، ترافیک واقعی با آن شماره از ترافیک mapشده قابل تشخیص نیست. اگر swap برای هر دو خانواده TCP و UDP فعال باشد، مقدارهای جایگزین آن‌ها نیز باید متفاوت باشند.

گزینه protoswap-tcp-2 حذف شده است و configuration قدیمی شامل آن رد می‌شود. برای مهاجرت، تنها از mapping برگشت‌پذیر protoswap-tcp استفاده کنید. این تغییر همچنین تضمین می‌کند همه fragmentهای یک datagram از IPv4 شماره پروتکل یکسانی دریافت کنند.

trickهای upstream زمانی اجرا می‌شوند که packet هنوز شماره پروتکل واقعی خود را دارد. درست پیش از خروج عادی، IpManipulator ابتدا port ghost را اعمال می‌کند، checksumهای IPv4 و transport را با پروتکل واقعی کامل می‌کند، شماره پروتکل را swap می‌کند و سپس checksumِ headerِ IPv4 را اصلاح می‌کند. در downstream، شماره پروتکل پیش از بازیابی port ghost یا parse شدن packet توسط هر trick مربوط به TCP/TLS بازیابی می‌شود.

اگر یک packet در upstream از قبل مقدار جایگزین تنظیم‌شده این نود را داشته باشد، IpManipulator این نود را بخش صریح unwrap در یک جفت transport زنجیره‌ای در نظر می‌گیرد: پروتکل واقعی را بازیابی می‌کند، checksum را با همان پروتکل کامل می‌کند و packet را بدون mapping به جلو می‌فرستد. اگر باید packet پس از یک نود بعدی همچنان wrapped بماند، همان protocol swap را روی آن نود فعال نکنید.

TCP Flag Rewrite

این همان بخشی است که در مستندات قدیمی با عنوان «بازی با بیت‌های TCP» توضیح داده شده بود. ایده اصلی تغییر نکرده است: می‌توانید مقدار flagهای TCP را اجباری یا toggle کنید، یا مقدارشان را از flag دیگری در packet بگیرید.

فرمت کلیدها:

up-tcp-bit-<flag>
dw-tcp-bit-<flag>

flagهای پشتیبانی‌شده:

cwr, ece, urg, ack, psh, rst, syn, fin

مقدارهای قابل قبول:

off
on
toggle
flip
switch
packet->cwr
packet->ece
packet->urg
packet->ack
packet->psh
packet->rst
packet->syn
packet->fin

flip و switch نام‌های مستعار toggle هستند.

نمونه swap کردن ACK و FIN:

{
"up-tcp-bit-ack": "packet->fin",
"up-tcp-bit-fin": "packet->ack",
"dw-tcp-bit-ack": "packet->fin",
"dw-tcp-bit-fin": "packet->ack"
}

همه بیت‌ها هم‌زمان اعمال می‌شوند، نه یکی پس از دیگری. یعنی مقدارها از flagهای packet اصلی خوانده می‌شوند، نه از نسخه‌ای که در همین مرحله تغییر کرده است.

اگر flagی در حالت ساده (بدون preserve-tcp-bitflags) تغییر کند، بایت flagهای TCP بازنویسی می‌شود و checksumِ TCP با updateIpv4TransportChecksum16() به‌صورت incremental و درجا به‌روز می‌شود. تغییر ساده flagها هر درخواست محاسبه دوباره checksum را که از قبل وجود داشته حفظ می‌کند، اما درخواست جدیدی ایجاد نمی‌کند.

حفظ TCP Bitflagها

گزینهنوعپیش‌فرضتوضیح
preserve-tcp-bitflagsbooleanfalseپیش از بازنویسی، بایت اصلی flagهای TCP را ذخیره می‌کند تا جهت مقابل بتواند packet را بازیابی کند.

این حالت زمانی مفید است که می‌خواهید packet در یک جهت با TCP flagهای تغییرکرده عبور کند و سپس در جهت برگشت به شکل اصلی TCP flags برگردد.

روند جهت‌ها:

  1. در جهتی که actionهای up-tcp-bit-* یا dw-tcp-bit-* دارد، IpManipulator بایت کامل flagهای اصلی TCP را به‌عنوان آخرین بایت packet IPv4 ذخیره می‌کند.
  2. سپس TCP flagهای فعال packet را طبق actionهای همان جهت بازنویسی می‌کند.
  3. طول کل IPv4 را یک بایت زیاد می‌کند و packet را برای محاسبه دوباره checksum علامت می‌زند.
  4. در جهت مقابل، اگر آن جهت action بازنویسی TCP-bit نداشته باشد ولی جهت دیگر action داشته باشد، آخرین بایت packet خوانده می‌شود، TCP flags از آن بازیابی می‌شود، آن بایت حذف می‌شود، طول IPv4 یک بایت کم می‌شود و checksumها باید دوباره محاسبه شوند.

این actionهای تنظیم‌شده TCP-bit هستند که مشخص می‌کنند کدام جهت بایت پشتیبان را اضافه و کدام جهت آن را بازیابی کند. صرفاً فعال‌کردن این گزینه، نقش ثابتی به upstream یا downstream نمی‌دهد:

actionهای تنظیم‌شده TCP-bitرفتار upstreamرفتار downstream
فقط upstreamflagهای اصلی را پشتیبان می‌گیرد، یک بایت اضافه می‌کند و actionهای upstream را اعمال می‌کند.flagها را از آخرین بایت بازیابی و آن بایت را حذف می‌کند.
فقط downstreamflagها را از آخرین بایت بازیابی و آن بایت را حذف می‌کند.flagهای اصلی را پشتیبان می‌گیرد، یک بایت اضافه می‌کند و actionهای downstream را اعمال می‌کند.
upstream و downstreamپشتیبان می‌گیرد، بایت را اضافه می‌کند و actionهای upstream را اعمال می‌کند.پشتیبان می‌گیرد، بایت را اضافه می‌کند و actionهای downstream را اعمال می‌کند؛ هیچ جهتی بازیابی نمی‌کند.
هیچ‌کدامپشتیبان‌گیری یا بازیابی انجام نمی‌شود.پشتیبان‌گیری یا بازیابی انجام نمی‌شود.

نمونه بازنویسی upstream و بازیابی downstream:

{
"preserve-tcp-bitflags": true,
"up-tcp-bit-ack": "packet->fin",
"up-tcp-bit-fin": "packet->ack"
}

در این شکل، packetهای upstream پیش از جابه‌جایی ACK و FIN، flagهای اصلی را در آخرین بایت packet حمل می‌کنند. در downstream چون هیچ dw-tcp-bit-*ای تنظیم نشده است، همان بایت برای بازیابی TCP flags استفاده و سپس حذف می‌شود.

این حالت فقط روی packet کامل و fragmentنشده IPv4 TCP با header کامل IP و TCP کار می‌کند. همچنین فرض می‌کند آخرین بایت اضافه‌شده در مسیر بدون تغییر باقی می‌ماند. اگر نود یا دستگاه شبکه‌ای trailing payload را حذف، بازنویسی، padding یا تفسیر کند، بازیابی ممکن است شکست بخورد یا flag اشتباه برگردد.

هشدار MTU و اندازه packet

هر عملیات بازنویسی همراه با preserve-tcp-bitflags یک بایت به packet اضافه می‌کند. پیش از خروج نهایی، اگر این بایت، بایت‌های port ghost یا هر دو باعث شوند یک packet کامل IPv4/TCP از GLOBAL_MTU_SIZE بزرگ‌تر شود، IpManipulator packetهای داده واجد شرایط را در سطح transport به segmentهای کوچک‌تر تقسیم می‌کند. flagهای زنده و ذخیره‌شده هر segment مستقل تعیین می‌شوند، هر segment یک مجموعه کامل trailer می‌گیرد و sequence space با درنظرگرفتن مصرف یک شماره sequence توسط SYN پیوسته می‌ماند. بنابراین یک ورودی TCP با اندازه 1500 بایت می‌تواند پشت MTU برابر 1500 در WaterWall باقی بماند و صرفاً برای این trailerها به headroom اضافی از طرف operator نیاز ندارد. اگر packet نهایی از نظر اندازه معتبر باشد اما buffer دقیقاً پر باشد، buffer بزرگ‌تر می‌شود.

IpManipulator برای این منظور IPv4 fragmentation یا reassembly انجام نمی‌دهد. UDP بزرگ‌تر از MTU، ‏RST، ‏URG پشتیبانی‌نشده و packetهای دیگری که segmentation امن آن‌ها ممکن نیست، با logging دارای rate limit کنار گذاشته می‌شوند. در deploymentهای UDP همچنان باید operator برای بایت‌های port ghost در MTU headroom در نظر بگیرد.

در نتیجه، فعال‌کردن این گزینه به‌تنهایی بایتی اضافه یا حذف نمی‌کند؛ حداقل در یکی از جهت‌ها باید یک action بازنویسی TCP-bit تنظیم شده باشد.

قواعد ترکیب Stateful SNI

در یک IpManipulator فقط یکی از first-sni، ‏smuggle-sni، ‏overlap-sni، synfin-sni و ech-sni-trick می‌تواند فعال باشد. هر یک از این trickهای stateful همراه با sni-blender یا packet-duplicate در همان instance نیز رد می‌شود، چون state machine مربوط transcript خود را مستقیماً ارسال می‌کند.

اگر هر دو عملیات لازم هستند، از دو نود استفاده کنید:

... -> IpManipulator(stateful SNI only)
-> IpManipulator(sni-blender and/or packet-duplicate)
-> ...

در traversal مربوط به upstream، نود stateful باید ابتدا قرار گیرد. duplication پیش از نود stateful، ورودی‌های آن را duplicate می‌کند و معادل duplicate کردن خروجی نهایی آن نیست. ترکیب sni-blender و packet-duplicate وقتی هیچ trick stateful مربوط به SNI فعال نباشد همچنان پشتیبانی می‌شود.

ترکیب‌های ردشده TCP-bit

در ترکیب‌های زیر ساخت configuration شکست می‌خورد:

  • هر action از نوع up-tcp-bit-* همراه با first-sni، ‏smuggle-sni، overlap-sni، ‏synfin-sni یا ech-sni-trick، چه preserve-tcp-bitflags فعال باشد چه نباشد. actionهای upstream مربوط به TCP-bit پیش از بررسی stateful SNI اجرا می‌شوند، اما packetهای ساخته‌شده یا replayشده توسط این state machineها به‌طور یکسان دوباره وارد پردازش TCP-bit نمی‌شوند.
  • preserve-tcp-bitflags همراه با حداقل یک action از نوع up-tcp-bit-* و sni-blender. بایت پشتیبان پیش از ساخت fragmentهای IPv4 توسط SNI Blender اضافه می‌شود، اما مسیر بازیابی peer از fragmentها عبور می‌کند و نمی‌تواند آن بایت را حذف کند.

ترکیب‌های زیر همچنان پذیرفته می‌شوند:

  • preserve-tcp-bitflags بدون هیچ action مربوط به TCP-bit که encoder بدون اثر است؛
  • actionهای فقط downstream از نوع dw-tcp-bit-* همراه با یک trick stateful مربوط به SNI یا sni-blender، چون SYN آغازکننده flow یا ClientHello در upstream را تغییر نمی‌دهند؛
  • actionهای ساده TCP-bit در upstream همراه با sni-blender وقتی preserve-tcp-bitflags غیرفعال است.

preserve-tcp-bitflags با هر stage آینده در upstream که یک packet TCP دارای metadata را fragment یا reshape کند پشتیبانی نمی‌شود.

SNI Blender

SNI Blender خود SNI را بازنویسی نمی‌کند؛ تنها شکل packet مربوط به TLS ClientHello در upstream ClientHello را تغییر می‌دهد.

گزینهنوعاجباریتوضیح
sni-blenderbooleanبلهقطعه‌قطعه کردن TLS ClientHello و فرستادن قطعه‌ها با ترتیب درهم را فعال می‌کند.
sni-blender-packetsintegerوقتی فعال است بلهتعداد fragmentها. مقدار معتبر 2 تا 16.

رفتار:

  • packetهای IPv4/TCP در upstream را که TLS ClientHello دارند تشخیص می‌دهد.
  • packetهایی را که از قبل fragment شده‌اند نادیده می‌گیرد.
  • headerِ TCP را در fragment نخست نگه می‌دارد.
  • payloadِ IP را به چند fragment از نوع IPv4 تقسیم می‌کند.
  • offsetها را طبق قواعد IPv4 روی مرزهای هشت‌بایتی قرار می‌دهد.
  • fragmentها را با ترتیب درهم می‌فرستد.

نکته‌های عملی از مستندات قدیمی هنوز مهم هستند:

  • TLS ClientHello باید در لایه packet ترافیک قابل مشاهده باشد.
  • اگر application کاربر خودش TLS دارد، WaterWall لازم نیست فقط برای این trick TLS بسازد.
  • اگر chain باید خودش TLS تولید کند، باید مسیر مناسب TlsClient / TlsServer داشته باشد تا قبل از رسیدن packet به این نود ClientHello قابل تشخیص شود.
  • در سناریوهای محلی یا serverless، مسیریابی و capture سیستم‌عامل باید طوری باشد که packetهای مورد نظر از WaterWall عبور کنند.

این trick روی همه شبکه‌ها کار نمی‌کند. بعضی مسیرها packetهای fragmentشده را دور می‌اندازند.

Packet Duplicate

گزینهنوعتوضیح
packet-duplicateinteger بزرگ‌تر از 0به این تعداد کپی اضافه از packet فرستاده می‌شود و سپس نسخه اصلی یک بار ارسال می‌شود.

این مرحله در مسیر نهایی ارسال و پس از دیگر trickهای فعال اجرا می‌شود.

Port Ghost

Port ghost در مرحله نهایی ارسال upstream اعمال و در ابتدای downstream بازیابی می‌شود.

گزینهنوعتوضیح
source-port-ghostbooleansource port اصلی به انتهای packet افزوده می‌شود و source port فعال به یک high port قطعی تغییر می‌کند.
dest-port-ghostbooleandestination port اصلی به انتهای packet افزوده می‌شود و destination port فعال تغییر می‌کند.

اگر هر دو فعال باشند، ابتدا source port و سپس destination port به انتهای packet اضافه می‌شود.

در packetهای downstream که tail مربوط را دارند، portهای اصلی بازیابی می‌شوند و packet به طول اولیه برمی‌گردد. Packetهای fragmentشده نادیده گرفته می‌شوند.

packetهای داده کامل TCP، چه عادی و چه ساخته‌شده، از یک segmenter نهایی مشترک استفاده می‌کنند. هر segment خروجی دقیقاً یک trailer مربوط به port ghost و، در صورت تنظیم، یک بایت flag ذخیره‌شده با قابلیت بازیابی مستقل دریافت می‌کند. SYN/CWR فقط روی segment نخست، ‏FIN/PSH فقط روی segment پایانی و ACK/ECE قابل‌اعمال روی همه segmentها قرار می‌گیرد. هر segment کامل‌شده در GLOBAL_MTU_SIZE جا می‌شود و سپس duplication نهایی packet روی همان segment اعمال می‌شود.

UDP بزرگ‌تر از MTU نمی‌تواند از TCP segmentation استفاده کند و با logging دارای rate limit کنار گذاشته می‌شود. IpManipulator، ‏IPv4 fragmentation/reassembly ایجاد نمی‌کند؛ بنابراین port ghost برای UDP همچنان به MTU headroom فراهم‌شده توسط operator نیاز دارد.

branchهای کمکی اختصاصی real-SNI، ‏mirrored-FIN و server-hello مربوط به overlap-SNI، tuple اصلی خود را حفظ می‌کنند و trailer مربوط به port ghost نمی‌گیرند.

first-sni

first-sni یک یا چند نسخه ساخته‌شده از ClientHello را پیش از نسخه اصلی می‌فرستد.

گزینهنوعپیش‌فرضتوضیح
first-snistring غیرخالیاجباری برای فعال‌سازیSNI نوشته‌شده در نسخه ساخته‌شده.
first-sni-countinteger بزرگ‌تر از 01تعداد کپی‌های جعلی پیش از نسخه اصلی.
first-sni-replay-delayms، >= 00تأخیر میان تکرارهای جعلی پس از نخستین packet.
first-sni-final-delayms، >= 00تأخیر میان آخرین نسخه جعلی و نسخه اصلی.
first-sni-ttl0 تا 255حفظ TTL اصلیTTL نسخه ساخته‌شده.
first-sni-random-tcp-sequencebooleanfalseانتخاب تصادفی TCP sequence در نسخه ساخته‌شده.

تنها نسخه ساخته‌شده تغییر می‌کند و نسخه اصلی در مسیر عادی باقی می‌ماند. اگر ClientHello شامل TLS 1.3 PSK binder باشد و تغییر SNI نیاز به recompute binder داشته باشد، trick برای احتیاط اجرا نمی‌شود.

capture چندsegmentی فقط وقتی آغاز می‌شود که یک packet شامل segment نخست قابل‌تشخیص ClientHello باشد. payloadهای TCP نامرتبط و غیر TLS بدون ورود به صف prestart حدسی بلافاصله fail-open می‌شوند. ClientHelloهای شناخته‌شده و in-order همچنان می‌توانند در چند segment از TCP قرار داشته باشند.

fragmentهای IPv4، شامل fragment نخست با MF=1 و fragmentهای بعدی با offset غیرصفر، هرگز وارد capture مربوط به First-SNI نمی‌شوند و در مسیر عادی fail-open ادامه می‌دهند. وقتی replay یا خروجی نهایی تأخیر دارد، tail مربوط به transcript پیش از packetهای بعدی در یک FIFO محدود قرار می‌گیرد و همه در یک deadline مطلق آزاد می‌شوند.

smuggle-sni

smuggle-sni یک TLS ClientHello record را در یک four-tuple و در یک یا چند TCP segment، تا سقف 16 segment و 16 کیلوبایت، capture و reassemble می‌کند. ClientHello واقعی را فوراً و با ترتیب درست از branch کمکی upstream می‌فرستد و پس از smuggle-sni-delay-ms یک batch چندsegmentی از ClientHello جعلی ساخته‌شده را روی مسیر عادی next ارسال می‌کند. batch جعلی مرزهای اصلی TCP segment، طول اصلی payload هر segment، شماره‌های اصلی TCP sequence و هر payload پس از انتهای ClientHello record را دقیقاً حفظ می‌کند. در صورت تفاوت طول، timeout یا ناپیوستگی fragmentها، یا شکست generation، ‏smuggle-sni فوراً به مسیر عادی fail-open می‌شود.

گزینهنوعپیش‌فرضتوضیح
smuggle-snistring غیرخالیاجباری برای فعال‌سازیSNI داخل نسخه ساخته‌شده.
smuggle-sni-delay-msms، >= 00تأخیر ارسال batch جعلی روی مسیر معمولی.
real-sni-upstream-nodestringاجباریbranchی که ClientHello واقعی را بی‌درنگ می‌گیرد.

این trick به next معمول در سطح اصلی نیاز دارد. نود کمکی باید وجود داشته باشد و نباید به خود IpManipulator اشاره کند. در عمل بهتر است این branch کمکی جدا از next اصلی باشد.

IpManipulator برای ساخت ClientHello دست‌کاری‌شده یک TlsClient داخلی no-chain می‌سازد.

packetهای بعدی در پنجره تأخیر وارد همان FIFO محدود می‌شوند و deadline مطلق flow را به کار می‌برند. transcript جعلی پیش از این FIFO schedule می‌شود؛ FIN و RST پشت داده‌هایی که از قبل در صف هستند باقی می‌مانند.

overlap-sni

overlap-sni packetهای اولیه TLS را capture می‌کند، یک ClientHello شبیه Chrome می‌سازد، یک packet دست‌کاری‌شده TLS در سمت سرور را از branch کمکی می‌فرستد و بایت‌های ساخته‌شده و واقعی را در محدوده TCP sequence با هم هم‌پوشان می‌کند.

گزینهنوعپیش‌فرضتوضیح
overlap-snistring غیرخالیاجباری برای فعال‌سازیSNI برای ClientHello generated.
overlap-sni-delay-msms، >= 00تاخیر packetهای بعد از fake SYN و packetهای بعدی در overlap window.
overlap-sni-syn-ttl0 تا 255حفظ TTL اصلیTTL برای fake TCP SYN.
crafted-server-hello-upstream-nodestringاجباریhelper branch برای crafted server-side TLS packet.

این trick به next معمول در سطح اصلی نیاز دارد. Branch کمکی باید وجود داشته باشد و نباید به خود IpManipulator اشاره کند.

packetهای بعدی upstream از deadline مطلق overlap window و یک FIFO محدود استفاده می‌کنند و برای هر packet یک نسخه تازه از overlap-sni-delay-ms دریافت نمی‌کنند. tail ساخته‌شده overlap پیش از آنکه FIFO بتواند آزاد شود schedule می‌شود.

ech-sni-trick

ech-sni-trick برای هماهنگی با TlsClient.settings.ech-sni-trick طراحی شده است. TlsClient یک fake ClientHello را داخل GREASE encrypted_client_hello payload قرار می‌دهد و IpManipulator همان byte range را به شکل TCP segment خارج از ترتیب می‌فرستد، بدون آنکه بایت‌های ClientHello اصلی را تغییر دهد.

گزینهنوعپیش‌فرضتوضیح
ech-sni-trickstring با طول 1 تا 255 بایتاجباری برای فعال‌سازیباید با مقدار مرتبط در TlsClient.settings.ech-sni-trick هماهنگ باشد.
data-shard-1-delayms، >= 00تاخیر آزاد کردن اولین original packet بعد از ارسال fake inner ClientHello segment.
data-shard-2-delayms، >= 00تأخیر اضافه پیش از آزاد کردن original packetهای 2 تا N به‌صورت متوالی و با ترتیب اصلی sequence. در capture تک‌packetی نادیده گرفته می‌شود.

مجموع تأخیرها باید در بازه unsigned 32-bit پشتیبانی‌شده جا شود. Packet خارج از ترتیب با flagِ PSH فرستاده می‌شود. اگر packet ساخته‌شده از GLOBAL_MTU_SIZE بزرگ‌تر باشد، flow به‌جای تغییر شکل رد می‌شود.

capture براساس sequence است و به شماره ترتیبی packetها وابسته نیست. یک TCP SYN بدون payload، چه با flagهای ECN یعنی ECE/CWR و چه بدون آن‌ها، flow را باز می‌کند. SYN دارای داده TCP Fast Open پشتیبانی نمی‌شود و بدون تغییر عبور می‌کند. capture فقط وقتی آغاز می‌شود که payload مربوط به upstream شامل prefix معتبر، کامل یا ناقص، از TLS ClientHello باشد؛ payloadهای نامرتبط و غیر TLS هرگز در صف prestart حدسی قرار نمی‌گیرند. byteهای پیوسته از header پنج‌بایتی TLS record و header چهاربایتی handshake اعتبارسنجی می‌شوند تا طول دقیق record کامل شود. packetهای بدون payload، از جمله ACK-only پیش از capture یا هنگام آن، بدون تغییر عبور می‌کنند و capture را جلو نمی‌برند یا غیرفعال نمی‌کنند.

capture به 16 packet، یک TLS record با اندازه 16384 بایت و timeout برابر 1500 ms محدود است. gap در sequence، record malformed یا پشتیبانی‌نشده، عبور از محدودیت packet یا اندازه، timeout، یا overlapی که نتوان دقیقاً retransmission بودن آن را اثبات کرد باعث fail-open می‌شود: originalهای نگه‌داشته‌شده بدون تغییر و با ترتیب TCP sequence آزاد می‌شوند و generation همان flow وارد حالت passthrough می‌شود. هنگام capture ناقص، retransmission دقیقی که sequence، طول و byteهایش با یک segment نگه‌داشته‌شده برابر باشد consume می‌شود و capture را غیرفعال نمی‌کند؛ overlap جزئی یا مبهم fail-open می‌شود.

پس از capture، ‏IpManipulator به یک outer SNI معتبر، یک extension معتبر encrypted_client_hello و دقیقاً یک TLS ClientHello داخلی جاسازی‌شده نیاز دارد که SNI آن کامل parse شود و byteبه‌byte با ech-sni-trick برابر باشد. candidate گمشده، malformed، ناقص، ناسازگار یا مبهم بدون ارسال packet جعلی داخلی fail-open می‌شود. این مکانیزم camouflage از نوع GREASE است، نه ECH رمزنگاری‌شده واقعی.

در حالت موفق، byteهای ClientHello اصلی، مرزهای packet و شماره‌های TCP sequence بدون تغییر می‌مانند. IpManipulator ابتدا یک packet خارج از ترتیب TCP را که فقط شامل byteهای ClientHello داخلی منطبق است ارسال می‌کند. سپس:

  1. original packet شماره 1 را پس از data-shard-1-delay آزاد می‌کند؛
  2. به‌اندازه data-shard-2-delay اضافه منتظر می‌ماند؛ و
  3. original packetهای 2 تا N را به‌صورت متوالی و با ترتیب اصلی آزاد می‌کند.

capture تک‌packetی فقط از تأخیر نخست استفاده می‌کند. capture دوpacketی timing دو shard اصلی را حفظ می‌کند، اما capture بزرگ‌تر چند timer message با deadline برابر ایجاد نمی‌کند که بتوانند packetها را reorder کنند.

هنگام آزادسازی با تأخیر، فقط retransmission دقیق یک original segment که هنوز pending است swallow می‌شود. ترافیک ACK-only، ‏payload بعدی بدون overlap و overlapهای جزئی یا مبهم بدون تغییر عبور می‌کنند. TCP FIN و RST همچنان ترافیک lifecycle اتصال هستند و هرگز swallow نمی‌شوند. close منطبق در هر جهت همه original packetهایی را که هنوز منتظر آزادسازی هستند cancel می‌کند؛ close در upstream هنگام capture ناقص ابتدا originalهای نگه‌داشته‌شده را آزاد می‌کند، اما close در downstream همان capture ناقص را بدون injection بعدی دور می‌اندازد.

هر original با تأخیر هم به generation غیرصفر flow مربوط به ECH که آن را capture کرده و هم به four-tuple آن متصل است. استفاده دوباره از همان four-tuple یک generation جدید آغاز می‌کند و ابتدا capture ناقص و originalهای pending مربوط به generation قدیمی را invalid می‌کند؛ بنابراین timer قدیمی capture یا release نمی‌تواند داده ClientHello قدیمی را inject کند. تا زمانی که originalها pending هستند، flow از idle expiry عادی محافظت می‌شود و پس از آخرین release، idle expiry عادی از سر گرفته می‌شود.

synfin-sni

synfin-sni یک trick پیشرفته در upstream برای TLS ClientHello است. ابتدا بخشی واقعی از داده TLS و سپس packet بستن، SYN جعلی، ClientHello جعلی ساخته‌شده، filler شبیه TLS و در پایان بقیه بایت‌های واقعی ClientHello را می‌فرستد.

گزینهنوعپیش‌فرضتوضیح
synfin-snistring غیرخالیاجباری برای فعال‌سازیSNI مربوط به ClientHello ساخته‌شده.
synfin-sni-additional-range-min0 تا 655350کمترین تعداد بایت اضافه از ClientHello واقعی در chunk نخست.
synfin-sni-additional-range-max0 تا 65535اگر فقط min باشد همان min، وگرنه 0بیشترین تعداد بایت اضافه؛ برای هر flow مقداری تصادفی انتخاب و در بازه محدود می‌شود.
synfin-sni-syn-ttl0 تا 255حفظ TTL اصلیTTL مربوط به SYN جعلی.
synfin-sni-fin-ttl0 تا 255حفظ TTL اصلیTTL مربوط به packet بستن.
synfin-sni-fake-ttl0 تا 255حفظ TTL اصلیTTL مربوط به packet کامل و جعلی ClientHello.
synfin-sni-random-syn-checksumbooleanfalsechecksumهای IPv4/TCP مربوط به SYN جعلی را تصادفی می‌کند.
synfin-sni-random-fin-checksumbooleanfalsechecksumهای packet بستن را تصادفی می‌کند.
synfin-sni-random-syn-sequencebooleanfalsesequence مربوط به SYN جعلی را تصادفی می‌کند.
synfin-sni-random-fin-sequencebooleanfalsesequence مربوط به packet بستن را تصادفی می‌کند.
synfin-sni-use-rstbooleanfalseدر packet بستن به‌جای `FIN

TTL برابر 0 واقعاً مقدار آن را صفر می‌کند. اگر می‌خواهید TTL اصلی حفظ شود، کلید TTL را اصلاً قرار ندهید.

sequence ساخته‌شده به‌صورت synchronous و با ترتیب مستندشده ارسال می‌شود و هیچ sleep مصنوعی 20 ms برای هر packet یا تأخیر pacing قابل تنظیم ندارد.

smuggle-fin

smuggle-fin یک FIN|ACK آینه‌شده روی branch کمکی تزریق می‌کند و ترافیک را روی worker مالک flow تا دریافت echo مورد انتظار در downstream و پایان تأخیر اختیاری در صف نگه می‌دارد. packetهای reverse منطبق که روی worker دیگری می‌رسند پیش از ورود به همان صف مرتب به worker مالک کپی می‌شوند. هر packet صف‌شده intent مستقل خود را برای محاسبه دوباره checksum حفظ می‌کند.

گزینهنوعپیش‌فرضتوضیح
smuggle-finbooleanاجباری برای فعال‌سازیتزریق FIN/ACK آینه‌شده را فعال می‌کند.
fin-sni-delay-msms، >= 00تأخیر پس از echo در downstream و پیش از پخش دوباره packetهای صف‌شده.
real-fin-upstream-nodestringاجباریbranch کمکی برای FIN/ACK آینه‌شده و دست‌کاری‌شده.

این trick به next معمول در سطح اصلی نیاز دارد. نود کمکی باید وجود داشته باشد و نباید به خود IpManipulator اشاره کند.

packetهای بدون TCP payload، packetهایی که از قبل SYN، FIN یا RST هستند، packetهای غیر TCP و packetهای fragmentشده IPv4 بدون تغییر عبور می‌کنند.

جدول‌های Stateful Flow

گزینهنوعپیش‌فرضتوضیح
stateful-flow-limitinteger از 32 تا 104857665536سقف سخت flowهای فعالی که هر trick فعال stateful می‌تواند دنبال کند.

هر یک از first-sni، ‏smuggle-sni، ‏overlap-sni، ‏synfin-sni، ech-sni-trick و smuggle-fin جدول محدود مستقل خود را با این limit دریافت می‌کند. جدول‌ها هرگز بیش از آن رشد نمی‌کنند. وقتی یک shard پر باشد، IpManipulator با بودجه ثابت و کوچک entryهایی را که idle deadline آن‌ها گذشته است reclaim می‌کند. اگر slot آزاد نشود، admission به‌صورت fail-open شکست می‌خورد، packet بدون تغییر ادامه می‌یابد و warning با rate limit ثبت می‌شود. برای پذیرفتن flow دیگر هیچ flow زنده‌ای evict نمی‌شود؛ در نتیجه recordهای ECH و Smuggle-FIN که مالک bufferهای packet نگه‌داشته‌شده هستند محافظت می‌شوند.

هر جدول یک four-tuple نرمال‌شده را به‌عنوان key canonical استفاده می‌کند؛ بنابراین packetهای forward و reverse، حتی میان workerها، به همان record، ‏hash، ‏shard و lock می‌رسند. یک SYN جایگزین معتبر، پیش از initialize دوباره همان record در جهت جدید، timerها، captureها و queueهای generation قبلی را صریحاً cancel می‌کند. entry canonical تکراری پذیرفته نمی‌شود.

تعداد shardها از تعداد workerها پیروی می‌کند و حداکثر 64 است. limit تنظیم‌شده طوری تقسیم می‌شود که مجموع limitهای shardها برابر مقدار درخواستی بماند. lookup از hash bucket و idle expiry از deadline heap مخصوص هر shard استفاده می‌کند تا در callbackهای packet نیازی به scan کل جدول نباشد. pointer مربوط به flow entry فقط تا زمانی معتبر است که mutex همان shard نگه داشته شده باشد؛ بنابراین timerها و messageهای میان workerها flow را با tuple نرمال‌شده و generation شناسایی می‌کنند.

delay barrierهای First-SNI، ‏Smuggle-SNI و Overlap-SNI در هر flow حداکثر 16 packet و 256 KiB نگه می‌دارند. یک generation در سطح tunnel، action مربوط به release را به instance دقیق tuple متصل می‌کند تا timer قدیمی نتواند tuple استفاده‌شده مجدد را آزاد کند. batchها زیر shard lock جدا می‌شوند و بیرون آن forward می‌شوند. تمام‌شدن ظرفیت count یا byte با ترتیب FIFO fail-open می‌شود و reset، ‏timeout یا destruction جدول همه packetهای نگه‌داشته‌شده را recycle می‌کند.

ناسازگاری‌ها

parser هر جفت از مجموعه پنج‌عضوی stateful SNI یعنی first-sni، ‏smuggle-sni، overlap-sni، ‏synfin-sni و ech-sni-trick را رد می‌کند. همچنین هر یک از آن‌ها با sni-blender یا packet-duplicate در همان instance رد می‌شود. برای chain دو‌نودی پشتیبانی‌شده، بخش «قواعد ترکیب Stateful SNI» در بالا را ببینید.

ترتیب اجرای Runtime

ترتیب upstream payload:

  1. smuggle-fin
  2. TCP flag rewrite
  3. synfin-sni
  4. ech-sni-trick
  5. overlap-sni
  6. smuggle-sni
  7. first-sni
  8. SNI Blender
  9. خروجی نهایی: port ghost، محاسبه checksum با پروتکل واقعی، protocol swap و سپس forward یا duplicate کردن packet

ترتیب downstream payload:

  1. بازیابی protocol
  2. بازیابی port ghost
  3. smuggle-fin
  4. ech-sni-trick
  5. overlap-sni
  6. TCP flag rewrite
  7. hook downstream مربوط به SNI Blender
  8. logging hook مربوط به smuggle-sni
  9. ارسال نهایی downstream

برای replay داخلی smuggle-fin، packetهای upstream از ابتدای pipeline مربوط به upstream دوباره شروع می‌شوند، چون smuggle-fin نخستین stage است. packetهای downstream بلافاصله پس از smuggle-fin ادامه می‌دهند؛ بازیابی protocol و port ghost پیش از قرارگرفتن آن‌ها در صف انجام شده است و تکرار نمی‌شود. این رفتار ترتیب عادی downstream بالا را تغییر نمی‌دهد.

Lifecycle و Packet Line

IpManipulator یک packet tunnel خالص است:

  • metadata نود، kNodeLayer3 را advertise می‌کند، در حالی که constraint هر دو همسایه kNodeLayerAnything باقی می‌ماند.
  • برای هر packet چرخه عمر اتصال جداگانه ندارد.
  • callbackهای payload مسیر اصلی runtime هستند.
  • Finish، Pause، Resume و Est برای استفاده عادی معتبر نیستند
  • نباید packet line را در runtime آزاد کند.

با Init مربوط به packet line در upstream، مسیر عادی next مقداردهی اولیه می‌شود. اگر branchهای کمکی برای real-SNI، ‏real-FIN یا server hello ساخته‌شده توسط overlap-SNI تنظیم شده باشند، آن‌ها نیز مقداردهی می‌شوند. اگر چند نقش کمکی به یک branch اشاره کنند، آن branch فقط یک بار Init دریافت می‌کند.

محدودیت‌ها و Padding

مقداراندازه
required_padding_left0 bytes
تعداد fragmentهای SNI Blender2 تا 16
protocol replacement number0 تا 255
stateful-flow-limit32 تا 1048576
طول hostname مربوط به ECH1 تا 255 bytes
capture مربوط به ECHحداکثر 16 packet و یک TLS record با اندازه 16384-byte

این نود از chain فضای left padding نمی‌خواهد. بعضی trickها برای ساخت packetهای دست‌کاری‌شده، بافر را کپی یا بافر تازه‌ای تخصیص می‌دهند.

نکته‌ها

  • پیاده‌سازی فعلی عمدتاً IPv4 را تغییر می‌دهد؛ این trickها معمولاً IPv6 را نادیده می‌شود.
  • بسیاری از trickها به packet کامل و fragmentنشده IPv4 TCP با header کامل TCP می‌خواهند.
  • بیشتر trickهای SNI فقط upstream و فقط روی TLS ClientHello کار می‌کنند.
  • این نود به این وابسته است که output packet بعدی line->recalculate_checksum را رعایت کند.
  • بهتر است branchهای کمکی از next معمول جدا باشند.
  • وقتی sni-blender فعال است، sni-blender-packets اجباری است و باید بین 2 و 16 باشد.
  • فیلد trick_sni_blender_packets_delay_max در struct زبان C وجود دارد، اما parser فعلی JSON آن را در دسترس قرار نمی‌دهد و از آن استفاده نمی‌کند.