В прошлом месяце два человека попросили у меня противоположных результатов: один хотел, чтобы IPv6 покрывался VPN, другой хотел, чтобы офисный принтер продолжал работать. Один маршрут ::/0 не может обеспечить оба сам по себе. Относится ли этот маршрут к внутренней части туннеля, зависит от трёх условий, и первое исключает его для большинства коммерческих выходов. Если выход не несёт IPv6, направление туда маршрута по умолчанию прячет ваш трафик в туннеле, которому некуда его отправить.

Ответ определяют три условия

Маршрут по умолчанию — одна строка, ::/0, и он соответствует любому адресату IPv6 без более конкретного маршрута (RFC 4291). Установить его легко. Решение, стоит ли это делать, сводится к трём условиям.

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

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

What you seeDecision
Exit answers IPv6 probes, no local IPv6 devices to protectRoute ::/0 into the tunnel
Exit answers IPv6 probes, printer or NAS reachable by a global addressRoute ::/0, then give those prefixes a route of their own
Exit is IPv4-only inside the tunnelDo not route ::/0; block IPv6 on the client
Endpoint is reached over IPv6 and sits inside ::/0Install a host route for the endpoint first

Действительно ли выход несёт IPv6?

Интерфейс туннеля с адресом IPv6 ничего не доказывает о другой стороне. Многие коммерческие профили внутри только IPv4: сервер выдаёт вам адрес v4 и отбрасывает всё остальное. Установите сверху маршрут по умолчанию из AllowedIPs, и ядро с удовольствием отправит IPv6 в интерфейс, чей партнёр никогда его не видит.

Проверьте с клиента, пока туннель поднят. ping -6 -c 3 2001:4860:4860::8888 и ip -6 route get 2001:4860:4860::8888 отвечают на разные вопросы: возвращается ли ответ и какое устройство выбрало ядро. Когда второе называет ваш туннель, а первое ничего не возвращает, проблема в выходе, а не в маршруте.

Если вы сами управляете выходом, тест клиента может пройти, а путь всё равно упрётся в тупик. Подтвердите, что на шлюзе включён net.ipv6.conf.all.forwarding=1 и что его брандмауэр принимает пакеты, пересылаемые из туннеля. Правильно маршрутизированный пакет в выход, который отказывается пересылать, со стороны клиента выглядит в точности как ошибка маршрутизации.

Может ли клиент сохранить путь к конечной точке?

Это тот случай, который поймал меня на ноутбуке с конечной точкой WireGuard только на IPv6. Адрес конечной точки — обычный глобальный адрес, поэтому он попадает внутрь ::/0. Маршрутизируйте весь префикс в интерфейс, и пакеты рукопожатия погонят сами себя в туннель, который они всё ещё пытаются построить. Туннель так и не поднимается, и ничто в выводе клиента не говорит почему.

wg-quick справляется с этим в Linux. Он выводит маршруты из AllowedIPs, устанавливает их в свою таблицу и добавляет правило политики, чтобы трафик к конечной точке сначала разрешался через основную таблицу: правило suppress_prefixlength 0, которое вы найдёте в ip -6 rule. Поэтому wg-quick up удаётся там, где написанный вручную ip -6 route add default dev wg0 отрезает вас.

Проверьте оба состояния с помощью ip -6 route get <endpoint-v6>, до и после поднятия туннеля. Если ответ меняется на ваше туннельное устройство, вы удалили путь, который нужен туннелю. Когда вы пишете маршруты сами, добавьте маршрут к хосту через исходный шлюз или подключитесь к конечной точке по IPv4 и оставьте IPv6 полезной нагрузке.

Что должно остаться вне туннеля?

Более конкретные маршруты побеждают маршрут по умолчанию, поэтому префикс на канале или диапазон ULA с собственным маршрутом продолжает работать без дополнительных усилий. Это тихая причина, по которой так много полных туннельных настроек IPv4 выживает: префиксы LAN конкретнее маршрута по умолчанию и никогда не попадают в туннель.

Это ломается, когда локальное устройство доступно по глобальному адресу, что нормально для принтера, которому тот же маршрутизатор выдал публичный префикс, для NAS или телевизора. Эти адреса соответствуют ::/0 и ничему больше, поэтому они переезжают в туннель вместе со всем остальным и исчезают.

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

Если ответ нет, заблокируйте его

Оставить IPv6 без маршрута — не то же самое, что оставить его в покое. Хост с работающим маршрутом по умолчанию для IPv6 продолжает им пользоваться, пока туннель несёт IPv4, поэтому трафик уходит рядом с VPN, и каждая служба, предпочитающая IPv6, видит ваш настоящий адрес. Это утечка, а не раздельный туннель.

Выберите одно и запишите, что именно выбрали. Направьте маршрут по умолчанию и примите компромиссы выше или отключите IPv6 на время работы туннеля. Пропустить префикс из AllowedIPs само по себе недостаточно, потому что операционная система держит свой собственный маршрут по умолчанию. В Linux грубый вариант — sysctl -w net.ipv6.conf.all.disable_ipv6=1; более тонкий сбрасывает IPv6 вне туннеля правилом брандмауэра. Телефонные клиенты имеют явный переключатель блокировки IPv6, который делает то же самое.

Проверьте это как утечку

Поднимите туннель и прочитайте состояние вместо того, чтобы доверять переключателю. ip -6 route show default должен показать один маршрут по умолчанию, а ip -6 route get для адресата вне вашей LAN должен назвать туннельное устройство. Повторите оба после отключения: маршрут по умолчанию, переживающий туннель, — это ошибка, которую вы искали.

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

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

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