Коли ви додаєте префікс IPv6 до тунелю між майданчиками, пакет подорожує не так, як підказує схема. Локальний шлюз бере весь пакет IPv6, шифрує його й ставить перед ним зовнішній заголовок IP, сімейство адрес якого визначають адреси кінцевих точок, а не те, що всередині. Віддалений шлюз знімає цей зовнішній заголовок і пересилає початковий пакет до своєї локальної мережі, де він знову звичайний пакет IPv6. Зовнішній тунель і внутрішній трафік — дві окремі розмови, які мають спільний ключ, пару кінцевих точок і більше нічого.

Два заголовки, дві родини адрес

У режимі тунелю IPsec внутрішній пакет IPv6 стає корисним навантаженням за заголовком ESP. Зовнішній заголовок, що несе цей пакет ESP, може бути IPv4 або IPv6 і дотримується local_addrs та remote_addrs. WireGuard робить те саме з меншою церемонією: внутрішній пакет — корисне навантаження дейтаграми UDP до кінцевої точки партнера. Обидва залишають внутрішній заголовок недоторканим, тому віддалена сторона може маршрутизувати його після декапсуляції.

Шлях між майданчиками бачить лише UDP 4500, коли IPsec використовує проходження NAT, або UDP 51820 для прослуховувача WireGuard за замовчуванням. Будь-який маршрутизатор на шляху може бути лише IPv4, і трафік IPv6 все одно проходить, бо на цьому шляху він ніколи не з'являється як пакет IPv6. Це дає чистий перший поділ: tcpdump -ni eth0 udp port 51820 показує зашифрований шлях, tcpdump -ni wg0 ip6 показує внутрішній трафік. Зайнятий зовнішній запис із мовчазним внутрішнім означає, що ядро не вкладає IPv6 у тунель; це маршрутизація або політика, а не стан каналу.

Шлюз також зменшує внутрішній Hop Limit під час пересилання, тож кінцеві точки рахуються як маршрутизатори на шляху IPv6. Зовнішня кількість стрибків і внутрішній Hop Limit рухаються незалежно, і пакет, що гине на віддаленому шлюзі, загинув у пересиланні, а не в шифруванні.

Селектори та AllowedIPs — це список дозволів

IKEv2 узгоджує, які внутрішні префікси може нести child SA. На strongSwan 5.9 і новіших їх явний запис у swanctl.conf виглядає так:

children {
  net6 {
    local_ts  = 2001:db8:aa::/48
    remote_ts = 2001:db8:bb::/48
    mode = tunnel
    esp_proposals = aes256gcm16-prfsha384-ecp384
    start_action = trap
  }
}

Якщо опустити local_ts і remote_ts, strongSwan виводить їх із підмереж обраного інтерфейсу. На вузлі з dual-stack цей короткий шлях часто дає пару лише IPv4: SA піднімається, а IPv6 відхиляється, не доходячи до коду ESP. Явний запис обох префіксів робить SA здатним їх нести, і start_action = trap піднімає його на першому відповідному пакеті.

У WireGuard один список виконує дві роботи. У блоці партнера AllowedIPs = 2001:db8:bb::/48, fd00:bb::/48 — це і маршрут, що встановлюється під час надсилання, і фільтр, що застосовується під час приймання: розшифрований пакет, внутрішнє джерело якого виходить за межі списку цього партнера, тихо відкидається. wg-quick додає маршрут для кожного запису, тож блоку партнера нижче не потрібна окрема команда ip -6 route.

[Interface]
Address = 2001:db8:aa:ff::1/64, fd00:aa:ff::1/64
ListenPort = 51820

[Peer]
PublicKey = <site-b-public-key>
Endpoint = 203.0.113.20:51820
AllowedIPs = 2001:db8:bb::/48, fd00:bb::/48

Обидва списки мають бути дзеркальними: майданчик B приймає префікс майданчика A як віддалений селектор і маршрутизує його назад, і тунель працює лише тоді, коли обидва напрямки збігаються. Асиметричний список дає симптом, що з'їдає найбільше часу, — здорове рукостискання, лічильники рухаються з одного боку, і ping з A до B, який проходить, тоді як ніщо з B не досягає A.

Пересилання, маршрути та зворотний префікс

