db2dhcp — DHCP сервер на SQL СУБД (В работе)

db2dhcp — DHCP сервер на SQL СУБД

На основной работе возникла необходимость поднять DHCP сервер, с возможностью выдачи IP адреса абоненту по следующим критериям: известный MAC адрес свича, известный порт на свиче с которого пришел запрос, причем данные чтоб хранились в БД MySQL. Вполне подошел проект http://www.netpatch.ru/devel/db2dhcp/. Одно НО, в нем не был реализован функционал ведения «истории авторизаций» в БД, да и проект в целом оказался заброшенным, а выйти на автора не удалось. Собственно поэтому и возник форк данного проекта.

Текущие изменения:

  • реализована возможность ведения истории авторизаций пользователя
  • можно указать IP адрес, который нужно «слушать» (по умолчанию «садился» на 0.0.0.0)
  • доработана возможность сборки проекта под FreeBSD 8.4, 9.2, 10

GIT репозитарий проекта здесь: https://github.com/donpadlo/dhcp2db

Возможности сервера

  • Работа с Ethernet сетями, IP протокол версии 4.
  • Поддерживаемые СУБД: PostgreSQL, MySQL.
  • Поддерживаемые ОС: сборка и базовое тестирование проведены в Linux, FreeBSD, Windows XP. Но ничего не должно помешать сборке в других ОС после минимальных доработок ОС зависимых функций.

ВНИМАНИЕ

Исходные коды сервера доступные на данный момент являются альфа-версией, потому: от сервера можно ожидать любого не корректного поведения (падений, утечек памяти, самоблокировки); есть ряд недоработок как в логике работы самого сервера (например нет ограничения по числу запросов в секунду к БД), так и в реализации DHCP (пока нет полного соответствия RFC).

Применение без доработки в реально эксплуатируемых системах на данный момент не рекомендовано! В то же время надеемся что все желающие помочь развитию проекта постараются произвести тесты  и присоединятся к совершенствованию кода в силу своих возможностей.

Сборка сервера

Требования для сборки

  • pthreads — поддержка POSIX threads, обязательно.
  • pcap — библиотека разработчика, обязательна.
  • pq — библиотека разработчика для PostgreSQL. Обязательна в случае использования PostgreSQL.
  • mysqlclient — библиотека разработчика для MySQL. Обязательна в случае использования MySQL.

Соответственно для работы уже скомпилированной версии нужны обычные версии этих библиотек (без файлов для разработчиков).

Сборка

Сборка производится абсолютно стандартно, единственное условие — обязательно не забудьте указать поддержку какой из СУБД необходимо активировать. В противном случае сервер соберётся но не будет поддерживать ни одной СУБД.

wget https://github.com/donpadlo/dhcp2db/archive/master.zip
$ unzip masrer.zip
$ ./configure --enable-mysql --enable-postgresql
$ make

Для сборки в FreeBSD командную строку configure нужно добавить:

LDFLAGS="-L/usr/local/lib -L/usr/local/lib/mysql" CFLAGS="-I/usr/local/include"

Настройка сервера

Настройка осуществляется через командную строку и конфигурационный файл.

Параметры командной строки

  • -L — показывает список интерфейсов на которых возможна обработка DHCP запросов.
  • -D — отключает режим демонизмации.
  • -d — включает отладочный вывод в лог.
  • -q — «тихий» режим запуска.
  • -s — включает вывод сообщений в stdout, имеет смысл применять в случае использования ключа -D
  • -c <config-filename> — задаёт имя конфигурационного файла.
  • -o — выводит таблицу смещений полей в заголовке DHCP.
  • <interface-name1> [... <interface-nameN>] — задаёт список интерфейсов на которых сервер будет принимать DHCP запросы от клиентов.

Формат конфигурационного файла

Конфигурационный файл состоит из набора параметров имеющих общий формат:

Имя-параметра = значение

