Таблицы маршрутизации Linux¶
Каждый раз, когда хост отправляет пакет, ядро отвечает на один вопрос: через какой интерфейс и какой следующий хоп отправлять этот адрес назначения? Ответ берётся из таблицы маршрутизации. Большинство проблем вида «локальная сеть доступна, а интернет нет» и «трафик уходит не через ту сетевую карту» решаются правильным чтением этой таблицы.
О выводе на этой странице
Весь вывод снят при реальном запуске в одноразовом сетевом namespace на Ubuntu 22.04 (iproute2 5.15) с фиктивными интерфейсами и документационными диапазонами адресов 192.0.2.0/24, 198.51.100.0/24 и 203.0.113.0/24. Ваши адреса и имена интерфейсов будут другими. Завершающая строка cache, которую ip route get печатает на этом ядре, опущена. Реальная сетевая конфигурация хоста не менялась.
Как это устроено¶
Чтение ip route¶
$ ip route
default via 192.0.2.1 dev eth0
192.0.2.0/24 dev eth0 proto kernel scope link src 192.0.2.10
198.51.100.0/24 dev eth1 proto kernel scope link src 198.51.100.10
Каждая строка — один маршрут: префикс назначения, затем способ его достижения.
192.0.2.0/24 dev eth0 proto kernel scope link src 192.0.2.10— подключённый маршрут. Ядро добавило его само, когдаeth0получил адрес192.0.2.10/24(proto kernel).scope linkзначит, что адресат непосредственно в этом сегменте, шлюз не нужен: пакеты уходят черезeth0с адресом источника192.0.2.10.default via 192.0.2.1 dev eth0— маршрут по умолчанию (0.0.0.0/0). Он подходит всему, что не совпало с более точным маршрутом, и отправляет это на шлюз192.0.2.1, который сам должен быть достижим через подключённый маршрут.
Побеждает самый длинный префикс¶
Ядро выбирает самый специфичный подходящий маршрут, то есть с самой длинной длиной префикса. Только если у нескольких маршрутов префикс одинаков, выбор делает метрика (меньше — приоритетнее). Добавим ещё два маршрута:
ip route add 203.0.113.0/24 via 198.51.100.1 dev eth1
ip route add 203.0.113.128/25 via 192.0.2.254 dev eth0 metric 50
$ ip route show
default via 192.0.2.1 dev eth0
192.0.2.0/24 dev eth0 proto kernel scope link src 192.0.2.10
198.51.100.0/24 dev eth1 proto kernel scope link src 198.51.100.10
203.0.113.0/24 via 198.51.100.1 dev eth1
203.0.113.128/25 via 192.0.2.254 dev eth0 metric 50
Спросите у ядра, что оно сделает для конкретного адреса, через ip route get:
$ ip route get 203.0.113.5
203.0.113.5 via 198.51.100.1 dev eth1 src 198.51.100.10 uid 0
$ ip route get 203.0.113.200
203.0.113.200 via 192.0.2.254 dev eth0 src 192.0.2.10 uid 0
$ ip route get 8.8.8.8
8.8.8.8 via 192.0.2.1 dev eth0 src 192.0.2.10 uid 0
$ ip route get 192.0.2.77
192.0.2.77 dev eth0 src 192.0.2.10 uid 0
Почему такие результаты:
203.0.113.5попадает в/24, но не в/25, поэтому побеждает маршрут/24: шлюз198.51.100.1черезeth1.203.0.113.200подходит под оба;/25длиннее, поэтому он выигрывает, хотя его метрика (50) больше, чем у другого (0). Сначала сравнивается длина префикса, метрика только разрешает ничью.8.8.8.8подходит только под маршрут по умолчанию.192.0.2.77подходит под подключённый маршрут: безvia, потому что адресат в том же сегменте. Ядро будет отправлять ARP-запрос напрямую для этого хоста.
ip route get — самая полезная команда в этой статье: она показывает решение, выбранный адрес источника и исходящий интерфейс вместо того, чтобы вычислять их на глаз.
Как выглядит «недостижимо»¶
Без маршрута по умолчанию и без подходящего префикса:
Пакет отбрасывается на отправляющем хосте и до сети не доходит. Шлюз, который не входит ни в одну подключённую подсеть, отвергается при добавлении маршрута:
Следующий хоп обязан быть в непосредственно подключённой сети, потому что ядро должно отправить для него ARP.
Постоянство и другие таблицы¶
ip route add меняет только работающее ядро; после перезагрузки маршрут пропадает. Делайте маршруты постоянными в инструменте настройки сети вашего дистрибутива (см. ниже).
Таблица, которую показывает ip route, — это таблица main. В Linux может быть несколько таблиц, выбираемых правилами политики (ip rule show); они нужны для серверов с несколькими подключениями, VPN и маршрутизации по адресу источника. Если ip route get расходится с тем, что вы видите в ip route, проверьте ip rule show и ip route show table all.
Для IPv6 аналог — ip -6 route. Пересылка между интерфейсами (работа в роли маршрутизатора) — отдельный переключатель, net.ipv4.ip_forward; хост с несколькими интерфейсами не маршрутизирует чужие пакеты, пока он не установлен в 1.
Различия Ubuntu/Debian и RHEL-подобных систем¶
| Ubuntu (22.04+) | Debian | RHEL-подобные | |
|---|---|---|---|
| Где хранятся постоянные маршруты | Netplan YAML в /etc/netplan/ (routes: у интерфейса) | /etc/network/interfaces (up ip route add ...) или systemd-networkd | NetworkManager: nmcli con mod <name> +ipv4.routes "203.0.113.0/24 198.51.100.1" |
| Применение | sudo netplan try (автооткат) | ifreload -a / перезапуск сети | nmcli con up <name> |
Вывод ip route и его смысл одинаковы везде. Различается только слой хранения. Предпочитайте netplan try: он откатывает изменения по тайм-ауту, если вы потеряли связь.
Практическое задание¶
Воспроизведите снимки выше, не трогая реальную сеть. Root не нужен: интерфейсы живут в частном сетевом namespace и исчезают вместе с ним.
unshare -rn bash # новая оболочка в частном сетевом namespace
ip link set lo up
ip link add eth0 type dummy; ip link add eth1 type dummy
ip addr add 192.0.2.10/24 dev eth0; ip addr add 198.51.100.10/24 dev eth1
ip link set eth0 up; ip link set eth1 up
ip route add default via 192.0.2.1 dev eth0
ip route add 203.0.113.0/24 via 198.51.100.1 dev eth1
ip route add 203.0.113.128/25 via 192.0.2.254 dev eth0 metric 50
ip route show
ip route get 203.0.113.5; ip route get 203.0.113.200
ip route del default
ip route get 8.8.8.8
Очистка: выйдите из оболочки командой exit. Namespace, интерфейсы и маршруты уничтожаются вместе с ней. Если unshare отказывает (непривилегированные user namespaces отключены), сделайте то же от root на запасной ВМ через sudo ip netns add lab и sudo ip netns exec lab bash, затем sudo ip netns del lab.
Как проверить, что всё сделано правильно¶
ip route showпоказывает пять маршрутов, включая оба подключённых/24сproto kernel.ip route get 203.0.113.5идёт через198.51.100.1 dev eth1;ip route get 203.0.113.200— через192.0.2.254 dev eth0.- После
ip route del defaultкомандаip route get 8.8.8.8печатаетRTNETLINK answers: Network is unreachable.
Типичные ошибки¶
- Добавление маршрута со шлюзом, не входящим в подключённую сеть. Ядро отвечает
Nexthop has invalid gateway. Сначала исправьте адрес/подсеть, затем маршрут. - Два маршрута по умолчанию с одинаковой метрикой на разных сетевых картах. Какой из них используется, не стоит считать гарантированным. Задайте разные метрики, чтобы приоритет был явным.
- Ожидание, что
ip route addпереживёт перезагрузку. - Чтение только
ip route, когда используется маршрутизация по политикам. Проверьтеip rule show. - Диагностика одним лишь
ping. Путь ответа может отличаться от пути отправки (асимметричная маршрутизация);ip route getвместе сtcpdumpна обоих интерфейсах показывает, что происходит на самом деле.
⚠ Опасные команды¶
ip route del defaultилиip route replace default ...на удалённом хосте по SSH могут оборвать вашу же сессию, если новый маршрут не работает. Сначала проверьте новый путь черезip route get, держите наготове консоль или внеполосный доступ и предпочитайтеnetplan tryили отложенный откат (( sleep 60; ip route replace default via <старый-шлюз> ) &).ip route flush all(илиflush table main) стирает все маршруты, включая подключённые; хост отрезан от сети, пока сеть не перезапущена.- Включение
net.ipv4.ip_forward=1превращает сервер в маршрутизатор. Делайте это осознанно и сочетайте с правилами файрвола, иначе хосты с одной стороны смогут свободно обращаться к другой.
Связанные статьи¶
- TCP/IP и набор команд ip в Linux
- Разрешение DNS и диагностика: когда маршрут в порядке, а имена не разрешаются
- Файрвол Linux: nftables и iptables: фильтрация пересылаемого трафика
Контрольные вопросы¶
- Под адрес подходят два маршрута:
10.0.0.0/8с метрикой 0 и10.1.0.0/16с метрикой 100. Какой использует ядро и почему? - Почему у подключённого маршрута нет
via? - Что говорит
Network is unreachableв отличие от тайм-аута ping? - Как проверить, какой маршрут и адрес источника ядро использует для адреса назначения?
- Почему маршрут, добавленный через
ip route add, пропадает после перезагрузки и как сделать его постоянным?