Linux не пересилає IPv6, доки ви не скажете, і налаштування діє на простір імен, а не на інтерфейс:

sysctl -w net.ipv6.conf.all.forwarding=1
ip -6 route add 2001:db8:bb::/48 dev wg0
ip -6 route get 2001:db8:bb::10 from 2001:db8:aa::10

Другий рядок — той, який забувають у схемах на основі маршрутів. IPsec на основі політик встановлює політику XFRM, що відповідає префіксам, але конфігурація на основі маршрутів з vti6 або WireGuard потребує справжнього маршруту IPv6, що вказує на цей пристрій. Збережіть sysctl у /etc/sysctl.d/; інакше перезавантаження тихо поверне пересилання у вимкнений стан.

Пересилання має побічний ефект, який варто знати: ядро тоді ігнорує оголошення маршрутизаторів, якщо net.ipv6.conf.<wan>.accept_ra не дорівнює 2, тому шлюз, чия адреса WAN надходить через SLAAC, може втратити маршрут за замовчуванням тієї миті, коли ви вмикаєте пересилання. Задайте accept_ra = 2 на цьому інтерфейсі в тому самому файлі, і маршрут виживе. Ядро майданчика B також має навчитися 2001:db8:aa::/48 від шлюзу тунелю — статичним маршрутом або протоколом маршрутизації поверх тунелю, інакше відповідь гине всередині власної мережі B.

Перевірка на чотири кроки, яка локалізує поломку

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

  1. Селектори: swanctl --list-sas має показати пару префіксів IPv6 на child SA, або wg show wg0 allowed-ips віддалений префікс зі свіжим latest-handshakes.
  2. Маршрут ядра: ip -6 route get 2001:db8:bb::10 має відповісти пристроєм тунелю, а не інтерфейсом WAN.
  3. Внутрішній запис: tcpdump -ni wg0 ip6 під час ping між справжніми хостами локальної мережі. Пакети, що виходять, але жоден не повертається, вказують на маршрут або брандмауер B; цілковито відсутність пакетів вказує назад на крок 1 або 2.
  4. Призначення: на хості B ss -6 -ltn підтверджує, що служба слухає на IPv6, а локальні лічильники брандмауера показують, чи пакет було прийнято або відкинуто.

MTU належить до того самого проходу. Маршрутизатори IPv6 не фрагментують, тож завеликий пакет відкидається і Packet Too Big повертається відправникові. Якщо рукостискання й маленькі ping працюють, а передавання файлу застрягає, зменште корисне навантаження, доки воно не пройде: ping -6 -s 1400 -c 3 2001:db8:bb::10. Інтерфейси WireGuard за замовчуванням мають MTU 1420 байтів, а IPsec поверх IPv4 віднімає більше, тож задати внутрішній MTU краще, ніж сподіватися на виявлення MTU шляху крізь брандмауер, який відкидає ICMPv6.

Режими відмови, схожі на проблеми з брандмауером

Найчастіший — зовсім не брандмауер: служба прив'язується до 0.0.0.0 замість ::, запис AAAA існує, і клієнт отримує швидку відмову через IPv6, яку браузер із dual-stack приховує, відкочуючись на IPv4. Якщо відмовляють лише деякі застосунки, перевірте родину прослуховування, перш ніж торкатися набору правил.

Другий — фільтрація ICMPv6. Neighbor Discovery і Packet Too Big — частина нормальної роботи, а не діагностики, і політика, що відкидає весь ICMPv6, створює тунель, який з'єднується, автентифікується й потім застрягає на всьому, більшому за маленький пакет. Дозвольте типи, від яких залежить шлях, і залиште лічильники ввімкненими, щоб відкидання було видно.

Третій — префікс, доданий у тунель, але ніколи — у план адрес майданчика, якому він належить, тож жоден маршрутизатор його не оголошує і жоден хост не може відповісти з нього. Почніть з одного префікса й однієї пари хостів, доведіть обидва напрямки чотирма кроками, потім додайте решту; зламаний префікс лишається одним рядком у конфігурації замість переробки. Єдине офіційне джерело, яке варто тримати відкритим, — RFC 8200, яке визначає заголовок, що його несе тунель.

Схожі статті