Распространенные в сети материалы касаются работы именно маршрутизаторов MikroTik. И все они для отлова DHCP используют скудные ресурсы CPU. При этом, относительно свежая линейка коммутаторов хронически остается без внимания. Между прочим, совершенно напрасно.
Итак, подопытный аппарат CRS125-24G-1S установлен на доступе. С некоторых пор изредка на пользовательские устройства стали выпадать посторонние IP-адреса. Естественно возникла задача «найти и обезвредить». В стандартном арсенале RouterOS есть инструмент «DHCP-server Alert», способный вызывать некие действия при обнаружении в сети стороннего DHCP-сервера.
Отлично? Пожалуй. Только инструмент при ближайшем рассмотрении оказался не слишком информативный. Чтобы понять почему, рассмотрим типичную архитектуру маршрутизирующего оборудования MikroTik (картинка из вики):
По сути основная часть устройства делится на две:
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)
Byteman
20.09.2016 20:11+1Мы некогда писали скриптик для сети общежития (сеть была полностью построена на неуправляемых свичах), который «выжирал» из чужого сервера весь диапазон раздаваемых адресов. Работало надежно)
VGusev2007
21.09.2016 09:40А как такое возможно? Как она понимала у кого выжирать, а у кого не выжирать? :) Можно же так и у себя всё выжрать.
conflict
21.09.2016 00:33Я так понимаю Ваш скрипт справится если злодей напрямую воткнут порт свитча. А если перед ним тупой свитч?
nkusnetsov
21.09.2016 06:24Если перед ним тупой свитч, трафик злодея микротик равно отрубит. Точнее не пропустит через себя. Но те, кто подключен к тому же тупому свитчу и общаются со злодеем напрямую будут видеть чужой DHCP.
В любом случае — тупой свитч, это беда, там уже не важно, что стоит выше — cisco, huawei, d-link или mikrotik.
Karpion
21.09.2016 00:33+1Непонятно, помешает ли такая блокировка получить ложный адрес первому клиенту. Похоже, что ответ первому клиенту проскочит до того, как ложный DHCP-сервер будет изолирован в его собственном VLAN.
Правильное решение — запрет доставлять DHCP-запросы во все порты, кроме тех, на которых находится законный DHCP-сервер. Правда, для него требуется, чтобы эту функцию имели все свичи, к которым подсоединены все потенциальные ложные DHCP-серверы.nkusnetsov
21.09.2016 06:34Между появлением DHCP-сервера в сети и срабатыванием алерта действительно есть небольшой временной лаг. По моим сведениям, алертер микротика срабатывает на DHCPOFFER, то есть до завершения процедуры выдачи адреса DHCP — до прохождения DHCPREQUEST, DHCPACK (именно из DHCPOFFER берется IP-адрес DHCP-сервера). По идее чужой DHCP должен быть изолирован на этой стадии, но временные издержки на запуск и исполнение скрипта, действительно могут дать интервал для одного клиента.
Скрипт делался как решение на случай «вообще нет DHCPsnooping'а». У людей вон, вообще грустно — тупые свитчи на выносах стоят.
elve
21.09.2016 21:34А просто блокировать на этом порту 67 udp нельзя? Зачем такие сложности? Я так делал на древней управляшке D-Link где тоже не было DHCP Snooping =)
nkusnetsov
22.09.2016 07:42Чтобы блокировать UDP 67 (L3+L4) нужно порт(ы) выводить в софтварный бридж и использовать для фильтрации CPU. Это сильно снизит пропускную способность. Люди сделавшие фильтр на CPU, потом ноют «фиговый свитч, что-то гигабит не прокачивается».
Моё решение сохраняет ресурсы процессора, перекладывая задачу на аппаратный свитч-чип.
AndreyKu
Вопрос, а что делать когда DHCP сервер на этом маке выключили и машинке нужен будет доступ в сеть, он ведь по прежнему будет изолирован от общей сети и данные ситуации в ручную разрешать придется?
nkusnetsov
Безусловно вручную. Такой вариант предполагает возможность некоей «профилактической работы» с пользователем-вредителем. Это минимальные издержки по сравнению с тем, что вредитель если его вовремя не остановить, может оставить без связи несколько десятков человек.
В общем-то, ничто не мешает написать еще один скрипт, который будет убирать маки из $stubvid при исчезновении их из массива алертера.