Маршрутизация
Таблица маршрутизации — структура данных ядра, определяющая интерфейс и следующий хоп для пакета. ip route показывает её, ip route get спрашивает ядро, какой маршрут победит для конкретного адреса. Маршрут default (0.0.0.0/0) — шлюз-ловушка.
Ты видишь, что у интерфейса есть IP — но почему машина вообще достигает интернета? Почему она достигает 10.0.0.20, но не 172.16.0.5? Ответ всегда в таблице маршрутизации. Каждый пакет, который ядро отправляет, проходит поиск: какой маршрут соответствует адресу назначения? Побеждает маршрут с самым длинным совпадающим префиксом, и он указывает egress-интерфейс и адрес следующего хопа. Когда конкретного маршрута нет — срабатывает маршрут по умолчанию 0.0.0.0/0. Когда этого маршрута нет — машина общается только с локальной подсетью. Классический инцидент: «интернет работает с ноутбука, но не с VM».
После этого урока ты сможешь читать вывод ip route и находить шлюз по умолчанию, использовать ip route get <ip> чтобы спросить ядро, какой маршрут победит для конкретного адреса, понимать правило longest-prefix match, добавлять и удалять статические маршруты, и диагностировать отсутствие маршрута по умолчанию.
ip route show — чтение таблицы маршрутизации.
ip route show
# default via 10.0.0.1 dev ens3 proto dhcp src 10.0.0.10 metric 100
# 10.0.0.0/24 dev ens3 proto kernel scope link src 10.0.0.10
# Разбор строк:
# default = 0.0.0.0/0 — маршрут-ловушка
# via 10.0.0.1 = следующий хоп: отправить пакет этому шлюзу
# dev ens3 = egress-интерфейс
# proto dhcp = маршрут установлен DHCP-клиентом
# src 10.0.0.10 = предпочтительный адрес источника для трафика по этому маршруту
# metric 100 = стоимость; меньший metric выигрывает при равных совпадениях
#
# 10.0.0.0/24 dev ens3 = для адресатов в 10.0.0.0/24 — отправить напрямую на ens3
# scope link = шлюз не нужен — адресат на том же канале
# proto kernel = создан автоматически при ip addr addМаршрут 10.0.0.0/24 со scope link означает: ядро знает, что хосты в этой подсети доступны без шлюза — оно выполнит ARP и отправит кадр напрямую. Всё за пределами этого диапазона попадает на маршрут по умолчанию.
Longest-prefix match: правило, определяющее победителя.
При отправке пакета ядро сравнивает адрес назначения со всеми маршрутами и выбирает наиболее специфичный — с самым длинным совпадающим префиксом.
# Таблица маршрутизации на host-a (10.0.0.10):
# default via 10.0.0.1 dev ens3
# 10.0.0.0/24 dev ens3 scope link
# 192.168.5.0/24 via 10.0.0.254 dev ens3
# Отправка на 10.0.0.20:
# Кандидаты: default (0 бит), 10.0.0.0/24 (24 бита)
# Победитель: 10.0.0.0/24 — напрямую на ens3
# Отправка на 192.168.5.100:
# Кандидаты: default (0 бит), 192.168.5.0/24 (24 бита)
# Победитель: 192.168.5.0/24 → следующий хоп 10.0.0.254
# Отправка на 8.8.8.8 (интернет):
# Кандидаты: только default (0 бит)
# Победитель: default → следующий хоп 10.0.0.1 (шлюз)/32 — самый специфичный маршрут (один хост). Хост-маршрут вроде 192.0.2.1/32 via 10.0.0.1 побеждает любой подсетевой маршрут, содержащий этот адрес.
ip route get <destination> — спросить ядро, какой маршрут победит.
Вместо того чтобы выполнять логику longest-prefix match в голове, спроси ядро напрямую:
# Какой маршрут использует ядро для 8.8.8.8?
ip route get 8.8.8.8
# 8.8.8.8 via 10.0.0.1 dev ens3 src 10.0.0.10 uid 1000
# cache
# Маршрут для машины в локальной подсети:
ip route get 10.0.0.20
# 10.0.0.20 dev ens3 src 10.0.0.10 uid 1000
# cache
# Нет "via" — прямая доставка, шлюз не нужен
# Если нет маршрута по умолчанию:
ip route get 8.8.8.8
# RTNETLINK answers: Network is unreachable
# → отсутствие default-маршрута подтвержденоip route get — самый быстрый способ диагностировать «почему хост не может достичь X» — он показывает ровно то, что сделает ядро, включая выбор адреса источника, до отправки единого пакета.
Добавление и удаление статических маршрутов.
DHCP поставляет шлюз по умолчанию автоматически. Статические маршруты нужны для дополнительных подсетей, доступных через конкретный шлюз:
# Добавить маршрут: подсеть 192.168.10.0/24 через 10.0.0.254
sudo ip route add 192.168.10.0/24 via 10.0.0.254
# Проверить:
ip route show
# default via 10.0.0.1 dev ens3
# 10.0.0.0/24 dev ens3 scope link
# 192.168.10.0/24 via 10.0.0.254 dev ens3
# Удалить:
sudo ip route del 192.168.10.0/24 via 10.0.0.254
# С явным интерфейсом:
sudo ip route add 192.168.10.0/24 via 10.0.0.254 dev ens3
# Хост-маршрут:
sudo ip route add 192.0.2.100/32 via 10.0.0.1Эти изменения не сохраняются. При перезагрузке таблица маршрутизации ядра пуста; сетевая инициализация (DHCP-клиент, netplan, systemd-networkd) пересоздаёт её. Для сохранения статических маршрутов используй /etc/network/interfaces со строками up ip route add ... или routes: в netplan.
Отсутствие маршрута по умолчанию: классический инцидент.
VM достигает 10.0.0.20, но не 8.8.8.8 и ничего за пределами подсети. Симптом: ping 10.0.0.20 работает, ping 8.8.8.8 падает с «Network is unreachable».
# Подтвердить: нет маршрута по умолчанию
ip route show | grep default
# (пусто)
# Подтвердить через route get:
ip route get 8.8.8.8
# RTNETLINK answers: Network is unreachable
# Временное исправление:
sudo ip route add default via 10.0.0.1
# Подтвердить исправление:
ip route get 8.8.8.8
# 8.8.8.8 via 10.0.0.1 dev ens3 src 10.0.0.10
# Причины:
# 1. DHCP-клиент не запустился или упал — проверь: systemctl status dhclient
# 2. Статическая конфигурация не содержит шлюза — проверь /etc/network/interfaces
# 3. Маршрут был удалён вручную и не восстановленДиагностика: host-a не может достичь staging-подсети.
host-a (10.0.0.10) должен достигать 192.168.20.0/24 (staging). Соединения зависают.
# Шаг 1: есть ли маршрут в таблице?
ip route show | grep 192.168.20
# (пусто)
# Шаг 2: ip route get подтверждает отсутствие маршрута
ip route get 192.168.20.5
# RTNETLINK answers: Network is unreachable
# Шаг 3: добавить маршрут (шлюз 10.0.0.254 обслуживает staging)
sudo ip route add 192.168.20.0/24 via 10.0.0.254
# Шаг 4: проверить
ip route get 192.168.20.5
# 192.168.20.5 via 10.0.0.254 dev ens3 src 10.0.0.10
# Шаг 5: проверить связность
ping -c 3 192.168.20.5
# Шаг 6: сохранить в netplan (/etc/netplan/01-netcfg.yaml):
# network:
# ethernets:
# ens3:
# routes:
# - to: 192.168.20.0/24
# via: 10.0.0.254▸Частая ошибка
Добавление маршрута без проверки достижимости шлюза. Если запустить sudo ip route add 192.168.20.0/24 via 10.0.0.254, но 10.0.0.254 не находится в локальной подсети (недоступен через scope link маршрут), ядро примет команду, но каждый пакет будет недоставляемым. Симптом: ip route get 192.168.20.5 успешен, но ping зависает. Всегда проверяй достижимость шлюза через ip route get 10.0.0.254 перед добавлением маршрутов, использующих его.
▸Почему это работает
На macOS таблица маршрутизации читается через netstat -rn (или route -n get <ip> для конкретного адреса). Логика — longest-prefix match, шлюз по умолчанию, scope link маршруты — идентична. macOS использует BSD routing sockets, а не Linux netlink; концепции переносятся полностью, хотя команды различаются.
У host-a такие маршруты: default via 10.0.0.1 dev ens3, и 10.0.0.0/24 dev ens3 scope link. Ты запускаешь: ip route get 10.0.0.50. Какой маршрут победит и что произойдёт дальше?
Таблица маршрутизации — структура данных ядра, которую консультирует каждый исходящий пакет. Longest-prefix match выбирает наиболее специфичный маршрут — /32 бьёт /24 бьёт default 0.0.0.0/0. ip route show читает таблицу; ip route get <destination> спрашивает ядро, какой маршрут реально победит для конкретного адреса, включая выбор адреса источника. ip route add/del управляет статическими маршрутами, но изменения теряются при перезагрузке — сохраняй их через netplan или /etc/network/interfaces. Отсутствие маршрута по умолчанию оставляет хосту только доступ к локальной подсети и является наиболее частой причиной «интернет недоступен» на новых VM.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.