NAT traversal
NAT traversal — общий термин для методов установки P2P-соединения когда одна или обе стороны за NAT. Проблема существует потому что NAT транслирует адреса и не пропускает входящие соединения без предварительного исходящего.
Методы
| Метод | Что делает | Работает когда |
|---|---|---|
| STUN | Узнать свой внешний IP+порт через сервер-эхо | Стороне нужно знать что сообщить |
| UDP hole punching | Обе стороны шлют друг другу через свои внешние endpoint'ы одновременно — NAT открывает дырку | Не symmetric NAT с обеих сторон |
| TCP hole punching | Аналогично, но с TCP SYN | Реже работает, TCP менее лоялен к таким трюкам |
| TURN | Ретранслятор | Всегда (если серверы работают) |
| UPnP-IGD | Клиент просит роутер открыть порт | Роутер поддерживает и включён UPnP |
| NAT-PMP / PCP | То же самое, современнее (RFC 6886/6887) | Apple-роутеры и современные Linux/OpenWrt |
| ALG | Application Layer Gateway — NAT сам понимает протокол и открывает нужное | SIP-ALG на роутере — часто мешает |
| Relaying через сигналинг | Data-канал через тот же сервер что и signaling | Fallback для маленьких данных |
UDP hole punching — как это работает
Классический трюк для двух клиентов A и B оба за NAT:
- Оба клиента подключаются к серверу-рандеву (rendezvous). Сервер видит их внешние endpoint'ы
A_extиB_ext. - Сервер сообщает A: «B на
B_ext». Сообщает B: «A наA_ext». - Оба одновременно шлют UDP-пакет: A →
B_ext, B →A_ext. - Первый пакет от A создаёт запись в NAT A: «исходящее на B_ext». NAT A теперь пропускает входящее с B_ext.
- Первый пакет от B аналогично для NAT B.
- Первый пакет от A к B может не дойти — B ещё не открыл дырку. Но B тоже отправляет к A, и его пакет попадает в открытую дырку.
- Дальше соединение работает симметрично, пока NAT-mapping не истечёт (обычно 30-120 сек, обновляется keepalive'ом).
Symmetric NAT — беда
Symmetric NAT назначает разный внешний порт для каждого назначения. Клиент шлёт к STUN — получает порт 54321. Шлёт к пиру — NAT назначает порт 54322. Пир пытается достучаться на 54321, но там ничего нет.
Способы:
- Port prediction. Наблюдаем что NAT назначает
54321, 54322, 54323последовательно — пробуем несколько подряд. Работает у некоторых Cisco NAT. У современных Linux — рандомизация, не работает. - TURN. Единственно надёжный вариант.
По статистике WebRTC: symmetric NAT — 3-8% клиентов, все они через TURN.
CGNAT — новый уровень боли
Carrier-Grade NAT — NAT у провайдера, поверх твоего NAT. Двойной NAT. У сотовых операторов почти всегда, у некоторых кабельных тоже. Обычно symmetric — hole punching бесполезен, только TURN.
Проверить: если твой внешний IP из диапазона 100.64.0.0/10 — ты за CGNAT.
UPnP IGD
Клиент шлёт SSDP-broadcast, находит IGD-роутер, просит: «пробрось порт 12345 наружу на меня». Роутер соглашается (если UPnP включён).
Плюсы: работает без сервера. Минусы: полная дыра в безопасности — любое приложение (включая малварь) может пробросить порт. Многие корпоративные и security-заботные юзеры отключают UPnP.
# Linux: пробросить порт через UPnP
$ upnpc -a 192.168.1.100 8080 8080 TCP
Adding TCP 8080->192.168.1.100:8080 : done
Keepalive
NAT-mapping имеет TTL — 30 сек для UDP, 300+ для TCP у роутеров consumer-класса. Приложения шлют «keepalive» пакеты (пустые UDP или STUN Binding Indication) каждые 15-20 сек, чтобы дырка не закрылась.