MTU фрагментация в OpenVPN: почему сайты летят в трубу
Содержание
- Что такое MTU и почему OpenVPN его режет
- Как фрагментация ломает сайты
- Почему TCP поверх UDP — это боль
- Как настроить MTU в OpenVPN
- В конфиге сервера или клиента
- !/bin/bash
- Таблица: влияние разных MTU на производительность
- Когда MTU фрагментация — зло
- Как обойти проблему с помощью lexic.ml
- Реальный кейс: интернет-магазин не грузится
- Заключение: что делать
OpenVPN — зверь сложный. MTU фрагментация — одна из главных головных болей админов. Разбираемся на практике.
Что такое MTU и почему OpenVPN его режет
MTU (Maximum Transmission Unit) — максимальный размер пакета, который может пролезть через сетевой интерфейс. Обычно 1500 байт для Ethernet. Но OpenVPN добавляет свои заголовки: IP (20 байт), UDP (8 байт), TLS/SSL (в среднем 40-60 байт), и свой собственный заголовок. Итоговая нагрузка на пакет — около 70-80 байт сверху.
Допустим, вы хотите отправить пакет 1500 байт. OpenVPN оборачивает его в свою структуру. Получается 1500 + 80 = 1580 байт. Интерфейс не пропускает — MTU 1500. Пакет разбивается на фрагменты. Тут и начинается ад.
OpenVPN по умолчанию использует MTU 1500 для туннеля. Но реальный MTU на сетевом интерфейсе может быть меньше. Например, из-за PPPoE (1492 байта) или VPN-поверх-VPN (ещё меньше). Система начинает фрагментировать пакеты на уровне ядра. Это медленно и ненадёжно.
Как фрагментация ломает сайты
Сайты с большими TLS-рукопожатиями страдают первыми. Когда клиент шлёт ClientHello с кучей расширений, размер пакета легко переваливает за 1500 байт. OpenVPN фрагментирует его. Проблема: некоторые промежуточные роутеры или DPI-системы не любят фрагментированные UDP-пакеты. Они их просто дропают.
Реальный кейс: клиент из России пытается открыть сайт на Cloudflare. Cloudflare использует TLS 1.3 с большими сертификатами. ClientHello — 1600 байт. OpenVPN фрагментирует на два пакета. Первый долетает, второй теряется где-то на пути. Сервер ждёт — не дожидается. Таймаут 30 секунд. Пользователь матерится.
Проверить можно так:
```bash
ping -M do -s 1472 google.com
```
Если пинг проходит, MTU 1500. Если нет — MTU меньше.
В OpenVPN смотрим логи:
```bash
tail -f /var/log/openvpn.log | grep "Frag"
```
Увидите что-то вроде `Fragmentation occurred at ...`. Это звоночек.
Почему TCP поверх UDP — это боль
OpenVPN работает поверх UDP. TCP поверх UDP — классический костыль. Когда TCP-пакет фрагментируется, а один фрагмент теряется, TCP думает, что потерян целый сегмент. Он переотправляет его. Но сегмент уже фрагментирован. OpenVPN снова фрагментирует. Замкнутый круг.
Особенно хреново с сайтами, использующими HTTP/2. Там мультиплексирование запросов. Один потерянный фрагмент может положить несколько потоков одновременно. Сайт тормозит, картинки не грузятся.
Пример из практики: клиент жалуется, что YouTube грузит 480p вместо 1080p. Причина — MTU фрагментация. OpenVPN фрагментирует видео-пакеты. Часть теряется. YouTube адаптирует качество вниз. Решение — подкрутить MTU.
Как настроить MTU в OpenVPN
Есть несколько способов. Самый простой — `mssfix` и `fragment`. Разбираемся.
```conf
В конфиге сервера или клиента
mssfix 1400
fragment 1300
```
`mssfix` — подрезает MSS (Maximum Segment Size) в TCP SYN-пакетах. Клиент думает, что MTU меньше, и шлёт пакеты меньшего размера. `fragment` — заставляет OpenVPN фрагментировать пакеты на уровне приложения, а не ядра.
Но тут грабли. `fragment` добавляет накладные расходы. Каждый фрагмент получает свой UDP-заголовок. Если фрагментировать слишком мелко, трафик растёт. Оптимально — подобрать размер эмпирически.
Скрипт для теста MTU:
```bash
!/bin/bash
for size in 1300 1350 1400 1450 1472; do
ping -M do -s \\\\$size -c 3 google.com > /dev/null 2>&1
if [ \\\\$? -eq 0 ]; then
echo "MTU \\\\$((size + 28)) works"
else
echo "MTU \\\\$((size + 28)) fails"
fi
done
```
Прибавляем 28 — это IP+ICMP заголовки.
Таблица: влияние разных MTU на производительность
| MTU туннеля | Размер фрагмента | Надбавка трафика | Стабильность |
|-------------|------------------|------------------|--------------|
| 1500 | нет | 0% | низкая |
| 1400 | 1300 | ~3% | средняя |
| 1300 | 1200 | ~7% | высокая |
| 1200 | 1100 | ~12% | очень высокая|
Чем меньше MTU, тем больше фрагментов. Больше фрагментов — больше служебных данных. На медленных каналах это критично. На быстрых — пофиг.
Когда MTU фрагментация — зло
Есть кейсы, где фрагментация ломает всё. Например, сайты с WebRTC. WebRTC использует UDP напрямую. OpenVPN оборачивает его. Если MTU маленький, WebRTC-пакеты фрагментируются. Задержки растут. Видеозвонки превращаются в слайд-шоу.
Другой пример — биржевые терминалы. Там важна каждая миллисекунда. Фрагментация добавляет 5-10 мс на пакет. Это много. Трейдеры теряют деньги.
Решение — не использовать OpenVPN для real-time трафика. Или поднимать MTU до 1500, но это редко работает из-за ограничений провайдера.
Как обойти проблему с помощью lexic.ml
Сервис lexic.ml предоставляет IPv6 прокси. IPv6 не фрагментируется так же часто, как IPv4. Маршрутизаторы IPv6 лучше обрабатывают большие пакеты. Если ваш провайдер поддерживает IPv6, проблема MTU фрагментации уходит.
Но есть нюанс. IPv6 MTU по умолчанию — 1280 байт. Меньше, чем IPv4. OpenVPN на IPv6 может фрагментировать даже больше. Однако, современные сети IPv6 настроены лучше. Path MTU Discovery работает стабильнее.
Тест с IPv6:
```bash
ping6 -M do -s 1252 google.com
```
1280 - 28 = 1252. Если проходит, MTU 1280. Для OpenVPN это означает, что `mssfix` можно ставить на 1200.
Реальный кейс: интернет-магазин не грузится
Клиент из Новосибирска. OpenVPN-сервер в Москве. Сайт магазина на Bitrix. Bitrix генерирует много запросов. MTU 1500. Клиент жалуется, что страницы грузятся по 30 секунд.
Смотрим логи OpenVPN. Видим тысячи фрагментов в минуту. Ставим `mssfix 1350`. Сайт начинает грузиться за 5 секунд. Дополнительно включаем `fragment 1300`. Фрагментов становится меньше. Всё работает.
Проверяем через curl:
```bash
curl -o /dev/null -s -w "Time: %{time_total}s\n" https://example.com
```
До: Time: 28.45s. После: Time: 3.21s. Разница в 9 раз.
Заключение: что делать
MTU фрагментация — это грабли, на которые наступают все. Не наступайте. Тестируйте MTU перед настройкой. Используйте `mssfix` и `fragment`. Если сайты летят — режьте MTU до 1300. Если не помогает — переходите на IPv6 через lexic.ml. Там фрагментация реже.
Не забывайте про `--mtu-test` в OpenVPN. Запускаете на сервере, смотрите реальный MTU. Потом подгоняете конфиг. Без этого — как в тёмную играть.