MTU, MSS и фрагментация: почему сайт «открывается через раз»
7 мин чтения
MTU, MSS и фрагментация: почему сайт «открывается через раз»

MTU, MSS и фрагментация: почему сайт «открывается через раз»

Ping идёт, SSH работает, главная открывается — а страница с картинками висит и отваливается по таймауту. Через VPN не грузится половина сайтов, при отключённом VPN всё в порядке. Это не «интернет барахлит», а почти всегда одна конкретная неисправность: слишком большие пакеты и потерянное сообщение о том, что они слишком большие.

MTU — максимальный размер пакета в байтах, который можно передать по каналу за один раз, без разбиения. MSS — производная величина: сколько из этого размера остаётся под полезные данные TCP после вычета заголовков. Для обычного Ethernet MTU равен 1500 байт, а MSS — 1460.

Стандартные значения MTU

Ethernet задаёт 1500 байт, и от этого числа считается всё остальное. Два значения в таблице — не рекомендации, а требования стандартов: пакет меньше минимального просто не пройдёт.

Среда MTU, байт Откуда
Ethernet 1500 Стандарт IEEE 802.3
PPPoE (домашний ADSL, часть провайдеров) 1492 RFC 2516: 6 байт PPPoE + 2 байта PPP
Jumbo frames 9000 Де-факто, в стандарте не закреплено
Минимум для IPv4 68 RFC 791
Минимум для IPv6 1280 RFC 8200, жёсткое требование

Сколько откусывает каждый туннель

Любой туннель заворачивает ваш пакет в дополнительные заголовки, и MTU внутри туннеля становится меньше. Отсюда правило: проблемы с MTU почти всегда начинаются в тот день, когда в схеме появился VPN.

Туннель Оверхед MTU от 1500 MSS
PPPoE 8 1492 1452
GRE over IPv4 24 1476 1436
L2TP поверх UDP 40 1460 1420
VXLAN over IPv4 50 1450 1410
IPsec ESP, tunnel mode 60 1440 1400
WireGuard поверх IPv4 60 1440 1400
WireGuard, значение по умолчанию — 1420 1380
OpenVPN поверх UDP, внешний IPv6 88 1412 1372
GRE поверх IPsec 84 1416 1376
OpenVPN поверх TCP, внешний IPv6 140 1360 1320

WireGuard ставит 1420 по умолчанию не случайно: это консервативное значение, которое покрывает и вариант с IPv6 снаружи, где оверхед составляет 80 байт.

Как из MTU получается MSS

MSS — это MTU минус заголовки IP и TCP. Формула разная для двух версий протокола, потому что заголовок IPv6 вдвое длиннее.

IPv4:  MSS = MTU - 20 (IP) - 20 (TCP) = MTU - 40
       1500 - 40 = 1460

IPv6:  MSS = MTU - 40 (IPv6) - 20 (TCP) = MTU - 60
       1500 - 60 = 1440

Эти же числа зашиты в ядро: опция --clamp-mss-to-pmtu в iptables вычитает ровно 40 байт для IPv4 и 60 для IPv6. MSS стороны сообщают друг другу один раз, в SYN-пакете при установке соединения, и дальше не пересматривают.

Что происходит, когда пакет не влезает

Сценариев ровно три, и именно третий даёт симптом «сайт открывается через раз».

Ситуация Что делает маршрутизатор Итог
IPv4, бит DF не стоит Режет пакет на фрагменты Работает, но медленно
IPv4, бит DF стоит, ICMP проходит Отбрасывает и шлёт ICMP «нужна фрагментация» Отправитель уменьшает пакет, всё работает
IPv4, DF стоит, ICMP режется файрволом Отбрасывает молча Чёрная дыра MTU
IPv6, любой случай Фрагментировать не имеет права вообще Только Packet Too Big или потеря

Сообщение, которое всё чинит, называется по-разному в двух версиях протокола:

Протокол Сообщение Тип и код
IPv4 Fragmentation Needed and DF was Set ICMP type 3, code 4
IPv6 Packet Too Big ICMPv6 type 2, code 0
«Закроем весь ICMP, так безопаснее» — это и есть корень проблемы

RFC 8900 формулирует прямо: операторы не должны фильтровать сообщения ICMPv6 Packet Too Big, если не доказано, что они подделаны, — фильтрация этих пакетов ломает определение MTU пути. Ровно то же касается ICMP type 3 code 4 в IPv4. Запрещать нужно echo-запросы, если очень хочется, а не весь протокол целиком.

Чёрная дыра MTU: как она выглядит со стороны пользователя

Симптоматика очень характерная, и по ней диагноз ставится почти без инструментов.

Что происходит Почему
Ping проходит Echo-запрос — 84 байта, влезает куда угодно
TCP-соединение устанавливается SYN и SYN-ACK маленькие
SSH работает, интерактив летает Нажатия клавиш — десятки байт
scp или git clone зависают на 0% Первый же полноразмерный пакет пропадает
Главная открылась, страница с картинками — нет Маленький HTML прошёл, большой ответ — нет
Сайт открывается у части пользователей У них другой провайдер и другой путь

Причины, по которым сообщение не доходит: файрвол или ACL режут весь ICMP; NAT или точка терминации туннеля не генерируют ответ; асимметричная маршрутизация и anycast — сообщение уходит не тому узлу; rate-limit на ICMP у оператора.

