Policy routing with linux

an introduction to policy-based routing in Linux

A few days ago I was assigned a task to create pods with multiple network interfaces in the Kubernetes. It is not a difficult task, because it only requires a few annotations for the pods, provided that the CNI network has been correctly installed and configured. Multus and Danm are all such CNI networks, which supports multiple network interfaces. What takes me a lot of time in the task is the routing issue, when the pod wants to connect to a server. However, in this post I will not dive into the routing issue I encountered. Instead, I will briefly introduce the policy-based routing in Linux, which I learned during the issue debugging.

When a IP packet comes to a router, usually the packet is routed according to the destination IP address of the packet. However, a different algorithm is used in Linux. This is called policy-based routing, which uses not only the destination IP address of the packet, but also the source IP address of the packet, the incoming network interface of the packet, the source/destination port of the packet and so on to make the routing decision. In the following sections I will describe how to setup the policy based routing step by step.

1. create IP routing rules using the ‘ip route add SELECTOR ACTION’ command.

The SELECTOR should be something that selects a particular IP packet, for example, the source/destination IP address, the source/destination port, the incoming network interface and so on. The ACTION should be some action, which the routing system should do when the packet matches the SELECTOR criteria, For example, looking up some routing table, selecting some realm, and so on. In practice looking up some routing table is the most common action.

ip rule add from 10.10.10.10 table 100
ip rule add to 12.12.12.12 priority 300 table main

The first rule designates that the routes for the packets with source IP address 10.10.10.10 should be looked up in routing table with ID 100. The second rule designates that the routes for packets with destination IP address 12.12.12.12 should be looked up in routing table with ID 200. This IP routing rule has priority 300. The below command can be used to list the current IP routing rules:

#ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default

2. populate IP routing table using the ‘ip route add ROUTE’ command.

There are three routing tables by default in Linux, which are local, main, default tables. The local table is maintained by Linux kernel and usually shouldn’t be modified. The main table is setup by Linux kernel and can be modified. This table contains some routes that are populated by Linux during bootup. The default table usually is empty.

Читайте также:  Vps сервер linux doska info

A custom routing table can be created by adding a number (1-255) and a name to /etc/iproute2/rt_tables file. For example,

echo 200 custom >> /etc/iproute2/rt_tables

This creates a routing table with ID 200 and name ‘custom’. With a routing table in hand, we can populate routes in the table now using ‘ip route add ROUTE’ command. For example,

ip route add table 200 to 192.168.10.0/24 dev eth1
ip route add table 200 to 192.168.20.0/24 via 10.10.10.23 dev eth0
ip route add table 200 default via 10.10.10.23 dev eth0

The first route specifies that the packets to subnet 192.168.10.0/24 should be routed to eth1 network interface, which connects to a switch. The packet will be directly routed to the destination by the switch. The second route specifies that the packets to subnet 192.168.20.0/24 should be routed to the gateway with IP 10.10.10.23 and the gateway can be reached directly through eth0 network interface. The third route specifies that all the other packets should be routed to the gateway with IP 10.10.10.23 through eth0 network interface. The gateway will know how to route those packets to the next hope.

This is a brief introduction about policy-based routing in Linux. Hope this can help you.

Источник

Роутинг и policy-routing в Linux при помощи iproute2

Речь в статье пойдет о роутинге сетевых пакетов в Linux. А конкретно – о типе роутинга под названием policy-routing (роутинг на основании политик). Этот тип роутинга позволяет маршрутизировать пакеты на основании ряда достаточно гибких правил, в отличие от классического механизма маршрутизации destination-routing (роутинг на основании адреса назначения). Policy-routing применяется в случае наличия нескольких сетевых интерфейсов и необходимости отправлять определенные пакеты на определенный интерфейс, причем пакеты определяются не по адресу назначения или не только по адресу назначения. Например, policy-routing может использоваться для: балансировки трафика между несколькими внешними каналами (аплинками), обеспечения доступа к серверу в случае нескольких аплинков, при необходимости отправлять пакеты с разных внутренних адресов через разные внешние интерфейсы, даже для отправки пакетов на разные TCP-порты через разные интерфейсы и т.д.
Для управления сетевыми интерфейсами, маршрутизацией и шейпированием в Linux служит пакет утилит iproute2.