Допустимые параметры конфигурационного файла

  • User — имя пользователя от которого будет выполняться сервер. По умолчанию — текущий пользователь. Работа от имени суперпользователя строжайше не рекомендована! Не поддерживается на win32 платформе.
  • LogFile — имя лог-файла. Пользователь заданный директивой User должен иметь право записи в целевой каталог.
  • DBType — тип СУБД. Возможные значения: PostgreSQL, MySQL.
  • DBServerAddress — IP адрес сервера БД.
  • DBServerPort — порт сервера БД. По умолчанию соответствует порту выбранной СУБД.
  • DBUserName — имя пользователя БД.
  • DBUserPassword — пароль БД.
  • DBName — имя БД.
  • DBClientsCount — число потоков работающих с БД. По умолчанию: 4.
  • QueryDiscover — SQL запрос выполняющийся при получении DHCPDISCOVER. Обязательно должен быть определён.
  • QueryRequest — SQL запрос выполняющийся при получении DHCPREQUEST. Если не указан, то выполняется QueryDiscover.
  • QueryRequestRej — запрос выполняемый в случае если клиент не выбрал данный сервер. Не реализовано.
  • QueryInform — — SQL запрос выполняющийся при получении DHCPINFORM. Не реализовано.
  • QueryRelease — SQL запрос выполняющийся при получении DHCPRELEASE. Если не указан, то ничего не выполняется.
  • MaxQpsHost — максимальное число DHCP запросов в секунду от одного клиента. Не реализовано.
  • MaxQpsTotal — суммарное максимальное число DHCP запросов.Не реализовано.
  • DHCPServerPort — прослушиваемый порт для обработки DHCP запросов. По умолчанию: 67.
  • DHCPClientPort — клиентский DHCP порт на который будут уходить ответы. По умолчанию: 68.
  • DHCPCacheTTL — время хранения DHCP ответа во внутреннем кэше db2dhcp, в секундах. Если кэш включен, то в случае получения запроса DHCPREQUEST db2dhcp сперва проверит наличие ответа в кэше, если ответ обнаружен то DHCP клиент получит информацию из кэша db2dhcp — SQL запрос в БД выполняться не будет. Данная опция позволяет очень существенно снижать загркузку на сервер БД если используется небольшое (в пределах нескольких часов) значение времени аренды. По умолчанию кэш не используется (значение 0).
  • Var — параметр создающий переменную значение которой определяется по различным характеристикам DHCP запроса клиента. Подробности смотрите ниже.
  • IPtoBind — на каком IP слушаем DHCP запросы. Обычно 0.0.0.0
  • QueryHistory — запрос для записи в БД сохранения истории авторизации

Настройка запросов к БД

Основная суть настройки сервера заключается в правильном конфигурировании запросов выполняемых к БД. Для этого нужно знать и соблюдать следующее:

  • В запросах кроме обычных SQL выражений можно (и нужно) использовать встроенные переменные сервера и переменные объявленные пользователем. Пользовательские переменные могут содержать в себе простые условные конструкции. Переменные в запросах используются как $ИМЯ-ПЕРЕМЕННОЙ$.
  • Сам по себе запрос может выглядеть абсолютно как угодно и использовать любые допустимые SQL и выбранной версией СУБД конструкции (например — JOIN, UNION).
  • В получаемом из БД результате число и тип (смысловое значение) полей должны строго соответствовать требуемуму сервером формату: код, тип, значение. Это значит что любой запрос для получения данных должен начинаться как
    SELECT code-field-name, type-field-name, value-field-name FROM ...

Встроенные переменные сервера

Встроенные переменные делятся на два типа — переменные относящиеся к системе хоста и переменные относящиеся к интерфейсу (сетевому устройству) на котором был получен DHCP запрос.

Переменная относящаяся к системе пока только одна:

  • SRV-HOSTNAME — имя хоста на котором запущен сервер.

