دو ماشین مجازی روی یک میزبان از طریق یک پل به LAN می‌رسند، و قاعده‌ای که در زنجیره ip forward قرار گرفته، شمارنده‌ای را نشان می‌دهد که هرگز حرکت نمی‌کند. قاب‌ها واقعی‌اند، ترافیک در حال عبور است، و قاعده به‌سادگی روی مسیر آن نیست. مقایسه دو روشی که Linux ترافیک پل‌شده را آشکار می‌کند این سکوت را توضیح می‌دهد، و همچنین توضیح می‌دهد چرا بارگذاری یک ماژول قدیمی پاسخ بدتری از آن چیزی است که به نظر می‌رسد.

دو مسیر از میان یک میزبان

بسته می‌تواند به دو روش بسیار متفاوت از این میزبان خارج شود، و این تفاوت تعیین می‌کند کدام هوک‌ها آن را می‌بینند. اگر مقصد آن‌سوی یک درگاه فیزیکی باشد و قاب میان دو درگاه پل سوئیچ شود، میزبان یک قاب Ethernet را هدایت می‌کند؛ کد مسیریابی مورد پرسش قرار نمی‌گیرد و هیچ جهش مسیریابی‌شده‌ای رخ نمی‌دهد. اگر مقصد خود میزبان باشد، یا شبکه‌ای که از طریق یک رابط تونل به‌دست می‌آید، بسته مسیریابی می‌شود و هوک‌های لایه IP واقعاً اعمال می‌شوند. این دو شاخه را در ذهن نگه دارید، زیرا هر پرسش بعدی درباره زنجیره‌ها و خانواده‌ها از این ناشی می‌شود که قاب کدام یک از دو مسیر را انتخاب می‌کند.

این تمایز تمام داستان پشت قاعده‌ای است که هیچ چیز را نمی‌شمارد. یک قاب پل‌شده از veth9a1 به enp3s0 از پل می‌گذرد، نه از پشته IP، بنابراین زنجیره ip forward چیزی برای تطبیق نمی‌بیند. نام‌های رابط در یک مثال قدیمی‌تر تعیین نمی‌کند کدام مسیر را دارید، و به همین دلیل گام اول این است که به توپولوژی نگاه کنید نه به مجموعه قواعد.

تونل یک مورد سوم را اضافه می‌کند که شایسته نام‌گذاری است. یک دستگاه TUN بسته‌های IP را به میزبان تحویل می‌دهد، پس آن‌ها مسیریابی می‌شوند و واقعاً از زنجیره‌های IP عبور می‌کنند، در حالی که یک دستگاه TAP متصل به پل قاب‌ها را تحویل می‌دهد و مانند حالت پل رفتار می‌کند. نوع دستگاه، نه نام محصول، تعیین‌کننده است.

دو خانواده‌ای را که می‌توانند یک قاب پل‌شده را ببینند مقایسه کنید

Linux فیلتر کردن بومی پل را از طریق خانواده bridge در nftables پشتیبانی می‌کند، و برای بازرسی و دور انداختن قاب‌ها در حال حرکت به هیچ ماژول قدیمی نیاز ندارد. خود مستندات پل کرنل این توصیه را مستقیماً ارائه می‌دهد، که دانستن آن پیش از آنکه نتیجه یک جست‌وجو به شما بگوید ماژولی بارگذاری کنید ارزش دارد.

Observation pointWhat it seesWhat it costs
nftables bridge family chainsEthernet frames forwarded between bridge ports, in both directionsRules must be written in bridge syntax with link-layer matches
Legacy br_netfilter plus an ip chainBridged IPv4 and IPv6 packets, re-exposed through IP-layer hooksExisting host rules may begin matching traffic they never saw before
inet chain for routed trafficPackets the host routes, including traffic arriving from a TUN deviceNo visibility into purely switched frames

