MikroTik. Правильный dst nat при использовании 2-х и более провайдеров
Приступая к выполнению задачи я рассчитывал на легкую прогулку в тени дубового парка, созерцая природу и предаваясь размышлениям… Однако позже стало понятно, что это будет тернистый и сложный поход сквозь горные реки с подводными камнями, обледеневшими скалами и глубокими пещерами.
Через медитации, борьбу со стихиями и собственной тупостью преодоление себя я, все таки, достиг желанной нирваны.
В этой статье я не только предоставлю готовый набор правил, но и постараюсь максимально доступно объяснить почему и как именно они работают.
Конкретно в моем случае, нужно было настроить роутер так, чтобы web-сервер в локальной сети за ним был доступен по IP любого из 3-х провайдеров.
Часть 1.
Для начала пробежимся по всем настройкам, дабы самые нетерпеливые могли скопипастить не читая.
Версия RouterOS:
# oct/11/2016 22:02:32 by RouterOS 6.37.1
# software id = X62B-STGZ
Интерфейсы:
/interface list
add name=WAN
/interface list member
add interface=ISP1 list=WAN
add interface=ISP2 list=WAN
add interface=ISP3 list=WAN
Не помню начиная с какой версии RouterOS появилась эта фишка. Она позволяет группировать интерфейсы, что весьма удобно (например, в правилах /ip firewall). У меня создана группа из 3-х WAN-интерфейсов.
IP-адреса (адреса, по понятным причинам, «левые»):
/ip address
add address=192.168.0.1/24 comment=defconf interface=bridge network=192.168.0.0
add address=95.11.29.240/24 interface=ISP1 network=95.11.29.0
add address=5.35.59.162/27 interface=ISP2 network=5.35.59.160
add address=5.98.112.30/30 interface=ISP3 network=5.98.112.28
Роутинг:
/ip route
add distance=1 gateway=95.11.29.254 routing-mark=ISP1-route
add distance=1 gateway=5.35.59.161 routing-mark=ISP2-route
add distance=1 gateway=5.98.112.29 routing-mark=ISP3-route
add check-gateway=ping distance=1 gateway=8.8.8.8
add check-gateway=ping distance=2 gateway=8.8.4.4
add check-gateway=ping distance=3 gateway=1.1.36.3
add distance=1 dst-address=8.8.4.4/32 gateway=5.35.59.161 scope=10
add distance=1 dst-address=8.8.8.8/32 gateway=95.11.29.254 scope=10
add distance=1 dst-address=1.1.36.3/32 gateway=5.98.112.29 scope=10
Для организации Failover’а я настроил рекурсивную маршрутизацию, подробнее расскажу в 2-ой части.
Firewall (для данной публикации я сознательно привожу правила, разрешающие все.
Не делайте так!):
/ip firewall filter
add action=accept chain=forward
add action=accept chain=input
add action=accept chain=output
dst и src nat:
/ip firewall nat
add action=masquerade chain=srcnat out-interface-list=WAN
add action=dst-nat chain=dstnat comment="HTTP" dst-port=80 \
in-interface-list=WAN protocol=tcp to-addresses=192.168.0.83 to-ports=80
add action=dst-nat chain=dstnat comment="HTTPs" dst-port=443 \
in-interface-list=WAN protocol=tcp to-addresses=192.168.0.83 to-ports=443
mangle
/ip firewall mangle
add action=mark-connection chain=input in-interface=ISP1 \
new-connection-mark=ISP1-conn passthrough=yes
add action=mark-routing chain=output connection-mark=ISP1-conn \
new-routing-mark=ISP1-route passthrough=no
add action=mark-connection chain=input in-interface=ISP2 new-connection-mark=\
ISP2-conn passthrough=yes
add action=mark-routing chain=output connection-mark=ISP2-conn \
new-routing-mark=ISP2-route passthrough=no
add action=mark-connection chain=input in-interface=ISP3 \
new-connection-mark=ISP3-conn passthrough=yes
add action=mark-routing chain=output connection-mark=ISP3-conn \
new-routing-mark=ISP3-route passthrough=no
add action=mark-connection chain=forward in-interface=ISP1 \
new-connection-mark=ISP1-conn-f passthrough=no
add action=mark-routing chain=prerouting connection-mark=ISP1-conn-f \
in-interface=bridge new-routing-mark=ISP1-route
add action=mark-connection chain=forward in-interface=ISP2 \
new-connection-mark=ISP2-conn-f passthrough=no
add action=mark-routing chain=prerouting connection-mark=ISP2-conn-f \
in-interface=bridge new-routing-mark=ISP2-route
add action=mark-connection chain=forward in-interface=ISP3 \
new-connection-mark=ISP3-conn-f passthrough=no
add action=mark-routing chain=prerouting connection-mark=ISP3-conn-f \
in-interface=bridge new-routing-mark=ISP3-route
Часть 2.
Рассмотрим все подробнее.
Роутинг:
/ip route
add distance=1 gateway=95.11.29.254 routing-mark=ISP1-route
add distance=1 gateway=5.35.59.161 routing-mark=ISP2-route
add distance=1 gateway=5.98.112.29 routing-mark=ISP3-route
add check-gateway=ping distance=1 gateway=8.8.8.8
add check-gateway=ping distance=2 gateway=8.8.4.4
add check-gateway=ping distance=3 gateway=1.1.36.3
add distance=1 dst-address=8.8.4.4/32 gateway=5.35.59.161 scope=10
add distance=1 dst-address=8.8.8.8/32 gateway=95.11.29.254 scope=10
add distance=1 dst-address=1.1.36.3/32 gateway=5.98.112.29 scope=10
/ip route
В первых трех строчках мы указываем default gateway каждого из 3-х наших провайдеров.
Маршруты имеют одинаковый вес (distance), но работают в разных таблицах маршрутизации.
Другими словами, эти маршруты работают для пакетов промаркированных соответствующим тегом (routing-mark). Теги пакетам мы будем навешивать в /ip firewall mangle (о чем я подобно расскажу ниже).
Следующие 3 строки — указание маршрутов по умолчанию в основной таблице маршрутизации.
Здесь стоит обратить внимание на:
- Параметр distance. Для каждого маршрута разный и, соответственно, «вес» маршрутов тоже разный.
ISP1 — основной, за ним ISP2 и ISP3 замыкает, т.е. при отказе провайдера ISP1 работать будем через ISP2, если и ISP2 почил в бозе, то за дело возьмется ISP3.
- Несколько непонятным может быть значение параметра gateway. Как это гугловые DNS и какой-то левый IP-адрес вдруг стали маршрутами по-умолчанию? Магия заключается в параметре scope из последних трех строк, а так же в самом механизме Nexthop lookup.
На самом деле трафик отправится активному провайдеру, а дальше тот отправит его в Интернет через свои аплинки.
- О параметре check-gateway=ping. Суть в том, что в самой простой схеме построения Failover, указывая check-gateway=ping в маршрутах по умолчанию, мы проверяем связь только до маршрутизатора провайдера. Если же за маршрутизатором провайдера будет недоступен Интернет, то мы этого не поймем. А с помощью финта с параметром scope мы проверяем связь уже не с провайдером, а смотрим за него, в Интернет.
Под спойлером мой подробный перевод/адаптация вики MikroTik на эту тему.
Во-первых, определимся с некоторыми терминами.
Nexthop — следующий прыжок, если дословно. Следующий шлюз/роутер/маршрутизатор на пути следования пакета из точки А(от моего роутера, например) в точку Б (допустим до гуглового DNS 8.8.8.8), т.е. следующий транзитный участок, на котором будет обрабатываться пакет. В переводе будет использоваться словосочетание “следующий хоп” (простите за англицизм).
Immediate nexthop — следующий шлюз/роутер/маршрутизатор на пути следования пакета из точки А в точку Б, доступный непосредственно. Для моего домашнего MikroTik, с маршрутом по-умолчанию:
dst-address=0.0.0.0/0 gateway=89.189.163.1 gateway-status=89.189.163.1 reachable via ether1-gateway
89.189.163.1 — это и есть immediate nexthop, т.к. доступен он через ether1-gateway. В переводе будет использоваться словосочетание “непосредственно доступный следующий хоп”.
Connected route — связанный маршрут. Маршрут, шлюз которого доступен непосредственно.
Gateway — сетевой шлюз/роутер/маршрутизатор.
Я буду пользоваться всеми тремя вариантами перевода.
scope — Используется в механизме поиска следующего хопа, т.е. решения какой же хоп станет следующим. Нужный маршрут может быть выбран только среди маршрутов, значения scope которых меньше либо равно значению target-scope. Значения по-умолчанию зависят от протокола:
- связанные маршруты: 10 (если интерфейс запущен(running))
- OSPF, RIP, MME маршруты: 20
- статические маршруты: 30
- BGP маршруты: 40
- связанные маршруты: 200 (если интерфейс не запущен)
target-scope — Используется в механизме поиска следующего хопа, т.е. решения какой же хоп станет следующим. Это максимальное значение параметра scope для маршрута, посредством которого может быть найден следующий хоп. Для iBGP значение установленно в 30 по-умолчанию.
Табличка значений обоих параметров.
Поиск следующего хопа.
Поиск следующего хопа является частью процесса выбора маршрута.
Маршрутам, находящимся в FIB, нужен интерфейс, соответствующий каждому из адресов шлюзов. Адрес следующего хопа-шлюза должен быть непосредственно доступен через этот интерфейс. Интерфейс, который следует использовать для посылки исходящего пакета каждому из шлюзов, находится с помощью поиска следующего хопа.
Некоторые маршруты (например, iBGP), в качестве адреса шлюза, могут иметь адрес принадлежащий маршрутизатору, находящемуся через несколько прыжков-шлюзов от нашего MikroTik. Для установки таких маршрутов в FIB необходимо найти адрес шлюза, доступного непосредственно (an immediate nexthop), т.е. напрямую от нас, который и будет использоваться для достижения адреса шлюза в этом маршруте. Непосредственный адрес следующего хопа также может быть найден при помощи механизма поиска следующего хопа.
Поиск следующего хопа выполняется только в основной таблице маршрутизации main, даже для маршрутов, имеющих отличное значение параметра routing-mark. Это необходимо для ограничения установки маршрутов, которые могут быть использованы для поиска непосредственно доступных хопов (immediate nexthops). В маршрутах для протоколов RIP или OSPF предполагается, что следующий маршрутизатор доступен непосредственно и должен быть найдены используя только связанные маршруты(connected routes).
Маршруты с именем интерфейса в качестве шлюза не используются в поиске следующего хопа. Если есть маршрут с именем интерфейса, а также маршрут с активным IP-адресом, то маршрут с интерфейсом игнорируется.
Маршруты, имеющие значение параметра scope больше максимально допустимого не используются в поиске следующего хопа. В каждом маршруте указывается максимально допустимое значение параметра scope, для его следующего хопа, в параметре target-scope. Значение по-умолчанию для этого параметра позволяет выполнить поиск следующего хопа только через связанные маршруты (connected routes), за исключением iBGP-маршрутов, которые имеют большее значение по-умолчанию и могут выполнить поиск следующего хопа также через IGP и статические маршруты.
Интерфейс и адрес следующего непосредственно доступного маршрутизатора выбираются основываясь на результатах поиска следующего хопа:
- Если наиболее точный активный маршрут, который был обнаружен в ходе поиска адреса следующего хопа, является связанным маршрутом, то интерфейс этого связанного маршрута используется как интерфейс следующего хопа и шлюз помечается как reachable. После того как маршрутизатор становится непосредственно доступным через этот интерфейс (именно это и следует понимать как “связанный маршрут” или “connected route”) его адрес используется как адрес непосредственно доступного маршрутизатора (immediate nexthop address).
- Если наиболее точный активный маршрут, который был обнаружен в ходе поиска адреса следующего хопа, имеет адрес шлюза, который уже был обнаружен, непосредственно доступный хоп и интерфейс копируются с этого маршрута, а шлюз помечается как recursive.
- Если наиболее точный активный маршрут, который был обнаружен в ходе поиска адреса следующего хопа является ECMP маршрутом, то используется первый доступный маршрутизатор этого маршрута.
- Если механизм поиска адреса следующего хопа не нашел ни одного маршрута, то шлюз помечается как unreachable.
А теперь, как говорят в КВН, зачем же я показал этот номер. Обратите внимание, мы устанавливаем scope=10 для статических маршрутов в последних трех строках, тем самым «заставляем» MikroTik принимать во внимание эти маршруты при поиске следующего хопа.
Он и принимает, и таким образом маршруты по-умолчанию через:
- 8.8.8.8
- 8.8.4.4
- 1.1.36.3
становятся recursive, т.е. непосредственно доступными хопами будут шлюзы провайдеров и трафик мы пошлем через них, но check-gateway=ping будет проверять доступность адресов ЗА провайдерскими сетями.
Надеюсь мой перевод и объяснение будут вам полезны.
Пока самые охочие до знаний читают под спойлером, расскажу немого короче и проще.
Когда мы указываем scope=10 в последних трех строках, мы даем понять MikroTik’у, что:
- 8.8.8.8
- 8.8.4.4
- 1.1.36.3
ему доступны не напрямую, а рекурсивно через данные статические маршруты. По одному IP на брата-провайдера.
/ip firewall mangle
/ip firewall mangle
add action=mark-connection chain=input in-interface=ISP1 \
new-connection-mark=ISP1-conn passthrough=yes
add action=mark-routing chain=output connection-mark=ISP1-conn \
new-routing-mark=ISP1-route passthrough=no
add action=mark-connection chain=input in-interface=ISP2 new-connection-mark=\
ISP2-conn passthrough=yes
add action=mark-routing chain=output connection-mark=ISP2-conn \
new-routing-mark=ISP2-route passthrough=no
add action=mark-connection chain=input in-interface=ISP3 \
new-connection-mark=ISP3-conn passthrough=yes
add action=mark-routing chain=output connection-mark=ISP3-conn \
new-routing-mark=ISP3-route passthrough=no
add action=mark-connection chain=forward in-interface=ISP1 \
new-connection-mark=ISP1-conn-f passthrough=no
add action=mark-routing chain=prerouting connection-mark=ISP1-conn-f \
in-interface=bridge new-routing-mark=ISP1-route
add action=mark-connection chain=forward in-interface=ISP2 \
new-connection-mark=ISP2-conn-f passthrough=no
add action=mark-routing chain=prerouting connection-mark=ISP2-conn-f \
in-interface=bridge new-routing-mark=ISP2-route
add action=mark-connection chain=forward in-interface=ISP3 \
new-connection-mark=ISP3-conn-f passthrough=no
add action=mark-routing chain=prerouting connection-mark=ISP3-conn-f \
in-interface=bridge new-routing-mark=ISP3-route
Для пояснения правил этого раздела пригласим несколько помощников.
- MANGLE — данная таблица предназначена для операций по классификации и маркировке пакетов и соединений, а также модификации заголовков пакетов (поля TTL и TOS) (викиучебник).
Таблица mangle содержит следующие цепочки:
- PREROUTING — позволяет модифицировать пакет до принятия решения о маршрутизации.
- INPUT — позволяет модифицировать пакет, предназначенный самому хосту.
- FORWARD — цепочка, позволяющая модифицировать транзитные пакеты.
- OUTPUT — позволяет модифицировать пакеты, исходящие от самого хоста.
- POSTROUTING — дает возможность модифицировать все исходящие пакеты, как сгенерированные самим хостом, так и транзитные.
- CONNECTION TRACKING — специальная подсистема, отслеживающая состояния соединений и позволяющая использовать эту информацию при принятии решений о судьбе отдельных пакетов.
- Packet flow MikroTik’а
Я разбил правила на группы, для облегчения их понимания. В первой группе 6 правил, которые отвечают за трафик в/из самого роутера, и во второй группе 6, для транзитного трафика.
Начем с первых двух.
В первом мы сообщаем роутеру, что все входящие chain=input соединения на интерфейс ISP1 нужно маркировать action=mark-connection new-connection-mark=ISP1-conn, а так же указаваем passthrough=yes, чтобы пакет, после прохождения этого правила, не покинул таблицу и продолжил следование по правилам.
Во втором говорим MikroTik’у, чтобы он отловил исходящие соединения chain=output помеченные как ISP1-conn и присвоил им метку роутинга(для помещения в соответствующую таблицу маршрутизации, вы ведь помните первые три маршрута?), а также сообщаем passthrough=no , как бы говоря — нечего делать здесь пакету после этого правила, т.е. пакет покинет таблицу.
Все описанное выше справедливо и для ISP2 и для ISP3. Таким образом мы добились того, что роутер ответит именно с того интерфейса, на который к нему прийдет запрос.
Переходим к завершающим шести правилам.
Уже понятно, они также разбиты на подгруппы по 2, для каждого из наших ISP.
Первое из них поручает роутеру следить за цепочкой FORWARD и если происходит соединение через интерфейс ISP1, то оно маркируется action=mark-connection новым тегом new-connection-mark=ISP1-conn-f (обратите внимание! отличным от тега трафика самого маршрутизатора, в данном случае мы маркируем транзитный трафик). passthrough=no, т.к. мы не хотим, чтобы пакет, после попадания в это правило, обрабатывался в таблице как-то еще.
Второе навешивает нужную метку роутинга new-routing-mark=ISP1-route в цепочке PREROUTING, т.е. ДО принятия решения о маршрутизации, и отслеживает трафик пришедший к нам из локальной сети in-interface=bridge.
Здесь нас выручает механизм CONNECTION TRACKING, позволяющий поймать промаркированные правилом выше соединения из локальной сети(от WEB-сервера) и навесить им необходимый тег роутинга.
Это позволяет транзитному трафику(здесь к/от web-сервера) идти именно тем путем, которым он и пришел, т.е. пришел через ISP1 — уходи через него же.