Этот набор утилит лишь задает настройки, реально вся работа выполняется ядром Linux. Для поддержки ядром policy-routing оно должно быть собрано с включенными опциями IP: advanced router (CONFIG_IP_ADVANCED_ROUTER) и IP: policy routing (CONFIG_IP_MULTIPLE_TABLES), находящимися в разделе Networking support -> Networking options -> TCP/IP networking.

ip route

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

# ip route 192.168.12.0/24 dev eth0 proto kernel scope link src 192.168.12.101 default via 192.168.12.1 dev eth0

Так будет выглядеть роутинг при использовании на интерфейсе eth0 IP-адреса 192.168.12.101 с маской подсети 255.255.255.0 и шлюзом по умолчанию 192.168.12.1.
Мы видим, что трафик на подсеть 192.168.12.0/24 уходит через интерфейс eth0. proto kernel означает, что роутинг был задан ядром автоматически при задании IP интерфейса. scope link означает, что эта запись является действительной только для этого интерфейса (eth0). src 192.168.12.101 задает IP-адрес отправителя для пакетов, попадающих под это правило роутинга.
Трафик на любые другие хосты, не попадающие в подсеть 192.168.12.0/24 будет уходить на шлюз 192.168.12.1 через интерфейс eth0 ( default via 192.168.12.1 dev eth0 ). Кстати, при отправке пакетов на шлюз, IP-адрес назначения не изменяется, просто в Ethernet-фрейме в качестве MAC-адреса получателя будет указан MAC-адрес шлюза (часто даже специалисты со стажем путаются в этом моменте). Шлюз в свою очередь меняет IP-адрес отправителя, если используется NAT, либо просто отправляет пакет дальше. В данном случае используются приватный адрес (192.168.12.101), так что шлюз скорее всего делает NAT.
А теперь залезем в роутинг поглубже. На самом деле, таблиц маршрутизации несколько, а также можно создавать свои таблицы маршрутизации. Изначально предопределены таблицы local, main и default. В таблицу local ядро заносит записи для локальных IP адресов (чтобы трафик на эти IP-адреса оставался локальным и не пытался уходить во внешнюю сеть), а также для бродкастов. Таблица main является основной и именно она используется, если в команде не указано какую таблицу использовать (т.е. выше мы видели именно таблицу main). Таблица default изначально пуста. Давайте бегло взглянем на содержимое таблицы local:

# ip route show table local broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 broadcast 192.168.12.255 dev eth0 proto kernel scope link src 192.168.12.101 broadcast 192.168.12.0 dev eth0 proto kernel scope link src 192.168.12.101 local 192.168.12.101 dev eth0 proto kernel scope host src 192.168.12.101 broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1

broadcast и local определяют типы записей (выше мы рассматривали тип default ). Тип broadcast означает, что пакеты соответствующие этой записи будут отправлены как broadcast-пакеты, в соответствии с настройками интерфейса. local – пакеты будут отправлены локально. scope host указывает, что эта запись действительная только для этого хоста.
Для просмотра содержимого конкретной таблицы используется команда ip route show table TABLE_NAME . Для просмотра содержимого всех таблиц в качестве TABLE_NAME следует указывать all , unspec или 0 . Все таблицы на самом деле имеют цифровые идентификаторы, их символьные имена задаются в файле /etc/iproute2/rt_tables и используются лишь для удобства.

Читайте также:  Linux проверить web камеру

ip rule

Как же ядро выбирает, в какую таблицу отправлять пакеты? Все логично – для этого есть правила. В нашем случае:

# ip rule 0: from all lookup local 32766: from all lookup main 32767: from all lookup default
  • from – мы уже рассматривали выше, это проверка отправителя пакета.
  • to – получатель пакета.
  • iif – имя интерфейса, на который пришел пакет.
  • oif – имя интерфейса, с которого уходит пакет. Это условие действует только для пакетов, исходящих из локальных сокетов, привязанных к конкретному интерфейсу.
  • tos – значение поля TOS IP-пакета.
  • fwmark – проверка значения FWMARK пакета. Это условие дает потрясающую гибкость правил. При помощи правил iptables можно отфильтровать пакеты по огромному количеству признаков и установить определенные значения FWMARK. А затем эти значения учитывать при роутинге.

