О проблеме отсутствия функции полноценного DHCP-snooping в устройствах MikroTik уже было сказано и написано слишком много. И везде для отлова злодейских DHCP предлагают нагружать CPU. Я же расскажу, как убить чужой DHCP-сервер с помощью свитч-чипа интегрированного в коммутаторы MikroTik CRS.

Распространенные в сети материалы касаются работы именно маршрутизаторов MikroTik. И все они для отлова DHCP используют скудные ресурсы CPU. При этом, относительно свежая линейка коммутаторов хронически остается без внимания. Между прочим, совершенно напрасно.

Итак, подопытный аппарат CRS125-24G-1S установлен на доступе. С некоторых пор изредка на пользовательские устройства стали выпадать посторонние IP-адреса. Естественно возникла задача «найти и обезвредить». В стандартном арсенале RouterOS есть инструмент «DHCP-server Alert», способный вызывать некие действия при обнаружении в сети стороннего DHCP-сервера.

Отлично? Пожалуй. Только инструмент при ближайшем рассмотрении оказался не слишком информативный. Чтобы понять почему, рассмотрим типичную архитектуру маршрутизирующего оборудования MikroTik (картинка из вики):

image

По сути основная часть устройства делится на две:

1) програмно-конфигурируемый свитч, осуществляющий коммутацию на максимальной скорости;
2) CPU отвечающий за маршрутизацию, фильтрацию а также умеющий осуществлять коммутацию на программном уровне, медленнее и более ресурсоёмко.

IP-сервисы на таком устройстве, само собой висят на верхнем уровне. На Master-интерфейсе восходящем к CPU Routerboard или на программном бридже. Там и живет DHCP-сервер и его служба алертов. Все виденные мною ранее решения имели также два недостатка вытекающие из этой архитектуры:

1) Фильтрация трафика от злодея выполнялась на бридже, через «use ip firewall» и отнимала без того скудные ресурсы CPU;
2) Злодей подключенный к свитчу продолжал неконтролируемо пакостить абонентам на соседних портах этого свича.

Например, абоненты висящие на port2 и port3 всё равно продолжали получать бяку прилетающую со стороны port4. Но всё изменилось, когда у MikroTik появилась линейка CRS — ребята установили более мощный и функциональный свитч-чип, который я и решил попытаться использовать, победить и сохранить ресурсы CPU.

Шаг №1


Настраиваем «DHCP-server Alert». Настройка сама по себе не сложная, почти всё описано в вендорской вики — включаем, задаем интерфейс на котором следить (например bridge-local), указываем доверенный наш DHCP. Можем добавить скрипт, который вызывать в случае чего. Ура — скрипту автоматически передаются параметры: $interface $mac-address $address обнаруженного злодея! Ура? Так легко? Но вот тут-то и спрятаны первые подводные грабли. И не одни.

Грабли первые: в $interface передается имя интерфейса на котором сидит сам алертер. Потому, если даже злодей подключен к интерфейсу «port4» (см. картинку выше), в переменной всё равно будет «bridge-local». В логе видим почти то же самое:

"dhcp-alert on bridge-local: discovered unknown dhcp server, mac xx:xx:xx:xx:xx:xx, ip xyz.xyz.xyz.xyz"

Кхм. Сколько у нас портов? 24? Ладно. Я иду искать…

Шаг №2


По граблям. Искать приходится в unicast-fdb свича, по переменной $mac-address. И вот тут-то меня ждали вторые грабли. Настоящие подводные. Скрипт вызывался, но никак не хотел работать. Кто занимается скриптописательством для MikroTik подтвердит — отладка автоматически запускаемого скрипта сама по себе задача нифига не тривиальная. Да еще и обработчик on-error{} почему-то молчал. Опытным путём было обнаружено, что код

:set portname [/interface ethernet switch unicast-fdb get [find mac-address=$mac-address] port ]

отлично отрабатывает в командной строке терминала и только в ней. В теле скрипта не желает. Причина — странная интерпретация имени собственной (определенной вендором!) переменной $mac-address.

Фича: интерпретация команд в скрипте идет через... иным путём. Для успешной работы, имя системной переменной надо экранировать: ($"mac-address") . При этом в дальнейшем использовании тоже не исключены фокусы.

Я пошел другим путем, создав локальную переменную с нормально интерпретируемым именем:

:local smac ($"mac-address")

Стало легче. Появилась возможность видеть название реального интерфейса на свитче, к которому подключен вредитель. Осталось придумать как его убить.

Как известно, настоящий джедай использует силу, а настоящий инженер использует мозг. Поэтому, идея тупо полностью отрубить найденный порт была мною отброшена — а вдруг за этим портом сидит несколько пользователей и/или устройств? К счастью свитч-чип в MikroTik CRS позволяет делать MAC-Based-Vlan. Решение пришло тут же — выделить трафик от злодея по MAC и завернуть в отдельный неиспользуемый Vlan. В итоге, получился компактный скрипт, надежно отключающий от сети чужое устройство с поднятым на нём DHCP-сервером.

