Перейти к содержанию

Таблицы маршрутизации 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 — самая полезная команда в этой статье: она показывает решение, выбранный адрес источника и исходящий интерфейс вместо того, чтобы вычислять их на глаз.

Как выглядит «недостижимо»

Без маршрута по умолчанию и без подходящего префикса:

$ ip route get 8.8.8.8
RTNETLINK answers: Network is unreachable

Пакет отбрасывается на отправляющем хосте и до сети не доходит. Шлюз, который не входит ни в одну подключённую подсеть, отвергается при добавлении маршрута:

$ ip route add 10.9.9.0/24 via 172.16.0.1
Error: Nexthop has invalid gateway.

Следующий хоп обязан быть в непосредственно подключённой сети, потому что ядро должно отправить для него 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 превращает сервер в маршрутизатор. Делайте это осознанно и сочетайте с правилами файрвола, иначе хосты с одной стороны смогут свободно обращаться к другой.

Связанные статьи

Контрольные вопросы

  1. Под адрес подходят два маршрута: 10.0.0.0/8 с метрикой 0 и 10.1.0.0/16 с метрикой 100. Какой использует ядро и почему?
  2. Почему у подключённого маршрута нет via?
  3. Что говорит Network is unreachable в отличие от тайм-аута ping?
  4. Как проверить, какой маршрут и адрес источника ядро использует для адреса назначения?
  5. Почему маршрут, добавленный через ip route add, пропадает после перезагрузки и как сделать его постоянным?