لود بالانسینگ و فیلترهای پیشرفته در MikroTik

سناریو را از همان دفتر کاری شروع می‌کنیم که همهٔ ما می‌شناسیم: یک لینک اینترنت که بدترین ساعت ممکن قطع می‌شود، یک ISP دوم که ارزان‌تر است ولی در ساعات شلوغی نفس کم می‌آورد، و ده‌ها کاربر که ترافیکشان از یک روتر MikroTik می‌گذرد. در این نوشته قدم‌به‌قدم پیکربندی می‌کنیم تا ترافیک بین دو ISP تقسیم شود، با قطع یکی از لینک‌ها شبکه نفس نکشد و فایروال روتر هم در حد یک روتر لبهٔ واقعی سخت‌گیرانه باشد. همهٔ کدها برای RouterOS 7 نوشته شده‌اند و تفاوت‌های نسخهٔ ۶ را هم جداگانه می‌بینید.

سناریو و نقشهٔ شبکه

سه رابط در بازی است؛ اسم‌ها و آدرس‌ها را از همین‌جا ثابت می‌کنیم تا کدهای مراحل بعد دقیقاً همین سناریو را پوشش دهند:

i
ترفند گام صفر: قبل از هر تغییری بکاپ متنی بگیرید. برخلاف فایل باینری .backup، خروجی export را می‌توانید در گیت نگه دارید، diff بگیرید و خط‌به‌خط بازبینی کنید — این همان چیزی است که یک بازگشت اضطراری را به یک کار دو دقیقه‌ای تبدیل می‌کند:
/system backup save name=before-lb
/export file=before-lb

گام ۱ — بالا آوردن دو WAN

اول آدرس‌ها و بعد NAT. نکتهٔ مهم این است که برای هر WAN یک قاعدهٔ masquerade جداگانه می‌نویسیم؛ یک قاعدهٔ سراسری وقتی ترافیک بین دو مسیر تقسیم می‌شود، بسته‌هایی را که از WAN دیگر خارج می‌شوند بدون ترجمهٔ درست رها می‌کند و سایت‌های مقصد بسته‌های «بی‌منبع» می‌بینند:

/ip address
add address=192.0.2.2/30 interface=ether1 comment="WAN1 - main ISP"
add address=198.51.100.2/30 interface=ether2 comment="WAN2 - backup ISP"

/ip firewall nat
add chain=srcnat out-interface=ether1 action=masquerade comment="NAT out WAN1"
add chain=srcnat out-interface=ether2 action=masquerade comment="NAT out WAN2"

/ip dns
set servers=1.1.1.1,8.8.8.8 allow-remote-requests=yes
i
اگر WAN شما با DHCP آدرس می‌گیرد: حتماً add-default-route=no و use-peer-dns=no بگذارید. مسیر پیش‌فرضی که DHCP کلاینت تزریق می‌کند فقط به جدول main می‌رود و هرگز نمی‌تواند ترافیک mark شده را ببرد؛ همان یک قاعدهٔ خودکار کافی است تا کل لود بالانسینگ شما بی‌صدا بی‌اثر شود.

گام ۲ — مسیرهای مستقل و failover خودکار

در RouterOS 7 برای هر ISP یک جدول مسیریابی جدا می‌سازیم؛ این جدول‌ها فقط توسط routing-mark استفاده می‌شوند. بعد در جدول main هم دو مسیر پیش‌فرض با distance متفاوت می‌گذاریم تا اگر ISP اول مرد، کل شبکه خودکار روی ISP دوم بیفتد — بدون هیچ اسکریپتی:

/routing table
add name=via-isp1 fib
add name=via-isp2 fib

/ip route
# dedicated tables used by routing marks
add dst-address=0.0.0.0/0 gateway=192.0.2.1 routing-table=via-isp1 check-gateway=ping
add dst-address=0.0.0.0/0 gateway=198.51.100.1 routing-table=via-isp2 check-gateway=ping
# main table: ISP1 primary, ISP2 automatic failover
add dst-address=0.0.0.0/0 gateway=192.0.2.1 distance=1 check-gateway=ping
add dst-address=0.0.0.0/0 gateway=198.51.100.1 distance=2 check-gateway=ping

