На Linux (Debian 8) роутере настроена маршрутизация от источника при помощи iproute2. Корректно маршрутизируются как FORWARD, так и INPUT\OUTPUT пакеты. После включения round-robin default gateway стали возникать проблемы при подключении OpenVPN клиентов. Кейс довольно редкий, поэтому решил поделиться найденным решением с сообществом в виде данной заметки.

Round-robin default gateway включен следующей командой:
ip route add default scope global nexthop via $GW1 dev $IF1 weight 1 nexthop via $GW2 dev $IF2 weight 1 nexthop via $GW3 dev $IF3 weight 1

В реальности с маршрутизацией все немного сложнее, но эти частности не сильно влияют на конечный вывод, поэтому их можно опустить. Как видно, попеременно используется три шлюза с одинаковым весом. OpenVPN сервер слушает все интерфейсы и работает по UDP протоколу. По идее, он должен работать при таких настройках, но клиенты перестали подключаться к данному серверу. Хотя тот же SSH продолжал работать при подключении по любому из интерфейсов. Можно предположить, что причина в UDP протоколе, но проверка на DNS сервере показала, что ответы на UDP запросы возвращаются с нужных интерфейсов. Таким образом, при включении Round-robin gateway, маршрутизация от источника сломалась только для OpenVPN и причину надо искать в нем.

При коннекте, перед началом рукопожатия, запрашивается логин с паролем, и авторизация проходит успешно, дальнейшее подключение не происходит, лог неудачного подключения клиента не говорит ровным счетом ничего полезного:
TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)
TLS Error: TLS handshake failed
SIGUSR1[soft,tls-error] received, process restarting
MANAGEMENT: >STATE:1451454269,RECONNECTING,tls-error
Restart pause, 2 second(s)

Интересно то, что у некоторых клиентов (по факту встретился только у одного) в логах прилетало кое-что интересное. При подключении на первый интерфейс $IP1 лог выдавал следующее:
Incoming packet rejected from [AF_INET]$IP1:1194[2], expected peer address: [AF_INET]$IP2:1194 (allow this incoming source address/port by removing --remote or adding --float)
Incoming packet rejected from [AF_INET]$IP1:1194[2], expected peer address: [AF_INET]$IP3:1194 (allow this incoming source address/port by removing --remote or adding --float)

И так по кругу. При чем, если на клиенте заменить IP адрес сервера на $IP2, то ответные пакеты будут приходить с $IP1 и $IP3, но не с $IP2. Т.е. к какому бы IP не подключался клиент, ответ никогда не приходит с того же адреса. Достаточно странное поведение. Повторю, в этот же момент маршрутизация от источника по другим подключениям работает исправно.

В процессе траблшутинга выяснилось, что при смене протокола на TCP, соединение происходит успешно и причину в надо искать в реализации работы по UDP. После этого нашлось и решение — параметр Multihome на стороне сервера OpenVPN. Man openvpn параметра multihome:

--multihome
Configure a multi-homed UDP server. This option needs to be used when a server has more than one IP address (e.g. multiple interfaces, or secondary IP addresses), and is not using --local to force binding to one
specific address only. This option will add some extra lookups to the packet path to ensure that the UDP reply packets are always sent from the address that the client is talking to. This is not supported on all
platforms, and it adds more processing, so it's not enabled by default.
Note: this option is only relevant for UDP servers.
Note 2: if you do an IPv6+IPv4 dual-stack bind on a Linux machine with multiple IPv4 address, connections to IPv4 addresses will not work right on kernels before 3.15, due to missing kernel support for the
IPv4-mapped case (some distributions have ported this to earlier kernel versions, though).

Теперь понятно, причина в принципе работы OpenVPN сервера по UDP протоколу. Опция multihome добавляет дополнительные обработки к udp пакетам, которые гарантируют отсылку ответных пакетов с адреса, на который пришел запрос. Конкретно же, что происходит, можно сказать после изучения исходников OpenVPN. Таким образом, при настройке round-robin default gateway для корректной работы OpenVPN надо или использовать TCP протокол, или добавлять параметр multihome при использовании UDP.

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


  1. Obramko
    24.02.2016 14:32
    +1

    В принципе, это нормальное поведение для UDP в Linux.


  1. lionsimba
    25.02.2016 06:43

    Удивлен, что TCP у клиентов (которые через FORWARD ходят) нормально работает...

    У меня при включенной round-robin default gateway маршрутизации TCP-соединение отказывалось устанавливаться, потому что round-robin происходил на уровне пакетов, а не на уровне соединений: т.е. первый пакет TCP-сессии идет через один гейтвей, второй через другой. TCP-сервер соответственно не ожидает, что второй пакет придет с другого IP, и соединение не устанавливается.

    Что интересно, TCP-соедиения, устанавливаемые с самого Debian-маршрутизатора, работают в такой схеме нормально.


    1. mureevms
      25.02.2016 19:06

      Предположу, что не настроена маркировка пакетов в iptables.


      1. lionsimba
        26.02.2016 07:55

        А, ну то есть у вас тоже с маркировкой для тех, кто FORWARD? Тогда вопросов больше нет. Я было подумал, что вам удалось без. Потому и удивился.


  1. igorsd
    27.02.2016 02:42

    Позвольте поинтересоваться — а зачем вам вообще rr default gw? Не могу представить себе ситуацию в которой он будет реально нужен...


    1. mureevms
      29.02.2016 08:52

      Речь конечно не идет об острой необходимости. Ситуация такова, что есть два своих провайдера с узкими каналами и третий с широким, но пустить туда весь трафик нельзя по определенным причинам. Поэтому было решено распределять нагрузку на всех троих более или менее равномерно. RRDG и является таким решением. Что характерно, после включения пользователи отметили, что "интернет стал быстрее".