оригинал тут http://www.k-max.name/linux/princip-raboty-routing-policy-database/

 

Пару вступительных слов о маршрутизации

 

 Итак, из основных понятий сетей мы знаем, если сетевой пакет предназначен для локальной сети, к которой подключен интерфейс, то он направляется прямо в сеть. Маршрут для такого пакета создается автоматически при поднятии настроенного интерфейса. Если пакет предназначен не локальной сети, то ядро просматривает таблицу маршрутизации на наличие маршрута для данного пакета и отправляет по маршруту, в котором адрес назначения пакета соответствует заданному в маршруте параметру - на адрес шлюза, который указан в поле gateway в маршруте.  При этом, может существовать несколько маршрутов для данного пакета. В таком случае выбирается тот маршрут, в котором в заданной подсети меньше всего компьютеров. Если для текущего пакета маршрут не обнаружен, то он направляется на маршрут "по-умолчанию". Это классическая маршрутизация протокола IPv4, основанная на поиске маршрута по адресу назначения в заголовках IP.

 

Маршруты в  Linux могут появляться различными способами. Во-первых, как я уже говорил, при поднятии сетевого интерфейса появляется соответствующий маршрут в локальную сеть, куда смотрит интерфейс. Во-вторых, маршруты могут добавляться в ручную. Это называется статическая маршрутизация. В-третьих, маршруты могут формироваться динамически, базируясь на информации о топологии и состоянии сети, получаемой с помощью протокола динамической маршрутизации. Это называется динамической маршрутизацией. Динамической маршрутизацией в Linux заведует демон gated или routed, возможно еще какой-то о котором я не знаю

 

Классическую маршрутизацию на основе адреса назначения применяют в небольших сетях. Данную маршрутизацию можно сравнить с походом из дома, например, в торговый центр. При этом, дорога к торговому центру может быть разделена перекрестком и на перекрестке вы выбираете пойти вам на право или налево, основываясь лишь на том, по какой из дорог вы попадете к ТЦ.

 

 

 

Маршрутизация на основе политик (Policy Routing)

 

Продолжая нашу аналогию, представим что вы уперлись в пересечение шести дорог. Усложняя задачу, представьте, что одна из дорог - асфальтированная, и предназначена для легковых автомобилей, вторая - гравийная и предназначена для движения тракторов, третья - ... В данном случае, необходимо принять решение не только на том, КУДА необходимо попасть, но и на том за рулем какого транспортного средства вы находитесь, где вам будет комфортней ехать и многих других параметрах. Аналогично, основной принцип работы механизма маршрутизации на основе политик базируется на основе анализа любых полей IP-пакета, таких как адрес отправителя, IP протокол, порты транспортного протокола, или даже содержимого, а не только на основании адреса получателя.

 

Основу маршрутизации на основе политик составляют 4 понятия. Это традиционные понятия address (адрес) , routes (маршрут), Routing table (таблица маршрутизации), а так же новое в Policy Routing (и являющееся его основой) - rules (правила). Давайте разберем каждый из них.

 

 

 

Адрес (Address) определяет место назначения пакета и источник. Маршрут (routes) задает путь пакета, данное понятие не сильно отличается от традиционной маршрутизации. Соответственно, при задании маршрута так же, как и в традиционном способе (с помощью команды route) можно указать шлюз и несколько опций для маршрута, задаваемого для хоста или подсети. Среди опций можно выделить такие как: метрика (metric),  размер-TCP-окна (window) и др. В Policy Routing помимо стандартных параметров добавления маршрута появились так же опции, позволяющие указать исходящий адрес источника пакета, интерфейс или тип ICMP ответа. Таблица маршрутизации (Routing table) состоит из последовательного набора маршрутов.  Правила (rule) можно рассматривать, как своеобразный фильтр, который отбирает пакеты, удовлетворяющие определенным требованиям и подходящие - направляет по заданному маршруту.

 

 

 

Routing Policy DataBase (RPDB)

 

