ARP — как IP-адрес превращается в MAC-адрес
Представьте: вы открываете в браузере https://github.com. Где-то в недрах системы он превратится в IP-пакет, который надо отправить на адрес условно 140.82.121.4. Но Ethernet-кадру нужен MAC-адрес получателя, а не IP. Откуда система берёт MAC, имея только IP?
Ответ: спрашивает у сети. Этот процесс называется ARP — Address Resolution Protocol. В этом уроке разберём, как именно работает ARP, что такое ARP-таблица, gratuitous ARP и почему ARP — это одна из самых старых, самых тупых и одновременно самых опасных частей сетевого стека.
Знать ARP надо потому, что 95% «странных проблем» в локальной сети — это ARP. Конфликты IP, машина «то видит, то не видит сосeда», задержки в 1-2 секунды на первый пакет к незнакомому хосту — всё ARP.
Pipes — anonymous, named FIFO и магия shellЗачем вообще нужен ARP
Если посмотреть на стек ещё раз: у нас есть IP-пакет с dst = 140.82.121.4. Этот пакет надо положить внутрь Ethernet-кадра. У кадра свои поля — dst MAC и src MAC. Какой MAC писать в dst MAC?
Тонкость: ARP нужен только для устройств в той же локальной сети (в том же broadcast domain). Если получатель за роутером (как github.com), мы используем ARP для MAC-адреса роутера — это так называемый default gateway. Дальше уже работа роутера.
То есть для пакета на github.com последовательность такая:
- ОС видит: dst IP =
140.82.121.4, это не в моей подсети. - Смотрит routing table: для этого IP надо отправить через шлюз
192.168.1.1. - ARP-запрос: «у кого IP
192.168.1.1?» - Роутер отвечает: «у меня, MAC
00:1c:42:00:00:18». - ОС кладёт IP-пакет (с IP-адресатом github.com) в Ethernet-кадр с MAC-адресатом роутера.
- Свитч кладёт кадр в порт, к которому подключен роутер.
- Роутер видит «не мой IP», смотрит свою routing table, шлёт пакет дальше.
ARP участвует только на шаге 3-4 — между ОС и устройствами внутри её локальной сети.
Структура ARP-запроса и ответа
ARP — это отдельный link-layer протокол. Поверх Ethernet, но не поверх IP. EtherType = 0x0806.
ARP-кадр выглядит так:
Что происходит дальше:
- Все устройства в сегменте получают этот broadcast-кадр.
- Каждое смотрит на
Target IPв запросе. - Если это не их IP — молча игнорируют (но фиксируют в своих логах sender’а).
- Если это их IP — отправляют ARP reply.
ARP reply — такой же кадр, но:
dst MAC= MAC запрашивающего (узнали изSender MAC).Operation= 2.Sender= (мой IP, мой MAC) — вот собственно ответ.Target= (IP того, кто спрашивал, его MAC).
Запрашивающий получает reply, запоминает связку IP -> MAC в свою ARP-таблицу, и теперь может слать обычные IP-пакеты.
ARP-таблица — что и как кэшируется
Чтобы не делать ARP-запрос на каждый IP-пакет (это было бы катастрофой — broadcast на каждый byte!), ОС кэширует пары IP -> MAC в ARP-таблице (она же neighbor table).
Посмотреть таблицу:
# Linux (старый способ):
arp -n
# 192.168.1.1 ether 00:1c:42:00:00:18 C eth0
# 192.168.1.42 ether a4:83:e7:1a:bb:cc C eth0
# Linux (современный, IPv4+IPv6):
ip neigh show
# 192.168.1.1 dev eth0 lladdr 00:1c:42:00:00:18 STALE
# 192.168.1.42 dev eth0 lladdr a4:83:e7:1a:bb:cc REACHABLE
# fe80::a683:e7ff:fe1a:bbcc dev eth0 lladdr a4:83:e7:1a:bb:cc REACHABLE
# macOS:
arp -a
# ? (192.168.1.1) at 0:1c:42:0:0:18 on en0 ifscope [ethernet]
# ? (192.168.1.42) at a4:83:e7:1a:bb:cc on en0 ifscope [ethernet]
Записи имеют состояния (state machine на Linux):
Дефолтные таймауты в Linux (/proc/sys/net/ipv4/neigh/default/):
gc_stale_time— 60 секунд. Через сколько REACHABLE становится STALE.base_reachable_time— 30 секунд (по умолчанию). Случайно варьируется (0.5x .. 1.5x) чтобы не было синхронных перепроверок.
Это значит: если хост в локалке не виден ~60 секунд, ОС перепроверит. Если виден — запись живёт.
Gratuitous ARP — кричу в пустоту
Gratuitous ARP — это ARP-запрос от своего IP про свой IP. Зачем?
-
Сообщить сети, что я тут. Когда машина поднимается, она шлёт gratuitous ARP с
Sender IP = Target IP = свой IP. Все, у кого в кэше был старый MAC для этого IP — обновят запись. -
Проверить конфликт IP. Если кто-то другой ответит на ARP про мой IP — значит в сети дублирующийся IP.
-
Failover. Когда в active-passive HA один сервер падает, второй забирает Virtual IP и шлёт gratuitous ARP, чтобы свитчи и соседи обновили MAC для этого IP. Без этого пакеты продолжали бы идти на мёртвый MAC ~60 секунд, пока ARP-таблица не протухнет.
В keepalived, Pacemaker, AWS Elastic IP — везде под капотом gratuitous ARP при failover. Если вы поднимали VIP — видели в логах строчку «Sending gratuitous ARP».
Реальный ARP в действии
Захватим ARP-обмен tcpdump’ом и разберём:
# В одном терминале -- слушаем ARP:
sudo tcpdump -i en0 -e -n 'arp'
# В другом терминале -- провоцируем ARP, удалив запись и запинговав:
ip neigh del 192.168.1.1 dev eth0 2>/dev/null || sudo arp -d 192.168.1.1
ping -c 1 192.168.1.1
# В первом терминале увидим:
# 12:34:56.789 a4:83:e7:1a:bb:cc > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806),
# length 42: Request who-has 192.168.1.1 tell 192.168.1.42, length 28
# 12:34:56.790 00:1c:42:00:00:18 > a4:83:e7:1a:bb:cc, ethertype ARP (0x0806),
# length 42: Reply 192.168.1.1 is-at 00:1c:42:00:00:18, length 28
Видим:
- Кадр
Requestидёт на broadcastff:ff:ff:.... - Текст «Request who-has X tell Y» означает «кто имеет IP X? скажите Y».
- Ответ — unicast напрямую запрашивающему.
- «Reply X is-at Y» — «X находится по MAC Y».
После этого ip neigh show покажет свежую REACHABLE-запись для 192.168.1.1.
Atаки и проблемы с ARP
ARP был спроектирован в эпоху, когда «свои» были только за столом в одной комнате. Никакой аутентификации в протоколе нет. Любой может ответить на любой ARP-запрос. Это даёт атаки:
1. ARP spoofing / ARP poisoning. Атакующий в той же локалке постоянно шлёт фейковые ARP reply: «IP 192.168.1.1 находится по MAC cc:cc:cc:...» (своему). Все жертвы обновляют ARP-кэш, теперь трафик к роутеру идёт через машину атакующего. Это даёт ему MITM.
2. ARP flood. Машина шлёт миллион ARP-запросов в секунду. Свитчи перегружаются, ARP-таблицы переполняются, маленькие свитчи начинают работать как hub’ы (failopen) и атакующий видит весь трафик.
3. Дублирующиеся IP. Если два устройства настроены с одним IP — они оба отвечают на ARP. Сетевой кэш «дрожит», пакеты идут то одному, то другому, всё лагает. В логах: kernel: ARP packet from 192.168.1.5 had src ... but src mac doesn't match.
Если в сети не работает ARP-spoofing-защита (Dynamic ARP Inspection на enterprise-свитчах) — ваша Wi-Fi сеть в кофейне беззащитна. Любой подключившийся может MITM ваш HTTP-трафик. HTTPS защищает от чтения содержимого, но метаданные (с какими доменами вы общаетесь) — всё равно видны. Защита: VPN, HSTS, использование Wi-Fi только с уважающим вас провайдером.
Защита на стороне свитчей:
- Dynamic ARP Inspection (DAI) — свитч проверяет, что src IP в ARP действительно соответствует src MAC из DHCP snooping table. Не своё — дропается.
- Port Security — каждый порт свитча привязан к ограниченному списку MAC. Чужой MAC — порт отключается.
Эти штуки умеют только managed switches (Cisco, Mikrotik с RouterOS, Aruba). Домашние роутеры — беззащитны.
Попробуй сам
# 1. Посмотреть свой ARP-кэш
ip neigh show # Linux
arp -a # macOS
# 2. Удалить запись и посмотреть, как она восстановится
sudo ip neigh del 192.168.1.1 dev eth0 # Linux
sudo arp -d 192.168.1.1 # macOS
ping -c 1 192.168.1.1
ip neigh show 192.168.1.1 # увидим INCOMPLETE -> REACHABLE
# 3. Захватить ARP-обмен tcpdump'ом
sudo tcpdump -i any -e -n 'arp' -c 4
# В другом терминале пингуйте новый IP:
ping -c 1 192.168.1.99 # если такого нет -- увидите только requests
# 4. Найти, какие IP активны в локалке (через ARP scan):
# Linux:
sudo apt install arp-scan
sudo arp-scan -l # быстро находит всех соседей в локалке через ARP
# macOS (через nmap):
nmap -sn 192.168.1.0/24
# 5. Посмотреть статистику ARP:
ip -s neigh # Linux: счётчики REACHABLE, STALE, etc
sysctl net.ipv4.neigh.default # таймеры и лимиты
# 6. Проследить, что происходит с конкретным IP:
# Включить логи neigh-table (требует root):
sysctl -w net.ipv4.neigh.default.app_solicit=3
# После этого сообщения о неответивших ARP попадут в kernel log:
dmesg -w | grep -i arp
Особенно полезно сделать пункт 3 и увидеть собственными глазами «Request who-has» / «Reply is-at». Это сразу делает ARP конкретной штукой, а не абстракцией.