RawSocket
RawSocket WaterWall را به capture و injection مربوط به packetهای خام IPv4 متصل میکند. وقتی range مربوط به capture تنظیم شده باشد، این node میتواند packetها را از network stack میزبان بگیرد و وارد یک chain لایه ۳ کند. مستقل از capture، packetهای خام IP برگشتی از chain را نیز روی raw output device مینویسد.
در مستندات قدیمی ممکن است نام RawDevice را ببینید؛ نام نوع فعلی node RawSocket است.
RawSocket یک adapter لایه ۳ است، نه یک tunnel مبتنی بر connection. packetهای captureشده روی worker packet lineها فرستاده میشوند و هر payloadی که از یکی از دو جهت chain به RawSocket برسد، یک packet خام و کامل IP در نظر گرفته میشود و روی raw output device نوشته خواهد شد.
در نسخهٔ فعلی، مرز backend منشأ checksum را نیز تعیین میکند: Windows flagهای
checksum/direction مربوط به WinDivert و Linux metadata مربوط به NFQUEUE را بررسی
میکند. packet malformed یا packetی که checksum لازم آن معتبر نیست پیش از
publication drop میشود. fragment IPv4 هیچگاه transport checksum ساختگی از یک
fragment ناقص دریافت نمیکند. reassemble پیش از publication، datagram کامل
میسازد و preserve-fragments مرز fragment و worker affinity را حفظ میکند.
buffer تحویلشده مالکیت معمول دارد و claim عمر fragment همراه آن نیست؛ ورودی
stack شرط کاملبودن datagram را هنگام دریافت enforce میکند.
جایگاه رایج
در لبه یک packet chain:
RawSocket -> PacketReceiver
PacketSender -> RawSocket
TunDevice -> IpManipulator -> RawSocket
همراه nodeهای packet disguise:
TunDevice -> PingClient -> RawSocket
RawSocket -> PingServer -> TunDevice
وقتی RawSocket در جایگاه اول است، packetهای wrapشده و captureشده را در جهت
upstream وارد PingServer میکند. PingServer آنها را به سمت TunDevice
بازیابی میکند و فقط با تنظیم send-replies: true ابتدا Echo Reply دقیق را از
raw output برمیگرداند. پیشفرض ارسال Reply در هر دو node Ping، false است و
میتوان آن را در هر سمت مستقل فعال کرد. packetهای سادهای که TunDevice
میگیرد در جهت downstream از PingServer عبور میکنند تا
در Echo Request جدید wrap شوند. Reply منطبق این request در PingServer مصرف
میشود و به TUN تحویل داده نمیشود؛ این مصرف به تنظیم ارسال Reply وابسته نیست.
RawSocket میتواند node قبلی، node بعدی یا هر دو را داشته باشد. جهت ارسال packetهای captureشده هنگام startup تعیین میشود:
| جایگاه در chain | packetهای IPv4 captureشده به کدام سمت فرستاده میشوند |
|---|---|
RawSocket آخرین node chain است | به node قبلی، در جهت downstream |
RawSocket آخرین node chain نیست | به node بعدی، در جهت upstream |
هر payloadی که از upstream یا downstream به RawSocket برسد، از همان raw output device تزریق میشود.
نمونه تنظیم
{
"name": "raw-ip",
"type": "RawSocket",
"settings": {
"fragment-policy": "preserve-fragments",
"capture-device-name": "capture-in",
"raw-device-name": "raw-out",
"capture-filter-mode": "source-ip",
"capture-ips": [
"192.0.2.10",
"198.51.100.0/24"
],
"bypass-conntrack": true
},
"next": "next-node-name"
}
برای packet injection فقط در جهت write، همه کلیدهای range مربوط به capture را حذف کنید:
{
"name": "raw-output",
"type": "RawSocket",
"settings": {
"fragment-policy": "preserve-fragments",
"raw-device-name": "raw-out",
"bypass-conntrack": true
}
}
فیلدهای ضروری
فیلدهای سطح بالا:
| فیلد | نوع | توضیح |
|---|---|---|
name | string | نام دلخواه node. باید داخل فایل config یکتا باشد. |
type | string | باید دقیقاً "RawSocket" باشد. |
settings | object | تنظیمات RawSocket. فیلدهای capture داخل آن اختیاری هستند. |
فیلدهای شرطی مربوط به capture داخل settings:
| گزینه | نوع | توضیح |
|---|---|---|
capture-ips یا capture-ip | string یا آرایه string | دو نام معادل برای یک آدرس/range یا فهرستی از آدرسها و rangeهای IPv4. هر دو کلید هر دو شکل ورودی را میپذیرند. |
capture-filter-mode | string | فقط وقتی حداقل یک range تنظیم شده باشد لازم است. parser مقدارهای "source-ip" و "dest-ip" را میشناسد، اما پیادهسازی فعلی فقط "source-ip" را میپذیرد. در حالت write-only خوانده نمیشود. |
مقدار تکی یا هر عضو آرایه در capture-ips و capture-ip میتواند یک IPv4 تکی باشد:
"192.0.2.10"
یا یک CIDR IPv4:
"198.51.100.0/24"
مقادیر IPv6 رد میشوند. اگر هیچکدام از کلیدهای capture وجود نداشته باشد، یا کلید انتخابشده یک آرایه خالی باشد، capture غیرفعال است و فقط raw output device راهاندازی میشود.
تنظیمات اختیاری
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
dont-capture-protocols | array of integers | [] | شناسههای protocol در IPv4 که capture نمیشوند؛ فقط اعداد صحیح ۰ تا ۲۵۵ پذیرفته میشوند و تکراریها نادیده گرفته میشوند. مقدار صفر همان protocol صفر است، نه همهٔ protocolها. |
multi-queue | boolean | true | در Linux، برای هر event worker یک receive queue درخواست میکند؛ اگر queue اضافی یا steering در دسترس نباشد، یک queue روی worker صفر استفاده میشود. مقدار false مستقیماً همین حالت تکصف را انتخاب میکند. پردازش packet همچنان از همهٔ workerها استفاده میکند. در Windows پذیرفته میشود ولی آرایش دریافت را تغییر نمیدهد. حالت write-only صف دریافت ندارد. |
kernel-queue-tune | boolean | در Linux برابر true و در دیگر پلتفرمها false | تنظیم صفهای کرنل capture هنگام startup. مقدار false تنظیم پایهٔ NFQUEUE و receive buffer را حفظ میکند و همچنان آنها را گزارش میدهد. مستقل از skip-sysctl و misc.tcp-tune است؛ در پلتفرمهای دیگر و حالت write-only اثری ندارد. |
kernel-queue-packets | integer | خودکار در صورت حذف گزینه | تعداد packet در NFQUEUE برای کل capture device؛ میان صفهای نهایی دریافت تقسیم میشود. |
kernel-receive-buffer | integer | خودکار در صورت حذف گزینه | تعداد بایت receive buffer در Netlink برای کل capture device؛ میان socketهای نهایی دریافت تقسیم میشود. واحد آن درخواست SO_RCVBUF پیش از حسابداری دوبرابری Linux است. |
capture-device-name | string | unnamed-capture-device | نامی که به capture-device backend داده میشود. |
raw-device-name | string | unnamed-raw-device | نامی که به raw-output backend داده میشود. |
bypass-conntrack | boolean | true | در Linux، NOTRACK را برای هر دو مسیر capture و raw output کنترل میکند: ruleهای source-range در raw PREROUTING و rule دقیق با mark خودکار در raw OUTPUT. مقدار false نصب هر دو را غیرفعال میکند؛ NFQUEUE و رفتار drop-and-dispatch همچنان فعال میمانند. در حالت write-only فقط rule خروجی لازم است. روی WinDivert ویندوز اثری ندارد. |
mark | integer | 0 در حالت غیرفعال بودن bypass | mark صریح برای raw output؛ فقط با bypass-conntrack=false مجاز است. وقتی bypass فعال باشد، حتی mark: 0 یا mark: null رد میشود. |
skip-sysctl | boolean | false | در Linux، batch مربوط به تنظیمات performance با sysctl را اجرا نمیکند. تنظیم NFQUEUE و ruleهای ضروری iptables همچنان اجرا میشوند. |
هر دو گزینهٔ عددی صف کرنل فقط عدد صحیح در بازهٔ 1..2147483647 با سقف INT_MAX
میپذیرند. صفر، عدد منفی یا کسری، string، boolean و null رد میشوند؛ فقط حذف
گزینه اندازهگذاری خودکار را انتخاب میکند. override جای هدف خودکار را میگیرد،
ولی مقدار پایه یا مقدار موجودِ بزرگتر را کاهش نمیدهد و فقط با
kernel-queue-tune: true اعمال میشود. هر سه گزینه حتی با tuning غیرفعال،
capture غیرفعال یا روی پلتفرم غیر Linux اعتبارسنجی میشوند.
packetهای ورودی با protocol موجود در dont-capture-protocols از هر دو rule مربوط
به capture و NOTRACK ورودی همین node عبور میکنند و پردازش معمول conntrack و
firewall میزبان را ادامه میدهند؛ حتی اگر bypass-conntrack فعال باشد. برای
سپردن ICMP و TCP ورودی به میزبان، از [1, 6] استفاده کنید. حذف این گزینه یا
آرایهٔ خالی، رفتار فعلی را حفظ میکند. این گزینه raw output را محدود نمیکند؛
packetهایی که WaterWall از raw socket میفرستد همچنان تابع bypass-conntrack هستند.
در Linux، آرایهٔ غیرخالی به matcher از نوع bpf در iptables (xt_bpf) نیاز دارد.
یک فیلتر classic BPF با اندازهٔ ثابت، فقط بایت protocol در header IPv4 را در
هر دو rule مربوط به NFQUEUE و NOTRACK بررسی میکند؛ fragmentها و protocol صفر
نیز پشتیبانی میشوند و header لایهٔ transport تفسیر نمیشود. نبود پشتیبانی matcher
باعث شکست startup و rollback میشود. آرایهٔ خالی matcher اضافه نمیکند. Windows
همین استثناها را با مقایسهٔ ip.Protocol در فیلتر WinDivert اعمال میکند.
Aliasهای سازگاری:
| Alias | معنا |
|---|---|
listen-ips | بهعنوان alias سازگاری پذیرفته میشود و مانند دو کلید اصلی string یا آرایه string میگیرد. |
capture-ip | نامی معادل capture-ips است؛ با وجود نام مفرد، string یا آرایه string را میپذیرد. |
اندازهٔ صف دریافت کرنل در Linux
اندازهگذاری از تعداد نهایی صفهای فعال پس از هر fallback استفاده میکند. مقدار پایه برای کل دستگاه 65,536 packet در NFQUEUE و 64 MiB درخواست receive buffer در Netlink است. تنظیم خودکار از این بودجهها شروع میکند و هدفهای ترجیحی زیر را بهازای هر صف به کار میبرد:
| RAM profile تنظیمشده | packet ترجیحی در هر صف | درخواست receive buffer ترجیحی برای هر socket |
|---|---|---|
| S1 / S2 | 1,024 | 1 MiB |
M1 / M2 (client / client-larger) | 2,048 | 2 MiB |
L1 / L2 (server) | 4,096 | 4 MiB |
برای هر منبع، هدف خودکار هر صف برابر بزرگترِ سهم پایه و هدف ترجیحی است؛ هدف ترجیحی با سهم آن صف از سقف 131,072 packet یا 128 MiB محدود میشود. تقسیم با تعداد واقعی صفها انجام میشود. سقفها فقط هدف خودکار را محدود میکنند، نه مقدار موجودِ بزرگتر یا override صریح. مقدار کل صریح جای هدف خودکار همان منبع را میگیرد و بین صفها تقسیم میشود، ولی حداقل سهم پایه حفظ میشود. با غیرفعال بودن tuning، تنظیم پایه همچنان اجرا میشود و overrideها اعمال نمیشوند.
profile سیاست حافظهٔ تنظیمشده است، نه RAM تشخیصدادهشدهٔ میزبان. بودجهٔ هر
capture device مستقل است؛ چند دستگاه بودجههای خود را به مصرف کل اضافه میکنند.
این عددها RSS واقعی یا سقف بایت payload نیستند. Linux مقدار SO_RCVBUF را با
حسابداری دوبرابری گزارش میکند و محدودیتهای socket میزبان میتوانند درخواست را
کاهش دهند. اگر افزایش مؤثر قابلتأیید نباشد، tuning مقدار buffer موجود را حفظ
میکند. صف بزرگتر میتواند burst را جذب کند،
ولی حافظه و تأخیر بیشتری مصرف میکند و تضمین throughput نیست.
startup تعداد صفهای فعال، منبع هدف (auto، override یا disabled)، سقف هر
NFQUEUE که کرنل با ACK تأیید کرده و SO_RCVBUF درخواستی و خواندهشده را گزارش
میدهد. سقف تأییدنشده بهصورت unknown گزارش میشود. گزارشها با tuning غیرفعال
نیز ثبت میشوند. رد شدن tuning یا نبود readback هشدار ایجاد میکند و صفهای
capture قابلاستفاده را متوقف نمیکند.
این تنظیم FIFO مشترک userspace، اندازهٔ batch callbackها یا backlog خروجی raw
را تغییر نمیدهد. sysctl سراسری جدیدی نمینویسد و مستقل از skip-sysctl و
misc.tcp-tune در core است.
مسیر Capture
در startup، RawSocket همیشه یک raw output device برای packet injection میسازد. فقط وقتی حداقل یک capture range تنظیم شده باشد، موارد زیر نیز ساخته و فعال میشوند:
- یک capture device با rangeهای IPv4 تنظیمشده
- NFQUEUE socket، capture reader و ruleهای capture موردنیاز
اگر capture range وجود نداشته باشد، هیچ capture device یا کد setup مرتبط با capture اجرا نمیشود و node فقط در مسیر write کار میکند.
در Linux هر receive queue یک worker مشخص دارد و خواندن آن غیرمسدودکننده است.
انتخاب صف TCP/UDP همچنان با hash متقارن آدرسها، protocol و portها انجام میشود.
در حالت چندصفی، همهٔ بستههای کامل روی worker خواننده ادامه مییابند؛ node مصرفکننده
پس از تبدیلهای لازم، مالکیت جریان را تعیین میکند. بستههای
کاملِ پروتکلهای دیگر بهطور پیشفرض با ruleهای statistic --mode random
بهصورت تصادفی میان صفها پخش میشوند و روی worker خواننده ادامه پیدا میکنند.
احتمالهای شرطی در ruleهای پشتسرهم سهم تقریباً برابری به هر صف میدهند؛ تعداد
دقیق بستهها و ترتیب آنها تضمین نمیشود. شمارندهٔ مشترک انتخاب nth حذف میشود،
اما این به معنی بدونقفل بودن همهٔ پیادهسازیهای مولد تصادفی کرنل نیست.
تنظیم جدید یا شناخت nodeهای بعدی لازم نیست. در حالت تکصف، packet کامل TCP/UDP
با portهای قابلخواندن با hash متقارن جریان به یک worker ثابت میرود تا ترتیب
ورودی همان جریان در FIFO حفظ شود. packetهای opaque یا غیرقابلتشخیص با شمارندهٔ
محلی session بین workerها بهصورت round-robin تقسیم میشوند؛ بنابراین پروتکل
۵۰ میان یک جفت IP همچنان موازی پردازش میشود و portی برای آن حدس زده نمیشود.
capture در Windows نیز همین سیاست را دارد. fragmentها سیاست
وابستگی قبلی را حفظ میکنند؛ datagram کاملشده پس از reassembly نیز مثل بستهٔ کامل توزیع میشود.
مصرفکنندهٔ stateful پس از decode یا تغییر tuple، خودش worker جریان را تعیین میکند.
در حالت چندصفی، برای هر capture range و receive queue یک rule جریان و یک rule
بستهٔ غیر TCP/UDP وجود دارد؛ در حالت تکصف فقط یک rule برای هر capture range
نصب میشود. هزینهٔ بررسی ruleها با تعداد queueها رشد میکند. توزیع تصادفی میتواند
ترتیب رسیدن بستهها از workerهای مختلف را تغییر دهد.
وقتی backend مربوط به capture یک packet دریافت میکند:
RawSocketبررسی میکند که packet از نوع IPv4 باشد.- packetهای غیر IPv4 به pool برگردانده میشوند و به chain راه پیدا نمیکنند.
- packetهای مطابق با فیلتر capture از network stack میزبان drop میشوند (توسط drop-and-dispatch در لینوکس یا packet diversion در ویندوز)؛ هنگام فعال بودن capture، تحویل معمول آنها به لایهٔ transport محلی متوقف میشود و WaterWall کپی دریافتشده را پردازش میکند.
- worker packet line متناظر از chain گرفته میشود.
- packet IPv4 captureشده به سمت node مجاور انتخابشده فرستاده میشود.
اعمال فیلتر IP تنظیمشده و جلوگیری از ادامهٔ پردازش packetها در kernel بر عهده backend مربوط به capture است. RawSocket برای هر packet یک line معمول per-connection نمیسازد.
مسیر output
وقتی upstream یا downstream payload به RawSocket میرسد:
- buffer مربوط به payload یک packet خام IP در نظر گرفته میشود.
- payload بررسی میشود تا دقیقاً یک packet کامل IPv4 باشد؛ header ناقص، طول ناسازگار و بایتهای اضافی در انتها drop میشوند.
- اگر buffer همان packet درخواست checksum داشته باشد، WaterWall پیش از نوشتن checksumهای قابل محاسبه را اصلاح میکند. شکست validation/checksum buffer را recycle میکند و raw writer فراخوانی نمیشود.
- packet از طریق raw output device نوشته میشود.
- اگر raw device در دسترس نباشد یا نوشتن شکست بخورد، buffer به pool برگردانده میشود.
هر دو callback upstream و downstream payload از همین write path استفاده میکنند.
خروجی در FIFO مربوط به همان event worker ارسالکننده صف میشود و بعداً روی همان worker نوشته میشود. bufferهای تمامشده به pool همان worker برمیگردند؛ raw writer دیگر thread یا pool اختصاصی ندارد و mutex سراسری نوشتن workerها را سری نمیکند. محدودیت کل device همچنان ۱۳۱٬۰۷۲ packet در Linux و ۲۵۶ packet در Windows است و خروجی صفشده و در حال ارسال را شامل میشود. ترتیب هر worker حفظ میشود؛ بین workerهای مستقل ترتیب سراسری وجود ندارد.
در Linux، هر flush حداکثر ۵۱۲ packet اصلی را با یک sendmmsg() غیرمسدودکننده
ارسال میکند. در ارسال ناقص فقط prefix ارسالشده آزاد میشود و suffix باقیمانده
پیش از packetهای جدید میماند. EINTR تلاش بعدی را به callback بعدی میسپارد.
پس از EAGAIN، retry روی worker با تأخیر ۱ ms و poll() با timeout صفر،
آمادگی socket را بررسی میکند؛ event worker منتظر نمیماند. خطاهای کمبود منابع
kernel و خطاهای packet یا مقصد همچنان تنها packet اول ارسالنشده را drop میکنند؛
خطای دائمی descriptor درخواست shutdown میدهد. برای پر شدن batch هیچ timerی صبر نمیکند.
در Windows، خروجی WinDivert بهصورت overlapped انجام میشود. packet اصلی تا پایان عملیات معلق حفظ میشود؛ اعلان native completion فقط کار را روی worker مالک صف میکند و به pool آن دست نمیزند. Stop پذیرش را میبندد، اعلانها را quiesce میکند و پیش از آزاد کردن packetها دسترسی native معلق را تکمیل یا لغو میکند.
در Linux، این قابلیت به iptables، iptables-save و ip از iproute2 و مجوز
مدیریت firewall (CAP_NET_ADMIN) نیاز دارد.
پیش از فعال شدن writer، WaterWall وضعیت ruleهای IPv4 را میخواند، mark تصادفی
انتخاب میکند، SO_MARK را تنظیم و rule اختصاصی NOTRACK را نصب میکند. شکست در
خواندن policy، انتخاب mark، تنظیم socket یا نصب rule باعث شکست startup و تلاش
برای rollback میشود.
انتخاب mark بازهٔ 0x80000000..0xffffffff را ترجیح میدهد و در صورت نیاز به
مقادیر پایینتر مراجعه میکند؛ مقادیر کمتر از 0x00010000 انتخاب نمیشوند.
مقایسهها و انتسابهای ثابت mark در iptables-save و selectorهای fwmark در
ip -4 rule show، از جمله maskها و نتیجهٔ تطبیق packet بدون mark، بررسی
میشوند. مقدار انتخابشده در log ثبت میشود. این کار فقط احتمال تداخل را کم
میکند و رزرو سراسری نیست: policy بومی nftables، tc/eBPF، mark دیگر socketها،
تغییرات همزمان و برنامههایی که بعداً اجرا میشوند ممکن است دیده نشوند.
این معافیت فقط conntrack را کنار میگذارد؛ routing و firewall همچنان اعمال میشوند و خروجی معاف نباید به NAT یا stateful filtering مبتنی بر conntrack میزبان متکی باشد. اولویت معمول raw، پردازش fragment و محدودیت MTU تغییر نمیکند. برای mark صریح از تنظیم زیر استفاده کنید:
"settings": {
"fragment-policy": "preserve-fragments", "bypass-conntrack": false, "mark": 10 }
این گزینه معافیت conntrack هر دو مسیر capture و output همین node را غیرفعال
میکند؛ ruleهای سایر مدیران مستقل میمانند. پیش از حذف rule با comment
WWRAW_NOTRACK_...، پذیرش خروجی بسته و نوشتنهای native پذیرفتهشده تمام میشوند.
Stop خروجی صفشده را آزاد میکند و callback قدیمی نمیتواند در device دوباره
راهاندازیشده بنویسد. rollback و destroy حذف ruleهای معلق یا
دارای نتیجهٔ نامشخص را دوباره امتحان میکنند. مرگ ناگهانی process یا شکست
مداوم cleanup ممکن است rule را در raw OUTPUT باقی بگذارد؛ در این حالت rule
با comment ثبتشده در log را دستی حذف کنید.
رفتار checksum
RawSocket درخواست checksum را که nodeهای قبلی روی buffer همان packet گذاشتهاند رعایت میکند.
اگر sbufGetRecalculateChecksum(buf) مقدار true بدهد:
- تلاش میشود checksum کامل packet دوباره محاسبه شود
- flag پس از محاسبه دوباره پاک میشود
- جزئیات checksum لایه transport در packetهای fragmentشده IPv4 بهوسیله helper مشترک checksum مدیریت میشود
- درخواست روی همان buffer در این مرز مصرف میشود؛ packetهای دیگر worker درخواست مستقل دارند
- اگر محاسبه شکست بخورد یا ساختار نامعتبر باشد، packet با ثبت warning دارای rate limit کنار گذاشته میشود
این رفتار پس از nodeهایی مانند IpManipulator یا IpOverrider مفید است؛ nodeهایی که فیلدهای IPv4، TCP، UDP یا ICMP را تغییر میدهند و پیش از خروجی نهایی، ترمیم checksum را درخواست میکنند.
درخواست پاکشده تضمین معتبر بودن checksum نیست. packet بدون درخواست پس از بررسی ساختار با همان checksum قبلی نوشته میشود، حتی اگر checksum عمداً نامعتبر باشد.
محدودیتهای Capture filter
settings parser این مقدارها را میشناسد:
source-ip
dest-ip
وقتی capture فعال است، رفتار فعلی سختگیرانهتر است:
source-ipپذیرفته میشود (packetهای مطابق از network stack میزبان drop میشوند و تنها WaterWall آنها را دریافت و پردازش میکند)dest-ipباعث شکست ساخت tunnel میشود و پیام خطا، استفاده ازTunDeviceرا برای capture ترافیک خروجی پیشنهاد میکند
در Linux، وقتی bypass-conntrack فعال باشد (پیشفرض)، برای هر source range
مربوط به capture یک rule از نوع
CT --notrack در raw PREROUTING نیز نصب میشود. شرط اضافهٔ
--dst-type LOCAL این معافیت را به مقصدهای unicast محلی محدود میکند؛ ترافیک
عبوری معمول، broadcast و multicast از conntrack معاف نمیشوند. فیلتر capture در
INPUT، source range و استثناهای protocol را بدون این محدودیت نوع مقصد بررسی میکند.
تطبیق در جدول raw پیش از DNAT انجام میشود؛
ترافیک معافشده نباید به NAT یا ruleهای stateful وابسته به conntrack همین میزبان
متکی باشد. مقدار bypass-conntrack=false معافیت هر دو مسیر را غیرفعال میکند؛
NFQUEUE capture و رفتار drop-and-dispatch آن همچنان فعال میمانند.
اولویت معمول raw حفظ میشود؛ reassembly مربوط به fragmentها و محدودیت capture
برابر با ۱٬۵۰۰ بایت تغییر نمیکنند. این ruleها MTU را تغییر نمیدهند و جایگزین
PMTU discovery نیستند؛ پیامهای ICMP لازم باید همچنان به stack مسئول برسند.
گزینهٔ skip-sysctl نصب NOTRACK را غیرفعال نمیکند.
ابتدا همهٔ ruleهای INPUT نصب میشوند، سپس در صورت فعال بودن این گزینه ruleهای
NOTRACK اضافه میشوند. coordinator هنگام اجرای این فرمانهای تحت نظارت، queueهای
غیرفعال را با NF_ACCEPT سرویس میکند. capture پس از نصب همهٔ ruleهای
انتخابشده فعال میشود. اگر نصب ruleهای چندصفی شکست بخورد، ruleهای جزئی حذف
میشوند و نصب با یک queue تکرار میشود. شکست نصب تکصفی، نصب NOTRACK یا
rollback ناقص باعث شکست startup میشود.
cleanup ابتدا حذف NOTRACK و سپس حذف NFQUEUE را امتحان میکند؛ شکست در حذف
NOTRACK مانع تلاش برای حذف NFQUEUE نمیشود. وضعیت هر rule، از جمله نتیجهٔ
نامعلوم پس از timeout، جداگانه در جدول مربوط به خودش پیگیری میشود. شکست reader
باعث درخواست shutdown منظم میشود و مالک lifecycle پاکسازی ruleها را انجام
میدهد. حذف NOTRACK رهگیری packetهای بعدی را برمیگرداند، نه packetهایی که قبلاً
untracked شدهاند. --queue-bypass فقط روی NFQUEUE اثر دارد؛ مرگ ناگهانی process
یا شکست نهایی cleanup ممکن است ruleهای WWCAP_NOTRACK_... را باقی بگذارد که
برای بازگشت conntrack باید توسط مدیر سیستم از raw PREROUTING حذف شوند.
در Linux، ruleهای NFQUEUE از --queue-bypass استفاده میکنند. اگر WaterWall
روی queue گوش نکند، packetهای matching بهجای drop شدن بهعلت نبود queue، از
firewall میزبان عبور میکنند. این یک سیاست availability از نوع fail-open است:
تا وقتی listener وجود ندارد، packetها capture یا transform نمیشوند. این گزینه
در زمانی که WaterWall هنوز به queue متصل است، overflow شدن queue را fail-open
نمیکند. WaterWall از شماره queueهایی که از قبل در ruleهای INPUT استفاده
شدهاند دوری میکند. شکست startup، queueها را میبندد؛ rollback کامل اجازهٔ
restart بعدی را میدهد. cleanup ناموفق ruleها یا خطای I/O در queue غیرفعال،
capture-device object را غیرقابل restart میکند. بستهشدن queueها باعث فعالشدن
--queue-bypass برای ruleهای NFQUEUE باقیمانده میشود.
پیش از فعالشدن capture و پس از جداشدن watcherهای workerها، coordinator در زمان
نصب rule، rollback و cleanup هنگام bring-down، خواندنهای محدود و nonblocking
با verdict برابر NF_ACCEPT انجام میدهد. raw output device پیش از فعالشدن
capture آماده است. فعالسازی دریافت پس از packet Init در صف هر worker قرار
میگیرد. هنگام فرمانهای بعدی startup، capture از NF_DROP استفاده میکند و
packetها را با همان بودجهٔ مشترک برای تحویل روی worker در صف میگذارد؛ پرشدن FIFO
نشست، خواندن native را متوقف میکند. تا زمان اجرای event loop ممکن است packetها
در queueهای محدود kernel و FIFO نشست باقی بمانند. worker فعال از drop-and-dispatch
استفاده میکند. هنگام shutdown،
capture غیرفعال و پذیرش دریافت بسته میشود؛ هر watcher روی worker مالک جدا
میشود و سپس ruleها پاک و descriptorها آزاد میشوند. خطای نهایی دریافت، پذیرش
را میبندد و cleanup منظم را درخواست میکند؛ بستهشدن socket صف باعث میشود
ruleهای باقیمانده fail-open شوند. در Linux هیچ thread اختصاصی برای capture
reader یا cleanup ساخته نمیشود.
بهصورت پیشفرض backend لینوکس پیش از ساخت resourceهای NFQUEUE یک batch از
تنظیمات best-effort مربوط به backlog و سایر پارامترهای TCP را با sysctl اعمال میکند.
نوشتن net.core.rmem_max، net.core.wmem_max، net.ipv4.tcp_rmem و
net.ipv4.tcp_wmem به گزینهی misc.tcp-tune در هسته تعلق دارد.
گزینه "skip-sysctl": true فقط
همین batch را حذف میکند؛ operationهای netlink و commandهای iptables لازم برای
ساخت و هدایت NFQUEUE همچنان اجرا میشوند.
اگر به رفتار virtual interface یا پردازش routed برای packetهای خروجی نیاز دارید، از TunDevice استفاده کنید. RawSocket برای زمانی است که مشخصاً capture از host stack همراه با تزریق خام IPv4 میخواهید.
رفتار direction و lifecycle
| callback | رفتار |
|---|---|
upstream Payload | payload را از طریق raw output device تزریق میکند. |
downstream Payload | payload را از طریق raw output device تزریق میکند. |
| captured packet callback | packet IPv4 captureشده را روی worker packet line به node مجاور انتخابشده میفرستد. |
Init, Est, Pause, Resume | terminal absorberهای بدون per-line resource هستند. |
Finish در هر جهت | نقض fatal قرارداد packet line است و process را abort میکند؛ no-op نیست. |
RawSocket یک adapter است و در runtime معمول packet lineها را از بین نمیبرد. worker packet lineها متعلق به chain هستند و line state این node صفر بایت است.
این node protocol header اضافه نمیکند و به left padding نیاز ندارد.
نکتههای عملی
- برنامه را با دسترسیهای لازم برای backend مربوط به raw/capture در همان platform اجرا کنید.
- برای حالت capture از هرکدام از
capture-ipsیاcapture-ipبا یک string یا آرایه string استفاده کنید. - حذف rangeها یا آرایه خالی عمداً حالت write-only را انتخاب میکند.
- مسیر دریافت فعلی فقط packetهای IPv4 را عبور میدهد.
- رفتار هر platform به raw socket و backendهای موجود برای capture و firewall mark وابسته است.
متادیتای node
متادیتای برگرفته از کد منبع:
| ویژگی | مقدار |
|---|---|
| node flags | kNodeFlagChainHead | kNodeFlagChainEnd |
can_have_prev | true |
can_have_next | true |
layer_group | kNodeLayer3 |
layer_group_prev_node | kNodeLayer3 |
layer_group_next_node | kNodeLayer3 |
required_padding_left | 0 بایت |
fragment-policy (اختیاری، پیشفرض: reassemble)
حذف این تنظیم مقدار reassemble را انتخاب میکند: fragmentهای IPv4 پیش از
تحویل به chain بازسازی میشوند. preserve-fragments بایتها و مرز fragmentها را
حفظ میکند. برای این policy فهرست مجاز نام node یا topology وجود ندارد؛ validation
عادی لایهها و chain همچنان برقرار است. مقدار نامعتبر، از جمله null، startup را رد میکند.
ورودی packet در جهت upstream برای PacketsToConnection و در جهت downstream
برای ConnectionToPackets فقط datagram کامل IPv4 میپذیرد. رسیدن fragment به هر یک
نقض قرارداد runtime است و با پیام fatal دربارهٔ نیاز به reassembly، کل برنامه در
Debug و Release خاتمه مییابد. پیش از این consumerها reassembly لازم است، مستقل
از policy دستگاه یا nodeهای میانی. بیت DF بهتنهایی fragment نیست؛ MF یا offset
غیرصفر نشانهٔ fragmentation است.
بازسازی تا ۶۵٬۵۳۵ بایت را تحویل میدهد، ولی سقف خروجی packet یا MTU را بالا نمیبرد و fragmentation دوبارهٔ خروجی را خودکار انجام نمیدهد. محدودیتهای نگهداری و مالکیت packet در بخش چهارم راهنمای توسعهدهنده آمده است.