Механизм маршрутизации на основе политик впервые был реализован в ядре версии 2.1. Он так же называется "база данных политик маршрутизации" (он же Routing Policy DataBase (RPDB)). RPDB - это связанный набор маршрутов, таблиц маршрутизации и правил. Механизм маршрутизации и ip адресации в ядре Linux 2.1 был переписан чуть более чем полностью, в результате чего появилась возможность поддерживать до 255 таблиц маршрутизации и 2^32 правил маршрутизации. Это позволяет создать более чем 4 миллиарда правил, данная цифра перекрывает все пространство адресов IPv4. Другими словами, Вы можете определить правило управления каждым отдельным адресом, доступным во всем адресном пространстве IPv4.

 

 

 

Давайте рассмотрим некоторый пример, который далее позволит нам более детально разобраться в Routing Policy.  В классическом примере на хосте Linux имеется единственная таблица маршрутизации, в которой согласно правил и места назначения пакета ищется маршрут (адрес шлюза и/или физической интерфейс). Давайте предположим, что у нас есть 3 маршрута к некоторой сети назначения, путь через которые (маршруты) проходит по каналам разного качества, соответственно разная скорость. Предположим, в локальной сети есть некоторая группа компьютеров, которой нужна гарантированная скорость, а другой группе высокая скорость не нужна. Если у нас будет единственная таблица маршрутизации, то мы сможем указать лишь единственный маршрут к сети назначения, через единственны шлюз. Данный функционал реализуем в Policy Routing - маршрутизации на основе адреса отправителя пакета.  Это наиболее распространенный и часто применяемый метод маршрутизации. При этом, в правилах отбираются пакеты на основании принадлежности хоста-отправителя заданной в правиле подсети.

 

 

 

Давайте рассмотрим работу Routing Policy в схеме и разберем порядок работы:

 

1. Правила (rules)

 

Как видно, при маршрутизации первый элемент, через который проходит пакет - это набор правил. Каждое правило состоит из критерия отбора пакетов - своеобразный "фильтр" (например, from 2.1.7.100 - отбирает все пакеты с адресом отправителя 2.1.7.100) и действия над "подходящим" пакетом (например, lookup table_name2 - направляет пакет в таблицу маршрутизации с именем table_name2. Дословно: просмотреть table_name2).

 

 

 

При этом, каждый фильтр/критерий может состоять из указания исходного адреса, адреса получателя, входящего интерфейса, TOS и fwmark (поле TOS задает тип сервиса, что это и с чем его едят я ответить не готов, но могу направить в RFC 1349, fwmark - это метки, которые можно задать силами iptables/netfilter).

 

 

 

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

 

 

 

  • unicast (lookup) - просмотр указанной таблицы маршрутизации, если при создании правила не указан тип, то используется данный тип.
  • nat - правило преобразовывает адрес отправителя, без запоминания состояния соединения! (обычно используется совместно с соответствующей записью, маршрутизирующей данный пакет)
  • unreachable - выбросить пакет и вернуть сообщение ICMP о недоступности сети
  • prohibit - выбросить пакет и вернуть сообщение ICMP о запрещении доступа
  • blackhole - молча выбросить пакет (дословно - черная дыра)

 

 

 

Каждое правило имеет свой приоритет от 0 до 32767, в соответствии с которым ядро просматривает данные правила. При старте системы по умолчанию создаются следующие правила:

 

 

 

[root@rpdb ~]# ip rule list

0: from all lookup local

32766: from all lookup main

32767: from all lookup default

 

 

 

Правило с приоритетом 0 (ноль) действует для всех пакетов (from all) и направляет в таблицу маршрутизации local. Таблица local - специальная таблица маршрутизации, содержащая высокоприоритетные маршруты управления для локальных и широковещательных адресов. Правило 0 (ноль) является особенным, оно не может быть удалено или переопределено.

 

 

 

Правило с приоритетом 32766 действует так же для всех пакетов (from all) и направляет в таблицу main. Таблица main - основная таблица маршрутизации, содержащая все маршруты не имеющие политик (то есть работающие по классическому принципу - месту назначения пакета) . Это правило может быть удалено или переопределено в других (вышестоящих) правилах.

 

 

 

