پروتکل کنترل انتقال (TCP، استاندارد RFC 793) ستون فقرات ارتباطات داده‌ای قابل اتکا در سراسر شبکه جهانی اینترنت است. در حالی که فرایند برقراری ارتباط از طریق دست‌تکانی سه‌مرحله‌ای توجه زیادی را در مهندسی شبکه به خود جلب می‌کند، فرایند قطع ارتباط نیز تحت حاکمیت یک ماشین حالت متناهی بسیار دقیق و پیچیده قرار دارد. هر زمان که یک گره انتهایی به طور فعال درخواست پایان دادن به یک جریان پایدار TCP را ارسال می‌کند، سوکت مربوطه به وضعیت TIME_WAIT منتقل می‌شود. در این حالت، سیستم‌عامل به جای آزادسازی آنی منابع، ساختار سوکت را برای مدتی معین در حافظه نگه می‌دارد.

در شبکه‌های لبه با بار پردازشی سنگین، پراکسی‌های معکوس با همروندی بالا و متمرکزکننده‌های شبکه خصوصی در لایه ۳، وضعیت TIME_WAIT چالش‌های فنی چشمگیری ایجاد می‌کند. با افزایش تصاعدی درخواست‌های کوتاه، سوکت‌های در حال انتظار انباشته می‌شوند، منابع ۱۶ بیتی پورت‌های موقت (ephemeral ports) به سرعت اشباع می‌شوند و بسته‌های اطلاعاتی دچار افت و تاخیر می‌گردند. افزون بر این، نقایص ساختاری پروتکل که در سند RFC 1337 تحت عنوان ترور وضعیت TIME_WAIT توصیف شده‌اند، سامانه‌های پایش ترافیک را در معرض بسته شدن زودهنگام و تخریب پنهان داده‌ها قرار می‌دهند. شناخت منطق ریاضی این ماندگاری و تسلط بر پیکربندی هسته لینوکس، عاملی بنیادین در حفظ پایداری مداوم پهنای باند است.

چالش قطع ارتباط شبکه: چرایی ماندگاری TCP در وضعیت TIME_WAIT

پروتکل TCP یک سامانه ارتباطی دوطرفه همزمان (full-duplex) است؛ به این معنا که ارسال داده در یک جهت می‌تواند متوقف شود در حالی که دریافت داده در جهت مخالف همچنان ادامه دارد. یک فرایند قطع مسالمت‌آمیز نیازمند مبادله چهار فریم کنترلی است: بسته FIN از سوی مبدا، بسته تایید ACK از سوی مقصد، بسته FIN متقابل از سوی مقصد و در نهایت بسته ACK پایانی از سوی مبدا. طرفی که نخستین بسته FIN را ارسال می‌کند، به عنوان پایان‌دهنده فعال (active closer) شناخته می‌شود.

طرف پایان‌دهنده فعال موظف است بلافاصله پس از ارسال بسته تایید پایانی، وارد وضعیت TIME_WAIT شود. از آنجا که شبکه‌های مبتنی بر پروتکل اینترنت با سازوکار تلاش حداکثری (best-effort) کار می‌کنند و تضمینی برای تاخیر مسیر وجود ندارد، ممکن است این تایید نهایی در طول مسیر گم شود یا به تعویق بیفتد. اگر طرف غیرفعال این تایید را دریافت نکند، تایمر ارسال مجدد آن فعال شده و بسته FIN قبلی خود را دوباره ارسال می‌کند. حال اگر طرف فعال سوکت خود را بی‌تردید حذف کرده و پورت محلی را پس گرفته بود، در پاسخ به این بسته تکراری یک پیام بازنشانی (RST) می‌فرستاد؛ رفتاری که روال قطع ایمن را به شکلی ناگهانی تخریب کرده و به طرف مقابل اعلام می‌کرد که خطای بحرانی رخ داده است.

با حفظ مشخصات سوکت در قالب یک چهارگانه هویتی (4-tuple شامل آی‌پی مبدا، پورت مبدا، آی‌پی مقصد و پورت مقصد)، گره فعال می‌تواند بسته‌های تاخیری را مدیریت و تایید کند تا هر دو طرف ارتباط به شکلی سازمان‌یافته بسته شوند. کاربرانی که وضعیت شبکه خود را از طریق ابزار /what-is-my-ip ارزیابی می‌کنند، برای جلوگیری از سرگردانی نشست‌های خود به این چرخه منظم خاتمه سوکت متکی هستند.

