Страх и ненависть в RouterOS: что такое сетевое соединение в ядре Linux (часть 2 — практика)

image-loader.svg


Ранее в первой (теоретической) части статьи была подробно описана сущность сетевого соединения глазами ядра маршрутизатора. В текущей части мы закрепим информацию в результате рассмотрения работы прикладного протокола DNS через подсистемы RouterOS.

В заключительной части речь пойдёт о диаграмме потока пакетов, при работе с которой важно понимать сущность рассматриваемого сетевого соединения, а также о не документированной в явном виде особенности работы NAT. Материала достаточно много, и чтобы читатель не потерял смысловую нить к концу статьи, она разделена на 3 части: теория, практика и особенность NAT.

Материалы носят в основном теоретический характер и предназначены для людей, тонко настраивающих Firewall, Qos и маршрутизацию, где им придётся непосредственно работать с рассматриваемыми connections. Цикл статей не предназначен для новичков и может их только запутать. Полагаю, что читатель хорошо знаком с предметом разговора.

Без лишних слов приступим к описанию практической задачи. Имеем классическую локальную сеть, в которой клиенты (в том числе беспроводные) подключены к маршрутизатору (со стороны LAN IP адрес 192.168.1.1, со стороны WAN IP адрес 10.0.2.47) и получают посредством работы DHCP протокола в качестве DNS сервера непосредственно IP адрес роутера. Сам роутер использует в качестве сервера DNS IP адрес Cloudflare (/ip dns set allow-remote-requests=yes servers=1.1.1.1). Пользователь загружает свой ноутбук (PC), подключается к Wi-Fi сети, получает от DHCP сервера IP адрес 192.168.1.2 и в браузере открывает сайт, например, mail.ru. Для простоты сосредоточимся на работе не шифрованного DNS протокола, позволяющего увидеть содержание пакетов на прикладном уровне.

Сколько типов DNS соединений будет создано в RouterOS?

Разберёмся детально и разложим всё по полочкам. Если кто-то не уверен в ответе, то после прочтения ясность будет внесена. В действительности имеем следующие DNS запросы и ответы, передающиеся по транспортному протоколу UDP:

  • PC → MikroTik;
  • MikroTik → DNS servers;
  • DNS servers → MikroTik;
  • MikroTik → PC.


Рассчитываю, что читатель понимает, почему это так, и не отвлекаюсь на описание работы непосредственно DNS протокола. Выделим пакеты каждого варианта запросов, для этого воспользуемся маркировкой трафика:

/ip firewall mangle
add action=mark-connection chain=prerouting comment=\
    "DNS catcher PC->MikroTik Connections" dst-address=192.168.1.1 dst-port=\
    53 new-connection-mark="DNS catcher PC->MikroTik Connections" \
    passthrough=yes protocol=udp
add action=mark-packet chain=prerouting comment=\
    "DNS catcher PC->MikroTik Packets" connection-mark=\
    "DNS catcher PC->MikroTik Connections" new-packet-mark=\
    "DNS catcher PC->MikroTik packets" passthrough=no
add action=mark-connection chain=prerouting comment=\
    "DNS catcher DNS Servers->MikroTik Connections" new-connection-mark=\
    "DNS catcher DNS Servers->MikroTik Connections" passthrough=yes protocol=\
    udp src-address-list="DNS servers" src-port=53
add action=mark-packet chain=prerouting comment=\
    "DNS catcher DNS Servers->MikroTik Packets" connection-mark=\
    "DNS catcher DNS Servers->MikroTik Connections" new-packet-mark=\
    "DNS catcher DNS Servers->MikroTik Packets" passthrough=no
add action=mark-connection chain=output comment=\
    "DNS catcher MikroTik->PC Connections" new-connection-mark=\
    "DNS catcher MikroTik->PC Connections" passthrough=yes protocol=udp \
    src-address=192.168.1.1 src-port=53
add action=mark-packet chain=output comment=\
    "DNS catcher MikroTik->PC Packets" connection-mark=\
    "DNS catcher MikroTik->PC Connections" new-packet-mark=\
    "DNS catcher MikroTik->PC Packets" passthrough=no
add action=mark-connection chain=output comment=\
    "DNS catcher MikroTik->DNS Servers Connections" dst-address-list=\
    "DNS servers" dst-port=53 new-connection-mark=\
    "DNS catcher MikroTik->DNS Servers Connections" passthrough=yes protocol= udp
add action=mark-packet chain=output comment=\
    "DNS catcher MikroTik->DNS Servers Packets" connection-mark=\
    "DNS catcher MikroTik->DNS Servers Connections" new-packet-mark=\
    "DNS catcher MikroTik->DNS Servers Packets" passthrough=no


