Linux VRF and Namespaces: различия между версиями
Sirmax (обсуждение | вклад) |
Sirmax (обсуждение | вклад) Метка: ручная отмена |
||
| (не показано 14 промежуточных версий этого же участника) | |||
| Строка 90: | Строка 90: | ||
* Сложно делать Route Leaking (когда нужно часть или весь трафик пускать между VRF). Понадобится "виртуальный патчкорд" <code>veth</code> между VRF. Сделать конечно можно, масштабируемость только очень сомнительная. |
* Сложно делать Route Leaking (когда нужно часть или весь трафик пускать между VRF). Понадобится "виртуальный патчкорд" <code>veth</code> между VRF. Сделать конечно можно, масштабируемость только очень сомнительная. |
||
| − | * Слишком полная изоляция |
+ | * Слишком полная изоляция |
| + | * VRF решает задачу изоляции FIB (при этом не стоит путать FIB и таблицу маршрутизации, FIB содержит лучшие маршруты, тогда как таблица маршрутизации - все маршруты, в том числе полученные от протоколов динамической маршрутизации но не используемые прямо в данный момент как неоптимальные) |
||
| + | * Неудобно работать - такая простая задача как просмотр всех интерфейсов требует сначала <code>ip netns</code> а потом смотреть отдельно по каждому неймспейсу |
||
| + | * В целом - управление сетью с netns вместо vrf становится слишком сложный - тот же процесс lldp должен будет запустить столько своих экземпляров сколько есть VRF и это же касается BGPв, и (кроме того что нужно будет написать сои расширения для всех демонов) еще и требует "пропихивания"в апстрим. |
||
| + | |||
| + | {{#spoiler:show=Оригинал| |
||
<PRE> |
<PRE> |
||
Network namespaces were designed to provide a complete isolation of the entire network stack — devices, network addresses, neighbor and routing tables, protocol ports and sockets. |
Network namespaces were designed to provide a complete isolation of the entire network stack — devices, network addresses, neighbor and routing tables, protocol ports and sockets. |
||
| Строка 96: | Строка 101: | ||
</PRE> |
</PRE> |
||
| − | * VRF решает задачу изоляции FIB (при этом не стоит путать FIB и таблицу маршрутизации, FIB содержит лучшие маршруты, тогда как таблица маршрутизации - все маршруты, в том числе полученные от протоколов динамической маршрутизации но не используемые прямо в данный момент как неоптимальные) |
||
<PRE> |
<PRE> |
||
VRF on the other hand is a network layer feature and as such should really only impact FIB lookups. |
VRF on the other hand is a network layer feature and as such should really only impact FIB lookups. |
||
| Строка 102: | Строка 106: | ||
</PRE> |
</PRE> |
||
| + | |||
| − | * Пример ниже - о том что бы посмотреть списки интерфейсов нужно сначала сделать <PRE>ip netns</PRE> а потом смотреть отдельно по каждому неймспейсу |
||
| − | * В целом - управление сетью с netns вместо vrf становится слишком сложный - тот же процесс lldp должен будет запустить столько своих экземпляров сколько есть VRF и это же касается BGPв, и (кроме того что нужно будет написать сои расширения для всех демонов) еще и требует "пропихивания"в апстрим. |
||
<PRE> |
<PRE> |
||
Let’s walk through a few a simple examples to highlight what I mean about impact on the software architecture and operational model of a network namespace as VRF. |
Let’s walk through a few a simple examples to highlight what I mean about impact on the software architecture and operational model of a network namespace as VRF. |
||
| Строка 109: | Строка 112: | ||
Because a process in Linux is limited to a single network namespace, it will only see network devices, addresses, routes, even networking related data under /proc and /sys local to that namespace. For example, if you run ‘ip link list’ to list network devices, it will only show the devices in the namespace in which it is run. This command is just listing network interfaces, not transmitting packets via the network layer or using network layer sockets. Yet, using a namespace as a VRF impacts the view of all network resources by all processes. |
Because a process in Linux is limited to a single network namespace, it will only see network devices, addresses, routes, even networking related data under /proc and /sys local to that namespace. For example, if you run ‘ip link list’ to list network devices, it will only show the devices in the namespace in which it is run. This command is just listing network interfaces, not transmitting packets via the network layer or using network layer sockets. Yet, using a namespace as a VRF impacts the view of all network resources by all processes. |
||
</PRE> |
</PRE> |
||
| + | |||
<PRE> |
<PRE> |
||
A NOS is more than just routing protocols and programming hardware. You need supporting programs such as lldpd for verifying network connectivity, system monitoring tools such as snmpd, tcollector and collectd and debugging tools like ‘netstat’, ‘ip’, ‘ss’. Using a namespace as a VRF has an adverse impact on all of them. An lldpd instance can only use the network devices in the namespace in which lldpd runs. Therefore, if you use a network namespace for a VRF, you have to run a separate instance of lldpd for each VRF. Deploying N-VRFs means starting N-instances of lldpd. VRF is a layer 3 feature, yet the network namespace choice means users have to run multiple instances of L2 applications. |
A NOS is more than just routing protocols and programming hardware. You need supporting programs such as lldpd for verifying network connectivity, system monitoring tools such as snmpd, tcollector and collectd and debugging tools like ‘netstat’, ‘ip’, ‘ss’. Using a namespace as a VRF has an adverse impact on all of them. An lldpd instance can only use the network devices in the namespace in which lldpd runs. Therefore, if you use a network namespace for a VRF, you have to run a separate instance of lldpd for each VRF. Deploying N-VRFs means starting N-instances of lldpd. VRF is a layer 3 feature, yet the network namespace choice means users have to run multiple instances of L2 applications. |
||
</PRE> |
</PRE> |
||
| + | |||
<PRE> |
<PRE> |
||
That limitation applies to system monitoring applications such as snmpd, tcollector and collectd as well. For these tools to list and provide data about the networking configuration, statistics or sockets in a VRF they need a separate instance per VRF. N-VRFs means N-instances of the applications with an additional complication of how the data from the instances are aggregated and made available via the management interface. |
That limitation applies to system monitoring applications such as snmpd, tcollector and collectd as well. For these tools to list and provide data about the networking configuration, statistics or sockets in a VRF they need a separate instance per VRF. N-VRFs means N-instances of the applications with an additional complication of how the data from the instances are aggregated and made available via the management interface. |
||
</PRE> |
</PRE> |
||
| + | |||
<PRE> |
<PRE> |
||
And these examples are just infrastructure for a network OS. How a VRF is implemented is expected to impact network layer routing protocols such as quagga/bgp. Modifying them to handle the implementation of a VRF is required. But even here the choice is either running N-versions of bgpd or modifying bgpd to open a listen socket for each VRF which means N-listen sockets. As N scales into the 100’s or 1000’s this is wasted resources spinning up all of these processes or opening listen sockets for each VRF. |
And these examples are just infrastructure for a network OS. How a VRF is implemented is expected to impact network layer routing protocols such as quagga/bgp. Modifying them to handle the implementation of a VRF is required. But even here the choice is either running N-versions of bgpd or modifying bgpd to open a listen socket for each VRF which means N-listen sockets. As N scales into the 100’s or 1000’s this is wasted resources spinning up all of these processes or opening listen sockets for each VRF. |
||
</PRE> |
</PRE> |
||
| + | |||
<PRE> |
<PRE> |
||
Yes, you could modify each of the code bases to be namespace aware. For example, as a VRF (network namespaces) is created and destroyed the applications either open socket local to the namespace, spawn a thread for the namespace or just add it to a list of namespaces to poll. But there are practical limitations with this approach – for example the need to modify each code base and work with each of the communities to get those changes accepted. In addition, it still does not resolve the N-VRF scalability issue as there is still 1 socket or 1 thread per VRF or the complexity of switching in and out namespaces. Furthermore, what if a network namespace is created for something other than a VRF? For example, a container is created to run an application in isolation, or you want to create a virtual switch with a subset of network devices AND within the virtual switch you want to deploy VRFs? Now you need a mechanism to discriminate which namespaces are VRFs. This option gets complicated quick. |
Yes, you could modify each of the code bases to be namespace aware. For example, as a VRF (network namespaces) is created and destroyed the applications either open socket local to the namespace, spawn a thread for the namespace or just add it to a list of namespaces to poll. But there are practical limitations with this approach – for example the need to modify each code base and work with each of the communities to get those changes accepted. In addition, it still does not resolve the N-VRF scalability issue as there is still 1 socket or 1 thread per VRF or the complexity of switching in and out namespaces. Furthermore, what if a network namespace is created for something other than a VRF? For example, a container is created to run an application in isolation, or you want to create a virtual switch with a subset of network devices AND within the virtual switch you want to deploy VRFs? Now you need a mechanism to discriminate which namespaces are VRFs. This option gets complicated quick. |
||
</PRE> |
</PRE> |
||
| + | |||
<PRE> |
<PRE> |
||
And then there is consideration of how the VRF implementation maps to hardware for offload of packet forwarding. |
And then there is consideration of how the VRF implementation maps to hardware for offload of packet forwarding. |
||
</PRE> |
</PRE> |
||
| + | |||
<PRE> |
<PRE> |
||
I could go on, but hopefully you get my point: The devil is in the details. Many people and many companies have tried using network namespaces for VRFs, and it has proven time and time again to be a square peg for a round hole. You can make it fit with enough sweat and tears, but it really is forcing a design that was not meant to be. Network namespaces are great for containers where you want strict isolation in the networking stack. Namespaces are also a great way to create virtual switches, carving up a single physical switch into multiple smaller and distinct logical switches. But network namespaces are a horrible solution when you just want layer 3 isolation. |
I could go on, but hopefully you get my point: The devil is in the details. Many people and many companies have tried using network namespaces for VRFs, and it has proven time and time again to be a square peg for a round hole. You can make it fit with enough sweat and tears, but it really is forcing a design that was not meant to be. Network namespaces are great for containers where you want strict isolation in the networking stack. Namespaces are also a great way to create virtual switches, carving up a single physical switch into multiple smaller and distinct logical switches. But network namespaces are a horrible solution when you just want layer 3 isolation. |
||
</PRE> |
</PRE> |
||
| + | }} |
||
=<code>ip link add vrf-1</code>= |
=<code>ip link add vrf-1</code>= |
||
| + | Тут все относительно несложно |
||
| + | |||
| + | Приведу тестовую схему: |
||
| + | <BR> |
||
| + | <BR> |
||
| + | [[Файл:VRF 2.drawio.png]] |
||
| + | <BR> |
||
| + | ([[Файл:VRF 2.drawio]]) |
||
| + | |||
| + | На ней есть |
||
| + | * CE1 - роутер, имитирующий клиента ( реализован с помощью network namespace) |
||
| + | * PE1 - виртуальный интерфейс, соединенный с CE1 и добавленный к vrf-Red |
||
| + | * Глобальная таблица маршрутизации (в общем случае это еще один VRF, просто в него добавлены все интерфейсы по-умолчанию) |
||
| + | * внешний интерфейс |
||
| + | ==Настройка== |
||
| + | |||
| + | ===CE1 - "виртуальный клиентский роутер"=== |
||
| + | В реальной жизни вместо интерфейса veth был бы физический интерфейс, но для тестирования сойдет и так. А вместо CE1 было б отдельное устройство |
||
| + | Создать network namespace ce1 |
||
| + | <PRE> |
||
| + | ip netns add ce1 |
||
| + | </PRE> |
||
| + | |||
| + | |||
| + | |||
| + | Добавить в него интерфейс ce1 |
||
| + | <PRE> |
||
| + | ip link set ce1 netns ce1 |
||
| + | </PRE> |
||
| + | Интерфес типа veth должен быть предварительно создан, например используя netplan |
||
| + | <PRE> |
||
| + | network: |
||
| + | version: 2 |
||
| + | virtual-ethernets: |
||
| + | pe1: |
||
| + | peer: ce1 |
||
| + | ce1: |
||
| + | peer: pe1 |
||
| + | dhcp4: false |
||
| + | dhcp6: false |
||
| + | addresses: ["192.168.98.1/24"] |
||
| + | </PRE> |
||
| + | Дальнейшие шаги уже требуется выполнять внутри network namespace ce1 |
||
| + | <BR> |
||
| + | "Поднять" интерфейсы lo и ce1 |
||
| + | <PRE> |
||
| + | ip netns exec ce1 ip link set up dev lo |
||
| + | </PRE> |
||
| + | |||
| + | <PRE> |
||
| + | ip netns exec ce1 ip link set up dev ce1 |
||
| + | <PRE> |
||
| + | Назначить адрес на интерфейс ce1 |
||
| + | <PRE> |
||
| + | ip netns exec ce1 ip addr add 192.168.98.1/24 dev ce1 |
||
| + | </PRE> |
||
| + | |||
| + | Добавить на виртуальном "клиентском роутере" шлюз "в провайдера" |
||
| + | <PRE> |
||
| + | ip netns exec ce1 ip ro add 0.0.0.0/0 via 192.168.98.101 |
||
| + | </PRE> |
||
| + | |||
| + | На этом настройка CE1 завершена |
||
| + | |||
| + | ===vrf-Red=== |
||
| + | Создать vrf, назначить идентификатор таблицы маршрутизации (1001 в примере) |
||
| + | <PRE> |
||
| + | ip link add vrf-Red type vrf table 1001 |
||
| + | </PRE> |
||
| + | Если проверить то сразу после создания можно увидеть интерфейс особого типа - vrf |
||
| + | <PRE> |
||
| + | ip link show vrf-Red |
||
| + | </PRE> |
||
| + | <PRE> |
||
| + | 9: vrf-Red: <NOARP,MASTER,UP,LOWER_UP> mtu 65575 qdisc noqueue state UP mode DEFAULT group default qlen 1000 |
||
| + | link/ether ce:f3:16:b7:a0:34 brd ff:ff:ff:ff:ff:ff promiscuity 0 allmulti 0 minmtu 1280 maxmtu 65575 |
||
| + | vrf table 1001 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 |
||
| + | </PRE> |
||
| + | |||
| + | Добавить интерфейс pe1 к vrf-Red |
||
| + | <PRE> |
||
| + | ip link set dev pe1 master vrf-Red |
||
| + | </PRE> |
||
| + | |||
| + | "Поднять" оба интерфейса |
||
| + | <PRE> |
||
| + | ip link set up dev pe1 |
||
| + | </PRE> |
||
| + | |||
| + | <PRE> |
||
| + | ip link set up dev vrf-Red |
||
| + | </PRE> |
||
| + | Назначить адрес на интерфейс "в сторону клиента" - pe1 |
||
| + | <PRE> |
||
| + | ip addr add 192.168.98.101/24 dev pe1 |
||
| + | </PRE> |
||
| + | |||
| + | После выполнения этих команд, можно проверить ping из виртуального клиентского роутера СЕ1 на его шлюз: |
||
| + | <PRE> |
||
| + | ip netns exec ce1 ping 192.168.98.101 |
||
| + | PING 192.168.98.101 (192.168.98.101) 56(84) bytes of data. |
||
| + | 64 bytes from 192.168.98.101: icmp_seq=1 ttl=64 time=0.036 ms |
||
| + | </PRE> |
||
| + | |||
| + | ==Route Leaking== |
||
| + | Пока все идет по плану, но в vrf-Red есть только один маршрут, и нет маршрута по-умолчанию: |
||
| + | <PRE> |
||
| + | ip ro show vrf vrf-Red |
||
| + | 192.168.98.0/24 dev pe1 proto kernel scope link src 192.168.98.101 |
||
| + | </PRE> |
||
| + | <big>'''Добавить маршрут через Route Leaking ведуший в другой VRF (глобальный!)'''</big> |
||
| + | <BR> |
||
| + | Это не совсем обычнй маршрут по-тому что указывается <code>dev external</code>, при этом интерфейс с именем <code>external</code> находится в глобальном VRF |
||
| + | <PRE> |
||
| + | ip route add 0.0.0.0/0 via 192.168.22.254 vrf vrf-Red dev external |
||
| + | </PRE> |
||
| + | |||
| + | <code>dev external</code> это не "магическое" слово, просто так переименован один из физических интерфейсов. |
||
| + | <PRE> |
||
| + | ip -d link show dev external |
||
| + | 4: external: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 |
||
| + | link/ether 00:99:00:00:00:03 brd ff:ff:ff:ff:ff:ff promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 parentbus virtio parentdev virtio2 |
||
| + | altname enp0s5 |
||
| + | </PRE> |
||
=Ссылки= |
=Ссылки= |
||
Текущая версия на 18:04, 30 апреля 2025
Network Namespaces или VRF
Эта статья задумывается как полуперевод-полукомпиляция и возможно с добавлением собственного опыта
Зачем нужен VRF
VRF это способ "виртуализировать" сеть, и иметь несколько полностью или частично независимых экземпляров сетевого стека.
Пример для банального небольшого провайдера на картинке:
Что тут нарисовано
- Есть 2 группу клиентов
- Беспроводные (картинка с 2 антенами это точка доступа). Им нужно строго ограничивать скорость - гонять через выделенный роутер который делает шейпинг. Если их не шейпить, то один клиент сможет занять всю полосу в эфире, остальные на той же базовой станции работать не смогут.
- Проводные, включены на скорости порта. Гонять их трафик через шейпер не только безсмысленно но и вредно - шейпер плохо работает на скоростях выше 50Мбит
|
Вместо шейпера тут может быть например файрволл для гостевой сети, суть от этого не меняется |
- Роутер/L3 свитч куда включены эти клиенты
- Шейпер, пограничный роутер и "интернет" добавлены для наглядности
(тут я не касаюсь L3VPN и вообще VPN ни в каком виде)
Задача описанная выше решается просто на любом более-менее современном L3 коммутаторе
- Для каждой группы клиентов создается свой VRF, своя таблица маршрутизации со своим шлюзом
- Так как шлюзы в разных VRF разные то часть клиентов можно направить через шейпер а другую часть мимо
- На Cisco при желании можно сделать маршрутизацию между VRF - Route Leaking что б клиенты из разных VRF могли общаться друг с другом (в случае интернет-провайдера это сделать можно, в случае с гостевой сетью конечно не желательно)
Это все было банально и все это все и так знают (VRF без MPLS в Cisco называют VRF-Lite)
Вопрос как это все сделать используя Linux
VRF в Linux
Прежде чем говорить о VRF перечислим проблемы которые решает VRF
- Изоляция на уровне L3 - разные VRF имеют независимые таблицы маршрутизации
|
C некоторой натяжкой можно считать VRF реализацией Policy Routing |
В Linux есть три способа получить разные таблицы маршрутизации для разных групп source адресов
ip rule
Первый, самый старый способ - использовать несколько таблиц маршрутизации
Насколько я помню он был "всегда" :) (пишут что с 99 года, те раньше чем я познакомился с Linux)
Для этого требуется создать табличку маршрутизации (прописывать в /etc/iproute2/rt_tables ну или зависит от дистрибутива если хочется именования)
echo «123 testtable» >> /etc/iproute2/rt_tables
ip route add default via 192.168.22.254 table testtable
Создаем правило, отправляющее нужные пакеты в нужную таблицу:
ip rule add from 192.168.1.20 table testtable
Или даже вот так
ip rule add iif eth1 table testtable
Недостатки ip rule
Это самый неудобный способ, и он не решает следующие проблемы
- Пересечение диапазонов IP адресов. Этой проблемы нет в примере, так как адреса клиентов одинаковые, но при желании делать L3VPN она обязательно возникнет
- Сложность управление и нагромождение ip rule и как следствие линейный поиск по списку с соответвующим падением производительности
- Проблемы с выбором исходящего интерфейса для локального софта
В целом для относительно небольших и не сложных конфигураций это решение можно применять и оно работает.
ip netns
Примерно с 2009 года для изоляции сети в Linux используют Network Namespaces
- Полная изоляция (в том числе можно иметь дублирование адресов в разных VRF)
- Гироко применяется, и более-менее документировано
- Используют всякие Докеры, Кубернетесы и прочие контейнеры
Подробно описывать я это все конечно не буду (документации просто полно), остановлючь не недостатках
Недостатки ip netns
Я просто оставлю цитаты со своими комментариями: Network Namespace as a VRF? Just say No
- Сложно делать Route Leaking (когда нужно часть или весь трафик пускать между VRF). Понадобится "виртуальный патчкорд"
vethмежду VRF. Сделать конечно можно, масштабируемость только очень сомнительная. - Слишком полная изоляция
- VRF решает задачу изоляции FIB (при этом не стоит путать FIB и таблицу маршрутизации, FIB содержит лучшие маршруты, тогда как таблица маршрутизации - все маршруты, в том числе полученные от протоколов динамической маршрутизации но не используемые прямо в данный момент как неоптимальные)
- Неудобно работать - такая простая задача как просмотр всех интерфейсов требует сначала
ip netnsа потом смотреть отдельно по каждому неймспейсу - В целом - управление сетью с netns вместо vrf становится слишком сложный - тот же процесс lldp должен будет запустить столько своих экземпляров сколько есть VRF и это же касается BGPв, и (кроме того что нужно будет написать сои расширения для всех демонов) еще и требует "пропихивания"в апстрим.
ip link add vrf-1
Тут все относительно несложно
Приведу тестовую схему:
(Файл:VRF 2.drawio)
На ней есть
- CE1 - роутер, имитирующий клиента ( реализован с помощью network namespace)
- PE1 - виртуальный интерфейс, соединенный с CE1 и добавленный к vrf-Red
- Глобальная таблица маршрутизации (в общем случае это еще один VRF, просто в него добавлены все интерфейсы по-умолчанию)
- внешний интерфейс
Настройка
CE1 - "виртуальный клиентский роутер"
В реальной жизни вместо интерфейса veth был бы физический интерфейс, но для тестирования сойдет и так. А вместо CE1 было б отдельное устройство Создать network namespace ce1
ip netns add ce1
Добавить в него интерфейс ce1
ip link set ce1 netns ce1
Интерфес типа veth должен быть предварительно создан, например используя netplan
network:
version: 2
virtual-ethernets:
pe1:
peer: ce1
ce1:
peer: pe1
dhcp4: false
dhcp6: false
addresses: ["192.168.98.1/24"]
Дальнейшие шаги уже требуется выполнять внутри network namespace ce1
"Поднять" интерфейсы lo и ce1
ip netns exec ce1 ip link set up dev lo
ip netns exec ce1 ip link set up dev ce1 <PRE> Назначить адрес на интерфейс ce1 <PRE> ip netns exec ce1 ip addr add 192.168.98.1/24 dev ce1
Добавить на виртуальном "клиентском роутере" шлюз "в провайдера"
ip netns exec ce1 ip ro add 0.0.0.0/0 via 192.168.98.101
На этом настройка CE1 завершена
vrf-Red
Создать vrf, назначить идентификатор таблицы маршрутизации (1001 в примере)
ip link add vrf-Red type vrf table 1001
Если проверить то сразу после создания можно увидеть интерфейс особого типа - vrf
ip link show vrf-Red
9: vrf-Red: <NOARP,MASTER,UP,LOWER_UP> mtu 65575 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether ce:f3:16:b7:a0:34 brd ff:ff:ff:ff:ff:ff promiscuity 0 allmulti 0 minmtu 1280 maxmtu 65575
vrf table 1001 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536
Добавить интерфейс pe1 к vrf-Red
ip link set dev pe1 master vrf-Red
"Поднять" оба интерфейса
ip link set up dev pe1
ip link set up dev vrf-Red
Назначить адрес на интерфейс "в сторону клиента" - pe1
ip addr add 192.168.98.101/24 dev pe1
После выполнения этих команд, можно проверить ping из виртуального клиентского роутера СЕ1 на его шлюз:
ip netns exec ce1 ping 192.168.98.101 PING 192.168.98.101 (192.168.98.101) 56(84) bytes of data. 64 bytes from 192.168.98.101: icmp_seq=1 ttl=64 time=0.036 ms
Route Leaking
Пока все идет по плану, но в vrf-Red есть только один маршрут, и нет маршрута по-умолчанию:
ip ro show vrf vrf-Red 192.168.98.0/24 dev pe1 proto kernel scope link src 192.168.98.101
Добавить маршрут через Route Leaking ведуший в другой VRF (глобальный!)
Это не совсем обычнй маршрут по-тому что указывается dev external, при этом интерфейс с именем external находится в глобальном VRF
ip route add 0.0.0.0/0 via 192.168.22.254 vrf vrf-Red dev external
dev external это не "магическое" слово, просто так переименован один из физических интерфейсов.
ip -d link show dev external
4: external: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 00:99:00:00:00:03 brd ff:ff:ff:ff:ff:ff promiscuity 0 allmulti 0 minmtu 68 maxmtu 65535 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 parentbus virtio parentdev virtio2
altname enp0s5