Переменные относящиеся к интерфейсу получившему DHCP запрос:

  • DEV-NAME — имя интерфейса.
  • DEV-ETHERADDR — Ethernet адрес интерфейса: шестнадцатиричные цифры в верхнем регистре, октеты разделены двоеточием.
  • DEV-IPADDR — строкое представление IP адреса интерфейса.
  • DEV-NETWORK — строковое представление IP адреса сети интерфейса.
  • DEV-NETMASK — строковое представление сетевой маски интерфейса.
  • DEV-NETMASK-CIDR — строковое представление сетевой маски интерфейса в формате CIDR (длина маски одним числом).
  • DEV-IPADDR-INT — числовое представление IP адреса интерфейса. Например 192.168.1.1 = 3232235777. Сделано т.к. поиск в SQL БД по числу может оказаться значительно быстрее поиска по строке.
  • DEV-NETWORK-INT — числовое представление IP адреса сети интерфейса.
  • DEV-NETMASK-INT — числовое представление сетевой маски интерфейса.
  • DEV-SRVPORT — номер порта сервера.
  • DEV-CLIPORT — номер порта клиента отправившего DHCP запрос.

Пользовательские переменные

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

Пользовательские переменные могут быть двух типов — содержащие данные из DHCP заголовка (описание переменной начинается с h) и содержащие данные из поля «опции» заголовка (описание начинается с o). Форматы обоих типов переменных:

Var = ИМЯ-ПЕРЕМЕННОЙ h:<смещение>:<длина>
Var = ИМЯ-ПЕРЕМЕННОЙ o:<код-опции>[:<смещение[:длина]>|<условная-конструкция>]
  1. h-переменные — поле смещение указывает смещение относительно начала заголовка DHCP пакета; поле длина указывает сколько байт необходимо скопировать из пакета в значение переменной. Пример объявления переменной для получения Ethernet адреса клиента:
    Var = CLI-ETHER-ADDR h:28:6
    По смещению в 28 байт от начала DHCP заголовка начинается поле аппаратного адреса. Ethernet адрес имеет длину 6 байт. Смещения полей заголовка вы можете узнать запустив db2dhcp с ключём -o.
  2. o-переменные — поле код-опции содержит код интересующей нас DHCP опции указанной клиентом в поле опций DHCP пакета. Если в строке больше ничего не указано — берётся всё значение опции. Если указаны смещение и длина, то смещение считается от начала данной опции; смысл поля длина такой же как и для h-переменных. Вместо указания смещения и длины можно указать условную конструкцию вычисляющую нужное смещение и длину. Если опция с указанным кодом не найдена в DHCP пакете — значением переменной будет пустая строка.

Формат условной конструкции:

(<смещение> = <значение>) <смещение[:код]_если_условие_верно> | \
 <смещение[:код]_если_условие_не_верно|вложенная_условная_конструкция>

В скобках, в левой части от знака равенства указывается смещение от нулевого байта опции, начиная с которого выполняется сравнение. Значение — hex-строка произвольной длины (в разумных пределах — не длиннее ожидаемого значения опции) начинающаяяся с префикса 0x.

В результате будет выполнено сравнение значения опции начиная с указанного смещения с шестнадцатиричным значением заданным строкой-значением на длину равную длине строки-значения. Если значение опции совпадает со строкой-значением, то результатом условной конструкции будут смещение и длина указанные сразу после скобок. Если не совпадает — результатом будет смещение и длина указанные после вертикальной черты |. В случае не совпадения вместо непосредственного результата (смещение:длина) можно указать следующую условную конструкцию с другим условием.

Механизм условных конструкций реализован в основном для возможности обработки DHCP опции 82 — ожидается что при использовании устройств разных производителей (а значит имеющих разный формат этой опции) можно будет делать правильный разбор опции в самом сервере, без необходимости отсылки в БД «сырых» данных.

Коды и типы значений в БД

Как было сказано выше — данные выборки полученной из БД для правильной интерпретации сервером db2dhcp должны содержать правильный код и тип. Условимся что запрос на получение данных имеет вид:

SELECT code, type, value FROM ...