#dhcpsnooper - script to cut off rogue DHCP server
#stubvid - Stub Vlan Id to move rogue DHCP server. Set your own value according your vlan config
:local stubvid 666
:local smac ($"mac-address")
:local portname [/int eth sw uni get [find mac-address=$smac] port ]
:local imac  [/int eth sw mac find src-mac-address=$smac]
if ([:len $imac]<1)   do={/interface ethernet switch mac-based-vlan add   new-customer-vid=($stubvid) src-mac-address=($smac) } else={
foreach i in $imac  do={/interface ethernet switch mac-based-vlan set $i new-customer-vid=($stubvid) src-mac-address=($smac) disabled=no }         }
/interface ethernet switch port set $portname allow-fdb-based-vlan-translate=yes
log error ("DHCP found on PORT:".($"portname")."   MAC:".($"mac-address")."   IP:".($address))

Переменная $stubvid, как должно быть понятно из названия — номер Vlan-заглушки. Переменная $imac — массив. MikroTik зачем-то позволяет делать несколько записей с одинаковым MAC в таблице mac-based-vlan, что вызывало сбой при попытке получить единственное значение. Предупреждения просто пишутся в log error, но ничто не мешает дописать более настойчивое информирование администратора сети.

Вызов алерта и скрипта у меня прописан следующим образом:

/ip dhcp-server alert add disabled=no interface=bridge-local on-alert=dhcpsnooper  valid-server=MAC_своего_DHCP

Спасибо, что дочитали до конца. Надеюсь, вам это когда-нибудь пригодится.
Поделиться с друзьями
-->

Комментарии (10)


  1. AndreyKu
    20.09.2016 18:35

    Вопрос, а что делать когда DHCP сервер на этом маке выключили и машинке нужен будет доступ в сеть, он ведь по прежнему будет изолирован от общей сети и данные ситуации в ручную разрешать придется?


    1. nkusnetsov
      20.09.2016 19:46
      +1

      Безусловно вручную. Такой вариант предполагает возможность некоей «профилактической работы» с пользователем-вредителем. Это минимальные издержки по сравнению с тем, что вредитель если его вовремя не остановить, может оставить без связи несколько десятков человек.
      В общем-то, ничто не мешает написать еще один скрипт, который будет убирать маки из $stubvid при исчезновении их из массива алертера.


  1. Byteman
    20.09.2016 20:11
    +1

    Мы некогда писали скриптик для сети общежития (сеть была полностью построена на неуправляемых свичах), который «выжирал» из чужого сервера весь диапазон раздаваемых адресов. Работало надежно)


    1. VGusev2007
      21.09.2016 09:40

      А как такое возможно? Как она понимала у кого выжирать, а у кого не выжирать? :) Можно же так и у себя всё выжрать.


  1. conflict
    21.09.2016 00:33

    Я так понимаю Ваш скрипт справится если злодей напрямую воткнут порт свитча. А если перед ним тупой свитч?


    1. nkusnetsov
      21.09.2016 06:24

      Если перед ним тупой свитч, трафик злодея микротик равно отрубит. Точнее не пропустит через себя. Но те, кто подключен к тому же тупому свитчу и общаются со злодеем напрямую будут видеть чужой DHCP.
      В любом случае — тупой свитч, это беда, там уже не важно, что стоит выше — cisco, huawei, d-link или mikrotik.


  1. Karpion
    21.09.2016 00:33
    +1

    Непонятно, помешает ли такая блокировка получить ложный адрес первому клиенту. Похоже, что ответ первому клиенту проскочит до того, как ложный DHCP-сервер будет изолирован в его собственном VLAN.

    Правильное решение — запрет доставлять DHCP-запросы во все порты, кроме тех, на которых находится законный DHCP-сервер. Правда, для него требуется, чтобы эту функцию имели все свичи, к которым подсоединены все потенциальные ложные DHCP-серверы.


    1. nkusnetsov
      21.09.2016 06:34

      Между появлением DHCP-сервера в сети и срабатыванием алерта действительно есть небольшой временной лаг. По моим сведениям, алертер микротика срабатывает на DHCPOFFER, то есть до завершения процедуры выдачи адреса DHCP — до прохождения DHCPREQUEST, DHCPACK (именно из DHCPOFFER берется IP-адрес DHCP-сервера). По идее чужой DHCP должен быть изолирован на этой стадии, но временные издержки на запуск и исполнение скрипта, действительно могут дать интервал для одного клиента.
      Скрипт делался как решение на случай «вообще нет DHCPsnooping'а». У людей вон, вообще грустно — тупые свитчи на выносах стоят.


  1. elve
    21.09.2016 21:34

    А просто блокировать на этом порту 67 udp нельзя? Зачем такие сложности? Я так делал на древней управляшке D-Link где тоже не было DHCP Snooping =)


    1. nkusnetsov
      22.09.2016 07:42

      Чтобы блокировать UDP 67 (L3+L4) нужно порт(ы) выводить в софтварный бридж и использовать для фильтрации CPU. Это сильно снизит пропускную способность. Люди сделавшие фильтр на CPU, потом ноют «фиговый свитч, что-то гигабит не прокачивается».
      Моё решение сохраняет ресурсы процессора, перекладывая задачу на аппаратный свитч-чип.