Правило с приоритетом 32767 действует так же для всех пакетов (from all) и направляет в таблицу default. Таблица default - по-умолчанию пуста и зарезервирована для использования. Может применяться для назначения маршрутов по умолчанию для пакетов, не направленных куда-либо в предыдущих правилах. Правило может быть удалено.

 

 

 

При добавлении правила в ручную, без указания приоритета (параметр  priority), новые правила будут получать номер с 32765 до 1.

 

 

 

Очень важно понимать разницу между правилом и таблицей маршрутизации. Правило маршрутизации указывает на таблицу. Несколько правил могут отправлять пакет к одной таблице маршрутизации. При этом, некоторые таблицы маршрутизации могут быть БЕЗ правила, указывающего на них (читай -  могут не использоваться).

 

 

 

Управление правилами осуществляется с помощью команды ip с ключом rule. Вот некоторые примеры:

 

 

 

[root@rpdb: ~]# # добавление правила, направляющего пакеты с хоста 192.168.100.1 в таблицу 5

[root@rpdb: ~]# ip rule add from 192.168.100.1 table 5 

[root@rpdb: ~]# # добавление правила для пакетов, помеченных netfilter меткой 4 в таблицу 4

[root@rpdb: ~]# ip rule add fwmark 4 table 4

[root@rpdb: ~]# # добавление правила для хоста 209.10.26.51 - пакеты будут "пропадать"

[root@rpdb: ~]# ip rule add blackhole from 209.10.26.51

 

 

 

2. Таблицы маршрутизации

 

 

 

Итак, следующим шагом на пути пакета по сетевой подсистеме ядра будет таблица маршрутизации, в которую пакет будет направлен соответствующим правилом. Ядро использует несколько таблиц маршрутизации с номерами от 1 до 255. Сопоставление номеров таблиц к их именам задается в файле /etc/iproute2/rt_tables. Фактически, задавать имена таблиц их номерным идентификаторам нет необходимости (в правилах можно просто использовать номера таблиц). По умолчанию при создании маршрута в Linux, если не задано имя таблицы, то используется таблица с ID 254 (main). Кроме таблицы main в ядре зарезервированы следующие имена:

 

 

 

[root@rpdb ~]# cat /etc/iproute2/rt_tables

255 local

254 main

253 default

0 unspec

 

 

 

Про main я уже сказал, но в ядре существует так же не менее важная таблица local с  ID 255, которая состоит из маршрутов для локальных и широковещательных адресов. Ядро поддерживает эту таблицу автоматически, и администраторы никогда не должны изменять ее содержимое и нет необходимости даже в нее заглядывать при нормальном функционировании. Назначение таблицы default я описывал выше при описании правил. Таблица unspec - это "псевдо-таблица", которая содержит в себе правила ВСЕХ таблиц маршрутизации системы. Именно по этому у нее номер ноль, который не входит в диапазон с 1 по 255.  Все таблицы маршрутизации ядра никак между собой не связаны, таким образом, у Вас может быть несколько идентичных маршрутов в различных таблицах, которые не будут конфликтовать. Нумерация таблиц большого значения не имеет. То есть все таблицы по сути имеют одинаковый вес и приоритет их определяется только заданными правилами.

 

 

 

Просмотреть содержимое таблицы можно командой ip (на примере таблицы main):

 

 

 

[root@rpdb ~]# ip route list table main

default via 10.0.0.254 dev eth0

10.0.0.0/16 dev eth0 proto kernel scope link src 10.0.0.2

 

 

 

3. Маршруты/правила

 

 

 