Маркировка осуществляется по схеме: сначала обозначаются соединения (mark-connection) затем пакеты этих соединений (mark-packet). Соединения »PC → MikroTik» отлавливаются по IP адресу роутера и 53 номеру порта назначения. Здесь и далее я опускаю одинаковый и потому не информативный в текущем контексте префикс »DNS catcher». Соединения »MikroTik → DNS servers» помечаются только по 53 номеру порта назначения. Соединения »DNS servers → MikroTik» маркируются по такой же схеме, только в правиле используется 53 порт отправителя. Соединения »MikroTik → PC» отлавливаются на основе IP адреса маршрутизатора и 53 порта отправителя. Для исключения ошибки перемаркировка пакетов ограничена (passthrough=no). Обращаю внимание, пакетов, а не соединений. Чтобы убедиться в корректности работы Mangle, по очереди зеркалируем пакеты каждого типа соединений. Пример, как это делается для пакетов »MikroTik → DNS servers», представлен ниже:

/ip firewall mangle
add action=sniff-tzsp chain=postrouting comment="Sniffer"
packet-mark="DNS catcher MikroTik->DNS Servers Packets" sniff-target=\
    IP_адрес_принимающего_сервера sniff-target-port=37008


Как сохранить трафик на принимающей стороне, рассмотрено здесь. Для зеркалируемых пакетов не забываем снять ограничение, озвученное выше, и выставить passthrough=yes, иначе правило /ip firewall mangle add action=sniff-tzsp будет проигнорировано. Почему для разных типов соединений используются различные цепочки в Firewall (Prerouting, Output, Postrouting) будет рассмотрено ниже. Для тех, кто вообще не в теме, коротко прокомментирую: пакет проходит через различные части сетевой подсистемы Linux, ядро применяет правила из определённых цепочек (chains) к пакетам. После получения нового пакета от физического уровня ядро активизирует правила в цепочках, соответствующих вводу. Все эти структуры данных обслуживаются ядром. Такая подсистема в целом и является Firewall-ом, который из пространства пользователя создаёт правила и управляет ими. Иллюстрация, взятая здесь, в очень упрощённом варианте отображает связь между различными цепочками:

image-loader.svg

В качестве DNS клиента удобно использовать встроенную во многие операционные системы программу nslookup, которой можно указать IP адрес DNS сервера (192.168.1.1) куда будет отправлен запрос:

nslookup mail.ru 192.168.1.1


Посмотрим, что представляет из себя каждый тип выделенных нами пакетов:

  • Соединения с меткой »PC→MikroTik Connections» (содержат пакеты »PC→MikroTik packets») передают DNS query;

    ynlwccqocuoupj3yuerss1ltqma.jpeg

  • Соединения с меткой »MikroTik→DNS Servers Connections» (содержат пакеты »MikroTik→DNS Servers Packets») также передают DNS query;

    99erfp4-mq6ion687jf1mwkrgxq.jpeg

  • Соединения с меткой »DNS Servers→MikroTik Connections» (содержат пакеты »DNS Servers→MikroTik Packets») передают DNS query response;

    iwumv-l08le-lekvyey7bxbx6wk.jpeg

  • Соединения с меткой »MikroTik→PC Connections» (содержат пакеты »MikroTik→PC Packets») также передают DNS query response.

    qsvmaz-95upskpkgj4jj0munvwu.jpeg


Видно, что маркировка соответствует своим названиям, и, следовательно, выполнена корректно. Пришло время ответить на вопрос, сколько типов соединений создано в RouterOS? Теоретическая часть цикла статей позволяет нам на него ответить — будет создано 2 типа соединения, которые должны получить метки »PC→MikroTik Connections» и »MikroTik→DNS Servers Connections». Внутри операционной системы созданы всего 2 типа соответствующих сокетов (с одинаковыми Stream index). Хотя с точки зрения UDP протокола, будет 4 типа отправки пакетов, ведь UDP ничего не знает про полезную нагрузку прикладного протокола DNS, и что в нём идут запросы и ответы на них, и какие там существуют клиент-серверные взаимосвязи. Чтобы это проверить, дополним правило для »MikroTik→PC Connections» параметром «established», т.е. соединение, которое установлено ранее:

/ip firewall mangle
add action=mark-connection chain=output comment=\
    "DNS catcher MikroTik->PC Connections" connection-state=established \
    new-connection-mark="DNS catcher MikroTik->PC Connections" passthrough=yes \
    protocol=udp src-address= 192.168.1.1 src-port=53


Ничего не изменится, счётчик пакетов будет отрабатывать, потому как соединение было установлено ранее на этапе »PC→MikroTik Connections». А если укажем connection-state=new, то счётчик замрёт, таких новых соединений нет. Попробуем ещё дополнительно повесить указанную марку на соединения, которые ранее не размечались (connection-mark=no-mark):

/ip firewall mangle
add action=mark-connection chain=output comment=\
    "DNS catcher MikroTik->PC Connections" connection-mark=no-mark \
    new-connection-mark="DNS catcher MikroTik->PC Connections"\
    passthrough=yes protocol=udp src-address=192.168.1.1 src-port=53


