FreeBSD, dhcp ip unnumbered і всі всі всі...

Наша невелика компанія підключає абонентів до інтернету по технології vlan-per-user.
Так історично склалося і в цьому є як плюси, так і мінуси, але розмова зараз не про це.
Зазвичай на кожен vlan виділяється мережа сірих адрес, яка потім через NAT випускається у великий світ. Але іноді абоненти хочуть реальний білий адресу, і до недавнього часу їм видавалася /30 мережу. Що в реаліях сьогодення дуже марнотратно і абоненти з реальними адресами переводяться на підключення за технологією SuperVLAN (RFC 3069).



Усі адреси — і сірі, і білі видаються абонентам через DHCP. Сервіси DHCP, NAT і шейпер працюють на одному сервері під керуванням FreeBSD 9.3. Залізо у сервера цілком звичайне — Core i7, 8Gb RAM, Intel E1G42ET.

Завдання:

Видавати кожному абоненту адресу той же реальний адресу, що у нього був, але вже не з маскою /30, а з нової, загальної для всіх маскою /24 і шлюзом x.x.x.1. Забезпечити неможливість роботи абонента з чужим реальним адресою.
Абонентам з сірими адресами продовжувати видавати мережу на абонента.

Будуємо SuperVlan ala FreeBSD

Vlan9 — технологічний vlan, до нього нічого не підключено, туди буде йти трафік для ще не виданих абонентам реальних адрес.
Vlan10 — абонент з сірим адресою
Vlan11 і vlan12 два абонента з реальними адресами.

ifconfig vlan9 inet 1.1.1.1/24 
ifconfig vlan10 inet 192.168.10.1/24
ifconfig vlan11 inet 1.1.1.1/32
route add 1.1.1.11-iface vlan11 
ifconfig vlan12 inet 1.1.1.1/32
route add 1.1.1.12-iface vlan12


Ставимо на клієнті vlan11 руками адреса 1.1.1.11/24, gw 1.1.1.1 — інтернетик у клієнта працює.

(Bridge private sticky vlan (man bridge) в даному випадку використовувати не вийшло, про причини буде сказано нижче)

Тепер займемося найцікавішим

Роздача адрес через DHCP

А конкретно ISC-DHCPD ver 4.3

Адреси треба видавати на підставі інтерфейсу, з якого прийшов запит.
На превеликий жаль автори isc-dhcpd твердо впевнені, що вони точно знають краще, що треба сисадміну, ніж сам сисадмін. І не дають йому прострелити собі ногу, відрубуючи сисадміну вказівний палець по самий лікоть.

У підсумку можна вказати список інтерфейсів на яких хотілося б слухати запити від клієнтів, але якщо в конфіги dhcpd.conf не вказати subnet в яку потрапляє адресу на цьому інтерфейсі — то він буде проігнорований.
А якщо на кількох інтерфейсах вказані однакові адреси — запити DHCPREQ будуть оброблятися в одному subnet.
І не можна прописати в dhcpd.conf ні subnet, ні pool, ні host які були жорстко прив'язані до конткретній інтерфейсу.

Це мрія, насправді так не працює:
subnet 1.1.1.11 netmask 255.255.255.255 {
option routers 1.1.1.1;
range 1.1.1.11;
interface vlan11;
}


Ну щож… Є ж DHCP relay. Запускаємо релей на потрібних портах, який додасть Option 82 Agent Circuit ID, і віддасть запит dhcpd.

Спроба 1:

Так як на лупбек інтерфейсі dhcp категорично не бажає слухати — заводимо додатковий vlan5

rc.conf:
ifconfig_vlan5="inet 192.168.5.1/24"
dhcrelay_flags ="a"
dhcrelay_servers ="192.168.5.1"
dhcrelay_ifaces ="vlan11 vlan12"
dhcpd_ifaces ="vlan5 vlan10"


Запускаємо — не запускається…
dhcpd повідомляє, що не може bind socket.
Хоча прямо вказані різні інтерфейси для релея і для демона, вони обидва на всякий випадок хочуть забиндить fallback socket на адресу *:67
про що і повідомляють ось цим рядком при старті «Sending on Socket/fallback».

Природно тому, хто стартує другим система говорить: «Зайнято!»
Як і можна відключити цей fallback сокет я не зміг знайти.

Спроба 2:

Альтернативні програми — dhcprelya і dhcprelay.
Знову повідомляють, що не можуть зробити bind socket. Дивимося в код і розуміємо їх кардинальна відмінність від isc-творінь. ISC використовує device bpf і вихоплює свої пакети у системи практично на самому початку мережевого стека, ще до фаєрвола і інших надмірностей… Альтернативники використовують стандартний механізм socket, але так як у нас на декількох інтерфейсах однакові адреси, то на другий bind на адресу 1.1.1.1:67 система знову повідомляє: «Зайнято!»

Спроба 3:

