Маршрутизация в Linux: VRF Lite

в 12:30, , рубрики: iproute2, linux, Сетевые технологии, сети, системное администрирование, метки: , ,

Обычно VRF (Virtual Routing and Forwarding, VPN Routing and Forwarding) используется совместно с MPLS, но без оной в терминологии Cisco называется VRF-Lite. Суть этой технологии в том, что маршрутная информация, принадлежащая различным классам (например, маршруты одного клиента) изолируется друг от друга. Считается, что такие возможности есть только у «взрослых» железных решений, но это не совсем так. Благодаря наличию множественных таблиц маршрутизации, гибких политик маршрутизации всё это можно сделать и в Linux. Кому интересно, добро пожаловать под кат.

Типы маршрутов, таблицы маршрутизации и PBR в Linux

Прежде чем понять суть происходящего, познакомимся с некоторыми отличительными чертами сетевого стека Linux.

Первый отличительный момент — это специальные типы маршрутов. Когда ip-пакет приходит с какого-нибудь интерфейса, надо определить, адресован ли он этому хосту, или другому. Определяется это довольно элегантно — просто для адреса назначения ищется нужный маршрут в таблицах маршрутизации. Если пакет попадает на маршрут типа «local», значит он адресован непосредственно хосту, если нет, то значит его надо маршрутизировать дальше (при этом дальнейший маршрут уже известен) или сделать что-то ещё, в зависимости от типа маршрутов.

На данный момент поддерживается несколько типов маршрутов (подробнее о них можно посмотреть в мане ip-route, если пишет, что такого мана нет, то обновите пакет iproute на более свежий). Нас в данный момент интересуют только маршруты следующих типов:

  • unicast — обычный маршрут. Ничего интересного.
  • local — адрес назначения находится на данном хосте. После того, как выяснится, что пакет попадает на этот маршрут, будет производиться поиск подходящего сокета для него.
  • broadcast — широковещательный маршрут. Для входящих пакетов, попадающих на этот маршрут, практически нет отличий от маршрута local, за исключением дополнительных проверок на игнорирование широковещательных пакетов. Для исходящих же есть небольшое отличие: в заголовке канального уровня так же выставляется широковещательный адрес назначения при использовании широковещательных сетей.
  • unreachable — запрещающий маршрут. Для пакетов, попадающий на этот маршрут будет отослан отправителю icmp-пакет с сообщением о недоступности.
  • prohibit — подобен типу unreachable, только сообщение другое будет отправлено.
  • blackhole — пакет на этом маршруте будет молча отброшен

Таким образом, для маршрутизации транзитных пакетов нам достаточно наличия маршрута типа unicast, а для того, чтобы хост мог отвечать на пакеты, нужны ещё маршруты типов local и, опционально, broadcast. Ещё следует учесть то, что нам также нужны маршруты direct-connected сетей для того, чтобы обеспечить связность с соседними маршрутизаторами.

Маршруты сгруппированы в таблицы маршрутизации. По-умолчанию изначально в системе присутствуют три таблицы:

  • local (255) — в этой таблице находятся локальные и широковещательные маршруты. Эта таблица обслуживается автоматически и генерируется на основе адресов, назначенных интерфейсам.
  • main (254) — основная таблица маршрутизации. Автоматически в неё добавляются direct-connected маршруты. Так же если в параметрах утилиты ip не указана таблица маршрутизации, то подразумевается таблица main.
  • default (253) — таблица для маршрутов по-умолчанию. Её использование не прижилось и поэтому она, как правило, пустует всё время.

Имена таблиц хранятся в файле /etc/iproute2/rt_tables. Под номер таблицы отдано 32 бита, но максимальное количество таблиц в данный момент жёстко ограничено числом 256. Как добавлять/удалять/редактировать маршруты можно почитать в мане к ip-route.

Таблица, в которой надо искать маршруты, определяется политиками маршрутизации. Эта технология называется Policy Based Routing — маршрутизация на основе политик. Суть её в том, что основываясь на каких-либо критериях сетевого пакета мы либо выбираем таблицу, в которой надо искать маршрут, либо определяем действие, которое надо выполнить над пакетом. Каждая политика имеет номер (он может быть даже не уникальным), он же определяем приоритет. Просмотр политик осуществляется в порядке возрастания их приоритетов. Новые политики добавляются перед существующими.

На данный момент «критериями» политики являются

  • адреса источника и/или назначения
  • значение поля tos
  • интерфейс, с которого получен пакет
  • интерфейс, к которому привязан сокет
  • метка файерволла

Каждая политика имеет тип, который определяет действие над пакетом, если он под неё попадает:

  • unicast — используется по-умолчанию, при этом будет производиться поиск маршрута в заданной таблице маршрутизации.
  • blackhole/prohibit/unreachable — по действиям аналогичны соответствующим типам маршрутов.
  • nat — трансляция адресов без учёта состояний, практически не используется.