استفاده از فرمان nft به‌جای iptables به‌خودی‌خود قاعده‌ای را در خانواده درست قرار نمی‌دهد. زنجیره‌ای که در خانواده bridge اعلام شده قاب‌ها را می‌بیند؛ زنجیره‌ای که در ip، ip6 یا inet اعلام شده آن‌ها را نمی‌بیند، مهم نیست کدام فایل دودویی آن را ساخته باشد. خانواده ویژگی زنجیره است، نه ابزاری که آن را تایپ کرده است.

مسیر قدیمی متفاوت کار می‌کند و همین دلیل آن است که باید مقایسه کرد نه اینکه از روی عادت طرفی را انتخاب کرد. بارگذاری br_netfilter و فعال کردن تنظیمات bridge-nf-call ترافیک IPv4 و IPv6 پل‌شده را دوباره از طریق هوک‌های لایه IP آشکار می‌کند، تا یک قاعده معمولی iptables سرانجام بتواند آن را تطبیق دهد. قاب‌ها تغییر نمی‌کنند؛ مجموعه هوک‌هایی که از آن عبور می‌کنند تغییر می‌کند، که یعنی هر قاعده موجود میزبان اکنون مخاطبان تازه‌ای دارد.

پیش از افزودن قاعده، مسیر مؤثر را بخوانید

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

bridge link show
lsmod | grep br_netfilter
nft list ruleset | grep -A4 "chain forward"

خروجی موارد را به‌روشنی جدا می‌کند. درگاه‌هایی که زیر پل فهرست شده‌اند به شما می‌گویند کدام رابط‌ها سوئیچ می‌شوند؛ نتیجه خالی lsmod یعنی هیچ‌گونه دیدی در لایه IP از قاب‌های پل‌شده وجود ندارد؛ یک زنجیره forward موجود در خانواده bridge یعنی صاحب سیاست از قبل وجود دارد و قاعده شما به آنجا تعلق دارد نه به زنجیره تازه خودش.

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

nft add rule bridge filter forward iifname "veth9a1" oifname "enp3s0" counter accept

کجا ماژول قدیمی رفتار را تغییر می‌دهد

فعال کردن مسیر قدیمی روی یک پل در حال کار، کرداری خنثی در افزودن دید نیست. قواعدی که پیش‌تر برای ترافیک پل‌شده دست‌نیافتنی بودند دست‌یافتنی می‌شوند، از جمله قواعد میزبان که برای رابط دیگری نوشته شده‌اند، و یک سیاست drop پیش‌فرض در زنجیره ip forward می‌تواند ناگهان بر قاب‌هایی اعمال شود که قبلاً دست‌نخورده عبور می‌کردند. روی میزبانی که از طریق همان پل مدیریت می‌شود، نتیجه قفل‌شدن بدون کنسول است. این یک حالت خرابی فرضی نیست؛ پیامد معمولی گسترش مجموعه هوک‌ها روی ماشینی است که از مسیری به آن می‌رسید که همین حالا گسترش داده‌اید.

توالی ایمن‌تر این است که تصمیم بگیرید کدام خانواده مالک سیاست است، قواعد را همان‌جا بنویسید، و پیش از تغییر هر sysctl یک مسیر بازیابی مستقل نگه دارید. اگر دو خانواده هر دو قاعده دارند، مستند کنید کدام مرجع است؛ دو سیاست هم‌پوشان همان روشی است که یک بسته در یک جا پذیرفته و در جای دیگر دور انداخته می‌شود بدون یک فایل واحد برای خواندن.

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

در پایان، حالت ورودی میزبان را جدا نگه دارید. ترافیکی که به IP خود پل نشانه رفته به input می‌رسد نه forward، و قاعده در forward هرگز آن را نمی‌بیند، هر خانواده‌ای که انتخاب کنید. سه مورد، دو خانواده، یک درس: مسیر هوک را تعیین می‌کند، و هوک تعیین می‌کند که آیا قاعده شما اصلاً وجود دارد یا نه.

مقالات مرتبط