Представьте, что вы отправляете посылку через несколько почтовых отделений. Каждое из них может разрешить только определённый размер коробки, но вы узнаёте только о последнем пункте в цепочке. Примерно так работает определение PMTU через MSS в TCP – метод удобный, но не всегда точный, особенно если маршруты туда и обратно различаются.
Давайте разберёмся, почему это происходит и как адаптировать подход для реальных сетевых условий.
Как MSS в SYN-ACK отражает MTU и где кроются ловушки
Когда вы устанавливаете TCP-соединение, сервер в SYN-ACK пакете указывает MSS (Maximum Segment Size) – максимальный размер сегмента, который он готов принимать. Это значение вычисляется на основе MTU (Maximum Transmission Unit) его сетевого интерфейса. Например, для Ethernet с MTU 1500 байт MSS будет 1460 (1500 минус 40 байт на IP и TCP заголовки).
Пример извлечения MSS через tcpdump:
tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0'
Но здесь есть три нюанса:
- Асимметричные маршруты. Если путь от клиента к серверу проходит через роутер с MTU 1400, а обратный – через устройство с MTU 1300, MSS из SYN-ACK покажет только MTU сервера (например, 1500), но не учтёт обратный путь.
- Вмешательство NAT. Некоторые NAPT (Network Address Port Translation) могут менять TCP-заголовки, включая MSS (это, кстати, одна из причин, почему IPv6 изначально не поддерживает NAT).
- PMTUD ещё не активирован. На этапе handshake механизм Path MTU Discovery не работает – он запускается только при передаче данных.
Кстате, если вы видите в SYN-ACK MSS 1360, это не обязательно означает, что весь путь поддерживает такой размер. Возможно, где-то на обратной дороге стоит роутер с MTU 1400, но другой участок маршрута «срежет» пакеты до 1300 байт.
Ограничения метода и обходные пути
Главная проблема асимметричных маршрутов в том, что TCP MSS даёт информацию только о первом прыжке (первом сетевом интерфейсе) на стороне сервера. Вот как это выглядит на практике:
- Клиент → Сервер: MTU маршрута – 1400 байт.
- Сервер → Клиент: MTU маршрута – 1300 байт.
- Сервер отправляет SYN-ACK с MSS 1460 (исходя из своего локального MTU 1500).
- Клиент считает, что может отправлять сегменты размером 1460 байт, но на обратном пути пакеты >1300 байт будут фрагментированы или отклонены.
Решение? Полагаться не только на MSS, но и на PMTUD (Path MTU Discovery). Вот как это работает:
- После установки соединения отправляйте пакеты с флагом DF (Don’t Fragment).
- Если промежуточный роутер обнаруживает, что пакет превышает его MTU, он отправляет ICMP-сообщение «Fragmentation Needed».
- Клиент уменьшает MTU и повторяет отправку.
Пример настройки PMTUD в Linux:
sysctl -w net.ipv4.ip_no_pmtu_disc=0 # Включить PMTUD
sysctl -w net.ipv4.tcp_mtu_probing=1 # Автоматический поиск MTU
Важно: если сеть блокирует ICMP (частая ошибка!), PMTUD ломается. В таких случаях выручает MSS Clamping – принудительное ограничение MSS на firewall. Например, для iptables:
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1300
Практические рекомендации для начинающих
Если вы только разбираетесь в тонкостях сетевых протоколов, запомните три правила:
Ситуация | Что делать |
Сеть разрешает ICMP | Полагайтесь на PMTUD + MSS из SYN-ACK |
ICMP заблокирован | Вручную настройте MSS Clamping |
Сложные асимметричные маршруты | Используйте инструменты вроде tracepath или ping с опцией -s для проверки MTU |
Пример проверки PMTU через tracepath:
tracepath -n 8.8.8.8
И ещё: если ваше приложение чувствительно к задержкам (например, VoIP), установите MTU вручную на 100-200 байт меньше стандартного. Это снизит риск фрагментации, даже если где-то в пути окажется «узкое» звено. Помните, что идеального метода нет – только комбинация подходов даёт стабильный результат.