Жили-были несколько серверов, в разных локациях. Сколько именно - это непринципиально, поэтому упростим, пусть будет два: A и B.

Каждый занимался своими задачами, а между собой они были соединены виртуальной приватной сетью (ну, то, что теперь нельзя называть).
Причем сеть эта использовалась по своему прямому назначению - просто обеспечить передачу локального трафика через публичные сети, дабы посторонние не совали в него свой любопытный клюв.

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

Как вы понимаете, нормальной работе это отнюдь не способствовало, пришлось искать какое-то решение. А ведь оно давно известно - динамическая маршрутизация!
Просто до поры она, казалось бы, совсем тут не нужна - не те масштабы, да и работало всё прекрасно...

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

Каналы связи в данном случае - различные виды того-что-нельзя-называть в различных сочетаниях.
Тут вполне подходит протокол OSPF: он как раз и предназначен для решения подобных задач.
Не буду пытаться обьяснять тонкости работы лучше, чем это делают профессионалы-сетевики, просто остановлюсь на некоторых практических нюансах реализации.

А роутеры будут программные - для этого можно использовать пакет FRR.
Он стандартно присутствует в репозитарии Debian (apt search frr), но как показалось - лучше сделать немного по-другому: запустить его через docker - тогда на всех узлах он будет одной версии, и не будет ни с чем конфликтовать.
Есть даже готовый образ - но, по обычаю, можно сделать и самостоятельно:

mkdir -p /etc/frr
docker run -ti --name frr -v /etc/frr:/etc/frr --privileged --network host debian:latest

Это запускает контейнер с правами, близкими к хост-системе - но при этом сохраняется некоторая изоляция от системных библиотек и установленных версий ОС.
Попадаем внутрь контейнера - устанавливаем:

apt update
apt upgrade
apt install frr
exit

Теперь в контейнере живет frr, а его настроечные файлы - в /etc/frr host-системы.
То есть, их можно редактировать вручную в привычном редакторе.

vim /etc/frr/daemons
ospfd=yes

Теперь немного сетевых настроек.
Разрешаем форвардинг, если еще не:

sysctl net.ipv4.ip_forward=1

Назначаем фиксированные, независимые от интерфейсов адреса для серверов, присваивая их локальному интерфейсу, ну например, адрес сервера A - 192.168.10.1
Таким образом, их наличие никак не будет связано с работоспособностью тех или иных сетевых интерфейсов:

ip addr add 192.168.10.1/32 dev lo

Разбираемся с каналами: это у нас различные типы того-что-нельзя-называть, причем нам от них по сути нужна связь "точка-точка": соединяются сервера A, B, а также несколько дополнительных, размещенных в других локациях - для организации подобия mesh-сети.

По сути для каждого из них нужны всего 2 адреса + широковещательный и адрес подсети, итого 4.
Для этого можно выделить специальную отдельную подсеть, например, 192.168.5.0, и нарезать ее на подсети /30.
Кто забыл: маска подсети - количество неизменяемых битов подсети, т.е. /24 - первые три октета фиксированы(192.168.5.), и еще 6 бит - тоже.
В двоичном виде последний октет будет выглядеть так:

(0000 00)00 - в скобках фиксированная часть подсети, без скобок - 00, 01, 10, 11 - адреса сети, узлов и широковещательный соответственно.
А номера сетей меняются от 0000 00 до 1111 11.

Поскольку всё это принято записывать в десятичном виде - можно для удобства использовать встроенную в шелл возможность расчёта:

echo $(( 2#00000001 ))  # 1 - 1 адрес сети 0
echo $(( 2#00000010 ))  # 2 - 2 адрес сети 0
echo $(( 2#00000101 ))  # 5 - 1 адрес сети 1
echo $(( 2#00000110 ))  # 6 - 2 адрес сети 1
echo $(( 2#00001001 ))  # 9 - 1 адрес сети 2
echo $(( 2#00001010 ))  # 10 - 2 адрес сети 2

Ну и так далее. Несложно.
К примеру, связываем сервера A и B двумя прямыми каналами:

192.168.5.1/30 - 192.168.5.2/30  # то-что-нельзя-называть тип 1
192.168.5.5/30 - 192.168.5.6/30  # то-что-нельзя-называть тип 2

Ну и так далее. Еще раз отмечу, что это чисто "связные подсети", на них не будет работающих сервисов.
Сервисы у нас на 192.168.10.1 и подобных.

Теперь запускаем frr:

docker start frr
docker exec -ti frr /usr/lib/frr/frrinit.sh start

У frr есть Cisco-подобный интерфейс, через который можно делать настройки и смотреть состояние:
(TAB помогает)

docker exec -ti frr vtysh
configure terminal
router ospf
ospf router-id 1.1.1.1
maximum-paths 1
network 192.168.10.1/32 area 0
network 192.168.5.0/24 area 0
exit
exit
write memory
exit

И смотрим, что получилось:

vim /etc/frr/frr.conf

frr version 10.3
frr defaults traditional
hostname alice
log syslog informational
service integrated-vtysh-config
!
router ospf
 ospf router-id 1.1.1.1
 maximum-paths 1
 network 192.168.10.1/32 area 0
 network 192.168.5.0/24 area 0
exit
!

Синтаксис простой, блоки выделяются отступом, символ ! или # - комментарий, в данном случае они вообще не обязательны и служат просто для визуальной отметки конца блока.
(тут, к слову, та самая ситуация, когда один лишний пробел может поломать всё)

Если что-то поменять - потом достаточно перезапустить frr:

docker exec -ti frr /usr/lib/frr/frrinit.sh restart

Ну или воспользоваться vtysh для правки, кому что привычнее.

Что сейчас настроено:
- роутер OSPF с ID 1.1.1.1 (похоже на IP, но на самом деле просто номер, номера должны быть разными)
- анонсируется маршрут к сети 192.168.10.1/32, состоящей из единственного адреса.
- анонсируются маршруты всех сетей, попадающих в маску 192.168.5.0/24, то есть всех "связных".
- ограничено количество одновременно работающих путей - чтобы не нарушалась работа TCP-соединений.

По интерфейсам, подпадающим под маску, будут рассылаться сообщения Hello от OSPF - предполагается, что с другой стороны их будут слушать другие OSPF-роутеры.

По такой же схеме настраиваем FRR на других машинах.
Если там нет своих "сервисных адресов" - достаточно указать только одну сеть, через которую идёт связь, в данном случае 192.168.5.0/24

И в общем-то это всё.
Перезапускаются frr, они обмениваются между собой данными о доступных маршрутах - и теперь на любой машине можно обратиться к 192.168.10.1 - запрос уйдет именно на сервер A, по кратчайшему маршруту, в зависимости от того какие каналы где сейчас работают.

Проверить работу можно так:

docker exec -ti frr vtysh
show running-config
show ip ospf neighbor
show ip ospf route
...

А вот теперь нюансы:

Протокол OSPF - это не TCP и не UDP, это отдельный протокол 89.
Поэтому некоторые, эээ, каналы, его не пропускают.
Решить эту проблему можно примерно следующим образом: сначала поднять просто TCP/IP канал, потом поверх него VXLAN (L2, "езернет", инкапсулированный в UDP), потом поверх него "связную сеть", и вот уже тогда пускать OSPF.
Слоёный пирог, но работает. Альтернатива - использовать BGP, но там настройка несколько сложнее.

Address = 192.168.3.1/30
Table = off
PostUp = ip link add vxlwgA type vxlan id 12 dev %i remote 192.168.3.2 dstport 4789
PostUp = ip link set dev vxlwgA up
PostUp = ip addr add 192.168.5.9/30 dev vxlwgA

В данном случае сначала устанавливается "транспортная подсеть" 192.168.3.0/30, поверх нее "связная подсеть" 192.168.5.4/30, и вот уже по ней и будет работать OSPF.
А 192.168.3.0 в маршруты не попадёт (хотя можно и ее добавить, еще одной network).

Заметим, что статические маршруты тут не нужны, и даже вредны - оно само всё разрулит лучше.

Теперь включаем ping, и начинаем портить:

  • отключаем один прямой канал - маршрут перестаивается через другой.

  • отключаем второй - маршрут перестраивается в обход, через другой интерфейс.

  • отключаем и там - маршрут пошел через третий сервер

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

Комментарии (2)


  1. aborouhin
    23.11.2025 11:19

    Иногда одна известная организация ломает инет так, что связность вроде бы и есть, но по факту пользоваться невозможно, потери пакетов >30% и latency плавает вплоть до нескольких секунд. И вот тут к OSPF (который у меня уже настроен ровно для той же задачи, что Вы решаете, только что bird, а не frr) неплохо бы добавить BFD. Никак руки не дойдут, так что изредка такие "зомби-каналы" приходится руками отключать, чтобы OSPF их мёртвыми признал.


    1. JBFW Автор
      23.11.2025 11:19

      Совершенно верно

      И bfd там тоже есть, кстати, надо его с ospf подружить...