А нехай dhcrelay слухає ВСІ інтерфейси і пересилає запити в dhcpd, який буде прибитий тільки до адресою 192.168.5.1
Пересобираем dhcpd, щоб він не використовував BPF, а підключався б через socket.
Для цього по швидкому підправляємо Makefile порту isc-dhcp43-server
Додаємо в нього:
CONFIGURE_ARGS+=--enable-use-sockets
Збираємо, запускаємо — запустилося, але не працює. Dhcpd в логи пише, що надав клієнтові адресу, але клієнт відповіді від dhcpd не отримує.

Дивимося tcpdump як намагається отримати адресу клієнт на vlan10, той, що з сірими адресами.
tcpdump-i vlan10 Запит від клієнта є — відповіді від сервера немає.
tcpdump-i vlan5 — тиша
tcpdump-i lo0 — а ось тут цікаво:
бачимо udp-пакет від релея до демона з адреси 192.168.5.1 на адресу 192.168.5.1
бачимо udp відповідь від демона до релею з адреси 192.168.5.1 на адресу 192.168.10.1

І тут стає зрозуміло, що так як dhcrelay слухає тільки через BPF — то пакети, які проходять через внутрішніх інтерфейс lo0 він просто не бачить, перезбирати dhcrelay на використання сокетів сенсу немає, тоді ми повернемося в ситуацію «Спроба 2»

Спроба 4:

Розносимо dhcrelay і dhcpd на ОКРЕМІ сервера.

Server dhcpd rc.conf:
ifconfig_vlan5="inet 192.168.5.1/24"


Server dhcrelay rc.conf:

ifconfig_vlan5="inet 192.168.5.2/24"
dhcrelay_flags ="a"
dhcrelay_servers ="192.168.5.1"


Не працює.
Ах так…
Згадує tcpdump з «спроби 3», прописуємо на dhcpd-сервері
route add 192.168.10.0/24 192.168.5.2
route add 1.1.1.0/24 192.168.5.2

Тепер працює. Адреси клієнту в vlan10 віддає.
Тепер додаємо в dhcpd інформацію про клієнтів з реальними адресами.
В інтернеті повно прикладів з класами, в яких вирізають підрядка з agent id і circuit-id, створюють підкласи, щоб можна було виділяти кілька адрес на порт підключення.

Мені достатньо однієї адреси, тому досить описати host ось з таким рядком всередині:
host-identifier option agent.circuit-id «vlan11»

Якщо зібрати SuperVLAN з допомогою bridge, то dhcrelay буде перенаправляти запити з усіх інтерфейсів об'єднаних в міст з agent.circuit-id=«bridge0», так що тільки інтерфейси з однаковими адресами і примусової маршрутизацією.

Разом:

Переваги рішення — клієнт автоматично отримає нові установки, при цьому у нього залишиться його старий адресу.
Недоліки — Знадобився другий сервер
ToDo: Перевірити як відпрацьовується ситуація зі зміною мак-адреси у клієнта. Видасть dhcpd той же реальний адресу host, якщо у нього ще не закінчився термін видачі на попередній мак.

І повертаючись до турботливим і безпосереднім авторам isc-dhcpd.
Щоб dhcpd перехоплював через bpf запити, які прийшли на інтерфейс треба прописати в конфіг dhcpd.conf секцію subnet в який входить адресу на цьому інтерфейсі.
У той же час немає необхідності вказувати, які саме subnet використовувати для видачі відповідей через relay — він шукає збіги у всіх.

Ось конфіг dhcpd.conf

option domain-name "example.org";
option domain-name-servers 8.8.8.8, 8.8.4.4;

default-lease-time 600;
max-lease-time 7200;
one-lease-per-client true;
stash-agent-options true;
update-conflict-detection false;

authoritative;
log-facility local7;

subnet 192.168.5.0 netmask 255.255.255.0 {
}

subnet 192.168.10.0 netmask 255.255.255.0 {
range 192.168.10.2 192.168.10.254;
option routers 192.168.10.1;
}

subnet 192.168.14.0 netmask 255.255.255.0 {
range 192.168.14.2 192.168.14.254;
option routers 192.168.14.1;
}

subnet 1.1.1.0 netmask 255.255.255.0 {
}

group realip1 {
option routers 1.1.1.1;
host client11 {
host-identifier option agent.circuit-id "vlan11";
fixed-address 1.1.1.11;
}
host client12 {
host-identifier option agent.circuit-id "vlan12";
fixed-address 1.1.1.12;
}
host client28 {
host-identifier option agent.circuit-id "vlan28";
fixed-address 1.1.1.13;
}
}


PS: Несподівані граблі і рішення:
якщо комусь знадобиться підняти isc dhcrelay на 8к інтерфейсів
треба поміняти
значення FD_SETSIZE у файлах
/usr/src/sys/sys/select.h
/usr/include/sys/select.h

з
#define FD_SETSIZE 1024U
на
#define FD_SETSIZE 16384U

Пересобираем isc-hcrelay або isc-dhcpd


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.