# MTU и Path MTU Discovery (PMTUD) _Сеть: L2 / L3 · LinuxLab Knowledge Base_ **TL;DR:** MTU - максимум байт IP-пакета на интерфейсе. PMTUD ищет минимум на пути через ICMP "Fragmentation Needed". Если ICMP режут - blackhole больших пакетов. MSS clamping в iptables/nftables - стандартный фикс на туннелях VXLAN/IPsec. ## Зачем знать про MTU **Maximum Transmission Unit (MTU)** - максимальный размер IP-пакета, который интерфейс готов отправить одним куском. На обычном Ethernet это 1500 байт (полезная нагрузка [[ethernet-frame|кадра]]). Когда пакет больше MTU - либо он фрагментируется на IP-уровне, либо отправитель должен прислать пакет меньше. В IPv6 фрагментацию ядер отправителей нет (она запрещена в спеке) - только PMTUD. Сломанный PMTUD - частая причина странных production-проблем: SSH работает, `apt update` зависает, `git clone` обрывается. Симптом один: маленькие пакеты проходят, большие - нет. ## Откуда берутся типичные значения | Среда | MTU | Заметки | |-------|-----|---------| | Ethernet (стандарт) | 1500 | минус 18 байт L2-заголовка из 1518 frame'а | | Jumbo frame | 9000 | дата-центр, требует поддержки всеми коммутаторами | | PPPoE (DSL) | 1492 | -8 байт PPPoE-заголовка | | OpenVPN UDP | ~1400 | overhead на TLS+UDP | | WireGuard | 1420 | -80 байт overhead | | IPsec ESP | ~1400 | зависит от mode (tunnel/transport, AES-GCM) | | VXLAN | 1450 | -50 байт (Ethernet+IP+UDP+VXLAN headers) | | GRE | 1476 | -24 байта (GRE+IP) | | Loopback | 65536 | в ядре, без сети | Посмотреть текущий MTU: ``` ip link show eth0 | grep mtu ``` ## Как пакет сравнивается с MTU При отправке пакета ядро смотрит на исходящий интерфейс. Если IP-payload + IP-header > MTU и **DF=0** (Don't Fragment не выставлен) - ядро режет пакет на фрагменты. Каждый фрагмент - самостоятельный IP-пакет с одинаковым ID; первый несёт L4-header, остальные - только данные. Собирает их получатель. Минусы фрагментации: - **Если хоть один фрагмент потерян - дропается весь пакет** (получатель не может его собрать) - **Многие firewall'ы дропают фрагменты** по дефолту (нельзя проверить L4-заголовки на не-первых фрагментах) - **CPU overhead** - и на отправителе и на получателе - **Conntrack** видит фрагменты странно (нужен `nf_defrag_ipv4` модуль) Поэтому в IPv6 фрагментацию запретили: только source-fragmentation если очень надо, и обязательный PMTUD. ## TCP MSS - решение проблемы со стороны TCP TCP не хочет фрагментироваться на IP. При [[tcp-handshake|SYN]] стороны обмениваются опцией **MSS (Maximum Segment Size)**: ``` MSS = MTU - 20 (IP header) - 20 (TCP header) = 1460 для MTU 1500 ``` Каждая сторона выбирает свой MSS на основе своего интерфейса. Эффективный MSS соединения = `min(MSS_клиент, MSS_сервер)`. После handshake TCP режет данные на сегменты ≤ MSS, никогда не фрагментирует IP. ## PMTUD - найти минимум на пути Между клиентом и сервером может быть link с MTU < 1500 (туннель, PPPoE). MSS-договорённость не помогает - клиент знает только свой MTU. Тут включается **Path MTU Discovery**: 1. TCP всегда ставит **DF=1** (Don't Fragment) на свои пакеты 2. Если на пути роутер видит пакет > MTU исходящего интерфейса - он дропает пакет и отправляет ICMP "Fragmentation Needed and DF Set" ([[icmp|ICMP type 3 code 4]]) 3. В ICMP-сообщении - предлагаемый MTU 4. Отправитель уменьшает свой Path MTU для этого destination'а (хранится в route cache) 5. Retransmit пакета меньшего размера ## MTU blackhole - сломанный PMTUD PMTUD требует, чтобы **ICMP проходил обратно к отправителю**. На многих файрволах/middlebox'ах ICMP режут "по соображениям безопасности". Тогда: 1. Клиент шлёт пакет 1500 байт через VPN с MTU 1400 2. Роутер дропает пакет, шлёт ICMP "Frag needed, MTU 1400" 3. **ICMP режут** - не доходит до клиента 4. Клиент ретрансмитит этот же 1500-байтный пакет 5. Роутер снова дропает... infinite loop Симптом классический: `curl --max-time 5 -v https://big-page.com` - TLS handshake проходит (мелкие пакеты), `> GET /` уходит, но крупный HTTP-ответ зависает. SSH в "интерактивном" режиме работает (мелкие пакеты), `cat largefile` через SSH висит. Для IPv6 PMTUD blackhole особенно болезнен: фрагментацию делать нельзя, TCP буквально не может работать с большими пакетами. ## MSS clamping - workaround Когда не уверен в PMTUD (типичная VPN/туннель), **переписывают MSS в SYN-пакетах** на роутере, через который идёт туннель. TCP-клиент думает, что MSS меньше - и сам не превышает. В nftables: ``` add rule inet filter forward tcp flags syn tcp option maxseg size set rt mtu ``` В iptables: ``` iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \ -j TCPMSS --clamp-mss-to-pmtu ``` `--clamp-mss-to-pmtu` берёт MTU исходящего интерфейса и переписывает MSS в SYN. Это **первое**, что включают на VPN-сервере, OpenVZ ноде, любом GRE/IPsec endpoint'е. Альтернатива - явный размер: `--set-mss 1360` если знаешь точно. ## Jumbo frames - 9000 MTU В дата-центре между серверами и SAN-стораджем часто включают **jumbo frames** (MTU 9000): - Меньше per-packet overhead (один TCP-пакет вместо 6) → больше throughput - Меньше CPU на сетевой стек - Полезно для NFS, iSCSI, репликация БД Подвох: **все** устройства в L2-сегменте (свитчи, NIC, бридж в ядре) должны поддерживать jumbo. Один свитч с 1500 - blackhole. Включается: ``` ip link set eth0 mtu 9000 ``` Для постоянной настройки - в NetworkManager / netplan / `/etc/network/interfaces`. ## MTU в туннелях и overlay сетях Любой туннель добавляет заголовки → effective MTU внутри туннеля меньше underlay MTU. Расчёт для **VXLAN** на underlay MTU 1500: ``` 1500 - 14 outer Ethernet - 20 outer IP - 8 UDP - 8 VXLAN header - 14 inner Ethernet = 1436 → округляют до 1450 (исторически) ``` Если pod в Kubernetes шлёт 1500-байтные TCP-пакеты по [[vxlan-overlay|flannel]], они внутри VXLAN превратятся в 1550-байтный underlay-пакет → дроп где-то. Решения: - Уменьшить pod MTU до 1450 (CNI делает это автоматически) - Включить jumbo frames на underlay (тогда pod MTU 9000) - MSS clamping на CNI-уровне Аналогично для [[gre-tunnel|GRE]], [[ipsec-ike|IPsec]] - вычитай overhead. ## Когда что-то пошло не так - **Маленькие пакеты идут, большие - нет** - классический blackhole. `ping -M do -s 1472 host` (DF=1, payload 1472=1500-28). Уменьшай `-s` пока не пройдёт - найдёшь PMTU. - **`tracepath host`** покажет PMTU по пути (как traceroute, но для MTU) - **`apt update` зависает на конкретном пакете** - 99% MTU. Включи `tcp_mtu_probing` или зафиксируй MTU интерфейса - **VPN tunnel работает плохо после миграции** - кто-то сменил underlay MTU. Пересчитай overhead, включи MSS clamping - **TCP retransmits high, throughput low** - проверь не из-за фрагментации (`netstat -s | grep -i frag`) - **iptables block ICMP type 3 code 4** - выпили это правило, иначе PMTUD не работает совсем ## sysctl-ручки - `net.ipv4.tcp_mtu_probing=1` - если PMTUD сломан, ядро пробует уменьшить MSS, кэширует результат - `net.ipv4.ip_no_pmtu_disc=0` - 1 отключает PMTUD (не делать!) - `net.ipv4.route.min_pmtu=552` - минимальный PMTU, ниже не опускаемся - `net.ipv6.conf.all.mtu=1280` - минимум для IPv6 (RFC) ## Команды ```bash ip link show eth0 | grep mtu ``` Текущий MTU интерфейса ```bash ip link set eth0 mtu 9000 ``` Сменить MTU на 9000 (jumbo frames). Действует до перезагрузки ```bash ping -M do -s 1472 -c 1 8.8.8.8 ``` Пинг с DF=1, payload 1472 байт. Если не проходит - PMTU < 1500 ```bash tracepath -n example.com ``` PMTU по hops до destination'а - покажет где режется ```bash iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu ``` Стандартный MSS clamping для VPN/туннеля. Переписывает MSS в SYN ```bash nft add rule inet filter forward tcp flags syn tcp option maxseg size set rt mtu ``` То же на nftables - clamping MSS до route MTU ```bash sysctl -w net.ipv4.tcp_mtu_probing=1 ``` Включить probing - ядро адаптирует MSS если PMTUD blackhole ```bash ss -ti | grep -i mss ``` Текущий MSS активных TCP-соединений - сверить с ожидаемым ## См. также - [Ethernet frame](/kb/ethernet-frame.md) - [VXLAN - L2 overlay поверх L3-сети](/kb/vxlan-overlay.md) - [GRE-туннели и IPIP - point-to-point поверх IP](/kb/gre-tunnel.md) - [TCP three-way handshake](/kb/tcp-handshake.md) - [ICMP](/kb/icmp.md) - [IPsec и IKEv2 - стандарт корпоративных VPN](/kb/ipsec-ike.md)