Сети для самых маленьких. Часть 11: DHCPv4
Во всех предыдущих частях у хоста уже был адрес 192.168.1.10, маска /24, шлюз 192.168.1.1 и адрес DNS-сервера. Откуда? В пятой части мы видели, что всё это можно задать руками, и в серверных сетях так и делают. Но ноутбук, который утром в офисе, днём в кафе, а вечером дома, руками не настроишь: в каждой сети у него должен быть свой адрес из её диапазона и её шлюз. Эту работу делает DHCP — Dynamic Host Configuration Protocol, RFC 2131. Он же причина, по которой домашняя сеть «просто работает»: роутер раздаёт адреса, а вы об этом не думаете.
Задача выглядит парадоксально: чтобы получить адрес, нужно отправить запрос, а отправить IP-пакет без адреса нельзя. Посмотрим, как это обходят.
Формат: наследство BOOTP
DHCP вырос из BOOTP — протокола 1985 года для загрузки бездисковых станций, которым нужно было узнать свой адрес и откуда взять образ системы. DHCP сохранил его формат сообщения целиком и добавил к нему опции и аренду адресов. Поэтому в tcpdump он до сих пор подписан BOOTP/DHCP.
Сообщение едет в UDP (часть 9): клиент шлёт с порта 68 на порт 67, сервер отвечает с 67 на 68. Фиксированная часть — 236 байт:
байты
┌───────┬───────┬───────┬───────┐
│ op │ htype │ hlen │ hops │ op: 1 запрос, 2 ответ; htype 1, hlen 6 — Ethernet; hops — счётчик relay
├───────┴───────┴───────┴───────┤
│ xid (4) │ идентификатор обмена, случайный, выбирает клиент
├───────────────┬───────────────┤
│ secs (2) │ flags (2) │ secs — сколько клиент уже пытается; flags: бит Broadcast
├───────────────┴───────────────┤
│ ciaddr (4) │ адрес клиента, если он у него уже есть (продление)
├───────────────────────────────┤
│ yiaddr (4) │ «your» — адрес, который сервер выдаёт
├───────────────────────────────┤
│ siaddr (4) │ адрес сервера для следующего шага загрузки (TFTP)
├───────────────────────────────┤
│ giaddr (4) │ адрес relay-агента, если запрос прошёл через роутер
├───────────────────────────────┤
│ chaddr (16) │ MAC клиента (6 байт, остальное нули)
├───────────────────────────────┤
│ sname (64) │ имя сервера, почти всегда пусто
├───────────────────────────────┤
│ file (128) │ имя файла загрузки, почти всегда пусто
├───────────────────────────────┤
│ magic cookie 63 82 53 63 │ «дальше опции DHCP»
├───────────────────────────────┤
│ опции: код, длина, значение … │ до 255 — конец
└───────────────────────────────┘
Всё интересное — в опциях, каждая в формате «код (1 байт), длина (1 байт), значение». Главная — опция 53, DHCP Message Type, по которой сообщение и называется:
| 53 = | Тип | Кто шлёт |
|---|---|---|
| 1 | DISCOVER | клиент: «есть тут DHCP-сервер?» |
| 2 | OFFER | сервер: «есть, предлагаю адрес» |
| 3 | REQUEST | клиент: «беру этот», а также продление |
| 4 | DECLINE | клиент: «адрес уже кем-то занят» |
| 5 | ACK | сервер: «подтверждаю, вот параметры» |
| 6 | NAK | сервер: «не могу, начни сначала» |
| 7 | RELEASE | клиент: «адрес больше не нужен» |
| 8 | INFORM | клиент: «адрес есть, нужны только параметры» |
DORA: четыре сообщения
Первое получение адреса — четыре сообщения, по первым буквам которых обмен называют DORA:
клиент (без адреса) сервер 192.168.1.1
│ │
├── DISCOVER ──────────────── broadcast ─────────────────────────►│
│ от 0.0.0.0:68 на 255.255.255.255:67 │
│ chaddr = мой MAC, xid = 0x1a2b3c4d │
│ опции: 53=1, 55 (что мне нужно), 61 (client id), 12 (hostname)│
│ │
│◄── OFFER ────────────────── broadcast или на MAC ───────────────┤
│ yiaddr = 192.168.1.10, xid тот же │
│ опции: 53=2, 54 (server id 192.168.1.1), 51 (аренда), │
│ 1 (маска), 3 (шлюз), 6 (DNS) │
│ │
├── REQUEST ──────────────── broadcast ──────────────────────────►│ (слышат все серверы)
│ опции: 53=3, 50 (хочу 192.168.1.10), 54 (у 192.168.1.1) │
│ │
│◄── ACK ─────────────────────────────────────────────────────────┤
│ yiaddr = 192.168.1.10, те же опции │
│ │
│ ARP probe на 192.168.1.10 (часть 7): │
│ свободен → адрес на интерфейс │
DISCOVER. У клиента нет адреса, поэтому в заголовке IP отправитель 0.0.0.0 — «адреса нет» из пятой части, — а получатель 255.255.255.255 — ограниченное широковещание, всем в этом сегменте. В кадре Ethernet получатель ff:ff:ff:ff:ff:ff. Пакет получат все, но ответит только DHCP-сервер. Клиент кладёт свой MAC в chaddr, случайный xid, по которому отличит ответы на свой запрос от чужих, и опцию 55 — список параметров, которые ему нужны: маска, шлюз, DNS, доменное имя. Если у клиента был адрес в прошлый раз, он попросит его опцией 50 — серверы стараются выдавать знакомым клиентам прежние адреса.
OFFER. Сервер выбирает свободный адрес из своего пула, временно резервирует его и отвечает. Как доставить ответ хосту, у которого нет адреса? Сервер пишет предлагаемый адрес в yiaddr, а сам пакет шлёт либо на 255.255.255.255 (если клиент выставил флаг Broadcast — так делают стеки, которые не примут unicast на ещё не назначенный адрес), либо unicast-кадром прямо на MAC из chaddr с IP-адресом назначения yiaddr, минуя ARP: спрашивать «у кого 192.168.1.10» бессмысленно, у него ещё нет этого адреса. Linux-клиенты принимают второй вариант и экономят широковещание.
REQUEST. Клиент выбирает предложение (если серверов несколько, обычно первое пришедшее) и просит именно его: опция 50 с адресом и опция 54 с адресом сервера. И снова широковещательно — не потому что не может иначе, а чтобы остальные серверы увидели, что их предложения отвергнуты, и освободили зарезервированные адреса.
ACK. Сервер записывает аренду в свою базу и подтверждает с полным набором опций. Только теперь клиент назначает адрес интерфейсу, маршрут по умолчанию через шлюз из опции 3 и DNS из опции 6. Перед этим аккуратный клиент проверяет адрес ARP-probe и, если кто-то откликнулся, шлёт серверу DECLINE и начинает заново; сервер помечает адрес как плохой.
В tcpdump это выглядит так (-v раскрывает опции):
$ sudo tcpdump -i eth0 -n -v 'udp port 67 or udp port 68'
IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 3c:52:82:1a:2b:3c, length 300, xid 0x1a2b3c4d, Flags [none]
Client-Ethernet-Address 3c:52:82:1a:2b:3c
Vendor-rfc1048 Extensions
Magic Cookie 0x63825363
DHCP-Message (53), length 1: Discover
Client-ID (61), length 7: ether 3c:52:82:1a:2b:3c
Hostname (12), length 6: "laptop"
Parameter-Request (55), length 13: Subnet-Mask, BR, Default-Gateway, Domain-Name, Domain-Name-Server, ...
IP 192.168.1.1.67 > 192.168.1.10.68: BOOTP/DHCP, Reply, length 300, xid 0x1a2b3c4d, Flags [none]
Your-IP 192.168.1.10
Client-Ethernet-Address 3c:52:82:1a:2b:3c
Vendor-rfc1048 Extensions
Magic Cookie 0x63825363
DHCP-Message (53), length 1: Offer
Server-ID (54), length 4: 192.168.1.1
Lease-Time (51), length 4: 86400
Subnet-Mask (1), length 4: 255.255.255.0
Default-Gateway (3), length 4: 192.168.1.1
Domain-Name-Server (6), length 4: 192.168.1.1
IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 3c:52:82:1a:2b:3c, length 300, xid 0x1a2b3c4d
DHCP-Message (53), length 1: Request
Requested-IP (50), length 4: 192.168.1.10
Server-ID (54), length 4: 192.168.1.1
IP 192.168.1.1.67 > 192.168.1.10.68: BOOTP/DHCP, Reply, length 300, xid 0x1a2b3c4d
DHCP-Message (53), length 1: ACK
...
Один xid на все четыре сообщения, 0.0.0.0 в отправителе у клиента, и OFFER, ушедший на 192.168.1.10, которого ещё ни у кого нет.
Опции
Несколько десятков кодов, из которых в жизни встречается дюжина:
| Код | Имя | Что |
|---|---|---|
| 1 | Subnet Mask | маска |
| 3 | Router | шлюз по умолчанию; может быть список |
| 6 | Domain Name Server | адреса DNS, по порядку предпочтения |
| 12 | Host Name | клиент сообщает своё имя; сервер может регистрировать его в DNS |
| 15 | Domain Name | домен, который клиент дописывает к коротким именам |
| 28 | Broadcast Address | широковещательный адрес сети |
| 42 | NTP Servers | серверы времени |
| 43 | Vendor Specific | что угодно, договорённость производителя: так точки доступа узнают адрес контроллера, а IP-телефоны — адрес АТС |
| 50 | Requested IP Address | клиент просит конкретный адрес |
| 51 | Lease Time | срок аренды в секундах |
| 54 | Server Identifier | адрес сервера; по нему клиент выбирает, кому отвечать |
| 55 | Parameter Request List | что клиенту нужно |
| 58, 59 | Renewal (T1), Rebinding (T2) | когда продлевать, по умолчанию 50 % и 87,5 % аренды |
| 61 | Client Identifier | чем клиент себя считает: обычно MAC, но может быть произвольная строка; по нему сервер ведёт резервирования |
| 66, 67 | TFTP Server, Bootfile | для сетевой загрузки: откуда и какой файл взять по TFTP — простейшему протоколу передачи файлов поверх UDP; так работает PXE, загрузка машины по сети прошивкой сетевой карты |
| 82 | Relay Agent Information | вставляет relay или коммутатор: с какого порта пришёл запрос; по ней сервер может выдавать адреса по розетке |
| 119 | Domain Search | список доменов для поиска коротких имён |
| 121 | Classless Static Routes | маршруты, которые клиенту надо добавить; если клиент её запросил и получил, опцию 3 он обязан проигнорировать, и об этом регулярно забывают |
| 255 | End | конец опций |
Клиент получает только то, что запросил в опции 55, и то, что сервер для него настроил. Если в сети «не работает DNS после получения адреса» — первым делом смотреть, есть ли опция 6 в ACK.
Аренда и её продление
Адрес выдаётся на срок — аренду (lease), опция 51. Это и отличает DHCP от BOOTP: адрес, которым перестали пользоваться, возвращается в пул. Срок задаёт администратор: сутки для офиса, час для гостевой сети с текучкой, неделя там, где хочется стабильности.
Клиент не ждёт истечения:
получен ACK T1 = 50 % T2 = 87,5 % 100 %
├────────────────────┼───────────────────────┼─────────────────────┤
│ BOUND │ RENEWING │ REBINDING │ адрес снят,
│ просто работает │ REQUEST unicast │ REQUEST broadcast │ DISCOVER заново
│ │ своему серверу │ любому серверу │
- В T1, на половине срока, клиент шлёт REQUEST unicast прямо серверу, выдавшему адрес (теперь у клиента есть адрес, и широковещание не нужно), с
ciaddr= свой адрес. Сервер отвечает ACK с новым сроком, и отсчёт начинается заново. Если сервер молчит, клиент повторяет через растущие интервалы. - В T2, на 87,5 %, клиент решает, что его сервер умер, и шлёт REQUEST широковещательно: продлить может любой сервер, знающий этот адрес.
- В 100 % адрес снимается с интерфейса, и всё начинается с DISCOVER. Соединения рвутся. При живом сервере до этого не доходит никогда.
При включении машина с сохранённой арендой не делает DISCOVER, а сразу шлёт REQUEST с опцией 50 — «у меня был 192.168.1.10, он ещё мой?». Если машина в той же сети — ACK, и адрес работает через полсекунды. Если её перенесли в другую сеть, сервер там ответит NAK: адрес не из моего пула, начинай сначала. Так ноутбук, приехавший из офиса домой, не держится за офисный адрес.
RELEASE клиент шлёт при аккуратном отключении (dhclient -r, ipconfig /release), чтобы сервер сразу освободил адрес. Обязательным это не является, и при выдернутом кабеле аренда живёт до конца срока. Поэтому в маленьком пуле с короткой текучкой адреса «кончаются», хотя машин в сети мало: пул занят арендами ушедших.
INFORM — для машин со статическим адресом, которым нужны только опции: DNS, домен, прокси. Сервер отвечает ACK без yiaddr и без аренды.
Через роутер: relay
Широковещание не проходит через роутеры (адрес 255.255.255.255 из таблицы в пятой части). Значит, DHCP-сервер должен стоять в каждом сегменте? В 1985 году так и было; в офисе с пятьюдесятью VLAN это невозможно. Поэтому роутер (или L3-коммутатор) умеет быть relay-агентом:
клиент 10.0.5.x роутер сервер 192.168.1.1
│ 10.0.5.1 │ 192.168.1.254 │
├── DISCOVER broadcast ───────►│ │
│ ├── DISCOVER unicast ─────────────────►│ giaddr = 10.0.5.1, hops = 1
│ │ │ сервер: giaddr в сети 10.0.5.0/24 →
│ │ │ берём пул для 10.0.5.0/24
│ │◄── OFFER unicast на 10.0.5.1 ────────┤ yiaddr = 10.0.5.20
│◄── OFFER ────────────────────┤ │
- Relay на интерфейсе
10.0.5.1ловит широковещательный DISCOVER, вписывает свой адрес в этом сегменте вgiaddr, увеличиваетhopsи отправляет пакет unicast серверу, адрес которого задан в его настройках (ip helper-addressв терминах Cisco). - Сервер видит
giaddr ≠ 0, понимает, что клиент не в его сегменте, и поgiaddrвыбирает пул:10.0.5.1принадлежит сети10.0.5.0/24, значит, адрес даём оттуда. Это главное: у сервера может быть сколько угодно пулов для разных сетей, и выбирает он их по адресу relay. - Ответ сервер шлёт unicast на
giaddr, relay принимает его на порту 67 и передаёт клиенту в своём сегменте: широковещательно или на MAC изchaddr.
Relay может добавить опцию 82 с номером порта коммутатора, с которого пришёл запрос, и тогда сервер способен выдавать конкретный адрес конкретной розетке, кто бы в неё ни воткнулся. Так делают провайдеры и крупные сети.
Резервирования, пулы, конфликты
На сервере адресное пространство сегмента делят на:
- пул (range, scope) динамических адресов, например
192.168.1.100–192.168.1.200; - резервирования: «MAC такой-то всегда получает
192.168.1.50» — для принтеров и серверов, которым нужен постоянный адрес, но хочется управлять им централизованно; - исключения: адреса, которые сервер не трогает, потому что они настроены руками.
Конфигурация в самом простом сервере, dnsmasq, умещается в три строки:
dhcp-range=192.168.1.100,192.168.1.200,24h
dhcp-option=option:router,192.168.1.1
dhcp-host=00:11:22:33:44:55,192.168.1.50,printer
Перед тем как предложить адрес, сервер обычно пингует его: если кто-то отвечает, адрес помечается занятым, и предлагается следующий. Это защищает от машин со статическими адресами внутри пула, но не вполне: выключенная сейчас машина не ответит.
Два сервера
Если в сегменте два DHCP-сервера, на DISCOVER ответят оба, а клиент возьмёт первый OFFER. С двумя настроенными серверами, которые делят пул или синхронизируют аренды (failover), это и есть отказоустойчивость. Со случайным вторым — принесённым из дома роутером, воткнутым LAN-портом в офисную розетку, — это классическая авария: часть машин получает адрес 192.168.0.x и шлюз, ведущий в никуда, причём какая именно часть — зависит от того, чей OFFER пришёл раньше. Половина офиса работает, половина нет, и перезагрузка меняет состав половин.
Защита — на коммутаторе: DHCP snooping. Коммутатор пропускает OFFER и ACK только с портов, помеченных доверенными (туда, где настоящий сервер или аплинк к нему), а с остальных отбрасывает. Заодно он запоминает, какой адрес на каком порту выдан, и эта таблица кормит Dynamic ARP Inspection из седьмой части. В протоколе самом никакой защиты нет: клиент верит любому, кто ответил.
Когда сервера нет
Клиент повторяет DISCOVER с растущим интервалом, и дальше поведение зависит от системы. Windows и macOS через минуту назначают себе адрес из 169.254.0.0/16 (APIPA из пятой части): связь с соседями, сделавшими то же самое, есть, с интернетом — нет. Увидели 169.254 — DHCP не ответил. Linux с NetworkManager делает то же, а dhclient в одиночку оставляет интерфейс без адреса и продолжает спрашивать. Клиент при этом продолжает слушать и подхватывает адрес, как только сервер появится.
Смотрим руками
Запросить адрес заново и посмотреть обмен (в отдельном окне — tcpdump из раздела про DORA):
$ sudo dhclient -r eth0 && sudo dhclient -v eth0
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 3
DHCPOFFER of 192.168.1.10 from 192.168.1.1
DHCPREQUEST for 192.168.1.10 on eth0 to 255.255.255.255 port 67
DHCPACK of 192.168.1.10 from 192.168.1.1
bound to 192.168.1.10 -- renewal in 39212 seconds.
«renewal in 39212» — T1, примерно половина суточной аренды. Полученные параметры:
$ cat /var/lib/dhcp/dhclient.leases # dhclient
$ nmcli device show eth0 | grep DHCP4 # NetworkManager
$ networkctl status eth0 # systemd-networkd
На Windows: ipconfig /all показывает сервер, время получения и истечения аренды; ipconfig /release и ipconfig /renew — RELEASE и новый DISCOVER.
Найти чужой DHCP-сервер: sudo nmap --script broadcast-dhcp-discover шлёт DISCOVER и перечисляет всех, кто ответил, с их адресами и предложениями. Или просто tcpdump на udp port 67 и смотреть, с каких адресов приходят OFFER.
Что запомнить
- DHCP — UDP 67/68, формат BOOTP плюс опции. Клиент без адреса шлёт с
0.0.0.0на255.255.255.255, сервер отвечает на MAC или широковещательно;xidсвязывает обмен. - DORA: DISCOVER, OFFER, REQUEST, ACK. REQUEST широковещательный, чтобы остальные серверы отозвали предложения. После ACK — ARP-probe, при конфликте DECLINE.
- Всё полезное в опциях: 1 маска, 3 шлюз, 6 DNS, 51 аренда, 54 сервер, 121 маршруты (перебивает 3).
- Аренда продлевается в T1 unicast своему серверу, в T2 широковещательно любому; при включении — REQUEST старого адреса, NAK в чужой сети.
- Через роутер — relay:
giaddrговорит серверу, из какого пула выдавать. - Два сервера в сегменте — половина сети без связи; защита — DHCP snooping.
169.254.x.x— сервер не ответил.
Дальше — Часть 12: DNS: как имя превращается в адрес, где лежит дерево имён и кто за какую его часть отвечает, полный путь запроса от вашей машины через корневые серверы до ответа, формат сообщения, типы записей, кеширование и TTL, и что такое dig +trace.
Оставить комментарий могут только зарегистрированные пользователи.
Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.