Счётчики остановятся и возобновят свою работу только, если мы укажем не пустой маркер, а »PC→MikroTik Connections». Для других правил будет аналогичная ситуация. Однако если заглянуть в Connection tracking, то там можно увидеть следующую картину:

b8dyjgsvztpnc_enmkiyfhs6y7a.jpeg

Как видно, маркировка как бы перевёрнута, что вначале может даже ошеломить. Чтобы такого с вами не произошло, разберу ситуацию подробно. Если мы укажем для соединений »MikroTik→PC Connections» и »DNS Servers→MikroTik Connections» условие connection-mark=no-mark, то Connection tracking даст ожидаемый результат:

wey26gcbyvdhu5gemqolug8sla8.jpeg

Без него происходит перемаркировка соединений, а с ним уже существующие соединения не получат новую марку. Вышесказанное звучит достаточно запутанно, поэтому передам туже самую мысль, касательно не корректной ситуации в Connection tracking, другими словами. Внутри маршрутизатора возникает следующая ситуация. DNS запрос от PC поступает на DNS сервер, интегрированный в MikroTik. RouterOS внутри себя создаёт первое сетевое соединение »PC→MikroTik Connections». Далее роутер осуществляет DNS запрос к серверу Cloudflare, и создаётся второе соединение »MikroTik→DNS Servers Connections». Далее от сервиса Cloudflare приходит DNS query response, что является продолжением и окончанием второго соединения. После этого роутер выполняет DNS query response в сторону PC, что является продолжением и окончанием первого соединения. Правилами Mangle мы вмешиваемся в вышеописанные действия.

Первая половина первого запроса у нас маркируется как »PC→MikroTik Connections» (корректно), а вторая половина первого запроса маркируется как »MikroTik→PC Connections» (что и должно было вызвать смуту в нашей голове при просмотре Connection tracking, как показано на рисунке с некорректной ситуацией). На деле это одно и то же соединение внутри RouterOS. Однако половина его вышла с одной меткой, а вторая половина с другой, и Connection tracking отрисовал только ту метку, которая пришла к нему последней (в последнем блоке по движению пакетов внутри операционной системы, об этом подробнее будет в третьей части цикла статей). Для соединений »MikroTik→DNS Servers Connections» и »DNS Servers→MikroTik Connections» возникает ровно такая же ситуация. Поэтому выровнять ситуацию нам помог параметр connection-mark=no-mark.

Отмечу, что если в правилах Mangle указать passthrough=no, которое при срабатывании пропустит трафик, минуя оставшиеся правила маркировки (значение по умолчанию для параметра passthrough=yes, поэтому в явном виде оно не указывается), то при отсутствующем параметре connection-mark=no-mark перемаркировка всё равно произойдёт. Ключевую роль опять же играет прохождение соединений и пакетов внутри операционной системы маршрутизатора, о чём мы подробно поговорим в третьей части цикла статей. Параметр passthrough=no попросту не отработает, так как первая половина соединения и вторая половина соединения проходят различными маршрутами внутри роутера.

Резюмирую вышесказанное. При работе описанной классической сети для одного DNS запроса клиента LAN в реальности UDP соединение отрабатывает 4 раза: в направление роутера, затем до DNS сервера Cloudflare и обратно. Однако RouterOS будет воспринимать их только как 2 соединения: от клиента Wi-Fi в направлении маршрутизатора и обратно, а также от роутера до DNS сервера Cloudflare и обратно. В глазах MikroTik, любой одиночный UDP пакет — это новое соединение до тех пор, пока не будет отправлен ответ в обратном направлении. Когда есть ответ, то это уже для него UDP соединение. Разумеется, вышесказанное правило ограничено во времени в соответствии с таймаутами, о которых речь шла в первой части цикла статей.

Ранее говорилось, что защита роутера от DOS атак на L3 уровне может базироваться на правилах обработки сетевых соединений. На практике это будет выглядеть так:

/ip firewall filter
add action=add-src-to-address-list address-list=DOS address-list-timeout=1h chain=input comment="List DOS" connection-limit=100,32 connection-state=new in-interface=WAN
add action=drop chain=input comment="Drop DOS list" src-address-list=DOS


Логика работы приведённых правил следующая: не более 100 новых сетевых соединений с IP адреса с 32 маской, иначе IP адрес идет в бан. Подробнее про возможности можно почитать здесь.

▍ Заключение


В статье с практической точки зрения рассмотрено, что такое сетевое соединение в ядре Linux. Внутри RouterOS и других, «Linux based» операционных систем, понятие сетевое соединение не идентично клиент-серверному соединению. MikroTik работает с виртуальными сокетами, под которыми следует понимать совокупность пар IP адрес отправителя и номера порта отправителя, а также IP адрес получателя и номера порта получателя, искусственно введённых для обеспечения функционирования устройства. Это особенно важно при настройке Firewall, а следовательно, и связанных с ним Qos и маршрутизации, в некоторых случаях.

Часть 1
Часть 2 (вы тут)

image-loader.svg

© Habrahabr.ru