Как измерить реальный MTU пути

Метод один: отправлять ping с запретом фрагментации и увеличивать размер, пока пакеты не начнут пропадать. Ключевая деталь, на которой ошибаются все: размер в команде — это полезная нагрузка ICMP, без заголовков. К результату нужно прибавить 28 байт для IPv4 (20 IP + 8 ICMP) и 48 байт для IPv6.

# Linux: -M do запрещает фрагментацию, -s задаёт нагрузку
ping -M do -s 1472 -c 3 8.8.8.8     # 1472 + 28 = 1500
ping -M do -s 1464 -c 3 8.8.8.8     # 1464 + 28 = 1492, типичный PPPoE
ping -M do -s 1392 -c 3 8.8.8.8     # 1392 + 28 = 1420, WireGuard
# при превышении: Frag needed and DF set (mtu = 1492)

# Windows: -f запрещает фрагментацию, -l задаёт нагрузку
ping -f -l 1472 -n 3 8.8.8.8
# при превышении: Packet needs to be fragmented but DF set.
netsh interface ipv4 show subinterfaces    # посмотреть MTU интерфейсов

# macOS и FreeBSD: -D вместо -M do
ping -D -s 1472 -c 3 8.8.8.8
Прошёл размер MTU пути На что похоже
1472 1500 Чистый Ethernet, всё в порядке
1464 1492 PPPoE у провайдера
1448 1476 GRE-туннель на пути
1412 1440 IPsec или WireGuard
1392 1420 WireGuard с дефолтными настройками
Меньше 1272 меньше 1300 Два туннеля подряд или что-то сломано

Искать нужно бинарным поиском: 1472 не прошло — пробуем 1400, прошло — пробуем 1436, и так далее. Пяти-шести итераций хватает, чтобы найти точное значение.

Оговорка для macOS 15

В macOS 15 Sequoia зафиксирован баг: ping -D не выставляет бит DF при включённом SIP, хотя в macOS 14 работал. Результат замера будет ложно оптимистичным. Проверяйте через tcpdump или меряйте с Linux-хоста.

MSS clamping: как чинят на практике

Правильное решение — не трогать MTU на клиентах, а подправлять MSS в проходящих SYN-пакетах на границе сети. Тогда стороны сами договорятся о безопасном размере сегмента, и большие пакеты просто не появятся.

# iptables: подогнать MSS под реальный MTU пути
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
         -j TCPMSS --clamp-mss-to-pmtu

# iptables: жёсткое значение, когда PMTU недостоверен
iptables -t mangle -A FORWARD -o ppp0 -p tcp --tcp-flags SYN,RST SYN \
         -j TCPMSS --set-mss 1452

# nftables
nft add rule inet filter forward tcp flags syn \
    tcp option maxseg size set rt mtu

# MikroTik RouterOS
/ip firewall mangle add chain=forward action=change-mss \
    new-mss=clamp-to-pmtu protocol=tcp tcp-flags=syn \
    out-interface-list=VPN passthrough=yes

Два нюанса, на которых обычно спотыкаются. Правило должно ловить и SYN, и SYN-ACK — иначе поправится только одна сторона соединения. И на MikroTik при жёстком значении добавляют tcp-mss=1453-65535, чтобы не поднимать MSS тем, у кого он и так меньше.

Проверить гипотезу за одну команду

Прежде чем перенастраивать сеть, стоит убедиться, что дело действительно в размере пакета. Самый быстрый способ — искусственно занизить MSS и посмотреть, ушла ли проблема.

# Скачать тот же файл с заниженным MSS
curl --max-time 30 -o /dev/null https://example.com/big.iso   # висит
iperf3 -c 203.0.113.10 -M 1300 -t 20                          # а так летит

# Посмотреть, какой MTU ядро само вывело для маршрута
ip route get 8.8.8.8
ip route show cache

# Сбросить закешированные значения PMTU
ip route flush cache

Если с заниженным MSS всё работает, а с обычным — нет, диагноз подтверждён: где-то на пути пакеты не проходят, а сообщение об этом до вас не доходит.

Почему такую поломку почти невозможно заметить вручную

Проблемы с MTU избирательны: они зависят от маршрута, а значит — от провайдера пользователя и от его географии. Сайт может годами работать для всех, кроме абонентов одного оператора в одном регионе, и никто из команды этого не увидит, потому что сами вы заходите с офисного канала, где всё в порядке.

Именно поэтому проверки из разных точек ловят такие вещи лучше, чем любой разовый тест: одна локация отдаёт таймауты, остальные — норму, и это сразу указывает на маршрут, а не на сервер. Помогает и сравнение результатов ICMP-проверки с HTTP: ping идёт отовсюду, а HTTP-ответ не приходит с одной точки — характерная подпись чёрной дыры MTU.

Увидеть географию проблемы

Пруфен проверяет сайт с интервалом от 2 минут из 13 точек — 11 из них в России — и показывает результат по каждой локации отдельно. Если из двенадцати точек сайт отвечает, а из одной стабильно отваливается по таймауту, это видно на вкладке «По локациям» до того, как придёт первая жалоба от клиента.

Была ли статья полезна?

Начните мониторинг прямо сейчас

Бесплатный тариф — до 5 проверок. 150 ₽ на баланс при регистрации.

Создать аккаунт