Мелкие ping целый день проходят через туннель, приходит баннер SSH, и в момент начала передачи файла сеанс замирает. Этот шаблон указывает на бюджет байт, посчитанный на неверном уровне: внешний туннель рассчитан под собственные накладные расходы, а внутренний никто не вычел. Вложенный пакет IPIP для WireGuard — просто ещё один IP-пакет, и именно поэтому он расходует байты, которые вы никогда не резервировали. Исправление — арифметика, и оно умещается в три команды и одну таблицу.

Симптом, который это выдаёт

Проблемы фрагментации заявляют о себе размером, никогда адресами. Ваш партнёр час отвечает на ping размером 56 байт, а затем замолкает на 1400 байтах, пока туннель остаётся поднятым и ни один маршрут не изменился. Рукопожатие помещается, первое полное окно TCP — нет, и передача останавливается через секунды после того, как выглядела здоровой.

Измерьте потолок до того, как что-либо менять, потому что неизмеренный потолок — тот, за которым вы будете гнаться дважды. Отправьте один зонд ниже подозрительного размера с установленным битом do-not-fragment, ping -M do -s 1000 10.9.0.2, затем повторите его на размере, о котором уже знаете, что он слишком велик. Когда переворот следует за размером полезной нагрузки, а пункт назначения остаётся неизменным, маршрутизация делает свою работу, и в бюджете не хватает одного заголовка.

Считайте каждый заголовок один раз, на правильном уровне

Начинайте с наименьшего MTU на пути, а не с интерфейса туннеля, потому что стена находится выше обоих концов. WireGuard тратит 60 байт на внешний транспорт IPv4: 20 на IP, 8 на UDP и 32 на собственный заголовок, и 80 байт, когда этот транспорт — IPv6. wg0 размером 1420 байт держит 20 байт запаса под 1440, которые путь в 1500 байт действительно может доставить, и поэтому число, скопированное из вики здесь всё ещё подходит. Нарисуйте весь стек на схеме, потому что тот, кто следующим изменит MTU, иначе выберет новое круглое число и унаследует вашу ошибку.

LayerBytesRemaining
Path MTU on the wired linknone yet1500
WireGuard outer: IP, UDP, WG header601440
Slack kept for safety201420 (wg0)
IPIP header inside the tunnel201400 (ipip0)
Original IPv4 and TCP headers401360 payload

Пятая строка — место, где бюджеты ломаются, потому что 1400 байт, которые несёт вложенный интерфейс, уже включают собственные 40 байт заголовков исходного пакета. До процесса, читающего файл, доходят только 1360 из этих байт. Считайте 1400 полезной нагрузкой приложения, к чему приглашает любая схема без этого столбца, и внутреннее устройство оказывается на 40 байт слишком щедрым перед стеной, которая не сдвинулась.

Задайте оба MTU по отдельности

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

ip link set wg0 mtu 1420
ip tunnel add ipip0 mode ipip remote 203.0.113.9 local 198.51.100.4
ip link set ipip0 mtu 1400

Правило, которое стоит запомнить: внутренний MTU = внешний MTU - 20, когда пакет, который несёт туннель IPIP, — IPv4, и - 40, как только любая сторона внутреннего пакета становится IPv6, потому что вложенный заголовок растёт с 20 байт до 40. Ничего другого в это вычитание не входит. Накладные расходы WireGuard уже потрачены, когда вы выбрали 1420, а их двойное взимание даёт туннель, который идеально работает и остаётся медленным, пока никто его не измеряет. Развёртывание, где оба устройства несут круглые 1400, — отпечаток именно этой ошибки.

IPv6 на любой стороне вложенного пакета обнуляет каждое число выше, поэтому пересчитывайте, а не копируйте число с похожего сайта. Вычитаемые заголовки — по 40 байт каждый, как только IPv6 появляется на внешнем пути или в исходной дейтаграмме, и число полезной нагрузки падает ровно на столько.

Правило брандмауэра, которое вам, вероятно, не нужно

Между двумя партнёрами WireGuard единственное, что идёт по проводу, — UDP, поэтому входящее правило для протокола 4, номера, который RFC 2003 назначает инкапсуляции IPv4-в-IPv4, не создаёт вложенный путь и не поддерживает его. Заголовок IPIP живёт внутри зашифрованной полезной нагрузки, где его не видит ни один пограничный брандмауэр. Открытие протокола 4 всё равно не покупает ничего, кроме ещё одного разрешённого пути на хост.

Политика, упоминающая источники и назначения исходного пакета, относится к периоду после декапсуляции, на интерфейсах туннеля и в таблице маршрутизации. Поместите её туда, и пограничное правило может оставаться закрытым.

Что WireGuard на самом деле проверяет

AllowedIPs судит источник пакета, который несёт WireGuard, а несёт он пакет, инкапсулированный в IPIP. Разрешённый источник IPIP принимается даже тогда, когда внутренний заголовок называет хост, который вы предпочли бы не маршрутизировать. Поэтому исходный источник — не то, что партнёр проверил за вас, и этой внутренней паре адресов нужно собственное принуждение после декапсуляции.

Обёртка даёт структуру маршрутизации, а не второй слой криптографии. Называть IPIP дополнительной защитой внутри WireGuard — описывать другой дизайн.

Проверяйте лестницей, а не одним ping

Проверьте размеры, охватывающие вычисленную границу, затем подтвердите, что ядро думает об обоих устройствах. Когда 1360 байт полезной нагрузки плюс 40 байт заголовков дают 1400, первое эхо ниже проходит, а второе сидит на байт за потолком:

ping -M do -s 1372 10.9.0.2
ping -M do -s 1373 10.9.0.2
wg show wg0
ip -d link show ipip0

Чистый проход ниже потолка и сбой на первом размере выше него означают, что арифметика держится от начала до конца. Затем снова запустите передачу, которая изначально зависла, потому что синтетический зонд доказывает бюджет и ничего не говорит о собственных размерах записи отправителя. Если вложенный путь всё ещё ломается на размере, который вы доказали как подходящий, проверьте декапсуляцию и маршруты дальнего конца, прежде чем срезать ещё десять байт с MTU.

Похожие статьи