26 октября 2019

Как я DHCP-сервер для PXE настраивал и тестировал.



На домашних роутерах в качестве сервера DNS и DHCP обычно используется dnsmasq. В прошивках типа TomatoUSB есть возможность задать для него дополнительные опции, в том числе и для загрузки по сети с помощью PXE. Вот с этим у меня и возникли заморочки, потому что для машин с UEFI надо отдавать не обычный pxe-загрузчик, а efi-приложение.

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

Теперь практика.
Из всех опций для идентификации архитектуры клиента нужна опция №93. Значения её полей можно найти в RFC4578.

Type   Architecture Name
----   -----------------
  0    Intel x86PC
  1    NEC/PC98
  2    EFI Itanium
  3    DEC Alpha
  4    Arc x86
  5    Intel Lean Client
  6    EFI IA32
  7    EFI BC
  8    EFI Xscale
  9    EFI x86-64

dnsmasq надо настроить так, чтобы для клиентов с 64-битным UEFI отдавался загрузчик в виде EFI-приложения, а для клиентов с BIOS отдавался обычный PXE-загрузчик. Если для isc-dhcp-server подобное делается с помощью расписывания условий, то для dnsmasq это делается с помощью установки значений тегов. Вот такой кусок конфига я задал в настройках роутера:

dhcp-match=set:efi-x86_64,option:client-arch,7
dhcp-match=set:efi-x86_64,option:client-arch,9
dhcp-match=set:bios,option:client-arch,0
dhcp-boot=tag:efi-x86_64,syslinux.efi,itxserver,192.168.1.30
dhcp-boot=tag:bios,pxelinux.0,itxserver,192.168.1.30

В строках 1-3 проверяется значение полученной от клиента опции "client-arch" (№93). Если оно равно 7 или 9, то устанавливается тег "efi-x86_64", если значение равно 0, то устанавливается тег "bios".
В 4 и 5 строке по соответствующим тегам отдаётся нужное имя файла и адрес tftp-сервера. В данном случае сервер один, но для UEFI-клиентов отдаётся имя файла "syslinux.efi", а для BIOS-клиентов - "pxelinux.0".

Второй проблемой стало тестирование настроек DHCP-сервера без привлечения реальных и виртуальных машин. Т.е. нужна была программа, которая может сформировать нужный запрос для DHCP-сервера и расшифровать полученный ответ. И такую программу я нашёл.  Она так и называется dhcptest. Она написана на языке D, установка компилятора которого - тот ещё квест, но есть готовые бинарники для Windows. Для правильной работы в Linux программе нужны права root.

Если программу запустить с параметром "--query" или набрать команду "d" в интерактивном режиме, то она сформирует запрос к DHCP-серверу и расшифрует полученный ответ, но в нём не будет ничего про файл-загрузчик и tftp-сервер:

op=BOOTREPLY chaddr=7F:DC:9E:3F:52:87 hops=0 xid=A6A4422B secs=0 flags=8000
ciaddr=0.0.0.0 yiaddr=192.168.1.26 siaddr=192.168.1.1 giaddr=0.0.0.0 sname= file=

Чтобы сделать правильный запрос, надо к списку добавить опцию №93 (client-arch) типа u16 со значением 0, 7 или 9. Делается это добавлением ключа "--option "93[u16]=7"". В данном случае опция имеет значение 7 (EFI BC). Теперь в ответе есть нужные поля:

op=BOOTREPLY chaddr=46:67:94:75:D6:FC hops=0 xid=43CA0C4E secs=0 flags=8000
ciaddr=0.0.0.0 yiaddr=192.168.1.6 siaddr=192.168.1.30 giaddr=0.0.0.0 sname=itxserver file=syslinux.efi

Ещё одной полезной программой при настройке является dhcpdump. Она тоже требует права root для работы. Для запуска требуется указать сетевой интерфейс для прослушивания. Из-за того, что по протоколу DHCP запросы и ответы посылаются широковещательными пакетами, то их можно подслушать на других узлах той же подсети.

С помощью этой программы я подсматривал за процессом получения настроек при загрузке по сети виртуальной машины.