check-gateway=ping هر ده ثانیه گیت‌وی را ping می‌کند و بعد از دو شکست، آن مسیر را از جدول حذف می‌کند؛ یعنی قطعی را حداکثر در حدود بیست ثانیه می‌فهمید. یک قاعدهٔ امنیتی هم اینجا کار می‌کند که خیلی‌ها نمی‌دانند: اگر ترافیکی با routing-mark به جدولی فرستاده شود که مسیر فعالی ندارد، RouterOS به‌طور خودکار به جدول main برمی‌گردد. پس وقتی ISP1 از دسترس خارج شود، ترافیک mark شدهٔ «به-ISP1» هم بدون هیچ تغییری از ISP2 می‌گذرد.

i
ترفند حرفه‌ای‌تر: گیت‌وی زنده بودنِ مودم را نشان می‌دهد، نه زنده بودن اینترنت. با مسیر بازگشتی (recursive) به یک IP عمومی مثل 1.1.1.1 probe بزنید تا «مودم روشن ولی اینترنت قطع» هم تشخیص داده شود؛ در RouterOS 7 یعنی gateway مسیر اصلی را همان IP عمومی بگذارید و گیت‌وی واقعی را با scope=10 target-scope=10 به‌عنوان مسیر host تعریف کنید.

گام ۳ — منطق Mangle در سه لایه

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

  1. هر اتصالی که از بیرون وارد یکی از WANها شده، پاسخش هم از همان WAN بیرون برود. وگرنه بانک‌ها، ریموت‌دسکتاپ و هر سرویس session محوری از هم می‌پاشد.
  2. فقط اتصال‌های جدید شبکهٔ داخلی با PCC تقسیم شوند — قید connection-mark=no-mark در اینجا یک قید تزئینی نیست؛ تضمین می‌کند اتصال‌های از قبل mark شده دوباره طبقه‌بندی نشوند.
  3. routing-mark از connection-mark مشتق شود، نه مستقیم از PCC. به این ترتیب تصمیم فقط یک بار برای هر اتصال گرفته می‌شود و تمام بسته‌های آن اتصال از یک مسیر می‌روند.
/ip firewall mangle
# 1) inbound WAN connections keep their own path
add chain=prerouting in-interface=ether1 connection-mark=no-mark action=mark-connection new-connection-mark=to-isp1 passthrough=yes
add chain=prerouting in-interface=ether2 connection-mark=no-mark action=mark-connection new-connection-mark=to-isp2 passthrough=yes
# 2) classify every NEW connection from the LAN with PCC (50/50)
add chain=prerouting in-interface=bridge-lan connection-mark=no-mark per-connection-classifier=both-addresses:2/0 action=mark-connection new-connection-mark=to-isp1 passthrough=yes
add chain=prerouting in-interface=bridge-lan connection-mark=no-mark per-connection-classifier=both-addresses:2/1 action=mark-connection new-connection-mark=to-isp2 passthrough=yes
# 3) translate the connection mark into a routing mark
add chain=prerouting connection-mark=to-isp1 action=mark-routing new-routing-mark=via-isp1 passthrough=no
add chain=prerouting connection-mark=to-isp2 action=mark-routing new-routing-mark=via-isp2 passthrough=no

قواعد فایروال در RouterOS از بالا به پایین خوانده می‌شوند و اولین تطبیق برنده است؛ پس ترتیب همین بلوک را دقیقاً حفظ کنید. passthrough=no در لایهٔ سوم یعنی «کار تمام شد، بسته را رها کن» و از خوردن دو بار کانتر جلوگیری می‌کند.

گام ۴ — PCC در عمق: کلاس‌بندی و نسبت دلخواه

per-connection-classifier سه پارامتر دارد: هش چه چیزی باشد، چند bucket بسازد و بسته به کدام bucket بخورد. حالت‌های پرکاربرد:

عدد بعد از دونقطه تعداد bucketها است و عدد بعد از ممیز، شمارهٔ bucket. چون هش به‌طور یکنواخت پخش می‌شود، می‌توانید نسبت غیرمساوی هم بسازید — ترفندی که تقریباً هیچ‌جا مستند نمی‌شود: به‌جای دو bucket ده bucket بسازید و هفت‌تای آن‌ها را به ISP پرسرعت بدهید:

# ~70/30 split: buckets 0..6 go to ISP1, buckets 7..9 to ISP2
add chain=prerouting in-interface=bridge-lan connection-mark=no-mark per-connection-classifier=both-addresses:10/0 action=mark-connection new-connection-mark=to-isp1 passthrough=yes
add chain=prerouting in-interface=bridge-lan connection-mark=no-mark per-connection-classifier=both-addresses:10/1 action=mark-connection new-connection-mark=to-isp1 passthrough=yes
# ... repeat the same rule for buckets 10/2 up to 10/6 (to-isp1)
add chain=prerouting in-interface=bridge-lan connection-mark=no-mark per-connection-classifier=both-addresses:10/7 action=mark-connection new-connection-mark=to-isp2 passthrough=yes
# ... and for buckets 10/8 and 10/9 (to-isp2)
i
استثناهای اجباری را بالای PCC بگذارید: سایت‌های بانکی، درگاه پرداخت و سرورهایی که IP خروجی برایشان مهم است را با یک address-list جدا کنید و قبل از قواعد PCC به یک ISP مشخص ببندید. قاعدهٔ کلی mangle: هرچه خاص‌تر، بالاتر.

گام ۵ — Nth: تقسیم بسته‌محور برای UDP

PCC در سطح اتصال تصمیم می‌گیرد؛ Nth در سطح بسته. nth=2,1 یعنی «از هر دو بسته، دومی» را تطبیق بده. برای TCP هیچ‌وقت مناسب نیست (بسته‌های یک اتصال از دو مسیر مختلف = فاجعه)، ولی برای ترافیکی که state ندارد — مثل فوروارد دوبارهٔ کوئری‌های DNS به دو رِزالور، یا رله‌های UDP — راه‌حل تمیزی است:

/ip firewall mangle
add chain=prerouting protocol=udp dst-port=53 dst-address=10.10.10.1 in-interface=bridge-lan nth=2,1 action=mark-connection new-connection-mark=to-isp2 passthrough=yes
add chain=prerouting protocol=udp dst-port=53 dst-address=10.10.10.1 in-interface=bridge-lan connection-mark=no-mark action=mark-connection new-connection-mark=to-isp1 passthrough=yes

قاعدهٔ دوم عمداً بعد از Nth می‌آید: هر کوئری‌ای که bucket دوم را نخورد، به ISP1 می‌رود. اگر این الگو را حفظ کنید، هرگز نیازی به شمارش معکوس کانترهای Nth ندارید.

گام ۶ — فایروال: از «باز بودن همه» تا روتر لبهٔ واقعی

با وجود لود بالانسینگ، سطح حملهٔ شما دو برابر شده است: دو مسیر ورودی به شبکه. زنجیرهٔ input را سخت ببندید و در forward فقط ترافیک state دار و داخلی را بپذیرید:

/ip firewall filter
# --- input: protect the router itself
add chain=input connection-state=established,related action=accept
add chain=input connection-state=invalid action=drop
add chain=input in-interface=bridge-lan action=accept
add chain=input in-interface=ether1 protocol=icmp action=accept
add chain=input in-interface=ether2 protocol=icmp action=accept
add chain=input action=drop comment="drop everything else from WAN"
# --- forward: police what crosses the router
add chain=forward connection-state=established,related,untracked action=accept
add chain=forward connection-state=invalid action=drop
add chain=forward in-interface=bridge-lan action=accept
add chain=forward in-interface=ether1,ether2 connection-nat-state=!dstnat action=drop

قاعدهٔ آخر ستون فقرات امنیت است: از بیرون فقط ترافیکی اجازهٔ عبور دارد که شما عمداً برای آن dstnat (port forwarding) نوشته‌اید؛ هر چیز دیگری drop می‌شود. اگر لازم است از بیرون Winbox بزنید، یک address-list از IPهای دفتر بسازید و یک قاعدهٔ accept محدود به همان لیست، قبل از قاعدهٔ drop نهایی بگذارید — و هرگز پورت Winbox را به کل اینترنت باز نگذارید.

گام ۷ — تست، اندازه‌گیری و تیونینگ

لود بالانسینگ بدون اندازه‌گیری فقط یک حدس است. این چهار دستور عادت روزمرهٔ شما باید شوند:

/ip route print where routing-table=via-isp1
/ip firewall connection print count-only where connection-mark=to-isp1
/ip firewall connection print count-only where connection-mark=to-isp2
/tool torch ether1 port=any