Попадая в какую-либо таблицу маршрутизации, пакет "просматривает" последовательно каждое правило. На данном шаге, дальнейшее направление пакета никак не отличается от "классического" определения маршрута по адресу назначения. Если в текущей таблице маршрут для пакета не обнаружен, то он (пакет) возвращается в базу данных маршрутизации на основе политик к следующему правилу. Стоит обязательно отметить, что существуют разные типы маршрутов (которые очень похожи с типами правил, но правила стоят несколько "выше" маршрутов):

 

 

 

  • unicast - обычный маршрут, определяет исходящий интерфейс и адрес следующего хопа для подсети/хоста назначения.
  • unreachable - пакет выбрасывается и посылается ICMP сообщение "host unreachable".
  • blackhole - пакет просто выбрасывается без возвращения какого-либо сообщения.
  • prohibit (дословно - запрещен) - пакет выбрасывается и посылается ICMP сообщение "administratively prohibited".
  • local - место назначения данного маршрута - локальный хост. В этом маршруте пакеты перемещаются локально.
  • broadcast - пакет пересылается в виде широковещательного сообщения
  • throw - специальный управляющий маршрут, используется совместно с правилами маршрутизации; Если пакет будет соответствовать данному маршруту, то поиск в этой таблице завершается, эмитируя что для данного пакета маршрут не найден и возвращается в RPDB для обработки в следующем правиле.
  • nat - адреса сети назначения преобразуются в соответствии с параметром via; маршрутизатор также обслуживает ARP запросы для этой сети; не предназначен для сжатия адресного пространства или разделения нагрузки; в частности, не хранит таблицу соединений и не заглядывает внутрь пакета; можно использовать при перенумерации сети; для обратного преобразования необходимо задать дополнительное правило маршрутизации "ip rule add from реальный-адрес nat виртуальный-адрес".
  • anycast - локальные адреса, которые нельзя использовать в качестве адресов источника. (непонятный мне тип маршрута)
  • multicast - Специальный тип, используемый для многоадресной маршрутизации. Он не присутствует в статичных таблицах маршрутизации.

 

 

 

Управление правилами в таблице маршрутизации осуществляется с помощью команды ip с ключом route.

 

 

 

Выбор IP-адреса для исходящих соединений

 

 

 

Выбор локального адреса для исходящих соединений в большинстве случаев системой осуществляется автоматически, исходя из имеющихся IP-адресов интерфейсов и таблиц маршрутизации. Во многих случаях сетевые сервисы (веб-сервер, почтовый сервер и др.) позволяют указывать исходный адрес с помощью конфигурационных файлов. Давайте рассмотрим пример. Пусть система имеет два интерфейса eth0(192.168.1.1/24) и eth1(192.168.56.102/24):

 

 

 

[root@rpdb ~]# ip addr show

1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast

        state UP qlen 1000 link/ether 08:00:27:23:22:97 brd ff:ff:ff:ff:ff:ff

        inet 192.168.1.1/24 brd 192.168.1.255 scope global eth0

        inet6 fe80::a00:27ff:fe23:2297/64 scope link

        valid_lft forever preferred_lft forever

2: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast

        state UP qlen 1000 link/ether 08:00:27:fd:e5:aa brd ff:ff:ff:ff:ff:ff

        inet 192.168.56.102/24 brd 192.168.56.255 scope global eth1

        inet6 fe80::a00:27ff:fefd:e5aa/64 scope link

        valid_lft forever preferred_lft forever

 

 

 

Маршрут по умолчанию у данной системы - 192.168.56.1:

 

 

[root@rpdb ~]# ip route show

192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.1

192.168.56.0/24 dev eth1 proto kernel scope link src 192.168.56.102

default via 192.168.56.1 dev eth1

 

 

 

При такой конфигурации для исходящих соединений будет использоваться интерфейс eth1 и IP-адрес 192.168.56.102 (кроме соединений с узлами сети 192.168.1.0/24 — eth0 и IP-адрес 192.168.1.1). Ниже показан дамп сетевого пакета, отправленного командой ping -c 1 192.168.3.4:

 

 

 

[root@rpdb ~]# tcpdump -ne -i eth1 host 192.168.3.4

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode  listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes

10:18:18.924084 08:00:27:fd:e5:aa > 0a:00:27:00:00:00, ethertype IPv4 (0x0800),

length 98: 192.168.56.102 > 192.168.3.4: ICMP echo request, id 960, seq 1, length 64

 

 

 

Однако, если добавить альтернативный маршрут для сети 192.168.3.0/24 через некоторый шлюз 192.168.1.254:

 

 

 

[root@rpdb ~]# ip route add 192.168.3.0/24 via 192.168.1.254

 

 

 

то, для пакетов, предназначенных узлу 192.168.3.4, будет использоваться интерфейс eth0 и исходящий адрес 192.168.1.1 (показан дамп сетевого пакета):

 

 

 

[root@rpdb ~]# tcpdump -ne -i eth0 host 192.168.3.4

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes

10:23:33.393706 08:00:27:23:22:97 > 00:00:00:00:00:aa, ethertype IPv4 (0x0800),

 length 98: 192.168.1.1 > 192.168.3.4: ICMP echo request, id 968, seq 1, length 64

 

 

 

Синтаксис команды ip route позволяет повлиять на выбор локального IP-адреса при соединении с удаленными системами. Для этого служит параметр src с указанием предпочитаемого IP-адреса (должен быть установлен на сетевом интерфейсе компьютера) для отправки пакетов на направление, определяемое в команде префиксом маршрутизации. Так, для указанной ниже конфигурации будет использоваться исходящий адрес 192.168.56.102 (кроме взаимодействия с узлами сети 192.168.1.0/24):

 

 

 

[root@rpdb ~]# ip addr show eth1

2: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000

         link/ether 08:00:27:fd:e5:aa brd ff:ff:ff:ff:ff:ff

         inet 192.168.56.102/24 brd 192.168.56.255 scope global eth1

         inet 192.168.1.10/24 brd 192.168.1.255 scope global eth1:1

         inet6 fe80::a00:27ff:fefd:e5aa/64 scope link

         valid_lft forever preferred_lft forever

[root@rpdb ~]# ip route show

192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.10

192.168.56.0/24 dev eth1 proto kernel scope link src 192.168.56.102

default via 192.168.56.1 dev eth1

 

 

 

Для того, чтобы использовать исходный адрес 192.168.1.10 для соединения с узлами сети 192.168.3.0/24 следует использовать команду:

 

 

 

[root@rpdb ~]# ip route add 192.168.3.0/24 via 192.168.56.1 src 192.168.1.10 dev eth1:1

 

 

 

Таблица маршрутизации при этом будет иметь вид:

 

 

 

[root@rpdb ~]# ip route show

192.168.3.0/24 via 192.168.56.1 dev eth1 src 192.168.1.10

192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.10

192.168.56.0/24 dev eth1 proto kernel scope link src 192.168.56.102

default via 192.168.56.1 dev eth1

 

 

 

Добавление альтернативного маршрута для "избранных" хостов

 

 

 

Рассмотрим классический пример, когда в локальной сети необходимо направить избранные хосты по альтернативному маршруту. Предположим, что в локальной сети 10.0.0.0/24 имеется некоторый шлюз с двумя интерфейсами, имеющими IP-адреса 10.0.0.1/24 - смотрит в локальную сеть, 12.13.14.15/24 - смотрит в глобальную сеть. Маршрут по умолчанию проходит через IP 12.13.14.1. При этом необходимо, чтобы хост 10.0.0.100 был направлен по маршруту 12.13.14.100. Для решения этой задачи, необходимо:

 

 

 

Добавить описание  дополнительной таблицы маршрутизации в файл /etc/iproute2/rt_tables (это действие необязательно, можно использовать просто номер таблицы)

 

 

 

# echo 100 newtable >> /etc/iproute2/rt_tables

 

 

 

Добавить правило, которое будет направлять пакеты с адресом отправителя 10.0.0.100 в описанную на прошлом шаге таблицу маршрутизации

 

 

 

# ip rule add from 10.0.0.100 lookup newtable

# # или

# ip rule add from 10.0.0.100 lookup 100