Исходя из этого разберём по отдельности значения каждого поля результирующей выборки:

  1. code — всегда дожен быть первым полем результирующей выборки данных. Может быть числом в диапазоне 0 — 255, либо 1000 и выше.
    Если код имеет значение 0 — 255, то данные в поле value интерпретируются как значение DHCP опции с таким кодом и добавляется в поле опций DHCP ответа. Возможные значения смотрите здесь.
    Если код имеет значение в диапазоне 1001 — 1015 (включительно), то данные поля value интерпретируются как значения соответствующего поля из заголовка DHCP пакета. Возможные значения смотрите в таблице кодов полей DHCP заголовка.
    Если код имеет значение выше 2000, то данные интерпретируются как служебные, не передающиеся в результирующий DHCP ответ и использующиеся самим сервером. Возможные значения смотрите в таблице служебных кодов.
  2. type — всегда должно быть вторым полем результирующей выборки данных. Указывает тип данных содержащихся в поле value. Указание типа необходимо что бы db2dhcp мог правильно преобразовать данные перед записью их в результирующий DHCP ответ. Возможные значения поля смотрите в таблице кодов типов значений.
  3. value — собственно данные которые будут непосредственно записаны в DHCP ответ клиенту. Вид и формат этих данных зависит от типа указанного в поле type.

Коды полей DHCP заголовка

Код Поле
1001 Код оператора сообщения (BOOTREQUEST/BOOTREPLY). Если это поле не задано в итоговой выборке – сервер сам его установит в правильное значение. Обычно явно указывать его не имеет смысла
1002 Тип аппаратного адреса. На данный момент допустим только Ethernet. Так же не имеет смысла устанавливать в базе – в случае отсутствия сервер сам устанавливает правильное значение
1003 Длина аппаратного адреса, по умолчанию – 6 (Ethernet)
1004 Поле hops (шаги)
1005 XID DHCP транзацкии
1006 Не должно присутствовать в данных выборки, сервером не устанавливается
1007 Флаги
1008 IP адрес клиента отправившего запрос. Не должно присутствовать в данных выборки
1009 IP адрес клиента предлагаемый DHCP сервером. Должно присутствовать в ответе сервера (данных выборки соответственно), только если это не ответ на DHCPINFORM
1010 Адрес следующего сервера загрузки. Если не указано в данных выборки, то db2dhcp проставляет в это поле IP адрес интерфейса на котором был получен DHCP запрос
1011 IP адрес агента пересылки (DHCP relay). Должно быть заполнено в случае использования агента. Если отсутствует в данных выборки – сервер сам заполняет его основываясь на запросе DHCP клиента. Обычно указывать его в базе не имеет смысла
1012 Аппаратный адрес клиента. Заполняется сервером самостоятельно
1013 Имя DHCP сервера
1014 Имя файла загрузки (при использовании сетевой загрузки хоста)
1015 Поле DHCP опций. Обычно не должно указываться в итоговом наборе данных т.к. опции задаются по отдельности

Служебные коды db2dhcp

Код Поле
2001 Запрет кэширования DHCPACK созданного из данного набора данных. Используется в случае если разрешено кэширование но для некоторых клиентов (например не имеющих привязки MAC→IP) это кэширование нужно отключить

Типы данных db2dhcp

Тип Код Пояснение
UINT1 1 Однобайтовое число
UINT4 2 Четырёхбайтовое число
HEX 3 Строка “сырых” hex-данных без разделителей. При интерпретации db2dhcp конвертирует строку в соответствующей ей набор двоичных данных без какой-либо дополнительной обработки. Удобно применять например для передачи маршрутной информации (опции 121 и 249)
STRING 4 Строковые данные. Записываются в ответ вообще без какой-либо дополнительной конвертации. Например имя хоста или сервера
IPADDRS 5 Список IP адресов. Может содержать один или более IP адресов разделённых запятой
BINARY 6 Двоичные данные. Копируются в ответ без какого-либо преобразования, разумеется для правильной обработки их клиентом – должны быть внесены в базу уже в подходящем двоичном представлении