Запираем маршруты в таблицу

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

Допустим, у нас есть вот такая схема:
Маршрутизация в Linux: VRF Lite

Задача состоит в том, чтобы изолировать трафик различного «цвета» друг от друга. При этом адресные пространства цветов могут пересекаться, что делает задачу ещё более интересной. Неформально сформулируем цель: сделать так, чтобы пакеты каждого цвета не уходили за пределы своей таблицы маршрутизации и передавались только по интерфейсам своего цвета.

Для этого для каждого цвета создадим свою таблицу маршрутизации, и для удобства дадим им имя. Затем, добавим в таблицы direct-connected маршруты принадлежащих цвету интерфейсов. И в конце, добавим политики маршрутизации, чтобы пакеты использовали только свою таблицу.

Сначала назначаем интерфейсам адреса. При этом подключенные маршруты будут попадать в таблицу main, а локальные и широковещательные — в таблицу local.

ip address add 192.168.0.1/24 dev eth0
ip address add 192.168.1.1/24 dev eth1
ip address add 192.168.2.1/24 dev eth2
ip address add 192.168.3.1/24 dev eth3

Добавляем для удобства имена таблиц маршрутизации.

echo "1 RED" >> /etc/iproute2/rt_tables
echo "2 GREEN" >> /etc/iproute2/rt_tables

Добавляем прямые маршруты в соответствующие таблицы. Нужны это для того, чтобы нам были доступны соседние маршрутизаторы.

ip route add 192.168.0.0/24 dev eth0 proto static scope link table RED
ip route add 192.168.1.0/24 dev eth1 proto static scope link table GREEN
ip route add 192.168.2.0/24 dev eth2 proto static scope link table GREEN
ip route add 192.168.3.0/24 dev eth3 proto static scope link table RED

Добавляем остальные маршруты.

ip route add 10.0.0.0/24 via 192.168.2.10 dev eth2 proto static table GREEN

Добавляем политики.

ip rule add iif eth0 pref 10 lookup RED
ip rule add iif eth3 pref 10 lookup RED
ip rule add iif eth1 pref 20 lookup GREEN
ip rule add iif eth2 pref 20 lookup GREEN

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

ip route add unreachable default proto static table RED
ip rule add unreachable iif eth1 pref 21
ip rule add unreachable iif eth2 pref 21

Так же, желательно перенести все маршруты local и broadcast из таблицы local в таблицы соответствующих цветов, чтобы невозможно было обращаться к локальным интерфейсам маршрутизатора из «чужого» цвета. Для этого лучше всего написать какой-нибудь скрипт, чтобы было не так утомительно.

В итоге, наши таблицы маршрутизации и политики будут выглядеть примерно так:

unreachable default  proto static
broadcast 192.168.0.0 dev eth0  proto static  scope link
192.168.0.0/24 dev eth0  proto static  scope link
local 192.168.0.1 dev eth0  proto static  scope host  src 192.168.0.1
broadcast 192.168.0.255 dev eth0  proto static  scope link
broadcast 192.168.3.0 dev eth3  proto static  scope link
192.168.3.0/24 dev eth3  proto static  scope link
local 192.168.3.1 dev eth3  proto static  scope host  src 192.168.3.1
broadcast 192.168.3.255 dev eth3  proto static  scope link

ip route list table GREEN
10.0.0.0/8 via 192.168.2.10 dev eth2  proto static
broadcast 192.168.1.0 dev eth1  proto static  scope link
192.168.1.0/24 dev eth1  proto static  scope link
local 192.168.1.1 dev eth1  proto static  scope host  src 192.168.1.1
broadcast 192.168.1.255 dev eth1  proto static  scope link
broadcast 192.168.2.0 dev eth2  proto static  scope link
192.168.2.0/24 dev eth2  proto static  scope link
local 192.168.2.1 dev eth2  proto static  scope host  src 192.168.2.1
broadcast 192.168.2.255 dev eth2  proto static  scope link

ip rule list
0:      from all lookup local
10:     from all iif eth0 lookup 10
10:     from all iif eth3 lookup 10
20:     from all iif eth1 lookup 20
20:     from all iif eth2 lookup 20
21:     from all iif eth1 unreachable
21:     from all iif eth2 unreachable
32766:  from all lookup main
32767:  from all lookup default

Собранный в GNS3 стенд показал правильность работы схемы. При обращении к чужому цвету, отправитель получает сообщение о недоступности точки назначения, как и было задумано.

На этом пока всё, но продолжение следует. В нём постараюсь рассказать про динамическую маршрутизацию применительно к vrf с помощью демона маршрутизации bird, и обмен маршрутами между таблицами (vrf leaking).

Автор: EvilMan


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js