# # после выполнения данной команды список правил примет следующий вид

# ip rule show

0: from all lookup local

32765: from 10.0.0.100 lookup newtable

32766: from all lookup main

32767: from all lookup default

 

 

 

Добавить новый маршрут по умолчанию, отправляющий пакеты на хост 12.13.14.100 в новую таблицу маршрутизации

 

 

 

# ip route add default via 12.13.14.100 dev eth1 table newtable

# # после внесенных изменений необходимо очистить кэш маршрутов, чтобы ядро обновило информацию о новых маршрутах

# ip route flush cache

# # после данных изменений таблица newtable (id 100) будет иметь следующий вид

# ip route show table 100

default via 12.13.14.100 dev eth1

 

 

 

Давайте рассмотрим путь пакета, согласно наших правил. Хост 10.0.0.100 отправляет пакет некоторому узлу 7.8.9.10, соответственно, в заголовках пакета источник - 10.0.0.100, назначение - 7.8.9.10. На хосте 10.0.0.100 шлюз по умолчанию - 10.0.0.1, согласно данного правила пакет попадает на шлюз 10.0.0.1. Ядро, получив пакет последовательно с нулевого правила просматривает соответствие пакета заданным в правилах фильтрам/критериям. Пакет подходит под действие правила 0 (0: from all lookup local) и направляется в таблицу маршрутизации local. Но т.к. пакет не принадлежит локальной системе и он не широковещательный, то маршрут в данной таблице не найден и пакет возвращается в RPDB для просмотра следующего правила. Следующее правило на пути пакета -  32765: from 10.0.0.100 lookup newtable. Пакет под критерии данного правила подходит, поэтому направляется в таблицу newtable (id 100). Согласно данной таблицы все пакеты направляются на единственный маршрут по умолчанию - 12.13.14.100. Пакет уходит согласно этого правила на указанный хост. Следующие правила не обрабатываются. Обращаю внимание, что в данном разборе я не учитывал прохождение пакета через таблицы netfilter.

 

 

 

Краткие итоги

 

 

 

В статье я рассмотрел работу механизма Routing Policy DataBase (RPDB) - маршрутизации на основе политик. Я долго вникал в работу этого механизма и постарался изложить свое понимание всего происходящего в ядре. Доходчивой документации на русском языке по данному вопросу в сети я не нашел. Даже всеми хваленый LARTC не дает прозрачного понимания RPDB. Надеюсь, что мои мысли помогут вам понять основные принципы. Подводя итог всему вышесказанному можно свести основной смысл к тому, что пакет в порядке приоритета правил (от 0 до 32767) сверяется с каждым правилом и в случае, если подходит под заданные условия, над пакетом совершается какое-либо действие (обычно отправляется в указанную таблицу). Если пакет в заданной таблице находит свой маршрут, то он отправляется по заданному маршруту. Если не находит - возвращается к списку правил для обработки в следующем правиле. Управление всем этим делом осуществляется командой ip с различными параметрами. В дальнейших статьях я постараюсь рассмотреть более интересные примеры реализации маршрутизации на основе политик. Кроме того, я бы обязательно посоветовал вам почитать приведенные ниже ссылки для более глубокого ознакомления.

 

 

 

Что еще почитать

 

 

 

Хорошая статья о том, что такое маршрут по умолчанию - http://xgu.ru/wiki/Маршрут_по_умолчанию
RFC 1394 (что такое TOS и с чем его едят) - http://www.ietf.org/rfc/rfc1349.txt
Обязательно к прочтению (Policy Routing With Linux) - http://www.policyrouting.org/PolicyRoutingBook/ONLINE/TOC.html
Guide to IP Layer Network Administration with Linux - http://www.linux-ip.net/html/
Команда ip на буржуйском от русского автора - http://www.linux-ip.net/gl/ip-cref/
Linux Advanced Routing & Traffic Control HOWTO - http://www.opennet.ru/docs/RUS/LARTC/index.html

 

 

NewMixer (c) 2017