Примеры настройки db2dhcp и БД

В качестве примера используем тестовую сеть: db2dhcp запущен на хосте 10.7.7.1; DHCP клиенты находятся в трёх физически разделённых сетях:

  1. Непосредственно в сегменте прослушиваемом DHCP сервером. IP адрес прослушиваемного интерфейса 10.7.7.1, сеть 10.7.7.0/24. Для определения правильной конфигурации клиента используется его Ethernet адрес.
  2. В удалённом сегменте сети 10.10.10.0/24. Обслуживается DHCP агентом пересылки (конкретно — ISC dhcrelay. Адрес агента 10.10.10.10. Для определения правильной конфигурации клиента используется его Ethernet адрес и IP адрес агента пересылки.
  3. В удалённом сегменте сети 10.70.70.0/24. Обслуживается DHCP агентом пересылки на свитче D-Link 3526. Адрес агегнта 10.7.7.100. Для определения правильной конфигурации DHCP клиентам используется IP адрес агента пересылки и опция 82.

Для обслуживания подобной сети используется три таблицы:

  1. dhcp_clients_by_ether — содержит конфигурационную информацию (IP адрес) для DHCP клиентов из первой и второй подсети. Имеется поле ether для идентификации клиента.
  2. dhcp_clients_by\ _relay — содержит информацию для DHCP клиентов из третьей подсети. Имеются поля relayid и relayport для идентификации клиентов.
  3. dhcp_subnets — содержит конфигурационную информацию общую для клиентов сети. Имеется поле subnet по которому выбирается конфигурация для конкретной сети. В случае с клиентами первой сети исользуется IP адрес интерфейса на котором был получен запрос; у клиентов второй и третьей сети используется IP адрес агента пересылки.

Создание БД

Подразумевается что пользователь dhcp уже создан. Команды выполняются от привелигерованного пользователя.

Для PostgreSQL:

CREATE DATABASE dhcp;
ALTER DATABASE dhcp OWNER TO dhcp;
\c dhcp;
CREATE TABLE dhcp_clients_by_ether (
code smallint,
type smallint,
ether character varying(12) DEFAULT NULL::character varying,
value character varying(32) DEFAULT NULL::character varying
);
ALTER TABLE dhcp_clients_by_ether OWNER TO dhcp;

CREATE TABLE dhcp_clients_by_relay (
code smallint,
type smallint,
relay_id character varying(32),
relay_port character varying(2),
value character varying(32)
);
ALTER TABLE dhcp_clients_by_relay OWNER TO dhcp;

CREATE TABLE dhcp_subnets (
code smallint,
type smallint,
subnet bigint,
value text
);
ALTER TABLE dhcp_subnets OWNER TO dhcp;

Для MySQL:

DELIMITER $$
CREATE PROCEDURE `insert2history`(IN `mac_sw_in` VARCHAR(20), IN `port_in` VARCHAR(20), IN `mac_client_in` VARCHAR(20))
DETERMINISTIC
BEGIN
DECLARE iip varchar(20);
DECLARE vgid int(11);
DECLARE vblocked int(11);
DECLARE ip_cur CURSOR FOR SELECT value,vg_id,blocked FROM dhcp_clients_by_relay WHERE mac_sw_in=relay_id AND port_in=relay_port;
OPEN ip_cur;
FETCH ip_cur INTO iip,vgid,vblocked;
CLOSE ip_cur;
IF vgid>0 THEN
INSERT into dhcp_hist(dt,mac_sw,port,mac_client,assigned_ip,vg_id,blocked) values (now(),mac_sw_in,port_in,mac_client_in,iip,vgid,vblocked);
ELSE SELECT «ok»;
END IF;
END$$
DELIMITER ;

CREATE TABLE IF NOT EXISTS `dhcp_clients_by_relay` (
`id` int(11) NOT NULL,
`code` smallint(6) NOT NULL,
`type` smallint(6) NOT NULL,
`relay_id` varchar(200) COLLATE utf8_bin NOT NULL,
`relay_port` varchar(200) COLLATE utf8_bin NOT NULL,
`vg_id` int(11) NOT NULL,
`segment_id` int(11) NOT NULL,
`value` varchar(200) COLLATE utf8_bin DEFAULT NULL,
`blocked` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

CREATE TABLE IF NOT EXISTS `dhcp_subnets` (
`id` int(11) NOT NULL,
`code` int(11) NOT NULL,
`type` int(11) NOT NULL,
`segment_id` int(11) NOT NULL,
`subnet` varchar(100) COLLATE utf8_bin DEFAULT NULL,
`value` varchar(200) COLLATE utf8_bin NOT NULL,
`record_id` varchar(200) COLLATE utf8_bin NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

CREATE TABLE IF NOT EXISTS `dhcp_hist` ( `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, `dt` datetime NOT NULL, `mac_sw` varchar(20) COLLATE utf8_bin NOT NULL, `port` varchar(20) COLLATE utf8_bin NOT NULL, `mac_client` varchar(20) COLLATE utf8_bin NOT NULL, `assigned_ip` varchar(20) COLLATE utf8_bin NOT NULL, `vg_id` int(11) NOT NULL ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

 

ALTER TABLE `dhcp_clients_by_relay` ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `vg_id` (`vg_id`);

ALTER TABLE `dhcp_subnets` ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `record_id` (`record_id`);

ALTER TABLE `dhcp_clients_by_relay` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=2;
ALTER TABLE `dhcp_hist` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=4;
ALTER TABLE `dhcp_subnets` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `dhcp_hist` ADD `blocked` INT NOT NULL AFTER `vg_id`;
ALTER TABLE `dhcp_clients_by_relay` ADD `blocked` INT NOT NULL AFTER `value`;
ALTER TABLE `dhcp_clients_by_relay` ADD `userlogin` VARCHAR(20) NOT NULL AFTER `blocked`;

ВНИМАНИЕ! Это тестовый пример для демонстрации возможностей сервера. В таблицах не создано ни одного индекса, а значит на больших наборах данных работа с такими таблицами будет затруднена. В реальности ваши таблицы могут быть совсем иными, потому построение индексов оставлено на ваше усмотрение.

Добавление данных в БД

Добавим минимально необходимые данные для клиентов из указанных выше подсетей:

— Конфигурируем IP адреса для каждого клиента в отдельности.

— IP адрес для клиента подключенного непосредственно к сети db2dhcp
INSERT INTO dhcp_clients_by_ether VALUES (1009, 5, ‘0003FF2C5290’, ‘10.7.7.70’);
— IP адрес клиента обслуживаемого ISC dhcrelay
INSERT INTO dhcp_clients_by_ether VALUES (1009, 5, ‘0003FF155290’, ‘10.10.10.11’);
— IP адрес клиента обслуживаемого агентом пересылки в свитче. Привязка к идентификатору
— свитча (001E5899156F) и порту (05) в который подключен клиент.
INSERT INTO dhcp_clients_by_relay VALUES (1009, 5, ‘001E5899156F’, ’05’, ‘10.70.70.70’);
— Ещё один IP адрес для той же подсети и того же свитча. Другой порт (07).
INSERT INTO dhcp_clients_by_relay VALUES (1009, 5, ‘001E5899156F’, ’07’, ‘10.70.70.71’);

— Настройка общих параметров подсетей.

— Маска подсети. Одинакова для всех.
INSERT INTO dhcp_subnets VALUES (1, 5, 168232704, ‘255.255.255.0’),
(1, 5, 168430090, ‘255.255.255.0’), (1, 5, 168232804, ‘255.255.255.0’);
— IP адрес шлюза для каждой подсети.
INSERT INTO dhcp_subnets VALUES (3, 5, 168232704, ‘10.7.7.1’),
(3, 5, 168430090, ‘10.10.10.10’), (3, 5, 168232804, ‘10.70.70.1’);
— DNS сервера, одинаковы для всех. В примере использован DNS сервер Google.
INSERT INTO dhcp_subnets VALUES (6, 5, 168232704, ‘8.8.8.8’),
(6, 5, 168430090, ‘8.8.8.8’), (6, 5, 168232804, ‘8.8.8.8’);
— Имя домена, одинаково для всех
INSERT INTO dhcp_subnets VALUES (15, 4, 168232704, ‘test.local’),
(15, 4, 168430090, ‘test.local’), (15, 4, 168232804, ‘test.local’);
— Время аренды IP адреса.
INSERT INTO dhcp_subnets VALUES (51, 2, 168232704, ‘7200’),
(51, 2, 168430090, ‘7200’), (51, 2, 168232804, ‘7200’);

Создание конфигурационного файла

Минимально необходимый набор данных внесён в БД. Теперь можно записать конфигурационный файл:

User=root
LogFile=/var/log/db2dhcp.log
DBType=MySQL
DBServerAddress=localhost
DBServerPort=3306
DBUserName=donpadlo
DBUserPassword=padlopavel
DBName=dhcp2db
DBClientsCount=1
DHCPCacheTTL=0
Var = CLI-GIADDR h:24:4
Var = CLI-ETHER-ADDR h:28:6 # Ethernet address
Var = OPT82-PORT o:82:(7=0x00)9:1|7:1
Var = OPT82-REMOTE-ID o:82:9:6
IPtoBind=172.17.117.2
#QueryDiscover=SELECT code, type, value FROM dhcp_subnets where (subnet = ‘$DEV-NETWORK-INT$’ and CONV(‘$CLI-GIADDR$’, 16, 10) = 0) or subnet = CONV(‘$CLI-GIADDR$’, 16, 10) UNION SELECT code, type, value FROM dhcp_clients_by_relay WHERE relay_id = ‘$OPT82-REMOTE-ID$’ AND relay_port = ‘$OPT82-PORT$’ ORDER BY CODE
QueryDiscover=SELECT code, type, value FROM dhcp_subnets where segment_id in (SELECT segment_id FROM dhcp_clients_by_relay WHERE relay_id = ‘$OPT82-REMOTE-ID$’ AND relay_port = ‘$OPT82-PORT$’) UNION SELECT code, type, value FROM dhcp_clients_by_relay WHERE relay_id = ‘$OPT82-REMOTE-ID$’ AND relay_port = ‘$OPT82-PORT$’ ORDER BY CODE
QueryHistory=CALL insert2history(‘$OPT82-REMOTE-ID$’,’$OPT82-PORT$’,’$CLI-ETHER-ADDR$’);

 

Определены 4 пользовательских переменные: CLI-GIADDR — IP адрес агента пересылки; CLI-ETHER-ADDR — аппаратный адрес DHCP клиента; OPT82-PORT — порт свитча на котором был получен запрос; OPT82-REMOTE-ID — идентификатор свитча получившего запрос (фактически — его MAC адрес).

В запросе QueryDiscover первый блок (до первого UNION) получает общую конфигурационную информацию для подсети клиента, второй блок пытается получить IP адрес клиента из таблицы с привязкой MAC->IP, третий блок — пытается получить IP адрес клиента из таблицы с привязкой к порту коммутатора. Если вы всё сделали правильно, то в результате обработки запроса от клиента внесённого в БД итоговый набор данных будет выглядеть например так:

code | type |     value
------+------+---------------
    1 |    5 | 255.255.255.0
    3 |    5 | 10.7.7.1
    6 |    5 | 8.8.8.8
   15 |    4 | test.local
   51 |    5 | 7200
 1009 |    5 | 10.7.7.70

Конфигурирование сервера закончено.

Запуск сервера

Проверим работоспособность сервера добавив при запуске ключи -Ds — это позволит читать лог сервера непосредственно из консоли и остановить сервер по Ctrl+C. (в лог ниже добавлены коментарии и удалены временные отметки и PID процесса)

sudo ./src/db2dhcp -Dsc db2dhcp.conf eth1
INFO: Program db2dhcp (DataBase to DHCP server) started.
Starting request handler threads (using DBM PostgreSQL) ...
Connected to PostgreSQL server.
Connected to PostgreSQL server.
All (2) request handlers started.
Starting DHCP listener thread on interface 'eth1', ether '00:02:44:75:77:E4'...
DHCP listener thread started. Waiting clients.
# С этого момента сервер готов обрабатывать DHCP запросы
# Первый запрос от клиента из собственной подсети db2dhcp. 
# В скобках указывается фактический код опции DHCP сообщения.
Got DHCPDISCOVER (1) message from client 00:03:FF:2C:52:90 on eth1/10.7.7.1
# Ответ DHCP клиенту. Через слеш указан IP адрес выданный клиенту.
Sending DHCPOFFER (2) to 00:03:FF:2C:52:90/10.7.7.70 via 10.7.7.1
Got DHCPREQUEST (3) message from client 00:03:FF:2C:52:90 on eth1/10.7.7.1
# Т.к. включено кэширование - при получении DHCPREQUEST сервер сперва проверяет
# наличие ответа в своём кэше. По скольку ответ найден - отвечает из кэша (запрос в БД не делается).
Found response for client 00:03:FF:2C:52:90 on 10.7.7.1 in DHCP cache.
Sending DHCPACK (5) to 00:03:FF:2C:52:90/10.7.7.70 via 10.7.7.1
Got DHCPINFORM (8) message from client 00:03:FF:2C:52:90 on eth1/10.7.7.1
# Обработка DHCPINFORM пока не реализована, потому запрос клиента игнорируется.
Ignore this message.
# Запрос от клиента подключенного к свитчу поддерживаюему опцию 82. 
# В скобках в конце строки указывается IP адрес агента пересылки.
Got DHCPDISCOVER (1) message from client 00:22:15:49:34:08 on eth1/10.7.7.1 (relay 10.7.7.100)
Sending DHCPOFFER (2) to 00:22:15:49:34:08/10.70.70.71 via 10.7.7.1 (relay 10.7.7.100)
Got DHCPREQUEST (3) message from client 00:22:15:49:34:08 on eth1/10.7.7.1 (relay 10.7.7.100)
Found response for client 00:1E:58:99:15:6F from relay 10.7.7.100 in DHCP cache.
Sending DHCPACK (5) to 00:22:15:49:34:08/10.70.70.71 via 10.7.7.1 (relay 10.7.7.100)
# Запрос от клиента чья подсеть обслуживается ISC dhcrelay.
Got DHCPDISCOVER (1) message from client 00:03:FF:15:52:90 on eth1/10.7.7.1 (relay 10.10.10.10)
Sending DHCPOFFER (2) to 00:03:FF:15:52:90/10.10.10.11 via 10.7.7.1 (relay 10.10.10.10)
Got DHCPREQUEST (3) message from client 00:03:FF:15:52:90 on eth1/10.7.7.1 (relay 10.10.10.10)
Found response for client 00:03:FF:14:52:90 from relay 10.10.10.10 in DHCP cache.
Sending DHCPACK (5) to 00:03:FF:15:52:90/10.10.10.11 via 10.7.7.1 (relay 10.10.10.10)

Отладка

Если сервер ведёт себя не так как ожидалось: не выдаёт адреса, выдаёт адреса не правильно и т.д. — скорее всего вы неправильно настроили запрос в базу, либо переменные участвующие в этом запросе (а может это просто бага в сервере 🙂 ). Что бы разобраться в чём дело — запустите сервер с опцией -d и смотрите в логи. Вывод достаточно подробен что бы можно было решить многие потенциальные проблемы. В частности в отладочном выводе вы можете увидеть исполняемые в БД запросы и попробовав выполнить их самостоятельно разобраться правильный-ли результат получается.

Кроме того рекомендую использовать tcpdump запущенный на интерфейсе прослушиваемом сервером с аргументами -nvvvs0 port 67.


Жизнь замечательных грибов