Зачем знать про 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:
- TCP всегда ставит DF=1 (Don't Fragment) на свои пакеты
- Если на пути роутер видит пакет > MTU исходящего интерфейса - он дропает пакет и отправляет ICMP "Fragmentation Needed and DF Set" ([[icmp|ICMP type 3 code 4]])
- В ICMP-сообщении - предлагаемый MTU
- Отправитель уменьшает свой Path MTU для этого destination'а (хранится в route cache)
- Retransmit пакета меньшего размера
MTU blackhole - сломанный PMTUD
PMTUD требует, чтобы ICMP проходил обратно к отправителю. На многих файрволах/middlebox'ах ICMP режут "по соображениям безопасности". Тогда:
- Клиент шлёт пакет 1500 байт через VPN с MTU 1400
- Роутер дропает пакет, шлёт ICMP "Frag needed, MTU 1400"
- ICMP режут - не доходит до клиента
- Клиент ретрансмитит этот же 1500-байтный пакет
- Роутер снова дропает... 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)