Вопрос или проблема
У нас установлено множество встраиваемых устройств у клиентов, все они подключаются к нашей службе OpenVPN. В общем, это работает нормально, но у некоторых клиентов есть серьёзные проблемы с MTU пути. Мы не можем повлиять на клиентов, чтобы они исправили свои сети, поэтому нам нужно, чтобы OpenVPN справлялся с этим. Вкратце, мой вопрос заключается в следующем:
Как я могу смягчить низкие MTU пути у некоторых клиентов на основе индивидуального подхода, без использования глобальных настроек, учитывающих крайние случаи для всех клиентов
Обратите внимание, что наш худший случай довольно плох: MTU пути 576 отбрасывает все фрагменты, сам не фрагментируется и не учитывает бит DF. Вы понимаете, почему я предпочёл бы не решать эту проблему глобально.
Страница мануала OpenVPN предлагает несколько вариантов, связанных с MTU, наиболее заметные из которых --link-mtu, --tun-mtu, --fragment и --mssfix
. Но там также говорится
–link-mtu […] Лучше не устанавливать этот параметр, если вы не знаете, что делаете.
–tun-mtu […] Лучше использовать параметры –fragment и/или –mssfix для решения проблем с размером MTU.
Я начал экспериментировать с --fragment
и --mssfix
, но вскоре понял, что как минимум первый параметр должен устанавливаться не только на стороне клиента, но также на стороне сервера. Затем я рассмотрел конфигурацию на стороне сервера на основе --client-config-dir
, но там сказано
Следующие параметры допустимы в контексте, специфичном для клиента: –push, –push-reset, –iroute, –ifconfig-push и –config.
Никаких упоминаний о параметрах MTU!
Итак, вот мои более конкретные вопросы:
- Почему именно
link-mtu
иtun-mtu
не рекомендуются? Какие потенциальные проблемы могут возникнуть с этими опциями? Обратите внимание, что я вполне уверенно разбираюсь в низкоуровневом редактировании заголовков IP. - Какие из опций
link-mtu tun-mtu fragment mssfix
должны быть зеркально установлены на стороне сервера, чтобы они работали? - Какие из опций
link-mtu tun-mtu fragment mssfix
можно использовать вclient-config-dir
? - Если все четыре опции необходимо зеркально устанавливать на стороне сервера и их нельзя использовать внутри
client-config-dir
, существуют ли альтернативы для борьбы с низким MTU пути для отдельных клиентов?
Заметки:
- Некоторые из моих вопросов уже задавались 5 лет назад здесь, но они тогда не получили ответа, поэтому я осмеливаюсь их повторить.
- Сервер OpenVPN в данный момент 2.2.1 на Ubuntu 12.04. Мы готовим обновление до 2.3.2 на Ubuntu 14.04
- Клиенты OpenVPN — 2.2.1 на Debian 7.6
- Я готов вручную определить MTU пути клиента
- В данный момент мы не можем много тестировать на стороне сервера. Но мы строим совершенно отдельную тестовую среду, которая скоро будет готова.
Я решил проблему на стороне клиента, добавив опцию mssfix 1300
в файл конфигурации.
Из мануала OpenVPN:
--mssfix max
Сообщает TCP-сессиям, работающим через туннель, чтобы они ограничили свои размеры отправляемых пакетов, чтобы после того, как OpenVPN их инкапсулирует, размер UDP-пакета, который OpenVPN отправляет своему пиру, не превышал max байт.
Изначальная идея моего решения пришла с personalvpn.org
Не уверен, что это может помочь с встраиваемыми устройствами, но я делюсь своим опытом на всякий случай, если это поможет другим.
Кратко: переходите к последнему вопросу/ответу для возможного решения
- Почему именно link-mtu и tun-mtu не рекомендуются? Какие потенциальные проблемы могут возникнуть с этими опциями? Обратите внимание, что я вполне уверенно разбираюсь в низкоуровневом редактировании заголовков IP.
По моему опыту, как пользователя OpenVPN, в большинстве распространённых случаев это на самом деле fragment
и/или mssfix
, с которыми лучше не возиться, то есть в отличие от того, что говорит документация.
Значения link-mtu
и tun-mtu
вычисляются относительно друг друга в зависимости от того, какой шифр (если он используется) используется для туннеля. По умолчанию tun-mtu
должно составлять классические 1500 байт, в то время как link-mtu
выводится из этого, но вы можете установить любое из них, и, следовательно, другое будет пересчитано соответственно. В этом отношении существует довольно точное правило практики: никогда не устанавливайте оба: установите только одно, потому что другое просто будет следовать за ним.
Единственный случай, когда я сталкивался с проблемами при их изменении, это когда я установил конечные точки с очень разными значениями и с сервером, на котором было очень низкое значение, например 576, а у клиента 1500. Такие настройки просто не осуществляют обмен данными, и я никогда не углублялся в дальнейшее исследование, потому что, по правде говоря, у меня были такие настройки только в условиях искусственных лабораторных испытаний. Обратный случай, то есть низкое значение у клиента, в то время как на сервере нормальное/высокое значение, работает просто отлично.
На самом деле, я обнаружил, что установка link-mtu
или tun-mtu
(в основном я предпочитаю устанавливать link-mtu
) фактически решает большинство распространённых проблем, то есть туннель работает так, как и должен, включая любой трафик, не относящийся к TCP.
Конечно, установка значений fragment
и/или mssfix
(если они также явно установлены) должны учитывать явные значения link-mtu
/tun-mtu
, но я发现, что просто оставлять их в покое (то есть даже не упоминать в конфигурации) обычно лучшее решение, потому что они просто корректируются автоматически в соответствии со значениями link-mtu
/tun-mtu
.
Тем не менее, верно, что установка только mssfix
(оставляя link-mtu
/tun-mtu
и fragment
неуказанными) решает всё, связанное с TCP, но все остальные протоколы, скорее всего, столкнутся с проблемами, единственной причиной, по которой они обычно не сталкиваются с проблемами, является то, что большинство трафика не относящегося к TCP либо DNS по UDP, что обычно имеет небольшой размер, либо ICMP эхо-запросы/ответы, которые, опять же, по умолчанию являются небольшими пакетами.
Обратите внимание, что я говорю о OpenVPN на Linux; на других операционных системах, особенно в линейке Windows и устройствах iOS, может быть другая картина, то есть изменение link-mtu
/tun-mtu
может быть неуместным или даже разрушительным, и вам может быть необходимо обращаться к изменению fragment
/mssfix
, как предлагается в документации. Особенно сложные случаи могут возникнуть, когда либо сервер, либо клиенты (или оба) не поддерживают фрагментацию/сборку IP, что может быть вызвано искусственным снижением link-mtu
/tun-mtu
, и в таких случаях вы можете только обращаться к включению fragment
в OpenVPN.
- Какие из опций link-mtu tun-mtu fragment mssfix должны быть зеркально установлены на стороне сервера для работы?
Согласно предупреждающим сообщениям, выдаваемым OpenVPN в его журнал, когда вы устанавливаете либо link-mtu
, либо tun-mtu
, не зеркаля их до конечной точки, значения link-mtu
и tun-mtu
должны быть точно зеркально установлены, либо явно, либо неявно. Однако на практике я никогда не сталкивался с проблемами при изменении любого из этих значений, даже когда я закончился с очень разными значениями между конечными точками туннеля.
fragment
должен либо быть присутствовать с обеих сторон, либо отсутствовать с обеих сторон, потому что его присутствие активирует фрагментацию, выполняемую самим OpenVPN с использованием своего внутреннего алгоритма (не IP-фрагментации), и, таким образом, это должно быть согласовано обеими конечными точками. Однако, когда он присутствует, он может иметь асимметричные значения.
mssfix
не нужно зеркалить, и оно также может иметь асимметричные значения между конечными точками.
- Какие из опций link-mtu tun-mtu fragment mssfix можно использовать в client-config-dir?
Никакие
Возможное решение
- Если все четыре опции необходимо зеркально устанавливать на стороне сервера и их нельзя использовать внутри client-config-dir: существуют ли альтернативы для борьбы с низким MTU пути для отдельных клиентов?
Одним из способов является использование параметров маршрутизации в Linux, которые позволяют устанавливать значения MTU произвольно, вплоть до MTU фактического интерфейса. Это делается следующим образом:
Для начала выполните ручное обнаружение MTU пути (например, используя команды ping -M do -s xxxx <public-address-пользователя>
с сервера к удалённым клиентам), чтобы выяснить правильное значение MTU между сервером и каждым конкретным удалённым клиентом. Затем,
на стороне клиента, если это именно клиент, а не маршрутизатор для других хостов в его локальной сети, просто установите значение link-mtu
OpenVPN на фактический MTU минус 28. Например, на удалённом клиенте с фактическим MTU 576 значение link-mtu 548
сработает.
Обратите внимание, что это будет действовать для всего трафика (не только TCP), исходящего от клиента, потому что значение link-mtu
используется OpenVPN как верхний предел для его собственных (UDP порт 1194) нагрузок, отправляемых в удалённую конечную точку, следовательно, фактический MTU удалённого клиента (как было установлено вручную) минус 20 (размер внешнего заголовка IP без опций) минус 8 (размер внешнего заголовка UDP).
Значение MSS, которое OpenVPN будет автоматически ограничивать для TCP-трафика в туннеле, будет link-mtu
минус собственные накладные расходы OpenVPN (которые варьируются в зависимости от используемого шифра), минус 20 (размер внутреннего заголовка IP без опций) минус 20 (размер внутреннего заголовка TCP без опций).
на стороне сервера, установите один маршрут для каждого “клиента с низким MTU”, чтобы установить правильный MTU для каждого из них. Каждое правильное значение, которое нужно установить в параметрах маршрута, должно быть тем, что вы установили для значения link-mtu
этого удалённого клиента (как было установлено ранее) минус собственные накладные расходы OpenVPN. Последние можно вывести из значения link-mtu
минус значение tun-mtu
, отчет о котором делает сервер OpenVPN для этого конкретного туннеля. Например, предположим, что сервер OpenVPN сообщает о link-mtu
1541 и tun-mtu
1500, тогда на машине, на которой работает ваш сервер OpenVPN, вы сделаете что-то вроде:
ip route add <vpn-address-клиента> via <vpn-address-сервера> mtu 507
Такую операцию можно удобно выполнять по требованию с помощью скрипта client-connect
, который получает эти значения (а также многие другие) в переменных окружения, устанавливаемых динамически OpenVPN. В терминах оболочки это будет:
ip route replace "$ifconfig_pool_remote_ip" via "$ifconfig_local" mtu "$(( 576 - 28 - (link_mtu - tun_mtu) ))"
С этого момента исходящий трафик в адрес конкретного клиента VPN будет соблюдать этот MTU, а не тот, который установлен для интерфейса туннеля.
Более того, если ваш сервер OpenVPN также выступает в качестве маршрутизатора (sysctl net.ipv4.ip_forward=1
) между своей локальной сетью и удалёнными клиентами, тогда применяемые настройки по маршрутам на машине сервера OpenVPN (его ядре Linux) также инициируют корректные ICMP-сообщения (тип 3 код 4, требуется фрагментация) на машины в локальной сети, когда они отправляют трафик DF к удалённым клиентам, в этом случае эти другие машины в локальной сети должны соблюдать эти ICMP-сообщения, и они также произведут IP-фрагментацию внутри туннеля от имени машин в локальной сети, когда они отправляют трафик, не относящийся к DF, в этом случае ваши удалённые клиенты должны поддерживать сборку IP для IP-фрагментов, вышедших из туннеля.
Имейте в виду, что на Ubuntu 14.04 HWE и более поздних версиях (или эквивалентных ядрах до v5.7.x) вам также необходимо установить sysctl net.ipv4.ip_forward_use_pmtu=1
, чтобы Linux выполнял указанные ICMP/фрагментацию при маршрутизации трафика других машин, в то время как эта дополнительная sysctl
не требуется для исходящего трафика сгенерированного самой машиной сервера.
В заключение, отметьте, что для полностью корректной конфигурации вам также следует установить link-mtu 1472
(при предположении, что MTU нижнего интерфейса составляет 1500) на стороне сервера. Я действительно делаю это всякий раз, в любом месте, как базовую конфигурацию, за исключением специфических случаев, требующих определённых обходных путей. Это потому, что OpenVPN не считает MTU нижнего интерфейса стартовым значением для его link-mtu
/tun-mtu
, и он также не выполняет PMTU-обнаружение, даже когда его опция mtu-disc
установлена в yes
на ОС, которые это поддерживают. Поэтому, явная установка link-mtu
на значение, отражающее MTU нижнего интерфейса (с использованием формул, которые я описал выше), на мой взгляд, является действительно оптимальным серёзным значением по умолчанию по крайней мере на Linux.
Надеюсь, это будет полезно
Учитывая отсутствие ответов, я сейчас публикую решение, которое не очень элегантно, но просто: запустите другой экземпляр OpenVPN на TCP для “плохих клиентов”
proto tcp
и уменьшите TCP MSS на стороне клиента, например,
iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -o ${OUT_DEV} -j TCPMSS --set-mss ${PATH-MTU-MINUS-40}
Преимущество этого решения в том, что каждый клиент может установить свой индивидуальный MSS.
Это, бесспорно, TCP-над-TCP, но, как должно работать неплохо в многих сценариях.
Обратите внимание, что я всё ещё очень заинтересован в решениях, которые не требуют proto tcp
, и я отмечу их как действительный ответ, если они более или менее удовлетворяют мои изложенные требования.
Ответ или решение
Чтобы смягчить проблемы с MTU (Maximum Transmission Unit) на стороне клиентов OpenVPN, необходимо рассмотреть несколько эффективных методов и принятия решений. Тем не менее, эти методы должны быть настраиваемыми индивидуально для каждого клиента, чтобы избежать влияния на всю сеть. Давайте разберем ваши вопросы по порядку.
1. Почему опции link-mtu
и tun-mtu
не рекомендуются?
Проблемы с link-mtu
и tun-mtu
:
- Эти параметры могут вызвать проблемы, если их устанавливают на обоих концах туннеля (сервер и клиент) с разными значениями. Например, если у сервера установлен низкий MTU, а у клиента — высокий, туннель может не устанавливаться вовсе.
link-mtu
иtun-mtu
определяют максимальный размер пакетов, которые отправляются по туннелю. Установив их неправильно, вы можете столкнуться с тем, что данные будут отбрасываться, что повысит вероятность потерь пакетов и падения производительности.
Рекомендуется использовать mssfix
и fragment
, которые более гибкие и меньше подвержены проблемам при изменении MTU.
2. Какие опции нужно дублировать на серверной стороне?
fragment
: Должен быть установлен на обеих сторонах. Это позволяет OpenVPN фрагментировать пакеты по своему собственному алгоритму.mssfix
: Не требует дублирования. Может иметь различные значения на клиенте и сервере.link-mtu
иtun-mtu
: Рекомендуется устанавливать одинаковые значения на обеих сторонах туннеля, иначе могут возникнуть проблемы с установлением туннеля.
3. Какие опции можно использовать в client-config-dir
?
К сожалению, в контексте client-config-dir
нельзя использовать link-mtu
, tun-mtu
, fragment
и mssfix
. Эти параметры необходимо устанавливать в глобальных конфигурациях на сервере.
4. Альтернативы для борьбы с низким MTU на уровне клиента
Если указанные параметры должны быть установлены на сервере и не могут быть настроены для клиентов через client-config-dir
, рассмотрите следующие подходы:
-
Проведение ручного тестирования PMTU: Используйте команды типа
ping -M do -s [размер] [адрес]
для проверки MTU клиента. Установите значениеmssfix
в зависимости от полученных данных, например,mssfix 1300
. -
Использование маршрутизации на уровне Linux: Установите
MTU
на специфичном маршруте для каждого клиента на серверной стороне:ip route add [адрес клиента] mtu [значение]
Это можно автоматизировать с помощью скрипта подключения.
Итоговое решение
В случае, если ни один из вышеперечисленных методов не подходит, вы можете рассмотреть возможность запуска отдельного экземпляра OpenVPN на TCP специально для клиентов с проблемами MTU. Этот подход позволит установить свои настройки MSS индивидуально для каждого клиента.
Пример настройки TCP:
proto tcp
Кроме того, используйте iptables для снижения MSS, если это необходимо:
iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -o ${OUT_DEV} -j TCPMSS --set-mss ${PATH-MTU-MINUS-40}
Ваша конфигурация может влиять на производительность, поэтому тестируйте каждое решение в лабораторных условиях перед внедрением в продуктивную среду.