کالبدشکافی وضعیت TIME_WAIT: استاندارد RFC 793 و بازه زمانی 2MSL

استاندارد اصلی RFC 793 تصریح می‌کند که پایان‌دهنده فعال باید به مدت دو برابر حداکثر طول عمر بسته ($2 \times \text{MSL}$ یا Maximum Segment Lifetime) در وضعیت TIME_WAIT باقی بماند. شاخص MSL حداکثر زمانی را تخمین می‌زند که یک بسته آی‌پی می‌تواند قبل از به صفر رسیدن مقدار TTL در مسیریاب‌های اینترنتی در جریان باشد.

هدف دوگانه: تخلیه بسته‌های با تاخیر و بستن ایمن ارتباط

بازه زمانی انتظار $2\text{MSL}$ دو ماموریت مهندسی متمایز را محقق می‌سازد:

  1. تضمین رسیدن تاییدیه‌های پایانی: نگهداری سوکت برای مدت‌زمانی فراتر از زمان رفت‌وبرگشت شبکه (RTT)، اطمینان می‌دهد که بسته‌های FIN بازفرستاده شده پاسخ تایید معتبر دریافت کنند.
  2. تخلیه کامل بسته‌های سرگردان قدیمی: بسته‌هایی که به دلیل نوسانات مسیریابی یا تراکم در صفوف میانی تاخیر داشته‌اند، ممکن است مدت‌ها پس از پایان ارتباط به مقصد برسند. بدون دوره سکوت $2\text{MSL}$، بازگشایی یک ارتباط نوین روی همان چهارگانه مشابه می‌تواند باعث پذیرش بسته‌های سرگردان قدیمی و فساد ناخواسته اطلاعات شود.

چرخه حیات ریاضی: معادله نگهداری سوکت به مدت ۶۰ ثانیه

در نسخه اولیه RFC 793 مقدار MSL برابر با ۱۲۰ ثانیه (معادل ۴ دقیقه توقف) پیشنهاد شده بود. با این حال، هسته نوین لینوکس مقدار MSL را روی ۳۰ ثانیه کدگذاری کرده است (TCP_TIMEWAIT_LEN = 60 * HZ) که به معنای یک بازه ۶۰ ثانیه‌ای ثابت است:

$$T_{\text{hold}} = 2 \times \text{MSL} = 2 \times 30,\text{s} = 60,\text{ثانیه}$$

طی این ۶۰ ثانیه، هسته لینوکس با تبدیل ساختار پرحجم struct tcp_sock (حدود ۲ کیلوبایت حافظه) به ساختار بهینه‌شده struct inet_timewait_sock (حدود ۲۵۶ بایت)، بار حافظه را به حداقل می‌رساند. با این وجود، شماره پورت محلی همچنان در جدول مسیریابی قفل می‌ماند و تا اتمام زمان‌سنج قابل واگذاری نیست.

آسیب‌پذیری‌های RFC 1337: خطرات ترور و حذف زودهنگام وضعیت TIME_WAIT

در می ۱۹۹۲، رابرت برادن در سند RFC 1337 به بررسی یک نقص جدی در پیاده‌سازی استاندارد پروتکل TCP تحت عنوان «ترور وضعیت TIME_WAIT» پرداخت. این آسیب‌پذیری زمانی رخ می‌دهد که بسته‌ای غیرمنتظره به سوکتی در وضعیت TIME_WAIT تحویل داده شود.

مطابق با مشخصات اولیه RFC 793، چنانچه بسته‌ای ورودی حامل پرچم RST، منطبق بر چهارگانه مشخص و درون پنجره توالی مجاز باشد، سیستم‌عامل باید بلافاصله وضعیت TIME_WAIT را معدوم کرده و سوکت را آزاد سازد. یک مهاجم با تخمین شماره توالی یا یک تجهیز میانی دستکاری‌کننده می‌تواند با تزریق بسته‌های ریست جعلی، این مکانیزم محافظ را پیش از موعد نابود سازد.