اشکال‌یابی و نکته‌های نسخه‌ها

  • برخی سایت‌ها یا اپ بانکی باز نمی‌شود — اتصال‌های قبلی‌اش روی bucket دیگری گیر کرده یا IP خروجی را عوض کرده. همان address-list استثنا را بسازید و به یک ISP ببندید؛ بعد از اعمال، اتصال‌های قدیمی را با /ip firewall connection remove [find] پاک کنید.
  • بعد از قطعی ISP، همه‌چیز چند دقیقه قفل است — جدول connection tracking پر از اتصال‌های مرده است. یا صبر کنید تا timeout بخورند یا همان دستور remove را بزنید.
  • port forwarding جواب می‌دهد ولی پاسخ به مشتری نمی‌رسد — لایهٔ اول Mangle را ننوشته‌اید؛ پاسخ دارد از WAN دیگر برمی‌گردد و سرویس گیرنده آن را نمی‌شناسد.
  • سرعت دانلودِ تکی جمع نشد — طبیعی است؛ تقسیم در سطح اتصال است نه بایت. برای جمع‌شدن سرعت یک فایل باید downloader چنداتصاله استفاده کنید.
  • در نسخهٔ ۶ جدول مسیریابی جداگانه وجود ندارد؛ به‌جای routing-table=via-isp1 روی خود مسیر routing-mark=via-isp1 می‌نویسید.
  • check-gateway=ping در هر دو نسخه یکسان است و منطق failover فرقی نمی‌کند.
  • در نسخهٔ ۶ قواعد mangle همین‌اند؛ فقط نام جداول را به routing-mark مسیرها گره بزنید.
  • قبل از ارتقا از ۶ به ۷ همیشه export بگیرید — چند دستور network-related تغییر نام داده‌اند و اسکریپت‌های خام ممکن است بشکنند.
  • از دو کلاینت مختلف سایت تشخیص IP را باز کنید؛ باید هر دو IP عمومی را ببینید.
  • کابل هر WAN را جدا بکشید و برگردانید؛ قطعی در ~۲۰ ثانیه باید بدون قطع شدن اتصال‌های جدید جبران شود.
  • یک port forwarding واقعی را از بیرون (موبایل با دیتا) تست کنید، نه از داخل شبکه.
  • کانتر قواعد PCC را ببینید؛ نسبت تقریباً مطابق bucketهاست؟
  • /export نهایی را در گیت کامیت کنید؛ پیکربندیِ بی‌نسخهٔ کنترل، پیکربندیِ گمشدهٔ فرداست.

سؤالات متداول

برای TCP و UDP بله، چون مفهوم اتصال دارند. ICMP و پروتکل‌های بی‌state با PCC طبقه‌بندی نمی‌شوند و از جدول main (یعنی ISP اصلی) می‌روند — که معمولاً همان چیزی است که می‌خواهید، چون ping های تستی شما از مسیر ثابت می‌گذرند.
بله؛ به‌جای دو bucket ده bucket بسازید (گام ۴) و تعداد bucketهای هر ISP را متناسب با ظرفیتش انتخاب کنید. ۵/۵، ۷/۳ و ۸/۲ همه با همین الگو ساخته می‌شوند؛ برای نسبت‌های دقیق‌تر denominator را بزرگ‌تر بگیرید.
ECMP ساده‌تر است (چند gateway در یک مسیر) ولی تصمیمش را با هش داخلی و بسته‌به‌بسته می‌گیرد و کنترل استثناها را از شما می‌گیرد. برای شبکهٔ اداری که بانک و ریموت‌دسکتاپ دارد، کنترل در سطح اتصال با PCC انتخاب مطمئن‌تری است.
dstnat را روی هر WAN جدا تعریف کنید و لایهٔ اول Mangle (گام ۳) را حتماً بنویسید. آن قاعده تضمین می‌کند پاسخِ هر اتصالِ ورودی از همان دروازه‌ای برگردد که آمده — بدون آن، دوربین‌ها و سرویس‌های ریموت عملاً غیرقابل استفاده می‌شوند.
یک address-list بسازید و یک قاعدهٔ mark-connection بر اساس dst-address-list بالاتر از قواعد PCC اضافه کنید. همان الگوی استثنای سایت‌های بانکی است؛ ترتیب قواعد، همه‌چیز است.

جمع‌بندی

چارچوبی که ساختیم سه لایه دارد: مسیرهای مستقل با check-gateway برای failover بی‌سکریپت، mangle سه‌لایه برای تقسیم در سطح اتصال، و فایروال زنجیره‌ای برای سطح حملهٔ دوتایی. اگر همین ترتیب را حفظ کنید، افزودن ISP سوم یعنی فقط تکرار همین الگو با یک bucket دیگر. اگر با OpenWrt کار می‌کنید، منطق mwan3 دقیقاً همین است؛ تجربهٔ من با OpenWrt را در فریمور سفارشی Google Wifi (Gale) و درس‌های واقعی میدانی را در درس‌های شبکه از یک سایت صنعتی خوانده‌اید.

← همه‌ی نوشته‌ها RSS