Session roaming: как Qeli переносит сессию
Смена физического пути без новой lease: TCP/UDP handover, ожидание пригодной Wi‑Fi/LTE сети, fail-closed TUN, stable multipath slots и диагностика.
Сессия отделена от физического соединения
Начиная с Qeli 0.8.0, session roaming сохраняет одну логическую VPN-сессию, когда у устройства меняется физический путь: Wi‑Fi сменяется мобильной сетью, меняется внешний адрес, происходит NAT rebinding или доступным становится другой A/AAAA endpoint. Новый carrier проверяется внутри уже аутентифицированного контракта, поэтому сервер не выдаёт второй lease и не создаёт вторую пользовательскую сессию.
reality-tls на udp-quic и не выбирает другой сервер. Он переносит ту же сессию того же профиля между доступными физическими путями.Prepare → проверка → commit
- Платформенный адаптер замечает изменение сети и готовит точный candidate path: временный host route, правила kill-switch и отдельный socket, привязанный к нужному интерфейсу.
- Общее ядро связывает candidate с текущей generation/epoch и проверяет его аутентифицированным обменом. Неверный CID, token, направление или stale epoch отклоняются.
- До подтверждения старый путь остаётся активным. После успешного commit новый carrier становится единственным текущим путём, а временное candidate-состояние передаётся обычному владельцу сессии.
- Ошибка до commit откатывает только candidate. Если результат после точки commit неоднозначен, Qeli завершает generation и выполняет чистый reconnect, не оставляя «живой», но чёрный туннель.
| Carrier | Как переносится путь | Защита |
|---|---|---|
| TCP | Make-before-break: новый socket проходит JOIN/проверку до закрытия старого. Commit переносит весь bonded carrier set, а не только один stream. | Двухфазный commit и reconnect при неоднозначной post-commit ошибке. |
| UDP | Новый peer/CID считается candidate. Аутентифицированные данные нового пути удерживаются в bounded-очереди до commit. | 128-битный challenge, anti-amplification, отклонение stale epoch и ограничение candidate. |
Тот же механизм покрывает смену внешнего IPv4 на IPv6 и обратно, если endpoint, listener и платформа поддерживают обе семьи. Внутреннее семейство TUN при этом не обязано совпадать с внешним carrier.
roaming = off | auto | required
| Значение | Поведение | Когда выбирать |
|---|---|---|
off | Capability скрывается; при смене пути выполняется обычный reconnect. | Диагностика или намеренное отключение функции. |
auto | Использует согласованный TCP/UDP roaming; при старом сервере, core или неподдерживаемой платформе безопасно возвращается к reconnect. | Рекомендуемый режим и дефолт поставляемых клиентских шаблонов. |
required | Завершает подключение до передачи credentials/полного AUTH, если весь transport-specific контракт недоступен. | Только полностью обновлённый и проверенный парк. |
local или ненулевой lport закрепляет carrier socket. В auto Qeli использует reconnect fallback, а required отклоняется проверкой конфигурации.Включение и ресурсные пределы
roaming.enabled = true
roaming.grace_secs = 30
roaming.max_orphaned = 256
roaming.max_orphan_bytes = 67108864
# client.conf · [qeli]
roaming = auto
| Ключ | Дефолт нового шаблона | Диапазон | Назначение |
|---|---|---|---|
roaming.enabled | true | bool | Feature gate профиля. Если ключ отсутствует в старом sparse-конфиге, фактический результат — false. |
roaming.grace_secs | 30 | 1…3600 | Сколько неожиданно отсоединённая TCP-сессия ждёт аутентифицированного resume. |
roaming.max_orphaned | 256 | 1…65536 | Предел отсоединённых TCP-сессий профиля, сохранённых для resume. |
roaming.max_orphan_bytes | 67108864 | 4194304…1073741824 | Общий предел памяти orphaned-сессий профиля. |
Новые профили установщика включают bounded roaming. Обновление бинаря не меняет существующий server.conf: добавьте ключи явно, проверьте qeli check-config и только затем перезапустите профиль.
Транспорты, платформы и маршруты
- Обычный TCP:
plain,fake-tls,obfs,realityиreality-tls. - UDP camouflage:
udp-fake-tls,udp-quic,udp-obfsиobfs-awgчерез единую state machine. - Android и iOS передают ядру физические network changes; Windows, macOS и Linux используют тот же fail-closed результат candidate/commit. Старый native core в режиме
autoозначает reconnect. - Явные
excludeи carrier host routes имеют приоритет: roaming не должен случайно направить endpoint внутрь самого туннеля. - Startup и live PMTU проверяются аутентифицированным 128-битным challenge; новая сеть может получить другой безопасный размер пакета.
Мобильное восстановление учитывает реальную доступность сети
- Android и iOS не расходуют reconnect backoff, пока нет пригодного Wi‑Fi или мобильного carrier. После появления связи попытка продолжается сразу.
- Android сохраняет fail-closed TUN между перезапусками native core, если NetworkPlan не изменился. Ручное Disconnect остаётся окончательным.
- iOS не применяет повторно идентичные NetworkExtension settings и передаёт новому поколению ядра только bounded-очередь uplink-пакетов, ещё не принятых старым.
- TCP multipath использует стабильные logical slot ID: исчезновение или возврат одного bonded carrier не переназначает здоровые потоки.
- Android сохраняет до 1000 последних событий, но не более 512 КиБ, в приватном no-backup журнале. Кнопка Clear удаляет историю; секреты и полный профиль туда не записываются.
1.15 — отдельно от compatibility floor.Как понять, что roaming действительно сработал
$ sudo qeli check-config --config /etc/qeli/server.conf
$ sudo journalctl -u qeli -f
- Подключитесь с
roaming=auto, запустите непрерывный ping и длинную загрузку. - Переключите Wi‑Fi на мобильную сеть либо измените доступный uplink, не отключая профиль.
- Проверьте, что внутренняя lease/TUN не были пересозданы. В панели и статистике должны увеличиться attempts/commits, а candidate после завершения вернуться к нулю.
- Если растут failures или reconnect fallbacks, проверьте server feature gate, версию native core, явные
local/lport, firewall обоих carrier-путей и PMTU.