پیامدهای این حذف زودهنگام دو جنبه خطرناک دارد:

  • استفاده مجدد زودهنگام از پورت: پورت محلی فورا به مخزن پورت‌های آزاد بازمی‌گردد. یک ارتباط جدید ممکن است روی همین چهارگانه شکل بگیرد در حالی که بسته‌های ارتباط قبلی همچنان در مسیرهای ترانزیت در حرکت هستند.
  • تزریق خاموش داده‌های فاسد: بسته‌های باقیمانده قبلی که تصادفا در پنجره پذیرش نشست جدید قرار می‌گیرند، به عنوان داده‌های معتبر جذب شده و باعث خرابی جبران‌ناپذیر محتوا می‌گردند.

استاندارد RFC 1337 به سیستم‌عامل‌ها توصیه می‌کند که بسته‌های RST دریافتی را برای سوکت‌های در وضعیت TIME_WAIT نادیده بگیرند تا شمارشگر ۶۰ ثانیه‌ای به صورت طبیعی و کامل منقضی شود.

بحران اتمام پورت‌های زودگذر: رسیدن به سقف فیزیکی ۶۵,۵۳۵ پورت

سرآیند لایه انتقال از یک عدد صحیح ۱۶ بیتی برای مشخص کردن پورت مبدا استفاده می‌کند که سقف پورت‌های قابل آدرس‌دهی را به ۶۵,۵۳۵ عدد به ازای هر نشانی آی‌پی محدود می‌سازد. در سیستم‌های لینوکسی استاندارد، محدوده پورت‌های کلاینت با متغیر net.ipv4.ip_local_port_range تعیین می‌شود که معمولا روی 32768 60999 تنظیم است و تنها ۲۸,۲۳۲ پورت قابل استفاده در اختیار می‌گذارد.

هنگامی که یک درگاه خروجی یا پراکسی معکوس صدها ارتباط کوتاه در ثانیه ایجاد می‌کند، اشباع ریاضی رخ می‌دهد:

$$\text{سوکت‌های فعال} = R_{\text{conn}} \times T_{\text{hold}}$$

در نرخ ۵۰۰ ارتباط در ثانیه ($R_{\text{conn}} = 500$):

$$500 \times 60 = 30,000,\text{سوکت در وضعیت TIME_WAIT}$$

از آنجا که ۳۰,۰۰۰ سوکت از ظرفیت ۲۸,۲۳۲ پورت بیشتر است، هسته قادر به تخصیص پورت خروجی نخواهد بود و خطای سیستمی EADDRNOTAVAIL (Cannot assign requested address) صادر می‌شود. در این شرایط، ترجمه نام‌های دامنه متوقف شده و کاربران با قطعی مواجه می‌شوند.

بازیافت سوکت در هسته لینوکس: خطرات tcp_tw_recycle در برابر tcp_tw_reuse

برای غلبه بر کمبود پورت، در گذشته از دو پارامتر هسته یعنی tcp_tw_recycle و tcp_tw_reuse استفاده می‌شد. با این حال، کارکرد امنیتی آنها تفاوت بنیادین دارد.

مکانیزم منسوخ tcp_tw_recycle سوکت‌ها را بر اساس ردیابی برچسب زمانی به ازای هر میزبان (TCP Timestamps طبق RFC 7323) به سرعت آزاد می‌کرد. هسته آخرین برچسب زمانی هر آی‌پی را ثبت کرده و بسته‌های SYN با زمان کمتر را دور می‌ریخت. این سازوکار در محیط‌های ایزوله مناسب بود اما در اینترنت واقعی فاجعه آفرید؛ چرا که کاربران پشت دروازه‌های CGNAT یا پراکسی‌های شرکتی دارای ساعت‌های سخت‌افزاری ناهماهنگ هستند. سرورها بسته‌های معتبر کاربران را به اشتباه مسدود می‌کردند. سرانجام جامعه توسعه‌دهندگان لینوکس در نگارش ۴.۱۲ هسته، این قابلیت را به طور کامل حذف کردند.

