IPv6 и dual-stack в Qeli
Внешний IPv4/IPv6 carrier, внутренний NetworkPlan v2, ULA/GUA, NAT66 или маршрутизация, встроенный NDP proxy для on-link префиксов, DNS, MTU и безопасная миграция.
Внешний carrier и внутренний TUN независимы
Начиная с 0.8.0 Qeli разделяет семейство соединения с сервером и семейства пакетов внутри туннеля. bind/listen/server могут использовать IPv4, IPv6 или hostname с A/AAAA, а tun.ip_mode, пулы и аутентифицированный NetworkPlan определяют внутренний IPv4/IPv6 data plane.
server = [2001:db8::10]:443.tcpdump.tun.ip_mode = ipv4 | dual | ipv6
| Режим | Что получает клиент | Применение |
|---|---|---|
ipv4 | IPv4 lease, маршруты и DNS. | Совместимость или сервер без готового IPv6 uplink. |
dual | Независимые IPv4 /32 и IPv6 /128 на L3 TUN, оба набора маршрутов и DNS. | Рекомендуемый обычный режим. |
ipv6 | Только IPv6 lease и план. | Специализированная IPv6-only сеть; доступа к IPv4-only сервисам без отдельного NAT64 не будет. |
NetworkPlan v2 отдельно передаёт assigned address, on-link prefix и gateway для каждого семейства и применяется атомарно. Полуприменённый план не считается успешным подключением.
Минимальный dual-stack пример
tun.ip_mode = dual
tun.name = vpn0
tun.address = 10.19.0.1
tun.ipv6_address = fd71:e1:8000:102::1
tun.mtu = 1400
tun.device_type = tun
pool.cidr = 10.19.0.0/24
pool.ipv6.cidr = fd71:e1:8000:102::/64
routing.nat.enabled = true
routing.ipv6.mode = nat66
dns.enabled = true
dns.listen = 10.19.0.1
dns.listen_ipv6 = fd71:e1:8000:102::1
dns.push_servers = 10.19.0.1, fd71:e1:8000:102::1
Используйте уникальный для развёртывания RFC 4193 ULA /48 и отдельный /64 на профиль. Не копируйте пример fd71:… буквально между независимыми установками и не используйте link-local fe80::/10 как пул. Quick Start генерирует случайный 40-битный Global ID.
routing.ipv6.mode
| Значение | Что делает | Требования | NDP proxy |
|---|---|---|---|
off | Блокирует IPv6-транзит между TUN профиля и другими интерфейсами в обе стороны. | Рабочий ip6tables нужен, чтобы DROP был гарантирован. | Только off |
manual · 0.8.2 | IPv6 firewall и forwarding настраивает администратор; NDP можно включить отдельно. | Ручная маршрутизация, DNS-доступ и изоляция. ip6tables не требуется. | off, auto, required |
route | Настраивает пересылку с сохранением исходного IPv6 клиента. | Для выхода в интернет — маршрутизируемый провайдером GUA-префикс и обратный маршрут. Для LAN/site-to-site достаточно маршрутов между своими сетями. | off, auto, required |
nat66 | Настраивает пересылку и NAT66 через IPv6 WAN сервера. | IPv6 default route, публичный GUA из 2000::/3 и рабочий ip6tables NAT. Удобно на обычном VPS, но это NAT. | Только off |
Для routed GUA задавайте reservation или static_ipv6; CLI поддерживает qeli add-client --static-ipv6 ….
Ручное управление IPv6 · 0.8.2
manual требует сборки 0.8.2 с поддержкой этого режима; бинарник 0.8.1 его не распознаёт. Пример ниже — фрагмент существующего INI-профиля с tun.ip_mode = dual или ipv6 и уже настроенными IPv6 gateway/pool.
Если маршрутизацией и firewall управляет firewalld или native nftables, используйте routing.ipv6.mode = manual. Qeli продолжает создавать туннель, назначать адреса и обслуживать клиентские маршруты и NDP. Он не устанавливает IPv6-правила транзита, MSS, DNS INPUT/REDIRECT и не включает forwarding/accept_ra. Для этого режима ip6tables не требуется; IPv4 настраивается независимо.
routing.ipv6.mode = manual
routing.ipv6.interface = ens3
routing.ipv6.ndp_proxy = required
routing.ipv6.ndp_proxy_interface = ens3Администратор обеспечивает forwarding, обратные маршруты, изоляцию профилей, ICMPv6 и доступ UDP/TCP к встроенному DNS. При dns.port != 53 он также настраивает IPv6-перенаправление порта 53. При смене режима Qeli убирает собственные старые правила и освобождает sysctl-аренды; настройки хоста должны существовать независимо. required по-прежнему отклоняет запуск при ошибке responder. off сохраняет блокировку IPv6-транзита.
required проверяет привязку responder к интерфейсу; успешный запуск не гарантирует правильную ручную маршрутизацию или firewall. Для обычного routed prefix без NDP используйте ndp_proxy = off.
- Подготовьте постоянные настройки forwarding, обратных маршрутов, firewall, ICMPv6, RA и DNS.
- В панели выберите Config → профиль → Routing → IPv6 forwarding / NAT66 →
manual. Настройте Upstream IPv6 NDP proxy и интерфейс согласно топологии. - Для правки файла выполните
qeli check-config --config /etc/qeli/server.conf, затем перезапустите сервис; в панели используйте Apply & Restart. - После перезапуска проверьте forwarding/RA, IPv6-доступ и DNS по UDP/TCP; при NDP — ответ для активного клиента со стороны uplink.
Переход route ↔ manual в форме сохраняет NDP. Выбор off или nat66 выключает NDP и очищает его интерфейс. В INI установите routing.ipv6.ndp_proxy = off самостоятельно: несовместимые сочетания отклоняются.
Команды ниже используют qeli.service, которую создаёт стандартный установщик/DEB. Если вы создали службу под другим именем, замените qeli на имя своей службы.
NDP proxy: когда нужен и как настроить
NDP (Neighbor Discovery Protocol) в IPv6 выполняет в том числе задачу, похожую на ARP в IPv4: соседний маршрутизатор отправляет Neighbor Solicitation (NS) и спрашивает, какой MAC-адрес обслуживает конкретный IPv6. Адрес VPN-клиента находится на TUN, а не на WAN-интерфейсе сервера, поэтому при on-link префиксе Qeli отвечает upstream-маршрутизатору сообщением Neighbor Advertisement (NA) с MAC-адресом сервера и затем маршрутизирует пакет в активную VPN-сессию.
routing.ipv6.ndp_proxy = off.Выберите режим по топологии провайдера
| Что делает провайдер | Признак | Настройка Qeli |
|---|---|---|
| Маршрутизирует отдельный префикс через WAN-адрес сервера | Для клиентских адресов на WAN не приходят NS; в панели провайдера указан route/next hop. | routing.ipv6.mode = routerouting.ipv6.ndp_proxy = off |
| Считает отдельный префикс on-link | На WAN приходят NS для каждого публичного IPv6 клиента. | routing.ipv6.mode = routerouting.ipv6.ndp_proxy = required |
| Топология пока неизвестна | Нет точного описания у провайдера. | Сначала проверьте трафик через tcpdump. auto допустим для диагностики, но для рабочей on-link схемы используйте required. |
Как понять, нужен ли proxy
Не назначайте клиентский префикс WAN-интерфейсу. Остановите профиль или сервер, запустите захват на фактическом IPv6 uplink и с независимого внешнего IPv6-хоста обратитесь к будущему адресу клиента:
# На сервере Qeli
$ sudo systemctl stop qeli
$ ip -6 addr show dev ens3
$ ip -6 route show table all
$ sudo tcpdump -ni ens3 'icmp6 && ip6[40] == 135'
# На внешнем IPv6-хосте
$ ping -6 2001:db8:1200:10::100Строка Neighbor Solicitation, who has 2001:db8:1200:10::100 означает, что upstream ищет MAC для клиентского адреса и этой схеме нужен NDP proxy. Если NS нет, сначала проверьте правильность адреса, WAN-интерфейс, firewall/security group и условия провайдера: одно отсутствие пакета ещё не доказывает L3-routed схему.
Полный фрагмент профиля
Адреса 2001:db8::/32 ниже предназначены только для документации. Замените их отдельным GUA-префиксом, выданным провайдером для клиентов.
[profile:public-v6-onlink]
tun.ip_mode = ipv6
tun.name = vpn-public
tun.ipv6_address = 2001:db8:1200:10::1
tun.mtu = 1280
pool.ipv6.cidr = 2001:db8:1200:10::/64
# Сохранять публичный IPv6 клиента; MASQUERADE не создаётся.
routing.ipv6.mode = route
routing.ipv6.interface = ens3
routing.ipv6.ndp_proxy = required
routing.ipv6.ndp_proxy_interface = ens3Закрепите адрес за существующим пользователем
Отдельно откройте users.conf, указанный в [auth] users_file, и добавьте или измените static_ipv6 в существующей секции [user:alice]. Сохраните password_hash, enabled, группы и лимиты; не создавайте вторую секцию с тем же именем.
[user:alice]
# Существующие параметры пользователя сохраните.
static_ipv6 = 2001:db8:1200:10::100Встроенные [user:*] в server.conf тоже поддерживаются, но при совпадении имени запись из users.conf имеет приоритет целиком. Поэтому настройка адреса только во встроенной записи не изменит существующую учётную запись из файла пользователей.
pool.ipv6.cidr интерфейсу ens3 и не создавайте для него connected route через WAN. Маршрут клиентского префикса должен вести только в TUN профиля; upstream выполняет NDP со своей стороны. Для dual-stack замените tun.ip_mode = ipv6 на dual и сохраните существующие параметры IPv4.Параметры и требования
| Режим | Поведение |
|---|---|
off | Responder выключен. Значение по умолчанию и правильный выбор для обычного L3-routed префикса. |
auto | Пытается открыть responder. При неподходящем интерфейсе, отсутствии Ethernet или CAP_NET_RAW пишет предупреждение и продолжает работу без него. |
required | Не запускает профиль, пока responder не привязан к выбранному интерфейсу. Рекомендуется для рабочей on-link схемы. |
Пустой routing.ipv6.ndp_proxy_interface использует фактический uplink из routing.ipv6.interface или IPv6 default route. Нужен Ethernet-совместимый Linux-интерфейс и право CAP_NET_RAW; обычный системный сервис Qeli уже запускается с необходимыми правами. NDP proxy поддерживает source-preserving режим route, а в разрабатываемой 0.8.2 — также manual. Сочетания с nat66 и off запрещены.
Какие адреса Qeli объявляет
- Точный IPv6 lease активной сессии из
pool.ipv6.cidr, включаяstatic_ipv6. - Адреса внутри non-default IPv6
client_subnet, зарегистрированного за подключённым router/site-to-site клиентом. - Только адреса живых сессий: disconnect, revoke или замена сессии немедленно снимают ownership.
/0, link-local, multicast, неверные NS и посторонние target-адреса игнорируются. Qeli не превращает responder в общий multicast relay.
Проверка после запуска
$ sudo qeli check-config --config /etc/qeli/server.conf
$ sudo systemctl restart qeli
$ sudo journalctl -u qeli -n 100 --no-pager | grep -F 'IPv6 NDP proxy'
$ ip -6 route show 2001:db8:1200:10::/64
$ sudo tcpdump -ni ens3 'icmp6 && (ip6[40] == 135 || ip6[40] == 136)'При required журнал должен сообщить session-aware IPv6 NDP proxy active on 'ens3', а маршрут клиентского /64 — вести через vpn-public. Пока Alice отключена, новый NS для её адреса не получает NA. После подключения Qeli отвечает своим MAC, а внешний трафик направляется в её TUN-сессию. После отключения новые ответы прекращаются; старая запись в neighbor cache upstream может сохраняться до истечения своего таймера.
| Симптом | Что проверить |
|---|---|
required не запускает профиль | Имя uplink, Ethernet-тип интерфейса, root/CAP_NET_RAW и сообщение в journal. |
В tcpdump нет NS | Правильность адреса и WAN-интерфейса, provider route/security group; возможно, префикс уже маршрутизируется и proxy не нужен. |
| NS есть, но NA нет при подключённом клиенте | Фактически выданный static_ipv6, состояние сессии и попадание target в pool или активный client_subnet. |
| NA есть, но трафик не доходит | Маршрут /64 в TUN, IPv6 forwarding, ip6tables/cloud firewall и firewall клиента. |
auto запустился без NDP | Найдите предупреждение NDP proxy auto mode is unavailable, исправьте причину и переключитесь на required. |
ipv6 = auto | required | off
| Политика | Результат |
|---|---|
auto | Принимает IPv4, dual или IPv6. Dual может безопасно понизиться до IPv4, если платформа не умеет полный IPv6-контракт. |
required | Требует внутренний IPv6 и fail-closed при старом сервере, IPv4-only профиле, MTU ниже 1280 или неполной поддержке платформы. |
off | Просит IPv4 у dual-профиля и отклоняет IPv6-only план. |
В full-tunnel отсутствие одного семейства блокируется по умолчанию. allow_ipv6_leak=true или allow_ipv4_leak=true — явный escape hatch, разрешающий отсутствующему семейству идти физическим путём. Оба значения по умолчанию false.
Настройте оба семейства целиком
dns.listenиdns.listen_ipv6должны совпадать с активными gateway-адресами профиля;dns.push_serversне должен раздавать недоступный listener.- IPv6 требует MTU не меньше 1280. Политика
requiredотвергает меньший план, а PMTU probing подбирает безопасный carrier budget. route,route_file, server push и ACL принимают IPv4/IPv6 CIDR, но применяются только для согласованного семейства.- Outer DNS для hostname endpoint может вернуть A и AAAA; roaming способен перенести сессию между семьями после аутентифицированной проверки пути.
Старый конфиг не становится dual-stack автоматически
- Сделайте backup и обновите сервер до 0.8.2.
- Выберите уникальный ULA /64 либо получите у провайдера routed GUA-префикс.
- Добавьте
tun.ip_mode,tun.ipv6_address,pool.ipv6.cidr, IPv6 DNS иrouting.ipv6.mode. - Выполните
qeli check-config; для NAT66 дополнительно проверьте default route, публичный source GUA и ip6tables NAT. - Оставьте клиентам
ipv6=auto, проверьте IPv4 и IPv6 отдельно, затем при необходимости ужесточайте доrequired.
Чего Qeli не делает
- Qeli не предоставляет NAT64/DNS64. IPv6-only клиент не откроет IPv4-only ресурс без отдельной NAT64-инфраструктуры.
- Qeli не превращает случайный WAN /64 в клиентский pool: нужен отдельный routed/on-link префикс и корректный обратный путь.
- IPv6 внутри туннеля не означает, что серверный uplink автоматически готов: проверяйте адрес, default route, firewall и обратный маршрут.