Простой пример

Теперь рассмотрим простой пример. У нас есть некий шлюз, на него приходят пакеты с IP 192.168.1.20. Пакеты с этого IP нужно отправлять на шлюз 10.1.0.1. Чтобы это реализовать делаем следующее:
Создаем таблицу с единственным правилом:

# ip route add default via 10.1.0.1 table 120
# ip rule add from 192.168.1.20 table 120

Доступность сервера через несколько аплинков

Теперь более реалистичный пример. Имеется два аплинка до двух провайдеров, необходимо обеспечить доступность сервера с обоих каналов:

В качестве маршрута по умолчанию используется один из провайдеров, не важно какой. При этом веб-сервер будет доступен только через сеть этого провайдера. Запросы через сеть другого провайдера приходить будут, но ответные пакеты будут уходить на шлюз по умолчанию и ничего из этого не выйдет.
Решается это весьма просто:
Определяем таблицы:

# ip route add default via 11.22.33.1 table 101 # ip route add default via 55.66.77.1 table 102
# ip rule add from 11.22.33.44 table 101 # ip rule add from 55.66.77.88 table 102

Думаю теперь уже объяснять смысл этих строк не надо. Аналогичным образом можно сделать доступность сервера по более чем двум аплинкам.

Читайте также:  Линукс для ноутбука что это

Балансировка трафика между аплинками

# ip route replace default scope global \ nexthop via 11.22.33.1 dev eth0 weight 1 \ nexthop via 55.66.77.1 dev eth1 weight 1

Эта запись заменит существующий default-роутинг в таблице main. При этом маршрут будет выбираться в зависимости от веса шлюза ( weight ). Например, при указании весов 7 и 3, через первый шлюз будет уходить 70% соединений, а через второй – 30%. Есть один момент, который при этом надо учитывать: ядро кэширует маршруты, и маршрут для какого-либо хоста через определенный шлюз будет висеть в таблице еще некоторое время после последнего обращения к этой записи. А маршрут до часто используемых хостов может не успевать сбрасываться и будет все время обновляться в кэше, оставаясь на одном и том же шлюзе. Если это проблема, то можно иногда очищать кэш вручную командой ip route flush cache .

Использование маркировки пакетов при помощи iptables

Допустим нам нужно, чтобы пакеты на 80 порт уходили только через 11.22.33.1. Для этого делаем следующее:

# iptables -t mangle -A OUTPUT -p tcp -m tcp --dport 80 -j MARK --set-mark 0x2 # ip route add default via 11.22.33.1 dev eth0 table 102 # ip rule add fwmark 0x2/0x2 lookup 102

Первой командой маркируем все пакеты, идущие на 80 порт. Второй командой создаем таблицу маршрутизации. Третьей командой заворачиваем все пакеты с указанной маркировкой в нужную таблицу.
Опять же все просто. Рассмотрим также использование модуля iptables CONNMARK. Он позволяет отслеживать и маркировать все пакеты, относящиеся к определенному соединению. Например, можно маркировать пакеты по определенному признаку еще в цепочке INPUT, а затем автоматически маркировать пакеты, относящиеся к этим соединениям и в цепочке OUTPUT. Используется он так:

# iptables -t mangle -A INPUT -i eth0 -j CONNMARK --set-mark 0x2 # iptables -t mangle -A INPUT -i eth1 -j CONNMARK --set-mark 0x4 # iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark

Пакеты, приходящие с eth0 маркируются 2, а с eth1 – 4 (строки 1 и 2). Правило на третьей строке проверяет принадлежность пакета к тому или иному соединению и восстанавливает маркировки (которые были заданы для входящих) для исходящих пакетов.
Надеюсь изложенный материал поможет вам оценить всю гибкость роутинга в Linux. Спасибо за внимание 🙂

Источник

Оцените статью
Adblock
detector