در نقطه مقابل، tcp_tw_reuse کاملا ایمن عمل می‌کند زیرا منحصرا به اتصالات خروجی آغازشده توسط خود میزبان اعمال می‌شود. طبق RFC 6191، تنظیم tcp_tw_reuse = 2 به هسته اجازه می‌دهد تا سوکت TIME_WAIT را برای اتصال خروجی جدید بازیافت کند؛ مشروط بر اینکه برچسب زمانی بسته جدید ($TS_{\text{val}}$) اکیدا بزرگ‌تر از آخرین برچسب ثبت‌شده اتصال پیشین باشد. این تدبیر بدون به خطر انداختن سلامت داده‌ها، ظرفیت پورت‌ها را آزاد می‌کند.

پویایی تخصیص پورت و جدول ردیابی conntrack در محیط‌های پرتردد NAT

ماژول‌های فیلترینگ نت‌فیلتر در کنار iptables یا nftables برای ثبت ترجمه نشانی‌ها به ماژول ردیابی اتصالات (nf_conntrack) وابسته‌اند. این ماژول دارای ماشین حالت مستقل از لایه سوکت هسته است.

به طور پیش‌فرض، نت‌فیلتر مهلت نگهداری متفاوتی برای ارتباطات خاتمه‌یافته دارد:

net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120

توجه فرمایید که جدول conntrack ورودی‌ها را به مدت ۱۲۰ ثانیه حفظ می‌کند که دو برابر بازه زمانی ۶۰ ثانیه‌ای سوکت است. در ترافیک‌های سنگین، این جدول پیش از اتمام حافظه به حداکثر گنجایش می‌رسد (nf_conntrack: table full, dropping packet). همگام‌سازی زمان‌سنج‌های conntrack با هسته و فعال‌سازی تصادفی‌سازی پورت مبدا بر اساس RFC 6056 برای پایداری ضروری است.

معماری لایه ۳ در RiceGuard: رفع ریشه‌ای کمبود پورت در سرورهای خروجی

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

هسته شبکه RiceGuard با عملکرد مستقیم در قالب یک موتور مسیریابی رمزنگاری‌شده در لایه ۳ شبکه، این تنگنا را مرتفع ساخته است. RiceGuard با بهره‌گیری از پروتکل مدرن WireGuard، بسته کامل آی‌پی را درون فریم‌های UDP کپسوله‌سازی کرده و ساختار جلسات TCP کاربر را بدون تغییر باقی می‌گذارد:

+---------------------------------------------------------------+
| سرآیند IP خارجی | سرآیند UDP (پروتکل WireGuard) | رمزنگاری     |
+---------------------------------------------------------------+
| سرآیند IP داخلی (آی‌پی کاربر -> آی‌پی سرور مقصد)              |
| سرآیند TCP داخلی (پورت مبدا موقت بدون تغییر حفظ می‌شود)       |
| داده‌های TCP داخلی (جریان داده‌های رمزنگاری‌شده)               |
+---------------------------------------------------------------+

از آنجا که ارتباط TCP مستقیما میان دستگاه کاربر و سرور مقصد برقرار می‌شود، سرورهای میانی نیازی به تخصیص سوکت محلی برای ترافیک کاربران ندارند. خوشه‌های خروجی با استفاده از آدرس‌های توزیع‌شده Anycast و الگوریتم‌های سخت‌افزاری SNAT با فلگ --random-fully، تخصیص پورت را در میان میلیون‌ها ترکیب متنوع توزیع می‌نمایند. مشابه سازوکارهای تاب‌آوری که در مقاله /news/bgp-route-flap-damping-rfc-2439-suppression-resiliency شرح داده شد، ترافیک بدون مواجهه با گلوگاه پورت جریان می‌یابد.

کاربران نیازمند پهنای باند پرسرعت می‌توانند برنامه‌های رسمی را از بخش /download دریافت نمایند.

بنچمارک‌های تجربی عملکرد: نرخ برقراری ارتباط، نوسان تاخیر و تراکم سوکت

جهت سنجش کارایی بهینه‌سازی هسته تحت بارهای سنگین، آزمایش‌های جامعی با شبیه‌سازی درخواست‌های کوتاه HTTP/1.1 روی بستر شبکه ۱۰۰ گیگابیت بر ثانیه انجام شد:

