IpManipulator
IpManipulator یک packet tunnel برای دستکاری packetهای خام IPv4 در chainهای
لایه ۳ WaterWall است.
این node برای مسیرهایی است که payload آنها یک packet خام IP است؛ مثلاً در کنار
TunDevice، RawSocket، WireGuardDevice یا nodeهای packet دیگر. این node را
نباید در chain معمولی بایتهای stream یا TCP قرار داد.
بیشتر قابلیتهای این node برای packet shaping و آزمایش رفتار middleboxها طراحی شدهاند. نتیجه کاملاً به مسیر شبکه بستگی دارد؛ ممکن است یک trick در یک شبکه کار کند، اما در مسیری دیگر باعث حذف packet یا رفتاری نامطلوب شود.
MTU هر نمونه
گزینهٔ اختیاری mtu از نوع عدد صحیح هنگام ساخت از misc.mtu هسته ارث میبرد (پیشفرض
هسته 1500). بازهٔ معتبر 68..65535 بایت است: طول کامل packet IPv4 همراه trailerهای مربوط، برای مسیرهای موجود خروجی و ترفندها.
هر نمونه مقدار معتبر خود را ثابت و مستقل نگه میدارد؛ ساخت نمونهٔ دیگر یا تغییر
بعدی هسته بر آن اثر ندارد. null، نوع نادرست، عدد اعشاری، صفر، منفی و مقدار خارج
از بازه ساخت node را رد میکنند. پیشفرض ارثرسیدهٔ نامعتبر نیز رد میشود؛ یک override
معتبر برای node تعیین کنید. این تنظیم اندازهگیری یا مذاکرهٔ MTU مسیر نیست.
همچنان فعال بودن یک ترفند معتبر لازم است؛ این گزینه فیلتر عمومی ورودی نیست و
fragmentation عمومی UDP اضافه نمیکند.
عملکرد کلی
- روی 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 میدهند.
این node با 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 به branch کمکی مثل real-sni-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"
}
فیلدهای لازم
| فیلد | نوع | توضیح |
|---|---|---|
name | string | نام دلخواه و یکتای node. |
type | string | باید دقیقاً "IpManipulator" باشد. |
settings | object | باید حداقل یک trick فعال داشته باشد. |
next | string | برای عبور عادی packet لازم است. بعضی trickها صریحاً به next در سطح اصلی نیاز دارند. |
اگر هیچ trickای فعال نباشد، ساخت node شکست میخورد:
IpManipulator: no tricks are enabled, nothing to do
خلاصه trickها
| خانواده | تنظیمات اصلی | جهت | کاربرد |
|---|---|---|---|
| protocol swap | protoswap-tcp, protoswap-udp | دو طرف | تغییر شماره پروتکل IPv4 و بازیابی آن در سمت مقابل. |
| SNI Blender | sni-blender, sni-blender-packets | upstream | قطعهقطعه کردن IPv4/TCP TLS ClientHello و فرستادن قطعهها با ترتیب درهم. |
| packet duplicate | packet-duplicate | مسیر نهایی ارسال | ارسال چند کپی اضافه از packet نهایی و سپس نسخه اصلی. |
| first-sni | first-sni | upstream | فرستادن کپیهای decoy از ClientHello با host name جایگزینشده، پیش از نسخه اصلی. |
| smuggle-sni | smuggle-sni, real-sni-upstream-node | upstream | فرستادن ClientHello واقعی روی helper branch، ClientHello generated همطول روی مسیر عادی و نگه داشتن ترافیک بعدی پشت ordering deadline. |
| overlap-sni | overlap-sni | upstream همراه cleanup مربوط به close در downstream | بازنویسی یک sequence range در دو نسخه که SYN جعلی میان آنها قرار دارد. |
| ech-sni-trick | ech-sni-trick | upstream | فرستادن ClientHello جعلی جاسازیشده توسط TlsClient در payloadِ ECH، به شکل TCP segment خارج از ترتیب. |
| synfin-sni | synfin-sni | upstream | ترکیب نخستین بخش واقعی TLS، packet بستن، SYN جعلی، ClientHello ساختهشده، filler اختیاری random/شبیه TLS و بقیه بایتهای واقعی. |
| smuggle-fin | smuggle-fin, real-fin-upstream-node | upstream همراه ردیابی echo در downstream | تزریق `FIN |
| TCP flag rewrite | up-tcp-bit-*, dw-tcp-bit-* | دو طرف | اجبار، toggle یا نگاشت دوباره flagهای TCP. |
| port ghost | source-port-ghost, dest-port-ghost | اعمال در upstream، بازیابی در downstream | حمل port اصلی در انتهای packet و تغییر port فعال transport. |
Protocol Swap
در IPv4، فیلد Protocol مشخص میکند payload از چه نوعی است. برای مثال TCP عدد
6 و UDP عدد 17 دارد.
| گزینه | نوع | توضیح |
|---|---|---|
protoswap | integer | نام مستعار قدیمی protoswap-tcp. |
protoswap-tcp | integer | protocol number جایگزین برای packetهای TCP. |
protoswap-udp | integer | protocol number جایگزین برای packetهای UDP. |
بازه معتبر شماره پروتکل جایگزین از 0 تا 255 است.
اگر شماره پروتکل packet برگشتی با یکی از مقادیر جایگزین منطبق باشد، node آن را به TCP یا UDP عادی برمیگرداند. برای تونل دوطرفه، سمت مقابل هم باید تنظیمات سازگار داشته باشد.
مقدار جایگزین نباید برابر شماره پروتکل واقعی TCP یعنی 6 یا UDP یعنی 17
باشد؛ حتی اگر فقط یکی از این دو خانواده تنظیم شده باشد. در غیر این صورت،
ترافیک واقعی با آن شماره از ترافیک mapشده قابل تشخیص نیست. اگر swap برای هر دو
خانواده TCP و UDP فعال باشد، مقدارهای جایگزین آنها نیز باید متفاوت باشند.
گزینه protoswap-tcp-2 حذف شده است و config قدیمی شامل آن رد میشود.
برای مهاجرت، تنها از mapping برگشتپذیر protoswap-tcp استفاده کنید. این تغییر
همچنین تضمین میکند همه fragmentهای یک datagram از IPv4 شماره پروتکل یکسانی
دریافت کنند.
ProtoSwap پیش از mapping، سهم آدرس مبدأ و مقصد در checksumِ TCP/UDP را به
صفر تبدیل میکند و هنگام decoding سهم آدرسهای فعلی را برمیگرداند. خود آدرسهای
IP تغییر نمیکنند. checksumِ packet mapشده همچنان از شمارهٔ واقعی TCP (6) یا
UDP (17) در pseudo-header استفاده میکند، نه شمارهٔ جایگزین. بنابراین تغییر
آدرس بین دو سمت، checksum معتبر را پس از decoding معتبر نگه میدارد؛ بدون اسکن
payload، افزایش اندازهٔ packet یا state برای هر flow. هر دو سمت باید همین قرارداد
checksum را اجرا کنند.
بدون درخواست قبلی محاسبهٔ مجدد، این اصلاح افزایشی باقیماندهٔ checksum نامعتبر و
قابلیت تشخیص خرابی payload را حفظ میکند. checksum صفر در UDPِ IPv4 به معنی
غیرفعال بودن است و صفر میماند؛ صفر حاصل از محاسبه برای UDPِ فعال به صورت
0xffff ذخیره میشود. صفر در TCP یک مقدار عادی checksum است.
همهٔ fragmentها شمارهٔ پروتکل یکسان میگیرند، اما تنها fragment حاوی هر دو بایت checksumِ transport اصلاح آدرس را انجام میدهد؛ حتی اگر checksumِ TCP پس از fragment اول ۸ یا ۱۶ بایتی در fragment بعدی باشد. fragmentation پیش از encoding یا بین دو سمت پشتیبانی میشود؛ خودِ تبدیل checksum به reassembly یا state ترتیب نیاز ندارد. انتخاب worker پس از decode از جدول وابستگی fragmentها استفاده میکند. آدرسهای نهایی همهٔ fragmentهای یک datagram باید سازگار باشند. طول نامعتبر IPv4/transport، فیلد checksum ناقص یا هندسهٔ محلی نامعتبر fragment بدون تغییر عبور میکند و درخواست checksum آن حفظ میشود. بایتهای اضافی buffer بیرون از طول IPv4 وارد تبدیل نمیشوند؛ بررسی طول در writer نهایی همچنان برقرار است.
شکلدهی packet native و port ghost پیش از normalization در upstream اجرا میشوند. در هر دو جهت callback، درخواست قبلی checksum باید پیش از encoding با پروتکل واقعی و آدرسهای واقعی کامل شود؛ شکست آن مانع mapping میشود. برای fragmentها این درخواست فقط headerِ IPv4 را اصلاح میکند. در decoding، سهم آدرس و پروتکل واقعی پیش از بازیابی port یا parse کردن TCP/TLS برگردانده میشوند. درخواست قبلی در downstream به مسیر بعدی تحویل داده میشود؛ decoding در upstream میتواند همانجا آن را کامل کند. ProtoSwap درخواست جدیدی برای محاسبهٔ مجدد ایجاد نمیکند. ویرایش دلخواه payload در حالت mapشده به تبدیلی نیاز دارد که این قرارداد checksum را بشناسد.
اگر یک packet در upstream از قبل مقدار جایگزین تنظیمشده این node را داشته باشد،
IpManipulator این node را بخش صریح unwrap در یک جفت transport زنجیرهای در نظر
میگیرد: پروتکل واقعی را بازیابی میکند، درخواست موجود checksum را با همان پروتکل کامل میکند
و packet را بدون mapping به جلو میفرستد. اگر باید packet پس از یک node بعدی
همچنان wrapped بماند، همان protocol swap را روی آن node فعال نکنید.
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-bitflags | boolean | false | پیش از بازنویسی، بایت اصلی flagهای TCP را ذخیره میکند تا جهت مقابل بتواند packet را بازیابی کند. |
این حالت زمانی مفید است که میخواهید packet در یک جهت با TCP flagهای تغییرکرده عبور کند و سپس در جهت برگشت به شکل اصلی TCP flags برگردد.
روند جهتها:
- در جهتی که actionهای
up-tcp-bit-*یاdw-tcp-bit-*دارد،IpManipulatorبایت کامل flagهای اصلی TCP را بهعنوان آخرین بایت packet IPv4 ذخیره میکند. - سپس TCP flagهای فعال packet را طبق actionهای همان جهت بازنویسی میکند.
- طول کل IPv4 را یک بایت زیاد میکند و packet را برای محاسبه دوباره checksum علامت میزند.
- در جهت مقابل، اگر آن جهت action بازنویسی TCP-bit نداشته باشد ولی جهت دیگر action داشته باشد، آخرین بایت packet خوانده میشود، TCP flags از آن بازیابی میشود، آن بایت حذف میشود، طول IPv4 یک بایت کم میشود و checksumها باید دوباره محاسبه شوند.
این actionهای تنظیمشده TCP-bit هستند که مشخص میکنند کدام جهت بایت پشتیبان را اضافه و کدام جهت آن را بازیابی کند. صرفاً فعالکردن این گزینه، نقش ثابتی به upstream یا downstream نمیدهد:
| actionهای تنظیمشده TCP-bit | رفتار upstream | رفتار downstream |
|---|---|---|
| فقط upstream | flagهای اصلی را پشتیبان میگیرد، یک بایت اضافه میکند و actionهای upstream را اعمال میکند. | flagها را از آخرین بایت بازیابی و آن بایت را حذف میکند. |
| فقط downstream | flagها را از آخرین بایت بازیابی و آن بایت را حذف میکند. | 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 کار میکند. همچنین فرض میکند آخرین بایت اضافهشده در مسیر بدون تغییر باقی میماند. اگر node یا دستگاه شبکهای trailing payload را حذف، بازنویسی، padding یا تفسیر کند، بازیابی ممکن است شکست بخورد یا flag اشتباه برگردد.
هر عملیات بازنویسی همراه با preserve-tcp-bitflags یک بایت به packet اضافه
میکند. پیش از خروج نهایی، اگر این بایت، بایتهای port ghost یا هر دو باعث شوند
یک packet کامل IPv4/TCP از mtu بزرگتر شود، 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 خود را مستقیماً ارسال میکند.
اگر هر دو عملیات لازم هستند، از دو node استفاده کنید:
... -> IpManipulator(stateful SNI only)
-> IpManipulator(sni-blender and/or packet-duplicate)
-> ...
در traversal مربوط به upstream، node stateful باید ابتدا قرار گیرد. duplication
پیش از node stateful، ورودیهای آن را duplicate میکند و معادل duplicate کردن
خروجی نهایی آن نیست. ترکیب sni-blender و packet-duplicate وقتی هیچ trick
stateful مربوط به SNI فعال نباشد همچنان پشتیبانی میشود.
ترکیبهای ردشده TCP-bit
در ترکیبهای زیر ساخت config شکست میخورد:
- هر 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 تغییر میدهد.
| گزینه | نوع | اجباری | توضیح |
|---|---|---|---|
sni-blender | boolean | بله | قطعهقطعه کردن TLS ClientHello و فرستادن قطعهها با ترتیب درهم را فعال میکند. |
sni-blender-packets | integer | وقتی فعال است بله | تعداد 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 به این node ClientHello قابل تشخیص شود. - در سناریوهای محلی یا serverless، مسیریابی و capture سیستمعامل باید طوری باشد که packetهای مورد نظر از WaterWall عبور کنند.
این trick روی همه شبکهها کار نمیکند. بعضی مسیرها packetهای fragmentشده را دور میاندازند.
Packet Duplicate
| گزینه | نوع | توضیح |
|---|---|---|
packet-duplicate | integer از 1 تا 16 | به این تعداد کپی اضافه از packet فرستاده میشود و سپس نسخه اصلی یک بار ارسال میشود. |
این مرحله در مسیر نهایی ارسال و پس از دیگر trickهای فعال اجرا میشود.
Port Ghost
port ghost در مرحله نهایی ارسال upstream اعمال و در ابتدای downstream بازیابی میشود.
| گزینه | نوع | توضیح |
|---|---|---|
source-port-ghost | boolean | source port اصلی به انتهای packet افزوده میشود و source port فعال به یک high port قطعی تغییر میکند. |
dest-port-ghost | boolean | destination 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 کاملشده در
mtu جا میشود و سپس 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، tuple اصلی خود را حفظ میکنند و trailer مربوط به port ghost نمیگیرند.
first-sni
first-sni یک یا چند کپی decoy از TLS ClientHello خود client را، که فقط host
name آنها جایگزین شده است، پیش از ClientHello واقعی همان اتصال TCP روی wire
قرار میدهد. این trick فقط در upstream عمل میکند و هرگز ترافیک downstream را
تغییر نمیدهد.
بسیاری از سامانههای inspection یک اتصال TLS را براساس نخستین ClientHelloای که
روی یک 4-tuple میبینند دستهبندی میکنند و همان verdict را تا پایان اتصال نگه
میدارند. first-sni این رفتار را هدف میگیرد: دستگاهی که به نخستین
ClientHello متکی باشد نام decoy را ثبت میکند، در حالی که انتظار میرود server
مقصد فقط نسخه واقعی را دریافت کند. این همان ایده کلاسیک desynchronization با
«fake packet» است که روی فیلد SNI اعمال شده است. خود ClientHello واقعی هرگز
بازنویسی، fragment، split یا reorder نمیشود و همچنان دستنخورده، دقیقاً یک بار
و با sequence واقعی خود فرستاده میشود.
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
first-sni | string غیرخالی با طول 1 تا 65535 بایت | اجباری برای فعالسازی | Host name نوشتهشده در کپیهای decoy ClientHello. فقط نخستین entry از نوع host_name در کپی بازنویسی میشود. |
first-sni-count | integer از 1 تا 16 | 1 | تعداد کپیهای جعلی پیش از نسخه اصلی. |
first-sni-replay-delay | integer برحسب ms، با مقدار >= 0 | 0 | فاصله زمانی میان کپیهای decoy پس از کپی نخست. فقط وقتی count بزرگتر از 1 باشد اثر دارد. |
first-sni-final-delay | integer برحسب ms، با مقدار >= 0 | 0 | تأخیر اضافه میان آخرین کپی decoy و ClientHello اصلی. |
first-sni-ttl | integer از 0 تا 255 | حفظ TTL اصلی | TTL مربوط به IPv4 برای کپیهای decoy. مقدار 0 یک override واقعی است؛ برای حفظ TTL اصلی این فیلد را حذف کنید. |
first-sni-random-tcp-sequence | boolean | false | برای هر کپی decoy یک TCP sequence تصادفی 32 بیتی تازه انتخاب میکند. |
اگر (first-sni-count - 1) * first-sni-replay-delay یا جمع آن با
first-sni-final-delay از بازه پشتیبانیشده millisecond بدون علامت 32 بیتی
بیشتر شود، startup شکست میخورد.
آنچه روی wire فرستاده میشود
با count پیشفرض 1 و هر دو delay برابر 0، همه مراحل داخل یک callback مربوط
به packet upstream انجام میشوند:
- TCP handshake دستنخورده است.
SYN، SYN|ACKو نخستینACKبدون تغییر عبور میکنند. - client، ClientHello خود را در یک segment با sequence برابر
S، طول payload برابرLو معمولاً flagهایPSH|ACKمیفرستد. IpManipulatorاین payload را parse میکند. یک TLS record از نوعhandshake (0x16)لازم است که پیام ClientHello دقیقاً تمام body آن record را پر کند و extension از نوعserver_nameبا دستکم یک entry از نوعhost_nameداشته باشد. payloadی که فقط ابتدای چنین recordی را دارد وارد capture چندsegmentی توضیحدادهشده در ادامه میشود؛ سایر حالتها fail-open هستند.- packet clone میشود و فقط نخستین string از نوع
host_nameجایگزین میگردد. شش فیلد طول با delta یکسان اصلاح میشوند: طولhost_name، طول server-name list، طول extension مربوط بهserver_name، طول extensions، طول handshake مربوط به ClientHello و طول TLS record؛ علاوه بر آن total length مربوط به IPv4 نیز اصلاح میشود. - سایر بایتهای کپی از packet اصلی هستند: IPv4 identification، flags و TTL
یکسان، TCP portها، sequence، acknowledgement، window، flags و optionهای
یکسان، و همان TLS version،
random، session id، فهرست cipherها و سایر extensionها. - اگر
first-sni-ttlتنظیم شده باشد، TTL کپی را بازنویسی میکند؛ اگرfirst-sni-random-tcp-sequenceفعال باشد نیز TCP sequence آن را جایگزین میکند. - ابتدا decoy با checksumهای محاسبهشده مجدد IPv4 و TCP ارسال میشود. این trick گزینه bad-checksum ندارد؛ هر دو packet همیشه با checksum معتبر خارج میشوند.
- بلافاصله پس از آن ClientHello اصلی، بدون تغییر و با checksumهای محاسبهشده مجدد forward میشود.
Sequence numberها و دور نگهداشتن decoy از server
تا وقتی first-sni-random-tcp-sequence فعال نباشد، decoy همان sequence واقعی
ClientHello یعنی S را حمل میکند؛ بنابراین هر دو segment یک جایگاه یکسان در
TCP byte stream را ادعا میکنند. اگر نام تنظیمشده d بایت بلندتر باشد، decoy
بازه S تا S + L + d را میپوشاند، در حالی که packet واقعی S تا S + L
را پوشش میدهد؛ نام کوتاهتر بازه کوچکتری میسازد. دو segment overlap دارند و
هر receiver نسخهای را نگه میدارد که سیاست overlap آن بپذیرد.
این overlap برای دستگاه inspection هدف trick است، اما برای server مشکل ایجاد میکند. اگر decoy بهصورت داده معتبر و in-window به server برسد، به TLS server تحویل میشود و segment واقعی بعدی duplicate محسوب خواهد شد؛ در نتیجه server به نام decoy پاسخ میدهد. چون TLS stack سمت client transcript را از ClientHello خودش محاسبه کرده است، چنین اتصالی معمولاً بهجای انتقال ترافیک با خطای certificate یا handshake شکست میخورد.
دستکم یکی از این دو سازوکار را برای دور نگهداشتن decoy از مقصد تنظیم کنید:
first-sni-ttlرا آنقدر کم انتخاب کنید که routerی میان نقطه inspection و server، decoy را drop کند. مقدار مناسب به مسیر وابسته است و باید مثلاً با traceroute اندازهگیری شود؛ router حذفکننده معمولاً ICMP time-exceeded را به آدرس client برمیگرداند. مقدار خیلی کم باعث میشود inspector اصلاً decoy را نبیند و مقدار خیلی زیاد آن را به server میرساند.first-sni-random-tcp-sequence، decoy را بسیار بیرون از receive window سمت server قرار میدهد. یک TCP stack منطبق، داده out-of-window را حذف میکند و حداکثر یک duplicate یا challenge ACK میفرستد، در حالی که inspectorی که بدون اعتبارسنجی sequence numberها فقط براساس جهت reassemble میکند ممکن است decoy را ClientHello همین اتصال parse کند. این گزینه روی inspectorهایی که sequence و window را دنبال میکنند اثری ندارد.
این دو گزینه را میتوان همزمان بهکار برد. خالی گذاشتن هر دو فقط زمانی منطقی است که مقدار تنظیمشده با host name واقعی برابر باشد؛ در آن صورت کپی به یک duplicate segment ساده تبدیل میشود و اثر decoy از بین میرود.
آنچه یک دستگاه میانی reconstruct میکند به سیاست overlap خودش بستگی دارد: inspector مبتنی بر packet دو ClientHello میبیند و معمولاً براساس اولی عمل میکند؛ inspector دارای reassembly که نخستین بایتهای رسیده برای هر sequence range را نگه میدارد نیز به decoy میرسد؛ دستگاهی که window یا TTL را validate میکند یا داده دیرتر را برای range همپوشان ترجیح میدهد، نام واقعی را میبیند. هیچ standardی این نتیجهها را تضمین نمیکند؛ رفتار کاملاً وابسته به مسیر است.
تکرارها و تأخیرها
- کپی شماره 1 همیشه synchronously و پیش از همه چیز خارج میشود.
- اگر
first-sni-replay-delayبرابر0باشد، تمامfirst-sni-countکپی در همان callback و پشت سر هم، بدون pacing ساخته و ارسال میشوند. - با replay delay غیرصفر، کپیهای 2 تا N در زمانهای
1 x delay، 2 x delayتا(N-1) x delayپس از کپی نخست schedule میشوند. همه آنها از قبل ساخته میشوند، پس هر کپی pending تا زمان ارسال یک buffer کامل packet را نگه میدارد؛ count را معقول نگه دارید. - ClientHello اصلی پس از
(count - 1) * replay-delay + final-delaymillisecond آزاد میشود؛ یعنی اگرfirst-sni-final-delayبرابر0باشد، بیدرنگ پس از آخرین decoy. - در طول این پنجره، packetهای بعدی upstream همان 4-tuple، شامل ACKها و
FIN/RST، پشت packet اصلی نگهداشتهشده در FIFO مخصوص flow با سقف 16 packet و 256 KiB قرار میگیرند تا چیزی از ClientHello دارای delay سبقت نگیرد. packetهای downstream هرگز delay نمیگیرند. delay دادن flow، ACKهای خود client را هم عقب میاندازد؛ مجموع delayها را در حد دهها millisecond نگه دارید. - overflow در FIFO، رسیدن packet همان flow روی worker دیگر یا ناتوانی در arm کردن timer همگی بهترتیب fail-open میشوند: ابتدا packetهای queued flush میشوند و سپس packet جاری.
- یک
SYNآغازین بدون payload روی همان 4-tuple، record مربوط به flow را reset میکند تا tuple استفادهشده مجدد بهصورت flow تازه و بدون delay آغاز شود. هر چیزی که generation قبلی هنوز نگه داشته است، بهجای forward شدن دور ریخته میشود. - اگر هر دو delay برابر
0باشند، هیچ timer یا state تأخیری ساخته نمیشود و trick کاملاً synchronous است. - record هر flow با
stateful-flow-limitمحدود میشود و پس از 20 دقیقه عدم فعالیت expire میگردد. اگر record جدید قابل admission نباشد، schedule با warning به ارسال فوری collapse میکند و همچنان ترتیب مستندشده حفظ میشود.
ClientHello چندsegmentی
capture فقط روی segmentی آغاز میشود که payload آن واقعاً با TLS ClientHello قابلشناسایی شروع شود؛ بنابراین payloadهای نامرتبط و غیر TLS بدون ورود به صف prestart حدسی فوراً fail-open میشوند. segmentهای بعدی که کاملاً contiguous هستند تا کامل شدن TLS record append میشوند. سقفها عبارتاند از 16 packet، record با اندازه 16384 بایت و timeout برابر 1500 ms از زمان آخرین segment پذیرفتهشده.
پس از تکمیل، trick روی یک packet assembled کار میکند: headerهای IPv4 و TCP از
segment نخست، بهعلاوه تمام بایتهای payload captureشده. segmentهای اصلی drop
میشوند و forward نخواهند شد. دو اثر روی wire دیده میشود: مقصد ClientHello
واقعی را بهصورت یک segment بزرگتر و از sequence نخستین segment دریافت میکند،
نه با الگوی segmentation اصلی client؛ و packet coalesced، TCP flags، window و
acknowledgement number نخستین segment را حمل میکند. محتوای بایتها و پیوستگی
sequence حفظ میشوند. اگر packet assembled یا decoy ساختهشده از آن از
mtu بزرگتر باشد، در egress نهایی دوباره با sequenceهای پیوسته به
transport segmentها تقسیم میشود.
وجود gap در sequence، segment خارج از ترتیب یا retransmitted، عبور از محدودیت packet یا record، timeout capture، یا رسیدن packet همان flow روی worker دیگر باعث میشود segmentهای نگهداشتهشده بدون تغییر و بهترتیب آزاد شوند.
fragmentهای IPv4، شامل fragment نخست با MF=1 و fragmentهای بعدی با offset
غیرصفر، هرگز وارد capture مربوط به First-SNI نمیشوند و در مسیر عادی fail-open
ادامه میدهند.
مواردی که first-sni کاری انجام نمیدهد
در شرایط زیر trick بهصورت fail-open ترافیک را بدون تغییر forward میکند:
- packet از نوع IPv4/TCP نیست یا یک fragment از IPv4 است
- payload یک TLS ClientHello record نیست و با آن هم آغاز نمیشود، ClientHello در بیش از یک TLS record پخش شده است، یا طول handshake با body آن record برابر نیست
- ClientHello، extension از نوع
server_nameیا entry از نوعhost_nameندارد - ClientHello یک extension از نوع TLS 1.3
pre_shared_keyبا PSK binderها دارد و مقدار تنظیمشده واقعاً بایتهای SNI را تغییر میدهد.IpManipulatorsecret مربوط به PSK را برای محاسبه binder معتبر ندارد، پس ساخت کپی را skip میکند و مسیر اصلی را دستنخورده میگذارد - substitution باعث overflow در یک فیلد طول 16 بیتی، طول 24 بیتی handshake یا total length مربوط به IPv4 شود، یا طول غیرمثبت بسازد. دلیل log میشود و packet اصلی forward میگردد
- bufferای برای کپی packet در دسترس نباشد
slotهای capture در سطح tunnel مشترک و برای هر worker برابر 16 هستند. وقتی همه مشغول باشند، قدیمیترین capture کنار گذاشته و segmentهای نگهداشتهشده آن بدون تغییر آزاد میشوند.
تفاوت first-sni با سایر trickهای SNI
sni-blenderخود ClientHello واقعی را در لایه IPv4 fragment میکند و host name جدیدی نمیسازد.first-sniیک packet کامل اضافه میکند و نسخه واقعی را دستنخورده میگذارد.smuggle-sni، ClientHello واقعی را از branch کمکی جداگانه میفرستد و کپی ساختهشده را روی مسیر عادی قرار میدهد.first-sniهر دو را از مسیر عادی و با ترتیب decoy سپس واقعی میفرستد.overlap-sniیک transcript چندpacketی باSYNجعلی و دو نسخه از یک sequence range میسازد.synfin-sniعلاوه بر آنFIN/RSTمصنوعی و filler شبیه TLS دارد.first-sniهیچ control packet مصنوعی منتشر نمیکند و ClientHello واقعی را split نمیکند.ech-sni-trickبایتهایی را reorder میکند که از قبل داخل ClientHello واقعی وجود دارند و decoy آن زودتر توسطTlsClientجاسازی شده است؛ در آن trickIpManipulatorهیچ host nameای نمینویسد.
محدودیتها و عوارض جانبی
- بهجز host name و فیلدهای طول آن، decoy با ClientHello واقعی byte-identical
است؛ از جمله IPv4 identification، TLS
randomو session id. دستگاهی که محتوای ClientHelloها را correlate کند میتواند این جفت را تشخیص دهد. - padding extension تغییر نمیکند؛ بنابراین نامی با طول متفاوت، decoyای با اندازه متفاوت از packet واقعی میسازد.
- هر کپی تنظیمشده کل payload مربوط به ClientHello را دوباره میفرستد و نام طولانی میتواند کپی را از MTU بزرگتر کند و در egress باعث transport segmentation بیشتر شود.
- این trick فقط ClientHello سمت client را shape میکند و روی DNS، filtering مبتنی بر address یا دادههای server اثری ندارد.
- همه packetهای یک flow باید روی یک worker بمانند؛ در غیر این صورت trick با warning برای آن flow fail-open میشود.
مثال:
{
"name": "ip-manipulator",
"type": "IpManipulator",
"settings": {
"first-sni": "www.example.com",
"first-sni-count": 2,
"first-sni-replay-delay": 5,
"first-sni-final-delay": 10,
"first-sni-ttl": 6
},
"next": "next-packet-node"
}
smuggle-sni
smuggle-sni از یک TLS ClientHello دو view وابسته به مسیر میسازد. پس از capture
کردن ClientHello واقعی، segmentهای اصلی TCP سمت client را از
real-sni-upstream-node میفرستد و یک ClientHello جداگانه و generated با طول
برابر و host name تنظیمشده را روی branch عادی next قرار میدهد. دو view همان
TCP sequence rangeها را اشغال میکنند، اما بهجای آنکه عمداً به شکل overlap
مرتبشده روی یک branch فرستاده شوند، از entryهای متفاوت branch عبور میکنند.
در topology موردنظر، دستگاه inspection روی branch عادی SNI ساختهشده را parse میکند و مقصد ClientHello واقعی را از helper branch میگیرد. خود trick این جداسازی را تضمین نمیکند. اگر branchها در نقطه نامناسب همگرا شوند، packetها reorder شوند یا مقصد view ساختهشده را بپذیرد، اتصال ممکن است شکست بخورد.
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
smuggle-sni | string با طول 1 تا 255 بایت | وجود ندارد (غیرفعال) | trick را فعال و host name مربوط به ClientHello ساختهشده روی branch عادی را تعیین میکند. limit مؤثر از TlsClient داخلی میآید. |
smuggle-sni-delay-ms | integer برحسب ms، با مقدار >= 0 | 0 | مدت زمان پس از capture موفق تا batch ساختهشده eligible شود. این گزینه پنجره جداگانه مربوط به ordering ترافیک بعدی را کنترل نمیکند. |
real-sni-upstream-node | string | هنگام فعال بودن اجباری | branch کمکی که packetهای ClientHello واقعی captureشده را دریافت میکند. |
تنظیم مشترک stateful-flow-limit نیز اعمال میشود، مقدار 32 تا 1048576
میپذیرد و پیشفرض آن 65536 است. node هم به next عادی در سطح اصلی و هم به
helper نیاز دارد. helper نامبرده باید وجود داشته باشد، خود این
IpManipulator نباشد، با next تفاوت داشته باشد و هنگام ساخت chain قابل
دسترسی و binding باشد.
{
"name": "ip-manipulator",
"type": "IpManipulator",
"settings": {
"smuggle-sni": "www.example.com",
"smuggle-sni-delay-ms": 20,
"real-sni-upstream-node": "real-clienthello-path"
},
"next": "generated-clienthello-path"
}
record ساختهشده از fingerprint مربوط به TlsClient داخلی، ترتیب ALPN پیشفرض
h2، http/1.1 و key share غیرفعال x25519mlkem768 استفاده میکند. timeout
حدسی prestart برابر 50 ms، timeout record fragmentشده برابر 1500 ms، پنجره
ordering برابر 50 ثانیه، limitهای capture، fingerprint ساختهشده و headerهای
packet هیچ setting اختصاصی Smuggle-SNI ندارند.
Flow و ClientHello انتخابشده
Smuggle-SNI فقط روی packetهای کامل و fragmentنشده IPv4 TCP عمل میکند و تنها پس
از مشاهده یک SYN آغازین بدون payload برای flow record میسازد. opener پذیرفته
شده SYN بههمراه ترکیب اختیاری ECE و CWR دارد؛ وجود ACK، FIN، RST،
PSH، URG، هر flag دیگری یا payload مربوط به TCP Fast Open آن را ineligible
میکند. اگر این node opener را ندیده باشد، packetهای بعدی آن tuple بدون پردازش
Smuggle-SNI عبور میکنند.
دو packet نخست upstream در flow دنبالشده بدون تغییر عبور میکنند و warmup
محسوب میشوند. معمولاً این دو packet، SYN آغازین و ACK نهایی handshake سمت
client هستند؛ SYN|ACK مربوط به downstream شمرده نمیشود. capture از packet
upstream بعدی آغاز میشود. این قاعده ordinal یعنی ClientHelloای که در دومین
packet upstream ارسال شده است در همان transmission تغییر نمیکند؛ فقط یک کپی
eligible بعدی، مثلاً retransmission پس از warmup، میتواند candidate capture
باشد.
در نقطه capture، TCP payload باید با TLS handshake recordی شروع شود که handshake
آن یک ClientHello دارای نخستین entry قابلاستفاده host_name در SNI باشد.
record کامل تکsegmentی فوراً پذیرفته میشود. record ناقص نگه داشته میشود تا
continuationهای کاملاً contiguous جمعآوری شوند. segment آغازین باید دستکم 9
بایت نخست TLS را داشته باشد تا طول record و ClientHello معلوم باشد؛ prefix
کوتاهتر capture را شروع نمیکند.
- هر continuation باید payload غیرصفر داشته باشد و با حساب معمول 32 بیتی TCP،
sequence آن برابر
previous.seq + previous.payload_lenباشد - capture کامل حداکثر میتواند 16 TCP packet داشته باشد
- کل TLS record شامل header پنجبایتی آن حداکثر میتواند 16384 بایت باشد
- handshake مربوط به ClientHello باید دقیقاً یک TLS record را پر کند؛ ClientHello پخششده در چند TLS record یا همراه با پیام handshake دیگری در همان record پشتیبانی نمیشود
- اگر continuation معتبری آن را جلو نبرد، capture پس از 1500 ms از آخرین fragment پذیرفتهشده expire میشود
ACK-only، retransmission، gap، overlap یا continuation خارج از ترتیب با capture فعال merge نمیشود. این packet تلاش را پایان میدهد و ابتدا packetهای retained و سپس packet جاری را روی مسیر عادی آزاد میکند. fragmentهای IPv4 و packetهای غیرقابل parse این capture را bypass میکنند و ممکن است از segmentهای نگهداشته شده سبقت بگیرند؛ capture تا رسیدن continuation معتبر یا timeout pending میماند.
Smuggle-SNI برای reordering packetها یک speculative prestart queue هم دارد. اگر نخستین candidate capture دارای payload غیرصفر حداقل 128 بایتی باشد، prefix قابل شناسایی ClientHello نداشته باشد و layout صریحاً پشتیبانینشده TLS نباشد، میتوان آن را تا 50 ms پس از آخرین update حدسی نگه داشت تا شاید start مربوط به ClientHello با sequence پایینتر بعداً برسد. حداکثر 16 packet با sequenceهای متمایز نگه داشته میشوند. پس از رسیدن start، فقط packetهایی که exact next sequence را تشکیل میدهند وارد capture ClientHello میشوند؛ packetهای باقیمانده نامرتبط عادی آزاد میشوند. timeout، sequence تکراری، فشار ظرفیت یا جایگزین شدن slot حدسی، packetهای نگهداشتهشده را بدون تغییر آزاد میکند و flow را passthrough میسازد. packet کوتاه غیر TLS یا candidate از نوع ACK-only اصلاً بهصورت حدسی نگه داشته نمیشود و فوراً passthrough را انتخاب میکند. صف prestart بهجز سقف 16 packet و اندازه هر packet IPv4، byte cap جداگانه ندارد.
slotهای capture و prestart از poolهای مشترک در سطح tunnel میآیند؛ اندازه هرکدام برای هر worker تنظیمشده 16 است و با سایر trickهای دارای TLS capture مشترکاند. اگر pool پر باشد، قدیمیترین slot فعال بدون تغییر آزاد میشود تا capture تازه پذیرفته شود. همه fragmentهای یک capture باید روی یک worker مربوط به WaterWall بمانند. continuation روی worker دیگر fail-open میشود: هر packet retained از worker مالک خودش برگردانده میشود، اما ترتیب global اولیه arrival قابل تضمین نیست.
نحوه ساخت دو view مربوط به ClientHello
فرض کنید TLS record واقعی ClientHello با احتساب header پنجبایتی record طول R
دارد. TCP packetهای captureشده P0 ... Pn، payloadهای contiguous با طولهای
L0 ... Ln را حمل میکنند و از TCP sequence برابر S شروع میشوند. packet
نهایی captureشده ممکن است بایتهایی پس از پایان ClientHello record داشته باشد؛
بنابراین L0 + ... + Ln میتواند از R بیشتر باشد.
پس از capture، TlsClient داخلی یک ClientHello کاملاً جدید با smuggle-sni
تنظیمشده میسازد. این یک edit درجا روی SNI یا clone از fingerprint سمت client
نیست. record ساختهشده randomness، extensionها، key share و ALPN مربوط به
client داخلی را دارد. باید دقیقاً به شکل یک ClientHello record parse شود، host
name تنظیمشده را داشته باشد و طول کامل آن G شرط G = R را برآورده کند.
تغییر host تنظیمشده میتواند G و در نتیجه موفقیت این برابری را عوض کند، هرچند
padding و انتخاب extensionها در نسخه generated یعنی رابطه طول لزوماً یکبهیک
نیست.
برای هر packet captureشده Pi، IpManipulator یک packet مربوط به branch عادی
به نام Fi میسازد؛ کل packet IPv4/TCP اصلی را کپی میکند و فقط بخشی از TCP
payload را که با نخستین R بایت stream تقاطع دارد با بایتهای متناظر generated
جایگزین میکند. بنابراین پیش از پردازش نهایی egress:
Fiهمان source/destination address و port، TCP sequence و acknowledgement، طول payload، TCP flags، window و optionها، IPv4 identification و TTL و مرز packet مربوط بهPiرا دارد- concatenate کردن نخستین
Rبایت payload مربوط بهF0 ... Fnدقیقاً ClientHello generated را میسازد - بایتهای payload پس از ClientHello record واقعی بدون تغییر از packet اصلی کپی میشوند
- چون TLS payload عوض شده، checksumها دوباره محاسبه میشوند
سپس packetهای اصلی P0 ... Pn با ترتیب sequence بازسازیشده از
real-sni-upstream-node ارسال میشوند. ارسال helper، tuple اصلی را حفظ میکند
و port-ghost trailer این node روی آن اعمال نمیشود. batch generated یعنی
F0 ... Fn از branch عادی next میرود که در آن protocol swap، port ghost،
checksum و handling مربوط به MTU سازگار همچنان میتوانند اجرا شوند. shaping
نهایی MTU ممکن است packet بزرگ را split کند، پس مرزهای capture فقط تا پیش از آن
مرحله egress حفظ میشوند.
originalهای helper و batch generated عادی، ادامه pipeline مربوط به Smuggle-SNI/stateful-SNI را bypass میکنند تا دوباره inspect نشوند. packet duplication همان node، SNI Blender، سایر trickهای stateful SNI و rewriteهای TCP bit در upstream با Smuggle-SNI ناسازگارند. protocol swap، port ghost، actionهای فقط-downstream مربوط به TCP bit و Smuggle-FIN میتوانند با رعایت رفتار خودشان همزمان باشند؛ packetهای helper واقعی همچنان از مسیر tuple-preserving بالا استفاده میکنند.
ترتیب branchها، delay و barrier پنجاهثانیهای
ممکن است segment نخست captureشده از قبل برای باقی ClientHello منتظر مانده باشد. پس از موفقیت capture و generation، transcript قابل مشاهده چنین است:
t0: send real P0 ... Pn to real-sni-upstream-node
t0 + D: send generated F0 ... Fn to normal next
t0 + W: release later queued upstream packets to normal next
D = smuggle-sni-delay-ms
W = max(50000, D + 1) milliseconds
همه packetهای واقعی پیش از eligible شدن batch جعلی به helper submit میشوند.
در D = 0، batch کامل جعلی synchronously پس از batch واقعی منتشر میشود. میان
segmentهای داخل هیچیک از batchها pacing وجود ندارد. با این حال، latency
branchها میتواند متفاوت باشد و این ترتیب submission، ترتیب arrival پس از عبور
از شبکه را تضمین نمیکند.
تنظیم D فقط batch generated را delay میدهد و زمان release ترافیک بعدی client
را تعیین نمیکند. barrier جداگانه ordering تا t0 + W باز میماند؛ برای تمام
delayهای 0 تا 49999 ms، از جمله صفر، این مقدار 50 ثانیه است و برای delayهای
بزرگتر یک millisecond پس از due time مربوط به fake خواهد بود. packetهای قابل
parse upstream همان flow که در این پنجره میرسند، شامل ACK، retransmission،
application data و FIN/RST، پشت batch کامل generated وارد FIFO میشوند.
packetهای downstream هرگز وارد این barrier نمیشوند. packetهای غیرقابل parse،
fragmentهای IPv4 و flowهای نامرتبط آن را bypass میکنند و ممکن است از ترافیک
retained سبقت بگیرند.
FIFO مربوط به ترافیک بعدی حداکثر 16 packet و 256 KiB نگه میدارد. اگر به هر یک
از limitها برسد، packetای روی worker دیگر بیاید، deadline گذشته باشد یا timer
قابل schedule نباشد، barrier بهصورت fail-open باز میشود: ابتدا همه packetهای
generated ارسالنشده، سپس FIFO retained و بعد packet جاری خارج میشوند. این
ترتیب را در هر owner worker حفظ میکند، اما ممکن است transcript generated را
زودتر از زمان تنظیمشده بفرستد. FIN یا RST upstream داخل صف پشت داده قدیمیتر
میماند و پس از release، record flow را حذف میکند.
packetهای speculative prestart که وارد ClientHello contiguous نشدهاند بیرون از این post-success barrier هستند. هنگام پایان پردازش prestart از مسیر عادی آزاد میشوند و در حالتهای reordering ممکن است پیش از batch generated ظاهر شوند.
آنچه هر participant ممکن است ببیند
- client: فقط segmentهای واقعی ClientHello را میفرستد. WaterWall آنها را از branch عادی suppress میکند، از helper میفرستد و ممکن است packetهای بعدی client را تا پایان ordering window نگه دارد. delayهای capture و barrier میتوانند TCP retransmission، RTO backoff، تغییر congestion window یا stall قابل مشاهده برای application ایجاد کنند.
- Middlebox مسیر عادی: انتظار میرود record generated را با sequenceهای
واقعی client reconstruct و SNI تنظیمشده را parse کند. چون اندازه segment،
flags، acknowledgement و شکل کلی headerها از packetهای client کپی شده است،
envelope مربوط به TCP شبیه flow captureشده است؛ هرچند fingerprint و محتوای
cryptographic مربوط به TLS از
TlsClientداخلی WaterWall میآید. - دستگاه helper path یا مقصد: segmentهای اصلی ClientHello را پس از پایان capture دریافت میکند. اگر route کمکی به مقصد برسد و packetهای generated از آن دور نگه داشته شوند، server همان TLS random، extensionها، key share، SNI و transcript واقعی client را میبیند. برابر بودن طول recordها باعث میشود sequenceهای TCP بعدی هنگام بازگشت ترافیک به مسیر عادی همراستا بمانند.
اگر مقصد فقط ClientHello generated را دریافت کند، ServerHello و key schedule آن با ClientHello مستقل WaterWall متناظر است، نه state مربوط به TLS client؛ پس client معمولاً قادر به ادامه handshake نخواهد بود. اگر هر دو view را بگیرد، بایتهایی متعارض روی sequence rangeهای یکسان دریافت کرده است؛ سیاست first-arrival/last-arrival در reassembly، loss، retransmission، normalization و ترتیب مسیر تعیین میکنند کدام view باقی بماند. batch واقعی نخست submit میشود، اما این امر رفتار first-arrival سمت server را در دو branch مجزا ایمن نمیکند.
در نتیجه trick به topology وابسته است: observerی که باید fake را ببیند باید روی branch عادی باشد؛ endpointی که باید TLS را کامل کند باید view واقعی branch را دریافت کند؛ و ترافیک و responseهای بعدی باید سازگار دوباره به هم بپیوندند. NAT، state مربوط به firewall، routing نامتقارن، anti-spoofing، TCP normalization و MTU متفاوت branchها همگی میتوانند این آرایش را شکست دهند.
Fail-open، بستهشدن و طول عمر flow
پیش از آنکه packet واقعی به helper ارسال شود، در این شرایط تلاش به branch عادی
fail-open میشود: opener flow دیده نشده یا record آن قابل admission نیست،
candidate capture پشتیبانی نمیشود، record malformed یا بدون SNI است، پیوستگی
sequence یا worker affinity از دست میرود، timeout یا limit مربوط به
capture/prestart میرسد، memory یا timer setup شکست میخورد، یا record generated
نامعتبر است یا طول آن با R تفاوت دارد. Smuggle-SNI، originalهای retained را
بدون تغییر از egress عادی forward میکند. در مسیرهای same-worker با ترتیب capture
آزاد میشوند؛ recovery میان workerها نمیتواند ترتیب global روی wire را تضمین
کند.
اگر ClientHello واقعی extension از نوع TLS 1.3 pre_shared_key با PSK binder
داشته باشد و SNI تنظیمشده با SNI واقعی فرق کند، Smuggle-SNI نیز fail-open
میشود. WaterWall secret مربوط به resumption را برای محاسبه binderهای ClientHello
تغییریافته ندارد. source فقط وقتی SNI عوض میشود این guard را اعمال میکند؛ SNI
تنظیمشده برابر بهخاطر این شرط رد نمیشود، هرچند validationهای دیگر record
generated و برابری طول همچنان لازماند.
پس از ارسال originalها به helper، شکست scheduler مربوط به delay نمیتواند آن branch transmission را undo کند و بهجای آن batch generated را فوراً روی branch عادی آزاد میکند. destruction مربوط به tunnel یا flow table، bufferهای capture، fake و FIFO را که هنوز held هستند در teardown دور میاندازد و forward نمیکند.
یک FIN یا RST upstream هنگام prestart یا capture مربوط به ClientHello،
originalهای held را روی مسیر عادی flush و flow را حذف میکند. در مسیر عادی
same-worker، packetهای held پیش از close submit میشوند؛ recovery میان workerها
ترتیب global را تضمین نمیکند. در post-success barrier، close پشت ترافیک قدیمیتر
upstream queue میشود. FIN یا RST downstream هرگز delay نمیگیرد؛ capture
ناتمام را flush میکند، اما flow post-success را فوراً حذف میکند و packetهای
generated یا follow-on upstream را که هنوز delay دارند دور میاندازد. سایر
ترافیک downstream بدون تغییر است. hook سبک downstream میتواند ServerHelloای
را که در یک TCP payload کامل آغاز میشود log کند، اما outcome مربوط به branch
را validate نمیکند و packet را تغییر نمیدهد.
rewriteهای TCP bit در downstream میتوانند در همین node فعال باشند و پیش از hook
logging/close اجرا میشوند. بنابراین اضافه یا حذف کردن FIN/RST با یک setting
از نوع dw-tcp-bit-* روی اینکه Smuggle-SNI close downstream را ببیند و cleanup
را انجام دهد نیز اثر میگذارد.
برای flow پذیرفتهشده، چه success و چه fail-open معمولی، four-tuple نرمالشده
در state مربوط به passthrough باقی میماند و Smuggle-SNI دوباره تلاش نمیکند.
activity میتواند record را تا 20 دقیقه idle نگه دارد. SYN آغازین معتبر یک
generation جدید میسازد و بهجای replay کردن بایتهای قدیمی، packetهای قدیمی
capture/prestart و delay barrier قبلی را دور میاندازد. FIN/RST upstream یا
downstream معمولاً record را طبق رفتار بالا حذف میکند.
تفاوت smuggle-sni با سایر trickها
first-sni، SNI و فیلدهای طول ClientHello واقعی را در کپیهای اضافه edit میکند و decoy و original را هر دو روی branch عادی میفرستد. Smuggle-SNI کل record جایگزین را generate میکند، برابری طول را لازم دارد و packetهای واقعی را از helper route میکند.overlap-sniوsynfin-sniروی branch عادی sequence rangeهای متعارض و TCP control packetهای مصنوعی منتشر میکنند. Smuggle-SNI هیچSYN، FINیاRSTجعلی ندارد؛ split view آن از routing دو branch ایجاد میشود.ech-sni-trickیک decoy byte-identical را که از قبلTlsClientجاسازی کرده استخراج و out-of-order ارسال میکند. Smuggle-SNI از host name تنظیمشده یک ClientHello معمولی تازه میسازد.sni-blenderبدون ساخت transcript دیگر، packet واقعی را در IPv4 fragment و shuffle میکند.smuggle-finاصلاً TLS را inspect نمیکند و هنگام نگه داشتن ترافیک عادی یکFIN|ACKمصنوعی reverse را route میکند.
overlap-sni
overlap-sni دو segment واقعی ClientHello را با یک transcript ساختهشده روی
مسیر عادی upstream جایگزین میکند. ابتدا ابتدای واقعی ClientHello، سپس یک TCP
SYN جعلی، بعد ClientHello generated با host name متفاوت روی همان sequence
numberها و در پایان باقی ClientHello واقعی را میفرستد. مسیر downstream هیچ
packetای را بازنویسی نمیکند و فقط FIN/RST را برای cleanup کردن flow متناظر
میبیند.
در حالی که first-sni به دیده شدن نخست decoy متکی است، overlap-sni، TCP
reassembly را هدف میگیرد. split view موردنظر این است که مقصد نخستین بایتهای
رسیده برای range همپوشان را نگه دارد، اما دستگاه میانی SYN جعلی را flow تازه
بداند یا بایتهای پیشین را با overlap دیرتر جایگزین کند و host name ساختهشده را
parse کند. handling مربوط به TCP overlap و state machineهای middleboxها متفاوت
است؛ بنابراین هیچیک از این viewها تضمین نمیشود.
این trick یکی از تهاجمیترین گزینههای node است. مرزهای TCP segment سمت client را
تغییر میدهد، داخل flow برقرار یک SYN خارج از handshake تزریق میکند، ترافیک
بعدی upstream را delay میدهد و بعضی layoutهای ClientHello را عمداً block
میکند. آن را با packet captureهای نماینده و روی مسیر واقعی آزمایش کنید.
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
overlap-sni | string با طول 1 تا 255 بایت | اجباری برای فعالسازی | Host name مربوط به ClientHello generated. مقدار طولانیتر startup node را متوقف میکند، چون TlsClient داخلی limit برابر 255 بایت دارد. |
overlap-sni-delay-ms | integer برحسب ms، با مقدار >= 0 | 0 | تأخیر میان جفت real-prefix/fake-SYN که فوراً ارسال میشود و packet X یعنی ClientHello generated. continuationهای واقعی با فاصله ثابت 2 ms دنبال میشوند. |
overlap-sni-hold-timeout-ms | integer برحسب ms، با مقدار > 0 | 50 | بیشترین زمان انتظار برای segment contiguous بلافاصله بعدی که ClientHello انتخابشده را کامل میکند؛ expiry، segment held را بدون تغییر آزاد میکند. |
overlap-sni-syn-ttl | integer از 0 تا 255 | کپی TTL از packet نگهداشتهشده | TTL مربوط به IPv4 فقط برای TCP SYN جعلی. مقدار 0 یک override واقعی است؛ برای کپی TTL از segment held، فیلد را حذف کنید. |
این trick به next عادی در سطح اصلی نیاز دارد.
hello generated توسط TlsClient داخلی ساخته میشود و از client واقعی کپی
نمیشود؛ بنابراین fingerprint همان client داخلی، فهرست ALPN پیشفرض
(h2، http/1.1) و key share غیر post-quantum را دارد. طول آن را G در نظر
بگیرید. host name تنظیمشده طولانیتر معمولاً G را افزایش میدهد، range
همپوشان را بزرگتر میکند و شرط قرار داشتن SNI واقعی در offset برابر یا بزرگتر
از G را دشوارتر میسازد. در runtime اگر G بیش از 900 بایت یا بیش از مجموع
دو payload واقعی captureشده باشد، generation بهصورت fail-open شکست میخورد.
overlap-sni-delay-ms در یک پنجره absolute پس از transcript نیز مشارکت دارد که
برابر max(5000 ms, overlap-sni-delay-ms + 5 ms) است. مقدارهای 0 تا 4995
timing مربوط به packet X را تغییر میدهند، اما deadline پنجثانیهای packetهای
بعدی upstream را عوض نمیکنند؛ مقدارهای بزرگتر deadline را امتداد میدهند.
افزایش delay به observer زمان بیشتری برای پردازش SYN جعلی میدهد و احتمال
سبقت X از prefix واقعی در شبکه را کم میکند، اما latency و خطر retransmission
یا timeout را بالا میبرد.
overlap-sni-hold-timeout-ms بهتر است بهطور محسوسی از TCP retransmission
timeout سمت sender کمتر باشد. افزایش آن فقط وقتی کمک میکند که segment بعدی
ClientHello بیش از 50 ms دیرتر برسد؛ اگر retransmission زودتر برسد، pair به شکل
fail-open آزاد میشود.
overlap-sni-syn-ttl کم میتواند باعث شود دستگاه inspection نزدیک
IpManipulator، SYN جعلی را ببیند ولی router بعدی پیش از مقصد آن را expire
کند. مقدار قابلاستفاده به طول مسیر و جایگاه WaterWall بستگی دارد. مقدار 0
معمولاً packet را به link محلی محدود میکند. این setting روی هیچ packet دیگری
از transcript اثر ندارد.
stateful-flow-limit مشترک، overlap flow table را نیز محدود میکند. اگر table
نتواند یک SYN آغازین را admit کند، flow دنبال نمیشود و بدون تغییر عبور میکند.
Flow و packetهای انتخابشده
flow record فقط با یک SYN آغازین بدون payload ساخته میشود: SYN، با ECN
اختیاری ECE/CWR، اما بدون ACK، FIN، RST یا payload. پس SYN data مربوط
به TCP Fast Open eligible نیست. ترافیک 4-tupleای که این node SYN آغازینش را
ندیده است بدون تغییر میگذرد. یک SYN آغازین بعدی روی همان tuple، generation
تازهای میسازد.
- Warmup — دو packet نخست upstream بدون تغییر عبور میکنند. در اتصال عادی
اینها خود
SYNوACKتکمیلکننده handshake سمت client هستند؛ بنابراین نقطه تصمیم packet سوم upstream، یعنی نخستین data segment سمت client است. - Decision — packet سوم inspect میشود. packet بدون payload، payload غیر TLS ClientHello و ClientHello record کامل همگی flow را مستقیماً به passthrough میبرند و بدون تغییر forward میشوند. فقط payloadی که با ClientHello شروع شود ولی آن را کامل نکند نگه داشته میشود.
- Hold — segment داخل
IpManipulatorretained میشود و هیچ چیز از آن ارسال نمیگردد. timer باoverlap-sni-hold-timeout-msآن را در صورت ادامه پیدا نکردن flow بدون تغییر آزاد میکند. - Pair — packet بعدی upstream باید payload غیرصفر داشته باشد که دقیقاً از
held.seq + held.payload_lenآغاز شود. دو payload در مجموع باید یک ClientHello قابل parse، شامل host name آن و در یک TLS record، داشته باشند. در صورت success با transcript زیر جایگزین میشوند. ACK-only، gap در sequence، overlap/retransmission، record همچنان ناقص، ClientHello پخششده در چند TLS record یا SNI malformed هر دو packet را بدون تغییر آزاد و flow را passthrough میکند. trick دقیقاً برای یک completion segment منتظر میماند، نه تعداد نامحدودی از segmentها.
چون فقط نخستین ClientHello ناقص hold میشود، برای clientی که ClientHello آن در یک TCP segment جا میگیرد کاری انجام نمیدهد. قاعده ordinal packet همچنین یعنی یک packet اضافه client میان handshake ACK و ClientHello میتواند حتی در صورت split بودن ClientHello این trick را غیرفعال کند.
Transcript موفق روی wire
segment held و segment تکمیلکننده strictly contiguous اعتبارسنجی میشوند:
current.seq == held.seq + held.payload_len و هر دو payload غیرصفر دارند؛ سپس
بهطور مجازی در یک payload ترکیب میشوند. IpManipulator از TlsClient داخلی
ClientHello خودش را برای overlap-sni میسازد و host name واقعی را از بایتهای
ترکیبشده parse میکند.
فرض کنید:
Sنخستین TCP sequence مربوط به segment held استHطول TCP payload آن segment استRمجموع طول دو payload واقعی استGطول کامل ClientHello generated استO، offset مربوط به host name واقعی از آغاز TCP payload ترکیبشده است
transcript موفق به 0 < G <= 900، G <= R و O >= G نیاز دارد. هنگام رسیدن
segment واقعی دوم، مسیر عادی upstream موارد زیر را emit یا schedule میکند:
- packet Y، فوری — بایتهای واقعی
[0, G)با sequence برابرS. headerهای IP/TCP و TCP flags را از packet held کپی میکند. این نسخه واقعی range همپوشان است. SYNجعلی، بلافاصله پس از Y — packet بدون payload روی همان 4-tuple با sequence برابرS - 1، acknowledgement برابر0و فقط flag مربوط بهSYN. سایر فیلدهای header IPv4/TCP از packet held کپی میشوند، از جمله TCP header length، optionها و window؛ فقط length، sequence، acknowledgement، flags، identification، TTL اختیاری و checksumها عوض میشوند.- packet X، پس از
overlap-sni-delay-ms— ClientHello generated با طول دقیقاًGو sequence برابرS. همان interval مربوط به packet Y را میپوشاند، اما host name تنظیمشده و fingerprint مربوط بهTlsClientداخلی را دارد. - Continuation واقعی، با فاصلههای 2 ms پس از X — اگر
G < Hباشد، یک packet بایتهای واقعی[G, H)را درS + Gحمل میکند و سپس packetای با segment تکمیلکننده در sequence اصلیS + Hمیآید. اگرG >= Hباشد، فقط بخش لازم باقیمانده از segment تکمیلکننده، یعنی بایتهای واقعی[G, R)، درS + Gفرستاده میشود؛ بایتهای زودتر آن قبلاً داخل Y قرار گرفتهاند.
همه packetها با checksum محاسبهشده مجدد خارج میشوند و IPv4 identification
افزایشی خودشان را دارند: Y از identification مربوط به packet held استفاده
میکند، سپس fake SYN، X و هر continuation آن را یکییکی افزایش میدهند. Y و X
از flags مربوط به packet held استفاده میکنند. وقتی continuation مربوط به
segment held پیش از continuation مربوط به segment تکمیلکننده قرار میگیرد،
اولی فقط CWR، ECE، URG و ACK را نگه میدارد و packet آخر flags مربوط
به segment تکمیلکننده را دارد.
دو packet اصلی recycle میشوند و هرگز روی wire ظاهر نمیشوند. بسته به اینکه
G وارد segment تکمیلکننده شود یا نه، replacement چهار یا پنج packet دارد.
scheduling محلی ترتیب بالا را حفظ میکند، اما شبکه هنوز ممکن است packetها را
drop یا reorder کند. delay تنظیمشده تنها separation قابل تنظیم است؛ فاصله 2 ms
continuationها ثابت است.
آنچه هر participant میبیند
- مقصد: انتظار میرود ابتدا packet Y را بپذیرد و بایتهای واقعی آن را نگه
دارد. اگر
SYNجعلی به endpoint TCP برقرار برسد، stackهای مبتنی بر standard معمولاً آن را drop و challenge یا duplicate ACK میفرستند؛ stackها، firewallها و load balancerهای دیگر ممکن است اتصال را reset یا مختل کنند. اگر اتصال برقرار بماند، receiver با سیاست first-arrival-wins، X را duplicate متعارض برای[S, S + G)میداند و continuation stream واقعی را کامل میکند. در این حالت TLS layer همان ClientHello اصلی را میگیرد. - Middlebox دارای reassembly: در Y یک prefix واقعی ناقص دریافت میکند؛ شرط
O >= Gتضمین میکند host name واقعی داخل آن prefix نباشد. اگرSYNجعلی state مربوط به flow را reset کند، X یک ClientHello کامل با host name تنظیمشده خواهد بود. دستگاهی که reset نمیکند ولی بایتهای دیرتر را در overlap ترجیح میدهد نیز ممکن است به همین نتیجه برسد. بایتهای واقعی بعدی از انتهای X یا پس از آن شروع میشوند و پس از دستهبندی hello generated ممکن است نادیده گرفته شوند. - client: دو segment اصلی را فرستاده، اما
IpManipulatorآنها را suppress و transcript جایگزین را بهجای آن منتشر کرده است. client ممکن است duplicate یا challenge ACK و reset ناشی ازSYNجعلی دریافت کند. اگر server بایتهای واقعی reconstructed را acknowledge نکند، client ممکن است segmentهای اصلی ClientHello را retransmit کند.
split view موردنظر به رسیدن Y پیش از X به server، پایان ندادن state سمت server
توسط SYN جعلی و متفاوت بودن policy مربوط به middlebox با server وابسته است.
loss، reordering مسیر، TCP normalization، SYN proxy و دستگاههایی که receive
window را validate میکنند همگی میتوانند تکنیک را بیاثر کنند. بهویژه اگر X
پیش از Y به مقصد برسد، مقصد ممکن است بهجای بایتهای واقعی، بایتهای generated
را بپذیرد و transcript موردنظر TLS شکل نگیرد.
Flowهای blocked
implementation لازم میداند hello generated حداکثر در ابتدای host name واقعی
پایان یابد: O >= G. به این ترتیب SNI واقعی کاملاً بیرون از range مبهم و داخل
continuation واقعی بعدی قرار میگیرد. اگر O < G باشد، trick به forwarding عادی
fallback نمیکند؛ نام واقعی و هر دو offset را log میکند، packet held و جاری را
drop میکند و flow را blocked علامت میزند.
flow blocked همه packetهای بعدی upstream را drop میکند. FIN یا RST upstream
record را حذف میکند ولی خودش هم drop میشود؛ FIN یا RST downstream بدون
تغییر forward میشود و record را هم حذف میکند. IpManipulator resetی برای
توضیح حذف داده به client inject نمیکند؛ نتیجه معمول stall اتصال و سپس timeout
سمت client یا close آغازشده از server/path است.
این انتخاب عمداً fail-closed است، نه fail-open، و خطر اصلی فعال کردن
overlap-sni محسوب میشود. وقتی hello generated نسبت به hello واقعی بزرگ باشد
احتمال آن بیشتر میشود. host name تنظیمشده طولانیتر معمولاً G را افزایش
میدهد و extension order و padding سمت client واقعی O را تعیین میکنند. بسیاری
از ClientHelloهای عادی SNI را برای این شرط بیش از حد زود قرار میدهند؛ بنابراین
پیش از deployment باید packet captureهای نماینده بررسی شوند.
پنجره delay، retransmissionها و packetهای close
پس از transcript موفق، flow با delay window برابر
max(5000 ms, overlap-sni-delay-ms + 5 ms) وارد passthrough میشود. تا وقتی این
پنجره باز است، packetهای بعدی upstream شامل ACKها و FIN/RST در FIFO هر flow
با سقف 16 packet و 256 KiB queue میشوند و همگی در deadline پنجره آزاد میگردند.
tail scheduleشده transcript همیشه پیش از این FIFO خارج میشود. ترافیک downstream
هرگز delay نمیگیرد.
overflow در FIFO، رسیدن packet flow روی worker دیگر یا ناتوانی در arm کردن timer بهجای ادامه stall، خروجی pending را بهترتیب آزاد میکند: ابتدا باقی transcript ساختهشده، سپس packetهای queued و بعد packet جاری. بنابراین در flow شلوغ، limit FIFO میتواند پنجره اسمی پنجثانیهای را زودتر تمام کند.
FIFO همچنین retransmissionهای TCP پس از ساخته شدن transcript را میگیرد. اما retransmission یا ACK-only که هنگام hold اولیه segment برسد، pair دوpacketی لازم را میشکند، segment held و سپس packet جاری را بدون تغییر آزاد میکند و flow را passthrough میسازد.
مسیر downstream هیچ بایت packetای را عوض نمیکند. FIN یا RST downstream در
hold اولیه، segment upstream held را بدون تغییر آزاد و flow را حذف میکند. در
post-transcript delay window، flow را حذف و continuation ساختهشده و packetهای
queued upstream باقیمانده را cancel میکند؛ خود close packet downstream بدون
تغییر ادامه مییابد. FIN یا RST upstream در delay window پشت transcript
ساختهشده و packetهای queued قدیمیتر باقی میماند.
مواردی که overlap-sni کاری انجام نمیدهد
در شرایط زیر trick بهصورت fail-open segment held را آزاد و ترافیک را بدون تغییر forward میکند:
- این node
SYNآغازین flow را ندیده باشد - packet از نوع IPv4/TCP نباشد یا fragment مربوط به IPv4 باشد
- packet سوم payload نداشته باشد، TLS ClientHello نباشد یا از قبل یک ClientHello record کامل داشته باشد
- segment تکمیلکننده در
overlap-sni-hold-timeout-msنرسد - packet بلافاصله بعدی با segment held از نظر sequence contiguous نباشد، payload نداشته باشد یا هنوز ClientHello تک-record پشتیبانیشده را کامل نکند
- بایتهای ترکیبشده به شکل ClientHello دارای host-name entry parse نشوند
TlsClientداخلی نتواند hello بسازد، hello generated بیش از 900 بایت یا طولانیتر از payload واقعی ترکیبشده باشد- packetهای replacement قابل ساخت نباشند، hold timer arm نشود، flow table نتواند
SYNآغازین را admit کند یا state retained میان workerها جابهجا شود
حالت O < G در بخش «flowهای blocked» استثنای عمدی است: fail-closed میشود و دو
packet واقعی انتخابشده را آزاد نمیکند.
تفاوت overlap-sni با سایر trickهای SNI
first-sni، ClientHello خود client را copy میکند، host name را عوض میکند و decoy را پیش از packet واقعی دستنخورده میفرستد. میتواند روی ClientHello تکsegmentی کار کند و با TTL یا sequence تصادفی تلاش میکند decoy را از server دور نگه دارد؛SYNجعلی handshake تزریق نمیکند.smuggle-sniتا 16 segment از ClientHello را capture میکند، segmentهای واقعی را از branch کمکی و بعد کپی generated را با حفظ segmentation captureشده روی branch عادی میفرستد.overlap-sniدقیقاً دو segment ClientHello را میپذیرد و هر دو view را روی همان مسیر عادی میگذارد.synfin-sniنیز با ClientHello دوsegmentی شروع میشود، اماFIN|ACKیاRST|ACKساختهشده، SYNجعلی، hello generated و filler شبیه TLS را در یک transcript فوری اضافه میکند.overlap-sniclose یا filler مصنوعی ندارد و به overlap دقیق Y/X همراه delay قابل تنظیم متکی است.sni-blenderبدون ساخت host name دیگر، packet واقعی را در لایه IPv4 fragment و reorder میکند.ech-sni-trick، ClientHello داخلی جعلی را که از قبلTlsClientجاسازی کرده reorder میکند؛ packet X این بخش را نمیسازد.
محدودیتها و عوارض جانبی
- فقط packetهای کامل و fragmentنشده IPv4 TCP eligible هستند. IPv6 و fragmentهای IPv4 بدون تغییر عبور میکنند.
nextعادی در سطح اصلی لازم است. همه packetهای crafted از همان مسیر میروند و helper branch اختصاصی overlap وجود ندارد.- hello generated از fingerprint و ALPN پیشفرض
TlsClientداخلی استفاده میکند، نه fingerprint client واقعی. فقط packet generated نام تنظیمشده را دارد و بایتهای ClientHello واقعی هرگز بازنویسی نمیشوند. - مرزهای دو segment اصلی client و IPv4 identification مربوط به segment تکمیلکننده حفظ نمیشوند. replacement چهار یا پنج packet با headerهای کپیشده، identificationهای پشتسرهم IPv4، checksumهای معتبر محاسبهشده مجدد و spacing ثابت 2 ms در tail دارد.
SYNجعلی روی اتصال برقرار یک protocol violation است و ممکن است normalize، challenge، drop یا با reset پاسخ داده شود.overlap-sni-syn-ttlکم میتواند آن را از server دور نگه دارد، ولی تفسیر دستگاه نزدیکتر را تضمین نمیکند.- هنگامی که packet یا delay barrier retained است، packetهای flow باید روی یک worker مربوط به WaterWall باقی بمانند. mismatch worker warning میدهد و fail-open میشود.
- flow recordها پس از 20 دقیقه عدم فعالیت expire میشوند و با
stateful-flow-limitمحدودند. استفاده دوباره tuple یک generation تازه میسازد تا timer قدیمی دادهای را داخل flow جایگزین آزاد نکند. - این trick نمیتواند در یک instance از
IpManipulatorبا stateful SNI دیگر،sni-blender، packet-duplicateیا action مربوط به TCP bit در upstream مشترک باشد. برای packet shaping سازگار دیگر از node جداگانه استفاده کنید.
مثال:
{
"name": "ip-manipulator",
"type": "IpManipulator",
"settings": {
"overlap-sni": "www.example.com",
"overlap-sni-delay-ms": 5,
"overlap-sni-hold-timeout-ms": 50,
"overlap-sni-syn-ttl": 6
},
"next": "next-packet-node"
}
ech-sni-trick
ech-sni-trick با TlsClient هماهنگ میشود تا پیش از آزاد شدن ClientHello
واقعی outer، یک ClientHello از نوع decoy به شکل TCP segment خارج از ترتیب در
معرض دید قرار گیرد. این trick نه ECH را encrypt میکند و نه ClientHello واقعی را
بازنویسی میکند. data transformation فقط در upstream است؛ مسیر downstream
بایتهای packet را دستنخورده میگذارد و فقط TCP FIN یا RST را برای cancel
کردن state retained مشاهده میکند.
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
ech-sni-trick | string با طول 1 تا 255 بایت | اجباری برای فعالسازی | Host nameای که باید در ClientHello جاسازیشده decoy وجود داشته باشد. همان byte string را در TlsClient متناظر تنظیم کنید. |
data-shard-1-delay | integer برحسب ms، با مقدار >= 0 | 0 | زمان میان ارسال decoy خارج از ترتیب و آزاد کردن original packet شماره 1. |
data-shard-2-delay | integer برحسب ms، با مقدار >= 0 | 0 | زمان اضافه پس از original packet شماره 1 تا آزاد کردن originalهای 2 تا N با هم و بهترتیب. در capture تکpacketای نادیده گرفته میشود. |
stateful-flow-limit | integer از 32 تا 1048576 | 65536 | setting مشترکی که ECH flow table را محدود میکند؛ بخش «جدولهای Stateful flow» را ببینید. |
مقدار ech-sni-trick بهصورت byte-for-byte و case-sensitive مقایسه میشود و هیچ
normalization مربوط به DNS name انجام نمیگیرد. تغییر آن فقط در IpManipulator
باعث mismatch در runtime و fail-open شدن ClientHello captureشده بدون تغییر
میشود. تغییر آن در هر دو node، SNI مربوط به decoy را عوض میکند و معمولاً طول
ClientHello جاسازیشده، جایگاهش داخل outer hello و اندازه packet خارج از ترتیب را
نیز تغییر میدهد.
دو delay نسبی هستند. اگر decoy در زمان T خارج شود، original packet شماره 1 در
T + data-shard-1-delay و originalهای 2 تا N در
T + data-shard-1-delay + data-shard-2-delay خارج میشوند. مجموع آنها باید در
بازه millisecond بدون علامت 32 بیتی پشتیبانیشده جا شود. افزایش delay نخست به
observer مبتنی بر packet زمان بیشتری میدهد تا پیش از رسیدن sequenceهای پایینتر
decoy را پردازش کند. افزایش delay دوم، نخستین shard اصلی را از بقیه جدا میکند.
هر دو انتخاب reordering مربوط به TCP و latency مربوط به TLS handshake را طولانی
میکنند. در مقدار 0، ترتیب send همان است اما packetها پشت سر هم خارج میشوند.
این trick به next عادی در سطح اصلی نیاز دارد و setting مربوط به TTL، sequence
تصادفی، packet count یا helper branch ندارد.
نمونه settingهای IpManipulator:
{
"name": "ech-packet-shaper",
"type": "IpManipulator",
"settings": {
"ech-sni-trick": "cover.example",
"data-shard-1-delay": 10,
"data-shard-2-delay": 10
},
"next": "next-packet-node"
}
TlsClient سازنده این flow باید همان مقدار decoy را داشته باشد، در حالی که
sni عادی آن نام مقصد است:
{
"sni": "origin.example",
"ech-sni-trick": "cover.example"
}
هدف trick
این trick دستگاههای inspectionای را هدف میگیرد که TLS record داخل یک TCP segment منفرد را parse میکنند، یا پیش از رسیدن sequence range پایینتر و مفقود، از داده out-of-order تصمیم زودهنگام میگیرند. یک ClientHello کامل decoy را با TCP sequence معتبر اما مربوط به آینده، پیش از بقیه روی wire قرار میدهد. سپس ClientHello عادی outer میآید و همان byte streamای باقی میماند که endpoint صحیح TCP/TLS دریافت میکند.
برخلاف overlap trick، segment زودهنگام با stream نهایی تعارض ندارد. این segment کپی بایتهایی است که در همان sequence interval داخل outer ClientHello قرار دارند. بنابراین TCP reassembler سختگیر فقط ClientHello اصلی outer را reconstruct میکند. view متفاوت موردنظر فقط در observerی ایجاد میشود که segment اولیه را بهتنهایی parse کند یا SNI آن را پیش از تکمیل reassembly به خاطر بسپارد. این رفتار به مسیر و middlebox وابسته است و تضمین نمیشود.
نحوه قرار گرفتن decoy داخل outer ClientHello
feature دو بخش هماهنگ دارد:
TlsClient.settings.ech-sni-trickیک TLS ClientHello record کامل و جداگانه با SNI مربوط به decoy تنظیمشده میسازد. همان فهرست ALPN تنظیمشده برایTlsClientو همان ترتیب را استفاده میکند، اما key share مربوط بهx25519mlkem768را برای این decoy غیرفعال میکند تا کوچکتر بماند.TlsClientبایتهای خام آن record را بدون encryption داخل payload مربوط به یک extension از نوع GREASEencrypted_client_helloدر ClientHello واقعی outer میگذارد. SNI واضح outer همانTlsClient.settings.sniباقی میماند.IpManipulator، outer ClientHello را capture میکند، record جاسازیشده را پیدا میکند و در TCP segment زودهنگام copy مینماید. هرگز decoy را generate نمیکند، هیچیک از SNIها را بازنویسی نمیکند و هیچ بایتی را از outer hello حذف نمیکند.
این سازوکار ECH confidentiality مبتنی بر standard نیست. extension شکل GREASE ECH دارد، اما payload آن plaintext قابلشناسایی ClientHello است، نه ECH ciphertext معتبر. هم SNI واقعی outer و هم SNI مربوط به decoy به شکل بایتهای واضح روی wire قابل مشاهدهاند.
رفتار موردانتظار مقصد این است که وقتی ECH configuration متناظر ندارد، با outer ClientHello ادامه دهد؛ اما serverها، proxyها و security deviceها نسبت به payload غیرمعمول GREASE بهطور یکسان tolerant نیستند.
Eligibility مربوط به flow و capture کردن ClientHello
این trick sequence-aware است، نه وابسته به ordinal packet:
- یک
SYNآغازین بدون payload، flow record را میسازد.ECEوCWRمیتوانند برای ECN negotiation جداگانه یا با هم وجود داشته باشند؛ACK، FIN، RST، هر flag دیگر یا payload مربوط به TCP Fast Open، opener را ineligible میکند. اگر nodeSYNآغازین را ندیده باشد، packetهای بعدی tuple بدون تغییر عبور میکنند. - packetهای upstream بدون payload، شامل
ACKنهایی TCP handshake و ACKهای میان segmentهای ClientHello، فوراً عبور میکنند و capture slot مصرف نمیکنند. - نخستین payload غیرخالی upstream باید با TLS handshake record و prefix مربوط
به ClientHello قابلشناسایی آغاز شود. ClientHello کامل تکpacketای فوراً قابل
استفاده است؛ prefix جزئی قابلشناسایی capture را شروع میکند؛ نخستین payload
نامرتبط این flow را تا رسیدن
SYNآغازین generation بعدی دائماً passthrough میکند. - data segmentهای بعدی فقط وقتی retained میشوند که هرکدام دقیقاً از TCP sequence موردانتظار بعدی شروع شوند. capture با کامل شدن نخستین TLS record اعلامشده متوقف میشود. ClientHello پخششده در چند TLS record پشتیبانی نمیشود. اگر TCP segment تکمیلکننده بایتهایی پس از آن record نیز داشته باشد، کل segment اصلی retained و بعداً replay میشود، هرچند فقط record نخست inspect خواهد شد.
حداکثر 16 packet اصلی میتوانند شرکت کنند و TLS record اعلامشده با header پنجبایتی آن نمیتواند از 16384 بایت بیشتر باشد. capture timeout عدم فعالیت 1500 ms دارد که با data معتبر یا exact retransmission refresh میشود. exact retransmission مربوط به packet retained به شکل duplicate drop میشود و capture ادامه پیدا میکند. gap، segment out-of-order، overlap جزئی یا متعارض، layout پشتیبانینشده TLS، resource failure، عبور از limit packet/size، timeout یا mismatch در worker affinity بهصورت fail-open عمل میکند: originalهای retained پیش از packet جاری forward میشوند و ادامه flow عادی میگذرد.
پس از capture، IpManipulator همه موارد زیر را لازم دارد:
- outer ClientHello قابل parse با نخستین entry قابلاستفاده
host_nameدر SNI - extension از نوع outer-form
encrypted_client_helloکه از نظر syntax قابل parse باشد و payload field محدود و معتبر داشته باشد - دقیقاً یک TLS ClientHello record کامل در هر جای آن payload که نخستین SNI host
name آن byte-for-byte با مقدار
ech-sni-trickتنظیمشده برابر باشد
candidateهای ClientHello نامنطبق شمرده نمیشوند. دو candidate منطبق ambiguous
هستند و fail-open میشوند. نبود match، candidate malformed یا truncated، outer
hello بدون SNI یا ECH و mismatch میان settingهای IpManipulator و TlsClient
نیز originalها را بدون انتشار segment decoy آزاد میکنند.
Transcript موفق TCP
فرض کنید:
Sنخستین TCP sequence در packet captureشده استO، offset مربوط به ClientHello decoy از آغاز TCP payload captureشده استLطول کامل ClientHello record مربوط به decoy است
decoy از قبل interval برابر [S + O, S + O + L) را داخل outer ClientHello
اشغال میکند. در حالت موفق ترتیب روی wire چنین است:
- همه packetهای اصلی ClientHello همچنان held میمانند.
IpManipulatorیک packet با sequence برابرS + Oو payload دقیقاً شامل همانLبایت embedded میسازد. اگر record جاسازیشده از مرز packetهای اصلی عبور کرده باشد، این کپی همچنان آن را در یک TCP segment coalesce میکند. - آن packet فوراً ارسال میشود. template مربوط به header IPv4 و TCP از data
segment captureشدهای میآید که offset برابر
Oرا میپوشاند؛ شامل address، port، acknowledgement، window، TCP options، TTL و IPv4 identification. طول و sequence اصلاح و checksumها دوباره محاسبه میشوند. flags، مقادیرCWR، ECE، URGوACKرا از template حفظ میکنند، PSHرا میافزایند وSYN، FINوRSTرا حذف میکنند. استفاده دوباره از identification مربوط به template یعنی decoy و یکی از originalها ممکن است IPv4 ID یکسان داشته باشند. - پس از
data-shard-1-delay، original packet شماره 1 با مرز payload، sequence و TCP flags اصلی خود آزاد میشود. - اگر بیش از یک packet captureشده باشد،
IpManipulatorبهاندازهdata-shard-2-delayاضافه صبر میکند و original packetهای 2 تا N را پشت سر هم و بهترتیب capture آزاد میکند. در این گروه دوم delay بین packetها نیست.
پس زمانهای اسمی send برابر T، T + delay1 و T + delay1 + delay2 هستند.
ClientHello تکpacketای فقط از delay نخست استفاده میکند. با هر دو setting برابر
0، ترتیب callback هنوز decoy را اول قرار میدهد، اما همه packetها پشت سر هم
خارج میشوند. اگر release timer قابل schedule نباشد، originalهای مربوط فوراً
flush میشوند تا stranded نمانند.
originalهای retained توسط این trick بازنویسی نمیشوند. بایتهای payload آنها
در [S + O, S + O + L) با کپی اولیه برابرند؛ بنابراین مقصد لازم نیست میان
محتوای متعارض overlap انتخاب کند. featureهای سازگار final-egress در همان node،
مثل protocol swap یا port ghost، پس از ساخت transcript اجرا میشوند و میتوانند
wrapper روی wire را تغییر دهند یا طبق بخش خودشان segmentation انجام دهند.
آنچه client، middlebox و server ممکن است ببینند
- client یک outer ClientHello عادی میفرستد و نمیداند
IpManipulatorآن را retained و کپی یک range داخلی را inject کرده است. segment زودهنگام با sequence بالاتر ممکن است باعث شود مقصد duplicate ACK برایSو در صورت negotiation شدن SACK، یک SACK block برای future range دریافتشده برگرداند. این responseها میتوانند loss recovery سمت client را تغییر دهند، هرچند بایتهای injectشده با دادهای که خود client submit کرده برابرند. - inspector مبتنی بر packet، پیش از دیدن outer hello با sequence پایینتر، یک TCP payload میبیند که با ClientHello کامل و SNI decoy تنظیمشده شروع میشود. دستگاهی که payloadهای out-of-order را parse میکند یا نخستین SNI را latch میکند ممکن است flow را از این decoy دستهبندی کند. stream reassembler سختگیر منتظر gap میماند، outer ClientHello اصلی را reconstruct میکند و میتواند SNI واقعی outer را ببیند؛ دستگاهی که هر دو را بررسی کند شاید verdict اولیه را نادیده بگیرد، جایگزین کند یا penalize نماید.
- TCP stack مقصد ممکن است segment اولیه را بهصورت data out-of-order buffer کند،
با SACK acknowledge کند یا براساس receive-window/normalization policy دور
بیندازد. پس از پر شدن gap توسط originalها، stack tolerant همان بایتهای outer
ClientHello ساختهشده توسط
TlsClientرا reconstruct میکند. انتظار میرود TLS endpoint از SNI outer استفاده و GREASE ECH غیرقابلاستفاده را سازگارانه مدیریت کند. TCP normalizer، SYN proxy، TLS gateway و server آگاه به ECH ممکن است رفتار دیگری داشته باشند.
delayها برای تصمیم زودهنگام middlebox زمان فراهم میکنند، اما تصمیم را تحمیل نمیکنند. مقدارهای بزرگتر latency مربوط به handshake و مدت TCP reordering را افزایش میدهند. مقدار نزدیک به retransmission timeout سمت sender یا timeout application میتواند بهجای بهبود trick اتصال را از کار بیندازد.
Retransmissionها، ترافیک بعدی و بستهشدن اتصال
پس از انتشار decoy، فقط exact retransmission مربوط به original segmentی که هنوز pending است—با sequence، payload length و payload bytes یکسان—swallow میشود. ACK-only، application data بعدی یا overlap جزئی/مبهم فوراً forward میگردد. داده بعدی پشت دو release timer queue نمیشود، بنابراین ممکن است خودش از ClientHello held سبقت بگیرد و data out-of-order بیشتری بسازد. پس از release شدن original، retransmissionهای آن دیگر swallow نمیشوند.
close handling در جایی که close عادی upstream هنوز معنا دارد، ترتیب TCP را حفظ میکند:
FINupstream هنگام capture ناقص، originalهای captureشده را پیش ازFINآزاد میکند. هنگام delayed release، همه originalهای pending را پیش ازFINflush میکند.RSTupstream هنگام delayed release، originalهای pending را دور میاندازد و سپس reset را عبور میدهد. هنگام capture ناقص، originalهای held پیش از reset packet آزاد میشوند.FINیاRSTdownstream بدون تغییر عبور میکند، اما flow record را حذف، capture ناقص را دور و originalهایی را که هنوز release نشدهاند cancel میکند.
flow recordها با TCP four-tuple و generation اتصال key میشوند. SYN آغازین
معتبر تازه روی tuple استفادهشده مجدد، capture و release timerهای قبلی را پیش از
شروع flow جایگزین invalid میکند؛ بنابراین بایت قدیمی ClientHello داخل آن inject
نمیشود. پس از release، record تا بستهشدن، جایگزینی یا expire شدن بعد از 20
دقیقه inactivity در passthrough میماند.
تفاوت ech-sni-trick با سایر trickهای SNI
first-sniیک یا چند کپی کامل از outer ClientHello سمت client میسازد، SNI هرکدام را تغییر میدهد و با TTL یا sequence تصادفی تلاش میکند decoyهای متعارض را از server دور نگه دارد.ech-sni-trickاز یک decoy از پیش embedded در sequence interval واقعی خودش استفاده میکند، setting مربوط به TTL یا random sequence ندارد و بایت متعارضی نمیسازد.smuggle-sni، ClientHello واقعی را روی helper branch و decoy generated را پس از delay روی branch عادی میفرستد.ech-sni-trickبه helper نیاز ندارد و هر دو view را روی مسیر عادی قرار میدهد.overlap-sniوsynfin-sniمرزهای segment captureشده را با sequence rangeهای عمداً متعارض جایگزین وSYNجعلی و packet close یا filler تزریق میکنند.ech-sni-trickcontrol packet مربوط به TCP ندارد و segmentation اصلی را replay میکند.sni-blender، ClientHello واقعی را در لایه IPv4 fragment و reorder میکند.ech-sni-trickfragment IPv4 نمیسازد؛ یک TCP data segment اضافه را از payload مربوط به GREASE ECH کپی میکند.
محدودیتها و عوارض جانبی
- فقط packetهای کامل و fragmentنشده IPv4 TCP eligible هستند. IPv6 و fragmentهای IPv4 بدون تغییر عبور میکنند.
nextعادی در سطح اصلی لازم است.IpManipulatorبایدSYNآغازین را ببیند و نخستین payload غیرخالی client باید با layout پشتیبانیشده ClientHello تک-record آغاز شود.- decoy همراه headerهای کپیشده IP/TCP باید در
mtuجا شود. decoy بزرگ بهجای split شدن توسط ECH trick بهصورت fail-open رد میشود. SNI طولانیتر میتواند outer record و این packet را بزرگتر کند. - flow table با
stateful-flow-limitمحدود است و flow پذیرفتهنشده بدون تغییر عبور میکند. captureهای ناقص TLS از slot pool محدود مشترک نیز استفاده میکنند؛ در فشار بالا یک capture ناقص قدیمی ممکن است آزاد و passthrough شود. - تمام packetهای retained یک flow باید روی یک worker مربوط به WaterWall بمانند. خود IpManipulator پیش از پردازش stateful، affinity مربوط به packet line را برقرار میکند؛ mismatch باقیمانده در worker affinity همچنان fail-open میشود.
- packet زودهنگام IPv4 identification مربوط به template را duplicate میکند و ممکن است duplicate ACK، SACK، heuristicهای retransmission، rule مربوط به traffic normalizer یا anomaly detection را trigger کند. delayها memory را هم نگه میدارند و TLS handshake را عقب میاندازند.
- این ECH واقعی نیست و SNI را confidential نمیکند: SNI واقعی در outer ClientHello و SNI decoy داخل GREASE payload قابل مشاهدهاند.
- این trick نمیتواند با stateful SNI دیگر،
sni-blender، packet-duplicateیا action مربوط به TCP bit در upstream داخل یکIpManipulatorمشترک باشد. operationهای سازگار که باید packetهای emitted را shape کنند درIpManipulatorبعدی قرار میگیرند.
synfin-sni
synfin-sni یک TCP overlap trick از نوع stateful و فقط-upstream برای TLS
ClientHelloای است که دقیقاً در دو data segment انتخابشده split شده باشد. آن دو
packet اصلی را suppress میکند و transcript جایگزینی شامل بایتهای واقعی، close
مصنوعی، SYN جعلی، ClientHello generated از نوع decoy، filler اختیاری و باقی
بایتهای واقعی منتشر میکند. این trick packetهای downstream را inspect یا
تغییر نمیدهد.
split view موردنظر هم از state اتصال TCP و هم از سیاست conflicting overlap
استفاده میکند. مقصدی که نخستین بایتهای رسیده برای sequence range را نگه دارد
میتواند ClientHello واقعی را reconstruct کند. دستگاه میانی ممکن است close و
SYN بعدی را مرز flow تازه بداند، یا بایتهای دیرتر overlap را ترجیح دهد و
ClientHello generated دارای synfin-sni را parse کند.
این trick یکی از تهاجمیترین رفتارهای protocol در node است. عمداً TCP data متعارض،
یک close و SYN دیگر را روی four-tuple برقرار میفرستد. با TTL، sequence و
checksum معتبر پیشفرض، packetهای مصنوعی ممکن است به مقصد برسند و اتصال را close
یا reset کنند. آن را با client، server، middlebox و packet captureهای نماینده
آزمایش کنید.
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
synfin-sni | string با طول 1 تا 255 بایت | اجباری برای فعالسازی | Host name مربوط به ClientHello generated از نوع decoy. limit مؤثر از TlsClient داخلی میآید. |
synfin-sni-hold-timeout-ms | integer برحسب ms، با مقدار > 0 | 50 | بیشترین زمان انتظار برای segment contiguous بلافاصله بعدی که ClientHello انتخابشده را کامل کند. expiry، segment held را بدون تغییر آزاد میکند. |
synfin-sni-additional-range-min | integer از 0 تا 65535 | 0 | حداقل extension درخواستی پس از طول hello generated برای overlap مربوط به real-first/fake-filler. اگر maximum تنظیم نشده باشد، همین مقدار maximum نیز میشود. clamp ایمنی runtime ممکن است آن را کاهش دهد. |
synfin-sni-additional-range-max | integer از 0 تا 65535 | 0، یا minimum تنظیمشده وقتی فقط minimum وجود دارد | حداکثر extension درخواستی overlap. اگر بهتنهایی تنظیم شود، minimum همان 0 میماند؛ minimum نباید از maximum بزرگتر باشد. |
synfin-sni-syn-ttl | integer از 0 تا 255 | در صورت وجود template، کپی TTL از SYN آغازین | TTL مربوط به IPv4 فقط برای SYN جعلی. اگر header کپیشده opener در دسترس نباشد، construction به header مربوط به data held fallback میکند. |
synfin-sni-fin-ttl | integer از 0 تا 255 | کپی TTL از data packet held | TTL مربوط به IPv4 فقط برای `FIN |
synfin-sni-fake-ttl | integer از 0 تا 255 | کپی TTL از data packet held | TTL مربوط به IPv4 فقط برای packet X یعنی ClientHello کامل generated. روی filler یا packetهای واقعی اثر ندارد. |
synfin-sni-random-syn-checksum | boolean | false | بهجای checksumهای معتبر محاسبهشده، مقدارهای random دلخواه داخل فیلدهای checksum مربوط به IPv4 و TCP در fake SYN میگذارد. |
synfin-sni-random-fin-checksum | boolean | false | همان arbitrary-checksum را فقط روی close packet مصنوعی اعمال میکند. |
synfin-sni-random-syn-sequence | boolean | false | بهجای S - 1 از sequence تصادفی 32 بیتی برای fake SYN استفاده میکند. packet X همچنان در S میماند. |
synfin-sni-random-fin-sequence | boolean | false | بهجای S + Y یعنی انتهای overlap interval، sequence تصادفی 32 بیتی برای close انتخاب میکند. |
synfin-sni-use-rst | boolean | false | برای close مصنوعی بهجای `FIN |
TTL برابر 0 یک override واقعی است. برای حفظ TTL مربوط به template، فیلد TTL
را کاملاً حذف کنید. مقدار 0 معمولاً packet routed را از عبور فراتر از link
محلی بازمیدارد، اما handling دقیق همچنان به مسیر وابسته است.
record generated از TlsClient داخلی با ترتیب ALPN پیشفرض h2، http/1.1
و x25519mlkem768 غیرفعال استفاده میکند و از fingerprint client واقعی کپی
نمیشود. طول کامل record را G فرض کنید. generation در runtime بهصورت fail-open
شکست میخورد مگر اینکه G حداکثر 900 بایت و حداکثر برابر مجموع دو payload واقعی
انتخابشده باشد. synfin-sni طولانیتر معمولاً G را افزایش میدهد و شرط جایگاه
SNI واقعی را که در ادامه میآید دشوارتر میکند.
additional range پس از clamp کردن دو سر تنظیمشده به بازه امنی که payload واقعی
و محل SNI اجازه میدهند، برای هر flow موفق یک بار انتخاب میشود. برای مقدار واقعی
انتخابشده A، اگر A < 5 باشد A بایت filler تصادفی ساخته میشود؛ اگر
A >= 5 باشد یک header مربوط به TLS Application Data record
(17 03 03 و record length برابر A - 5) بههمراه بایتهای random ساخته میشود.
بنابراین minimum تنظیمشده فقط request است و وقتی safe range کوچکتر باشد تضمین
نمیشود.
کل replacement sequence synchronously و پشت سر هم منتشر میشود. hold timeout
برای pacing نیست و setting delay دیگری برای SynFIN وجود ندارد. تنظیم مشترک
stateful-flow-limit جدول SynFIN را محدود میکند و پیشفرض آن 65536 است. این
trick به next عادی در سطح اصلی نیاز دارد و helper branch ندارد.
مثالی شامل همه کنترلها؛ این مقدارها فقط illustrative هستند و recommendation مستقل از مسیر محسوب نمیشوند:
{
"name": "ip-manipulator",
"type": "IpManipulator",
"settings": {
"synfin-sni": "www.example.com",
"synfin-sni-hold-timeout-ms": 50,
"synfin-sni-additional-range-min": 8,
"synfin-sni-additional-range-max": 24,
"synfin-sni-syn-ttl": 5,
"synfin-sni-fin-ttl": 5,
"synfin-sni-fake-ttl": 5,
"synfin-sni-random-syn-checksum": false,
"synfin-sni-random-fin-checksum": false,
"synfin-sni-random-syn-sequence": false,
"synfin-sni-random-fin-sequence": false,
"synfin-sni-use-rst": false
},
"next": "next-packet-node"
}
Flow و packetهای انتخابشده
record فقط وقتی ساخته میشود که این node یک SYN آغازین بدون payload ببیند.
ECE و CWR میتوانند همراه SYN باشند؛ ACK، FIN، RST، هر flag دیگر
یا payload مربوط به TCP Fast Open، opener را ineligible میکند. packetهای tupleای
که SYN آغازین آن دیده نشده بدون تغییر عبور میکنند. SYN آغازین معتبر بعدی
روی همان tuple یک generation جایگزین آغاز میکند.
- Warmup — دو packet نخست upstream که close نیستند بدون تغییر عبور میکنند
و warmup محسوب میشوند. معمولاً اینها
SYNآغازین وACKنهایی handshake سمت client هستند؛SYN|ACKdownstream شمرده نمیشود. پس packet سوم upstream معمولاً نخستین segment مربوط به ClientHello است. - Decision — اگر packet سوم payload نداشته باشد، با TLS ClientHello قابل شناسایی شروع نشود یا از قبل نخستین TLS record کامل را داشته باشد، فوراً عبور میکند و flow passthrough میشود. فقط ابتدای ClientHello تک-recordی partial یا fragmented نگه داشته میشود.
- Hold — segment انتخابشده داخل
IpManipulatorمیماند و در مدت timer هیچ بخشی از آن ارسال نمیشود. timeout آن را بدون تغییر آزاد میکند و flow را passthrough میسازد. - Pair — packet بلافاصله بعدی upstream باید payload غیرصفر داشته و دقیقاً
در
held.seq + held.payload_lenآغاز شود. بایتهای ترکیبشده باید ClientHello قابل parse داشته باشند که handshake آن دقیقاً یک TLS record را پر میکند و نخستین entry قابلاستفادهhost_nameدر SNI دارد. ACK، exact retransmission، gap، overlap، segment out-of-order، record همچنان ناقص یا layout malformed، packet held و سپس packet جاری را بدون تغییر آزاد و passthrough را انتخاب میکند. trick برای data segment سوم منتظر نمیماند.
نخستین TLS record اعلامشده حداکثر میتواند 16384 بایت باشد. ClientHello پخششده در چند TLS record پشتیبانی نمیشود. اگر TCP segment تکمیلکننده بایتهایی پس از record انتخابشده داشته باشد، آن بایتها در payload واقعی ترکیبشده میمانند و در continuation واقعی حفظ میشوند.
قاعده ordinal packet سختگیرانه است. ClientHello تکsegmentی هرگز transform نمیشود و یک packet اضافه client میان handshake ACK و نخستین segment ClientHello میتواند باعث شود packet سوم passthrough را انتخاب کند.
Transcript موفق TCP
فرض کنید:
Sنخستین TCP sequence مربوط به segment held استHطول payload آن segment استRمجموع طول دو payload واقعی استGطول ClientHello record generated از نوع decoy استO، offset مربوط به host name واقعی SNI از آغاز TCP payload ترکیبشده استA، additional range انتخابشده پس از clamp در runtime استY = G + Aطول interval عمداً همپوشان است
transcript موفق به 0 < G <= 900، G <= R و O >= G نیاز دارد.
IpManipulator، additional range را طوری clamp میکند که
0 <= A <= min(R - G, O - G). در نتیجه Y <= O است: packet واقعی Y میتواند
دقیقاً پیش از نخستین بایت SNI واقعی پایان یابد، اما هیچ بایتی از host name واقعی
را حمل نمیکند.
packet held و تکمیلکننده اصلی recycle میشوند. پیش از egress segmentation بعدی، پنج تا هفت replacement packet بهترتیب زیر و synchronously خارج میشوند:
- packet واقعی Y — بایتهای واقعی
[0, Y)با sequence برابرS. header IP/TCP و TCP flags دقیق را از packet held کپی میکند. payload آن میتواند از مرز اصلیHعبور و بایتهایی از هر دو segment انتخابشده را coalesce کند. - Close مصنوعی — packet header-only از نوع
FIN|ACK، یا در صورت فعال بودنsynfin-sni-use-rstاز نوعRST|ACK. sequence پیشفرض آنS + Yاست. acknowledgement، window، TCP options، addressها و portها از data packet held میآیند. - SYN جعلی — packet header-only فقط با flag مربوط به
SYN، acknowledgement برابر0و sequence پیشفرضS - 1. معمولاًSYNآغازین captureشده را به عنوان header template استفاده میکند و TCP header length، options و window همان SYN را نگه میدارد. اگر حفظ template شکست خورده باشد، header مربوط به data held استفاده میشود. - packet جعلی X — دقیقاً
Gبایت ClientHello generated در sequence برابرS. flags، مقادیرCWR، ECE، URGوACKرا از segment held حفظ وPSHرا اضافه میکنند؛SYN، FINوRSTحذف میشوند. X با Y واقعی در interval برابر[S, S + G)تعارض دارد. - Filler جعلی اختیاری — وقتی
A > 0باشد، یک packet interval[S + G, S + Y)را میپوشاند. payload آن همان بایتهای random یا شکل TLS Application Data توضیحدادهشده است و از همان policy مربوط به continuation-plus-PSHدر X استفاده میکند. - یک یا دو tail واقعی — بایتهای واقعی
[Y, R)stream اصلی را کامل میکنند. اگرY < Hباشد، suffix مربوط به segment held درS + Yخارج میشود و segment تکمیلکننده با sequence اصلیS + Hمیآید. در غیر این صورت فقط suffix استفادهنشده segment تکمیلکننده درS + Yخارج میشود.
Y واقعی با IPv4 identification مربوط به packet held شروع میشود. close، fake
SYN، X، filler و tailها identificationهای پیدرپی میگیرند؛ پس identification
اصلی packet تکمیلکننده حفظ نمیشود. packetهای rebuilt معمولاً checksum معتبر
IPv4/TCP دارند. وقتی suffix مربوط به segment held پیش از tail دوم میآید، suffix
نخست فقط CWR، ECE، URG و ACK را نگه میدارد؛ tail نهایی flags مربوط به
segment تکمیلکننده را دارد.
هیچ sleep مصنوعی یا pacing قابل تنظیمی میان این خروجیها نیست. behavior سازگار final-egress در همان node، شامل port ghost، protocol swap و TCP segmentation مبتنی بر MTU، پس از ساخت transcript اجرا میشود و میتواند wrapper را تغییر دهد یا تعداد نهایی packetها را افزایش دهد.
آنچه هر participant ممکن است ببیند
- client: دو segment عادی ClientHello را فرستاده، اما WaterWall آنها را suppress و transcript جایگزین را ارسال کرده است. controlهای مصنوعی و data متعارض میتوانند duplicate ACK، challenge ACK، تغییر SACK یا reset ایجاد کنند. اگر acknowledgement progress با transmission اصلی client هماهنگ نباشد، TCP stack ممکن است مرزهای segment اصلی را retransmit کند؛ retransmissionهای بعدی پس از موفقیت transcript suppress نمیشوند.
- Middlebox دارای state: ابتدا prefix واقعی را میبیند که پیش از host name
واقعی تمام میشود، سپس close و
SYNدیگر را میگیرد. با sequenceهای پیشفرض،SYNتازه درS - 1باعث میشود X درSشبیه نخستین data در flow جایگزین باشد. دستگاه ممکن است SNI decoy و filler اختیاری شبیه TLS را parse کند. دستگاهی که controlها را نادیده میگیرد ولی در overlap از last-arrival-wins استفاده میکند نیز به همان payload view میرسد. reassembler سختگیر با first-arrival-wins، Y را نگه میدارد. - مقصد: انتظار میرود Y واقعی را برای
[S, S + Y)نگه دارد، X و filler را duplicateهای متعارض بداند و tail واقعی را append کند تا ClientHello اصلی reconstruct شود. این امر نیاز دارد Y اول برسد و controlهای مصنوعی state سمت مقصد را terminate نکنند.FINمعتبر و in-window درS + Yمیتواند receive stream را دقیقاً جایی close کند که tail واقعی آغاز میشود؛RSTپذیرفتهشده اتصال را فوراً پایان میدهد.
نتیجه موردنظر وابسته به مسیر است. loss، reordering شبکه، policy مربوط به first/last overlap، checksum validation، receive-window validation، TCP normalization، SYN proxy، state مربوط به NAT/firewall و رفتار TCP سمت server همگی میتوانند نتیجه را عوض کنند. بهویژه اگر X پیش از Y به مقصدی با first-arrival-wins برسد، بایتهای generated میتوانند TLS stream سمت server را خراب کنند.
اثر settingهای TTL، checksum، sequence و close
سه setting مربوط به TTL مستقلاند. synfin-sni-syn-ttl فقط روی fake SYN،
synfin-sni-fin-ttl فقط روی close و synfin-sni-fake-ttl فقط روی X اعمال
میشود، نه filler. TTL کم میتواند packetای را به observer نزدیک نشان دهد و پیش
از مقصد expire کند، اما middlebox خاصی را target نمیکند و تغییر route مقدار
انتخابی را بیاعتبار میسازد.
دو setting مربوط به random checksum، هر دو فیلد checksum مربوط به IPv4 و TCP را روی control packet منتخب arbitrary میکنند. آنها معمولاً نامعتبر خواهند بود، هرچند مقدار random ممکن است اتفاقی درست باشد. classifier passive ممکن است چنین packetای را inspect کند ولی endpoint دارای validation آن را drop میکند؛ middlebox دارای validation هم drop خواهد کرد. Y، X، filler و tailهای واقعی checksum معتبر دارند. operation بعدی egress که packet را rebuild میکند، مثل port-ghost tailing یا MTU segmentation، ممکن است checksum arbitrary را repair کند؛ اگر bad checksum ضروری است stageهای بعدی را در نظر بگیرید.
synfin-sni-random-syn-sequence فقط fake SYN را جابهجا میکند. X در S باقی
میماند، پس رابطه منسجم پیشفرض SYN(S - 1) -> data(S) از دست میرود. این میتواند
control را بیرون از window مقصد قرار دهد، اما ممکن است middlebox sequence-aware
را هم از تلقی X به عنوان data مربوط به flow جدید بازدارد. به همین شکل،
synfin-sni-random-fin-sequence فقط close را جابهجا میکند و شاید پذیرش آن توسط
مقصد یا باورپذیری آن برای observer sequence-aware را کاهش دهد.
synfin-sni-use-rst بدون تغییر sequence پیشفرض S + Y، FIN|ACK را به
RST|ACK تبدیل میکند. RST پذیرفتهشده معمولاً از FIN عادی مخربتر است و close
نیمه graceful که یک sequence number مصرف میکند محسوب نمیشود.
افزایش additional range هم coverage مربوط به نخستین arrival در Y واقعی و هم
coverage بعدی filler را بزرگتر میکند و close و tail واقعی را به S + Y
میبرد. هیچگاه از ابتدای SNI واقعی عبور نمیکند. طول و extension order واقعی
ClientHello ممکن است حتی minimum تنظیمشده را به مقدار کمتر clamp کنند.
رفتار fail-open و blocked
بیشتر حالتهای پشتیبانینشده fail-open هستند. packet held و جاری در شرایط زیر بدون transcript ساختهشده آزاد یا forward میشوند:
SYNآغازین دیده نشده، packet از نوع IPv4 TCP کامل نیست یا flow table نمیتواند record را admit کند- packet سوم payload ندارد، غیر TLS است یا از قبل ClientHello record کامل دارد
- hold expire میشود یا timer آن قابل arm نیست
- packet بلافاصله بعدی payload ندارد، strictly contiguous نیست یا ClientHello تک-record پشتیبانیشده دارای SNI را کامل نمیکند
- hello داخلی قابل generation نیست،
G > 900یاG > Rاست - replacement packet قابل ساخت نیست یا state retained میان workerها جابهجا شده است
حالت O < G استثنای عمدی fail-closed است. اگر host name واقعی پیش از پایان
record generated شروع شود، IpManipulator mismatch را log میکند، هر دو packet
واقعی انتخابشده را drop میکند و flow upstream را blocked علامت میزند. همه
packetهای بعدی upstream drop میشوند. FIN یا RST upstream record را حذف
میکند ولی خودش هم drop میشود. SYN آغازین معتبر تازه روی همان tuple،
generation جدید را شروع میکند. reset محلی برای توضیح failure ارسال نمیشود؛
نشانه معمول stall اتصال است.
Retransmissionها، closeها و طول عمر flow
در hold اولیه، FIN یا RST upstream ابتدا segment held را آزاد میکند، سپس
close را forward و record را حذف میکند. ACK-only یا retransmissionی که بهجای
completion موردانتظار برسد، pair دوpacketی را میشکند و packet held سپس جاری را
بدون تغییر آزاد و flow را passthrough میکند.
پس از transcript موفق، flow فوراً passthrough میشود. ACKهای بعدی، application
data، retransmissionهای ClientHello اصلی و closeها پشت delay window queue
نمیشوند. بنابراین retransmission بعدی میتواند segmentation اصلی client و
overlap policy را دوباره روی wire قرار دهد. FIN یا RST upstream، record
passthrough را حذف و عادی ادامه مییابد.
SynFIN ترافیک downstream، شامل FIN یا RST downstream را inspect نمیکند و
آن ترافیک record را حذف نمیکند. recordهای بدون استفاده پس از 20 دقیقه عدم
فعالیت expire میشوند. هر pair retained باید روی یک worker مربوط به WaterWall
بماند؛ completion میان workerها fail-open میشود، اما packet جاری روی worker
خودش ادامه میدهد و packet held به owner worker بازگردانده میشود؛ پس در این
error path ترتیب اصلی روی wire تضمین نمیشود.
SYN آغازین جایگزین، generationهای old مربوط به hold timer را پیش از شروع دوباره
warmup invalid میکند. اگر tuple reuse هنگام hold فعال رخ دهد، buffer held قدیمی
بهجای وارد شدن به اتصال جایگزین دور انداخته میشود.
تفاوت synfin-sni با سایر trickهای SNI
first-sni، ClientHello واقعی را clone و host name را عوض میکند و با TTL یا sequence تصادفی تلاش میکند decoy را از server دور نگه دارد. روی ClientHello تکsegmentی هم کار میکند و control مربوط به state اتصال تزریق نمیکند.smuggle-sniتا 16 segment را capture میکند و originalهای واقعی را از helper branch میفرستد، در حالی که hello generated پس از delay روی branch عادی قرار میگیرد. SynFIN دقیقاً یک segment held و completion contiguous بلافاصله بعدی را میپذیرد، helper ندارد و همه viewها را پشت سر هم میفرستد.overlap-sniاز همان انتخاب دوsegmentی و overlap مربوط به real-Y/fake-X استفاده میکند، اما فقط fakeSYNتزریق میکند، طول overlap را درGثابت نگه میدارد و X و tail واقعی را با delay schedule میکند. SynFIN پیش از آنFIN|ACK/RST|ACKاضافه میکند، میتواند overlap را با filler بزرگتر کند، کنترل checksum و sequence دارد و post-transcript delay window ندارد.ech-sni-trickیک کپی byte-identical خارج از ترتیب از decoy جاسازیشده توسطTlsClientمیفرستد و هیچ TCP byte یا control متعارضی نمیسازد.sni-blenderpacket واقعی را در IPv4 fragment و shuffle میکند، نه اینکه TCP segmentation را جایگزین یا ClientHello دیگری generate کند.
محدودیتها و عوارض جانبی
- فقط packetهای کامل و fragmentنشده IPv4 TCP eligible هستند. IPv6، fragmentهای IPv4 و SYN data مربوط به TCP Fast Open بدون تغییر عبور میکنند.
- ClientHello باید روی packet سوم upstream آغاز شود، در آن packet ناقص باشد و پس از دقیقاً یک data segment contiguous قابل parse گردد. ClientHelloهای تکsegmentی یا سهsegmentی و بیشتر transform نمیشوند.
- hello generated از fingerprint و ALPN پیشفرض
TlsClientداخلی استفاده میکند، نه fingerprint client واقعی. نام تنظیمشده باید 1 تا 255 بایت باشد وG، RوOشروط بالا را برآورده کنند. - مرزهای data اصلی و IPv4 identification مربوط به packet تکمیلکننده کنار گذاشته میشوند. MTU shaping نهایی میتواند data rebuilt را دوباره split و تعداد اسمی پنج تا هفت packet را بیشتر کند.
- close/SYN مصنوعی روی اتصال برقرار ممکن است توسط host، firewall، NAT، load balancer، SYN proxy و intrusion systemها normalize، challenge، rate-limit، drop یا attack تلقی شود.
- flow table محدود از
stateful-flow-limitاستفاده میکند و فشار admission به شکل fail-open است. hold فعال memory مربوط به packet را تا زمان pair، timeout، close upstream، جایگزینی یا destruction نگه میدارد و worker affinity سازگار لازم دارد. nextعادی در سطح اصلی لازم است. این trick نمیتواند در همانIpManipulatorبا stateful SNI دیگر،sni-blender، packet-duplicateیا action مربوط به TCP bit در upstream مشترک باشد. packet shaping سازگار بعدی را در node دیگری بگذارید و اثرش روی TTL، checksum، segmentation، tuple و ترتیب packet را در نظر بگیرید.
smuggle-fin
smuggle-fin یک trick مربوط به ordering با دو branch است. یک data packet عادی
را روی مسیر normal نگه میدارد، FIN|ACK باورپذیر در جهت reverse را از helper
path جداگانه میفرستد و منتظر میماند کپی دقیق آن FIN در مسیر downstream برگردد.
هدف این است که دستگاه stateful روی helper path پیش از آزاد شدن data اصلی، signal
بستهشدن اتصال را ببیند. اینکه این signal classification یا connection state را
تغییر دهد به topology و خود دستگاه بستگی دارد؛ trick تضمین نمیکند middlebox
FIN را echo یا accept کند یا براساس آن عمل نماید.
برخلاف نام fin-sni-delay-ms، این trick TLS، ClientHello یا SNI را parse
نمیکند. هر packet کامل و fragmentنشده IPv4 TCP که ACK داشته باشد، دستکم یک
بایت TCP payload حمل کند و هیچیک از SYN، FIN یا RST را نداشته باشد،
میتواند trigger شود. flagهایی مانند PSH، ECE، CWR و URG packet eligible
را رد نمیکنند. IPv6، fragmentهای IPv4، packetهای غیر TCP، ACK-only و SYN data
مربوط به TCP Fast Open بدون شروع trick عبور میکنند.
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
smuggle-fin | boolean | false | trick را فعال میکند. این setting host name یا TLS selector نیست. |
fin-sni-delay-ms | integer برحسب ms، با مقدار >= 0 | 0 | delay پس از consume شدن `FIN |
fin-pause-timeout-ms | integer برحسب ms، با مقدار > 0 | 1000 | حداکثر hard pause از زمان data packet trigger. حتی اگر exact echo نرسد، queue را آزاد میکند. |
real-fin-upstream-node | string | هنگام فعال بودن اجباری | helper branch متمایزی که `FIN |
setting مشترک stateful-flow-limit نیز اعمال میشود و پیشفرض 65536 دارد.
هیچ settingی برای TTL، flags، sequence، acknowledgement یا checksum مربوط به FIN
mirrored وجود ندارد؛ این مقدارها از trigger مشتق میشوند. node هم به next عادی
در سطح اصلی و هم helper نیاز دارد. helper نامبرده باید وجود داشته باشد، خود این
IpManipulator نباشد، با next تفاوت داشته باشد و هنگام ساخت chain قابل
دسترسی و binding باشد.
{
"name": "ip-manipulator",
"type": "IpManipulator",
"settings": {
"smuggle-fin": true,
"fin-sni-delay-ms": 20,
"fin-pause-timeout-ms": 1000,
"real-fin-upstream-node": "fin-helper"
},
"next": "normal-path"
}
عددهای بالا illustrative هستند. بهویژه hard timeout را براساس round trip
موردانتظار helper، delay موردنظر و stall قابل قبول application انتخاب کنید و
1000 ms را مقدار universally suitable ندانید.
Trigger packet و FIN mirrored
فرض کنید trigger packet چنین باشد:
client A:a -> server B:b
SEQ = S, ACK = K, payload length = L
IpManipulator ابتدا کل packet را بهجای forward کردن روی branch عادی next
retained میکند. سپس headerهای IPv4/TCP را copy، همه TCP payload را حذف، هر دو
IP address و هر دو TCP port را swap و packet زیر را منتشر میکند:
server B:b -> client A:a
SEQ = K, ACK = S + L, flags = FIN|ACK, payload length = 0
sequence arithmetic همان حساب 32 بیتی TCP است. trigger نمیتواند SYN یا FIN
داشته باشد، پس فقط طول payload آن acknowledgement را جلو میبرد. header کپیشده
فیلدهایی مثل IPv4 identification و TTL مربوط به trigger و TCP header length،
options و window را حفظ میکند. total length مربوط به IPv4 به طول header-only
کاهش مییابد، flags و فیلدهای sequence مطابق بالا جایگزین و checksumهای معتبر
IPv4/TCP برای egress درخواست میشوند. هیچ settingی این فیلدها را جداگانه تغییر
نمیدهد.
عددهای reverse طوری انتخاب شدهاند که برای همان اتصال زنده باورپذیر باشند: K
همان next server sequence است که client اعلام کرده انتظار دارد و S + L همه
بایتهای packet held سمت client را acknowledge میکند. خود client فقط data packet
اصلی را فرستاده و این FIN را نفرستاده است. اگر helper path واقعاً reverse packet
spoofشده را به client برساند و TCP stack آن را in-window بپذیرد، FIN میتواند
جهت receive را half-close کند، expected server sequence را یک واحد جلو ببرد،
ACK ایجاد کند یا اتصال را به شکل دیگری مختل کند. این اثر endpoint ممکن است رخ
دهد، اما trick به آن نیاز ندارد.
packet crafted فوراً از real-fin-upstream-node فرستاده میشود و از stageهای
TCP-bit در upstream، stateful-SNI، SNI Blender، packet duplication یا port
ghost این node عبور نمیکند. protocol swap و final checksum handling همچنان
میتوانند اعمال شوند. data packet اصلی برای مسیر عادی queue میماند و هنگام
release از stageهای بعدی تنظیمشده با ترتیب معمول آنها عبور میکند.
تشخیص echo و timing مربوط به release
برای release مبتنی بر echo، topology مربوط به helper باید FIN ساختهشده را به
عنوان packet downstream از همین IpManipulator برگرداند. پس از restore شدن
protocol و port ghost در downstream، packet فقط در صورت برقراری همه شروط زیر
match میشود:
- یک packet کامل و fragmentنشده IPv4 TCP در جهت
B:b -> A:aباشد - TCP payload آن صفر باشد
- sequence دقیقاً
Kو acknowledgement دقیقاًS + Lباشد - پس از نادیده گرفتن فقط
ECEوCWR، flags دقیقاًFIN|ACKباشند
IPv4 identification، TTL، TCP window و TCP options مقایسه نمیشوند. response
عادی TCP به FIN معمولاً ACK با جهت و sequence semantics متفاوت است، پس echo
موردنیاز نیست. هر flag اضافهای بهجز ECE یا CWR نیز match را رد میکند.
نخستین exact echo consume میشود و هرگز بهسمت client forward نمیگردد. duplicate
دقیق آن هنگام pending بودن release نیز consume میشود. در نخستین match،
fin-sni-delay-ms، release queue را schedule میکند؛ اما timer مربوط به
fin-pause-timeout-ms که هنگام capture trigger شروع شده فعال میماند. بنابراین
sequence مؤثر چنین است:
t0: trigger is queued; hard timeout starts; mirrored FIN is sent
te: exact echoed FIN is consumed, if one arrives
release: earlier of (te + fin-sni-delay-ms) and
(t0 + fin-pause-timeout-ms)
در نتیجه اگر delay کامل اهمیت دارد، pause timeout باید از round trip موردانتظار
helper بهعلاوه delay دلخواه بیشتر باشد. delay برابر 0 روی owner worker فوراً
release میکند. اگر echo نرسد، hard timeout همچنان flow را آزاد میکند. failure
در arm کردن initial hard timer، trigger queued را پیش از ارسال helper FIN فوراً
آزاد میکند؛ failure در setup release میان workerها پس از echo، timer سخت اولیه
را به عنوان fallback نگه میدارد. callbackهای timer به generation دقیق pause
متصلاند، پس callback قدیمی نمیتواند استفاده جدید همان tuple را release کند.
پس از پایان release، packet بعدی که با FIN قدیمی match کند ترافیک downstream
عادی است و دیگر توسط Smuggle-FIN consume نمیشود.
موارد queueشده و نحوه replay
تا وقتی pause در orientation عادی packet chain فعال است، همه packetهای parseable
upstream روی forward tuple و همه packetهای parseable downstream روی reverse tuple،
صرفنظر از flags یا payload، نگه داشته میشوند. این شامل data retransmitted،
ACK، application data بعدی و FIN/RST واقعی است. trigger نخستین entry queue
است؛ exact echo مربوط به FIN مصنوعی استثناست و به عنوان signal release consume
میشود. packetهای غیرقابل parse یا fragmented، queue را bypass میکنند و ممکن
است از ترافیک held سبقت بگیرند. flowهای نامرتبط هرگز پشت این flow نگه داشته
نمیشوند.
queue حداکثر 256 packet نگه میدارد و byte limit جداگانه ندارد. اگر packet شماره 257 برسد یا queue نتواند رشد کند، batch موجود به اجبار و با ترتیب queue آزاد میشود، packet جاری عادی ادامه مییابد و flow confirmed علامت میخورد تا دوباره pause نشود. timeout و سایر forced-release pathها همان رفتار no-retry را دارند.
یک flow-owner worker queue را نگه میدارد. packetهای reverse که روی worker دیگر
میرسند پیش از queue شدن به owner worker کپی میشوند. ترتیب replay همان ترتیبی
است که owner worker entryها را پذیرفته و در حالت چند worker لزوماً با یک ترتیب
global روی wire برابر نیست. اگر cross-worker handoff ساخته یا submit نشود، آن
reverse packet روی worker فعلی عبور میکند و pause فعال میماند؛ در نتیجه ممکن
است از queue سبقت بگیرد. روی هر worker در هر لحظه فقط یک flow میتواند توسط
smuggle-fin pause شود؛ packet eligible برای flow دیگر روی همان worker عادی
عبور میکند.
هر queue entry به خاطر میسپارد checksum آن به recalculation نیاز داشته یا نه. هنگام release، entryها یکییکی و با ترتیب mixed-direction ذخیرهشده replay میشوند:
- packetهای upstream از ابتدای pipeline مربوط به upstream در
IpManipulatorrestart میشوند؛ record اکنون confirmed مربوط به Smuggle-FIN از reinjection جلوگیری میکند و trickهای بعدی upstream و egress عادی یک بار اجرا میشوند - packetهای downstream بلافاصله پس از
smuggle-finresume میشوند، چون restore شدن protocol و port ghost پیش از queue شدن انجام شده بود؛ stageهای بعدی downstream یک بار اجرا میشوند
Smuggle-FIN بایتهای داخل TCP packet اصلی را split، overlap، rewrite یا reorder نمیکند. تغییر عمدی ordering آن temporal است: FIN مصنوعی reverse پیش از data اصلی held خارج میشود و ترافیک نامرتبط یا bypassشده ممکن است هنگام pause عبور کند. نگه داشتن هر دو جهت latency اضافه میکند و ممکن است retransmission timer، delayed-ACK، تغییر congestion/receive window یا stall قابل مشاهده application ایجاد کند.
آنچه هر participant ممکن است ببیند
- client: data اصلی خود را عادی میفرستد و queue شدن توسط WaterWall را مستقیم نمیبیند. اگر helper path FIN spoofشده را به آن route کند، ممکن است آن را دریافت کند؛ پذیرش FIN میتواند اتصال را طبق توضیح بالا مختل یا close کند. با نزدیک شدن pause به RTO، client ممکن است data held را retransmit کند.
- Middlebox روی helper path: میتواند پیش از ظاهر شدن data روی branch عادی،
FIN|ACKاز server به client با sequence و acknowledgement مشتقشده از data فعلی client ببیند. دستگاهی که آن را برای flow state خود بپذیرد ممکن است یک جهت را closed علامت بزند، packetهای بعدی را drop کند، parser state را عوض کند یا exact downstream echo مورد استفاده WaterWall را بسازد. دستگاه stateless یا دستگاهی که path، window، checksum یا flow history متفاوتی را validate میکند ممکن است آن را نادیده بگیرد. - server مقصد: معمولاً باید data اصلی client را بعداً از
nextدریافت کند؛ Smuggle-FIN خود آن را تغییر نداده است. packet مصنوعی reverse tuple دارد و از helper branch استفاده میکند، اما routing و طراحی branch در نهایت تعیین میکنند به endpoint واقعی یا observer سمت server برسد یا نه.
پس تکنیک به تفاوتی وابسته به مسیر نیاز دارد: دستگاه میانی باید FIN باورپذیر اولیه را معنادار بداند، در حالی که اتصال end-to-end واقعی آنقدر usable بماند که data اصلی delayed موفق شود. TCP normalization، routing نامتقارن، NAT state، SYN/FIN proxy، sequence-window validation، anti-spoofing، checksum offload یا validation و رفتار endpoint همگی میتوانند این فرض را باطل کنند.
طول عمر flow و رفتار هنگام failure
پس از exact echo یا هر forced release، four-tuple نرمالشده confirmed میماند و
injection بعدی Smuggle-FIN را bypass میکند. activity میتواند record confirmed
را تا 20 دقیقه idle نگه دارد. SYN، FIN یا RST در orientation یکسان record
را صریحاً reset نمیکند؛ پس reuse سریع دقیقاً همان tuple ممکن است تا expiry
همچنان trick را bypass کند. در chainهای غیرمعمول که data packet eligible در جهت
reverse از callback upstream میآید، record canonical واحد میتواند reorient
شود؛ در آن حالت queue فعال قدیمی بهجای replay دور انداخته میشود.
چون exact echo فقط با tuple، flags، payload length، sequence و acknowledgement
شناسایی میشود، یک FIN|ACK legitimate از server که اتفاقاً همه آن فیلدها را
match کند قابل تمایز نیست و consume خواهد شد. برعکس، تغییر کوچک helper در sequence
یا flags مانع confirmation میشود و flow را تا hard timeout نگه میدارد. هنگام
shutdown node، packetهای retained بهجای forward شدن در teardown دور انداخته
میشوند.
stateful-flow-limit جدول Smuggle-FIN را مستقل از سایر tableهای stateful محدود
میکند. فشار admission بهصورت fail-open است: candidate packet روی مسیر عادی
میماند و FIN mirrored ارسال نمیشود. تغییر flow affinity برای reverse packetها
با owner-worker handoff بهشکل خاص تحمل میشود، اما qualificationهای ordering
بالا را دارد.
تفاوتها و interactionها
برخلاف trickهای TLS/SNI، smuggle-fin نه ClientHello را پیدا میکند و نه تغییر
میدهد. برخلاف smuggle-sni، یک FIN header-only تازهساختهشده را از helper
branch میفرستد و data اصلی را نگه میدارد؛ smuggle-sni، ClientHello واقعی
captureشده را از helper میفرستد. برخلاف synfin-sni، TLS record، fake SYN،
close در همان جهت یا TCP sequence range همپوشان نمیسازد. برخلاف packet
duplication، helper packet آن tuple reverse، flags و sequence متفاوت، payload
صفر و release schedule جداگانه دارد.
وقتی ruleهای سازگاری خودشان اجازه دهند، میتوان آن را همراه stateful SNI، SNI Blender، TCP-bit rewrite، port ghost، protocol swap یا packet duplication تنظیم کرد. این به معنی اجرای stageها روی FIN مصنوعی نیست: helper FIN از bypass path بالا استفاده میکند، اما packetهای اصلی releaseشده وارد pipeline معمولی میشوند. timing اضافه pause و replay همچنان میتواند نحوه classification flow توسط trickهای stateful بعدی را عوض کند. MTU shaping نهایی ممکن است data packet بزرگ releaseشده را segment کند، هرچند خود Smuggle-FIN آن را split نکرده است.
جدولهای Stateful Flow
وقتی یکی از first-sni، smuggle-sni، overlap-sni، synfin-sni،
ech-sni-trick یا smuggle-fin فعال باشد، خود IpManipulator مالکیت worker
را برای جریان packet برقرار میکند. packetهای TCP/UDP بومی در upstream پیش از
پردازش stateful به worker جریان میرسند. در downstream این انتخاب پس از بازیابی
protocol و port و پیش از ادامهٔ stateful انجام میشود. این مرزهای داخلی dispatcher
به node اضافی در تنظیمات یا انتخاب flow owner توسط RawSocket و TunDevice نیاز
ندارند. trickهای stateless چنین الزامی اضافه نمیکنند. ProtoSwap ساده، هم encode
و هم بازیابی protocol و checksum را روی worker فعلی انجام میدهد؛ خروجی fragment
نیز مستقیم ارسال میشود و این تبدیل worker جریان را انتخاب یا fragmentها را
نگهداری نمیکند. protocolهای opaque که هنوز tuple بومی TCP/UDP ندارند نیز روی
worker فعلی ادامه مییابند.
packetهای retained به workerای pin میشوند که capture، delay barrier یا گروه held-packet را باز کرده است. بررسیهای fail-open برای mismatch worker باقی میمانند؛ lineهای اتصال معمولی مالک اصلی خود را حفظ میکنند و dispatcher packet آنها را جابهجا نمیکند. Smuggle-FIN نیز handoff جداگانهٔ ترافیک reverse به مالک flow دارای pause را با محدودیتهای ordering و handoff بخش اختصاصی حفظ میکند.
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
stateful-flow-limit | integer از 32 تا 1048576 | 65536 | سقف سخت 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 شناسایی میکنند.
هر جدول از یک seed هش مخصوص tunnel که توسط CSPRNG تولید میشود استفاده میکند تا انتخاب bucket از روی tupleهای مشاهدهشده قابل پیشبینی نباشد.
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 میکند.
هر delayed release یک مسیر fail-open دارد: اگر timer آن قابل arm شدن نباشد، بایتهای نگهداشتهشده بهجای باقی ماندن در state، بیدرنگ و بهترتیب flush میشوند.
نگهداریهای Overlap-SNI و SynFIN-SNI از timerهای fail-open جداگانه برای هر flow استفاده میکنند که با tuple نرمالشده و generation غیرصفر نگهداری مشخص میشوند. timeout، segment نگهداشتهشده را بیرون از shard lock و بدون تغییر آزاد میکند. بستهشدن upstream نگهداری زنده هر دو trick را آزاد میکند و Overlap-SNI در بستهشدن downstream نیز همین کار را انجام میدهد. اگر idle expiry یا teardown جدول، record را پیش از callback مربوط به timer حذف کند، destructor آن record عمداً packet نگهداشتهشده را دور میاندازد تا هنگام در اختیار داشتن shard lock یا در زمان shutdown چیزی forward نشود.
ناسازگاریها
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:
smuggle-fin- TCP flag rewrite
synfin-sniech-sni-trickoverlap-snismuggle-snifirst-sni- SNI Blender
- خروجی نهایی: port ghost، محاسبه checksum با پروتکل واقعی، protocol swap و سپس forward یا duplicate کردن packet
ترتیب downstream payload:
- بازیابی protocol
- بازیابی port ghost
smuggle-finech-sni-trickoverlap-sni- TCP flag rewrite
- hook downstream مربوط به SNI Blender
- logging hook مربوط به
smuggle-sni - ارسال نهایی 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 node،
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 تنظیم شده باشند، آنها نیز initialize
میشوند. اگر branchهای کمکی alias باشند، فقط یک بار Init دریافت میکنند.
محدودیتها و Padding
| مقدار | اندازه |
|---|---|
required_padding_left | 0 bytes |
| تعداد fragmentهای SNI Blender | 2 تا 16 |
| protocol replacement number | 0 تا 255 |
stateful-flow-limit | 32 تا 1048576 |
| طول hostname مربوط به ECH | 1 تا 255 bytes |
| capture مربوط به ECH | حداکثر 16 packet و یک TLS record با اندازه 16384-byte |
این node از chain فضای left padding نمیخواهد. بعضی trickها برای ساخت packetهای دستکاریشده، buffer را کپی یا buffer تازهای تخصیص میدهند.
نکتهها
- پیادهسازی فعلی عمدتاً IPv4 را تغییر میدهد؛ این trickها معمولاً IPv6 را نادیده میشود.
- بسیاری از trickها به packet کامل و fragmentنشده IPv4 TCP با header کامل TCP میخواهند.
- بیشتر trickهای SNI فقط upstream و فقط روی TLS ClientHello کار میکنند.
- این node به این وابسته است که مصرفکنندهٔ خروجی درخواست checksum روی buffer
همان packet را با
sbufGetRecalculateChecksum()رعایت کند. کپیها، packetهای نگهداریشده و هر خروجی segmentation درخواست مستقل دارند؛ پس از رشد فقط buffer بازگشتی معتبر است. پیام handoff شامل بایت خام، درخواست را همراه همان بایتها نگه میدارد و روی buffer بازسازیشده قرار میدهد. - ترمیم incremental مربوط به یک تغییر، درخواست کامل قدیمی را حفظ میکند. ProtoSwap پیش از encoding ترمیم در پروتکل بومی را کامل و درخواست را پاک میکند؛ refusal یا no-match بایتها و درخواست را تغییر نمیدهد. درخواست مصرفشده نباید از snapshot قدیمی دوباره تنظیم شود. خروجی دارای checksum عمداً خراب درخواست ندارد؛ نبود درخواست تضمین معتبر بودن checksum نیست.
- بهتر است branchهای کمکی از
nextمعمول جدا باشند. - وقتی
sni-blenderفعال است،sni-blender-packetsاجباری است و باید بین2و16باشد. - فیلد
trick_sni_blender_packets_delay_maxدر struct زبان C وجود دارد، اما parser فعلی JSON آن را در دسترس قرار نمیدهد و از آن استفاده نمیکند.
متادیتای نود
metadata برگرفته از کد منبع:
| ویژگی | مقدار |
|---|---|
| node flags | kNodeFlagNone |
can_have_prev | true |
can_have_next | true |
layer_group | kNodeLayer3 |
layer_group_prev_node | kNodeLayerAnything |
layer_group_next_node | kNodeLayerAnything |
required_padding_left | 0 bytes |