Вы поднимаете туннель, пингуете fe80::1, указав туннель исходным интерфейсом, и ничего не возвращается. Это не забытое правило брандмауэра. Link-local адрес имеет смысл только на том единственном канале, где он сформирован, а маршрутизируемый VPN — не этот канал. fe80::1 удалённого маршрутизатора находится в удалённом сегменте LAN; вы находитесь на один переход дальше в другом сегменте, и в IPv6 вообще нет механизма, позволяющего маршрутизатору пересылать link-local пакеты.

Префикс fe80 описывает область действия, а не диапазон

Link-local unicast — это fe80::/10, и определяющим свойством является не число, а область действия. Адрес действителен только в одном канале, и каждый интерфейс формирует свой из собственного идентификатора, поэтому две несвязанные машины в двух несвязанных сетях могут обе отвечать на fe80::1 без малейшего конфликта. Префикс, по сути, говорит «в каком-то канале»; сам канал берётся из контекста.

На хосте этот контекст — зона, или scope ID, которую вы добавляете после знака процента: fe80::1%eth0 в Linux, fe80::1%3 или fe80::1%12 в Windows и в инструментах, показывающих индексы интерфейсов. Ядро разрешает адрес вместе с индексом интерфейса и затем отправляет. Уберите зону, и назначение станет неоднозначным, поэтому у стека нет способа выбрать сегмент.

Почему маршрутизируемый туннель не может его доставить

Маршрутизируемый VPN — это интерфейс «точка-точка», несущий собственные адреса, обычно /64 из unique-local или частного пространства IPv6 внутри туннеля. Удалённый LAN — отдельный канал, до которого конечная точка туннеля доходит, маршрутизируя и пересылая от вашего имени. Это два разных сегмента, поэтому адрес из одного не является допустимым назначением в другом.

Правило пересылки прямо записано в стандарте, а не является причудой вашего клиента: маршрутизаторы не пересылают пакеты с link-local источником или назначением. Именно поэтому пакет гибнет даже тогда, когда таблица маршрутизации выглядит правильной. Добавьте маршрут для fe80::/64 в сторону туннеля, и соответствующий стандарту стек либо отклонит его, либо примет и отбросит каждый подходящий пакет: область назначения говорит «только этот канал», а исходящий интерфейс — не этот канал.

Обнаружение соседей укрепляет ту же границу. Хосты разрешают link-local следующий переход запросом соседа, отправляемым в multicast с областью канала и ограничением переходов 255, поэтому маршрутизаторы могут отклонять всё пересланное. Заставить это работать через туннель означало бы проксировать обнаружение для удалённого сегмента, а это мост Layer 2 в другой шляпе.

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

Как сбой выглядит на практике

Вывод инструмента при ошибке области читается как ошибка маршрутизации или брандмауэра, и именно поэтому люди проводят полдня не в том слое.

What you runWhat you seeWhat it means
ping fe80::1 with no zoneInvalid argument on Linux, or a transmit failure on WindowsThe stack cannot pick an interface for an ambiguous destination
ping -6 -I tun0 fe80::1Destination unreachable, or a silent timeoutThe address is not on the tunnel link, so there is nothing on-link to answer
ssh user@fe80::1 after the tunnel is upHangs, then no route to hostThe SYN never left the machine in a deliverable form; the server logs nothing
ip -6 neigh show dev tun0Empty tableNo neighbor entry can exist for a link-local host that is not on this link
fe80::/64 inside AllowedIPs or route-ipv6Config accepted, traffic still droppedThe tunnel carried the packet; the receiving stack refused to forward it

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

Используйте адрес, который был предназначен для маршрутизации

Для всего, до чего нужно добраться через туннель, используйте адрес, который служба получила именно для этой цели: глобальный unicast адрес или unique-local адрес из fc00::/7, диапазон, зарезервированный именно для этого внутреннего случая. Убедитесь, что прослушиватель действительно его привязывает, и проверьте обратный путь и любое правило брандмауэра хоста.

Посмотрите на удалённый хост вместо догадок. ip -6 addr show перечисляет существующие адреса, а ss -6 -ltn перечисляет то, что слушает демон. Служба, привязанная только к fe80::, доступна только из своего сегмента, что обычно является значением по умолчанию, которое никто не выбирал намеренно и которое можно изменить с согласия администратора.

Когда службу действительно нельзя привязать повторно, поднимите мост на уровень приложения, а не сетевого уровня. Посредник на удалённом хосте, который слушает маршрутизируемый адрес и подключается к link-local, решает проблему без расширения какого-либо сегмента:

socat TCP6-LISTEN:8080,bind=[fd00:1:2::10] TCP6:[fe80::1%eth0]:80

Здесь зона находится на правильной стороне, на хосте, у которого действительно есть eth0 в этом канале. Ваш клиент общается с fd00:1:2::10, который маршрутизируется нормально, а посредник выполняет link-local переход локально. Тот же приём работает для веб-интерфейса устройства или старого BMC, который объявляет только link-local адрес.

Когда мост действительно является ответом, и что проверять

Некоторым развёртываниям действительно нужен один общий сегмент: устройство, которое говорит только обнаружением link-local, прибор, зависящий от multicast, лаборатория, воспроизводящая плоскую LAN. Это проблема Layer 2, и ей нужен ответ Layer 2, например tap-устройство, соединённое мостом с удалённым интерфейсом, или клиентский продукт с явным режимом моста. Это стоит вам широковещательного и multicast-трафика, пересекающего туннель, плюс обнаружения соседей, которое должно работать от конца до конца.

Не приближайтесь к этому, добавляя fe80::/64 в маршрутизируемый профиль. Это даёт худшее состояние: конфигурация выглядит законченной, таблица маршрутизации показывает запись, а каждый пакет всё равно отбрасывается где-то между двумя стеками, которые оба ведут себя правильно.

Проверяйте интерфейсом, а не ping. ip -6 addr show tun0 сообщает, сформировал ли канал туннеля собственный link-local адрес, а ip -6 route get fe80::1%tun0 сообщает, что ядро на самом деле сделает с назначением, имеющим область. Когда ядро говорит, что назначение недостижимо на этом интерфейсе, никакая настройка туннеля этого не изменит, и пора прекратить редактировать профиль.

Короткая последовательность, которая решает вопрос:

  • Прочитайте адрес на удалённом хосте и отметьте, fe80:: это или маршрутизируемый префикс.
  • Если это fe80::, привяжите службу заново, сделайте посредника на этом хосте или соедините сегмент мостом; если он маршрутизируемый, исправьте маршрут, брандмауэр или адрес привязки.
  • Проверяйте зону каждый раз при повторном тесте, потому что скопированная команда обычно сохраняет неправильный интерфейс.

Источники: RFC 4291, архитектура адресации IPv6.

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