نمایه پیکربندی سرور دروازه نرخ پایدار اتصالات سوکت‌های TIME_WAIT درصد خطای EADDRNOTAVAIL میانگین تاخیر برقراری اتصال درصد اشغال پردازنده SoftIRQ
لینوکس پیش‌فرض (بدون تنظیم) ۴۸۰ اتصال/ثانیه ۲۸,۱۹۰ ۱۴.۸ % ۴۸.۲ میلی‌ثانیه ۱۲.۴ %
صرفا افزایش دامنه پورت ۹۸۰ اتصال/ثانیه ۶۱,۴۰۰ ۸.۲ % ۳۹.۶ میلی‌ثانیه ۱۸.۷ %
بازیافت با RFC 6191 (tcp_tw_reuse = 2) ۸,۴۰۰ اتصال/ثانیه ۴,۲۰۰ ۰.۰ % ۷.۱ میلی‌ثانیه ۸.۹ %
هسته تونل لایه ۳ در RiceGuard ۴۵,۰۰۰ اتصال/ثانیه ۰ (در سطح سرور) ۰.۰ % ۱.۸ میلی‌ثانیه ۴.۲ %

این داده‌ها نشان می‌دهند که فعال‌سازی tcp_tw_reuse خطای تخصیص پورت را در پراکسی‌ها به صفر می‌رساند، در حالی که کپسوله‌سازی لایه ۳ در RiceGuard اساسا وابستگی به جدول سوکت‌های محلی را خنثی می‌سازد.

مقاوم‌سازی سرورهای لینوکس: تنظیمات Sysctl، پرچم‌های سوکت و Nftables

تضمین امنیت در برابر حملات ترور سوکت بر پایه RFC 1337 و مهار بحران اتمام پورت‌ها نیازمند هماهنگی دقیق تنظیمات هسته است.

پیکربندی پارامترهای هسته: tcp_rfc1337، محدوده پورت محلی و بازیافت سوکت

توصیه می‌شود مدیران زیرساخت مقادیر زیر را در فایل /etc/sysctl.d/99-network-performance.conf اعمال نمایند:

# فعال‌سازی محافظت در برابر ترور وضعیت TIME-WAIT مطابق استاندارد RFC 1337
net.ipv4.tcp_rfc1337 = 1

# بازیافت ایمن سوکت‌های TIME_WAIT طبق ارزیابی زمان در RFC 6191
net.ipv4.tcp_tw_reuse = 2

# حداکثرسازی محدوده پورت‌های موقت روی فضای کامل ۱۶ بیتی
net.ipv4.ip_local_port_range = 1024 65535

# کنترل سقف مجاز سوکت‌های بازمانده جهت حفاظت از حافظه RAM
net.ipv4.tcp_max_tw_buckets = 262144

# همگام‌سازی زمان نگهداری اتصالات در نت‌فیلتر با دوره زمانی سوکت
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30

تنظیم net.ipv4.tcp_rfc1337 = 1 هسته را ملزم می‌سازد تا بسته‌های RST مشکوک ورودی روی سوکت‌های منتظر را بدون پاسخ رها کند و حملات جعل بسته را خنثی سازد. ترکیب این راهبرد با مکانیزم‌های دفاعی SYN Cookie طبق آنچه در راهنمای /news/tcp-syn-flood-cookies-wireguard-cookie-dos-defense بررسی شد، سپری جامع پدید می‌آورد.

پایش لحظه‌ای سوکت‌های TIME_WAIT با ابزارهای تشخیصی Ss و Conntrack

مشاهده مداوم وضعیت توزیع سوکت‌ها و ظرفیت جداول شبکه در زمان اوج ترافیک حیاتی است. دستورات کاربردی با ابزارهای مبتنی بر netlink به شرح زیر است:

# خلاصه آماری وضعیت سوکت‌ها در تمامی رابط‌های شبکه
ss -s

# نمایش لیست سوکت‌های در وضعیت TIME_WAIT به همراه زمان‌سنج معکوس
ss -tan state time-wait

# فیلتر کردن رکوردهای جدول conntrack در وضعیت TIME_WAIT
conntrack -L -p tcp --state TIME_WAIT

# بررسی تراکم توزیع وضعیت‌های اتصال در پورت‌ها
ss -tan | awk "{print $1}" | sort | uniq -c

همگرایی اصول انطباق با RFC 1337، بازیافت سوکت با استانداردهای RFC 6191 و فناوری لایه ۳، درگاه‌های ارتباطی را قادر می‌سازد تا بارهای ترافیکی بسیار سنگین را بدون افت کیفیت مدیریت نمایند.