Pull to refresh

Трудности администрирования гостевых хотспотов (часть 1)

Reading time4 min
Views6.6K
В этой небольшой статье я расскажу как снять с себя головную боль по администрированию больших подсетей завязанных на раздачу интернета приходящим пользователям.
Основные инструменты:
  • Голова
  • Еще раз голова
  • Руки
  • FreeBSD
  • PF
  • isc-dhcpd
  • memcached
  • perl

Желающие узнать про всё написанное под кат, остальным хорошего просмотра остальных топиков.
Ссылка на вторую часть

Основные проблемы больших dhcp серверов


Все кто имел дело с isc-dhcpd знает насколько проблематично рулить доступом в пределах нескольких ip сетей. Основная проблема это выделение статических записей для определённых хостов, прописывание правил в firewall для обеспечения доступа и прочая сисадминская соответствующая рутина. Данное решение было найдено, написано и оттестировано буквально пару месяцев назад. Итак…

Существующие методы управления


ISC-DHCPD сервер практически не имеет встроенных методов управления своим состоянием. Единственное исключение это появившийся придаток omshell, который обеспечивает доступ во внутреннюю базу isc-dhcpd и позволяет получать и менять значения внутренних полей без перезапуска сервера. ЕМНИП конфигурационный файл dhcpd.conf остаётся неизменным. Изменения автоматически заносятся в dhcpd.leases. Для контроля за появлением, выдачей адресов и их состоянием многие из Вас уже пробовали методы парсинга системного лога isc-dhcpd и возможно даже получали результат, но не то чтобы были довольны на все 100%. Но как показывает опыт многие из Вас пропустили в мануалах возможность использования dhcpd events.

Что такое dhcpd events ?


При каждой операции(событии) выдачи, освобождения и удаления адреса срабатывает внутренний обработчик который можно переназначить. Ниже небольшая выдержка из man dhcpd.conf

There are three kinds of events that can happen regarding a lease, and
it is possible to declare statements that occur when any of these
events happen. These events are the commit event, when the server has
made a commitment of a certain lease to a client, the release event,
when the client has released the server from its commitment, and the
expiry event, when the commitment expires.

To declare a set of statements to execute when an event happens, you
must use the on statement, followed by the name of the event, followed
by a series of statements to execute when the event happens, enclosed
in braces. Events are used to implement DNS updates, so you should
not define your own event handlers if you are using the built-in DNS
update mechanism.

The built-in version of the DNS update mechanism is in a text string
towards the top of server/dhcpd.c. If you want to use events for
things other than DNS updates, and you also want DNS updates, you will
have to start out by copying this code into your dhcpd.conf file and
modifying it.

Что же значат сии загадочные письмена?
А значат они следующее. isc-dhcpd используя свои внутренние события может обновлять DNS записи в BIND и если Вы хотите переназначить обработчики событий, то не забудьте добавить код обновления DNS записей в свой обработчик.

Запутались? Распутываю!

Для любого пула адресов внутри конфигурационного файла dhcpd.conf можно задать внешний обработчик событий. В любом месте конфига выглядит он следующим образом:
on commit {
}
on release {
}
on expiry {
}

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

Практическое применение


Ниже вы можете увидеть мой текущий конфиг вызова обработчиков. Вдаваться в подробности и разбирать внутренний формат данных используемых во внутренней базе dhcpd не будем, остановимся только на нескольких моментах которые могут пригодиться.
  • clientIP — текущий адрес с которым происходят действия
  • clientMAC — текущий mac адрес для clientIP
  • host-name — внутреннее имя хоста(может получаться при выдаче адреса от клиента или задаваться автоматически как в нашем случае
  • ddns-hostname — имя хоста которое будет отправляться при обновлении в bind

В нашем случае записи host-name и ddns-hostname должны генерироваться автоматически если имя хоста пустое(Примечание: имя хоста в UTF-8 или в любой другой кодировке не считается пустым)
on commit {
set clientIP = binary-to-ascii(10, 8, ".", leased-address);
set clientMAC = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
if option host-name = "" {
option host-name = concat( "wifi-" , binary-to-ascii( 10, 8, "", substring( reverse( 1, leased-address), 0, 1)));
ddns-hostname = concat( "wifi-" , binary-to-ascii( 10, 8, "", substring( reverse( 1, leased-address), 0, 1)));
}
set clientHostName = option host-name;
execute("/usr/local/etc/dhcp_helper/publish-ip-mac.pl", "commit", clientIP, clientMAC);
execute("/usr/local/etc/dhcp_helper/publish-ip-mac.pl", "hostname", clientIP, clientHostName);
}

on release {
set clientIP = binary-to-ascii(10, 8, ".", leased-address);
set clientMAC = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
execute("/usr/local/etc/dhcp_helper/publish-ip-mac.pl", "release", clientIP, clientMAC);
}

on expiry {
set clientIP = binary-to-ascii(10, 8, ".", leased-address);
if(exists agent.remote-id) {
set clientMAC = binary-to-ascii(16, 8, ":", substring(option agent.remote-id, 2, 6));
execute("/usr/local/etc/dhcp_helper/publish-ip-mac.pl", "expiry", clientIP, clientMAC);
} else {
execute("/usr/local/etc/dhcp_helper/publish-ip-mac.pl", "expiry", clientIP);
}

Собственно как показывает кусок вышеприведённого кода, на каждый тип операции вызывается скрипт с параметрами. Параметры передаются в ARGV. Скриптом может быть всё что угодно(shell script, php, python и любая другая программа).
Ну с release и expiry вроде всё понятно. Почему в commit используется 2 вызова скрипта отдельно для адреса и для имени хоста? Потому что если имя хоста будет не в ANSI, то обработчик тупо падает и скрипт не вызывается.

Ну и на сладкое код скрипта
#!/usr/bin/perl
use strict;
use warnings;

my $type = shift;
my $ip = shift;
my $mac = shift;

# Fork so we can return control
# to the dhcp server ASAP.
my $pid = fork();

# If we're the child, do whatever we need to do
if($pid == 0) {
open OUT,">>/var/tmp/publish.tmp";
$mac = join(":",map { sprintf("%02s",$_); } split(":",$mac));
print OUT "$type $ip $mac\n";
close OUT;
}


Для всех желающих узнать как связать pf, dhcpd и web морду для управления всем этим хозяйством, продолжение будет во второй части.
А пока Ваш мозг осознаёт написанное тут, методы применения и простоту решения, можете задавать вопросы.

© Aborche 2011
Tags:
Hubs:
Total votes 35: ↑28 and ↓7+21
Comments7

Articles