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
ALGApplication Layer Gateway — NAT сам понимает протокол и открывает нужноеSIP-ALG на роутере — часто мешает
Relaying через сигналингData-канал через тот же сервер что и signalingFallback для маленьких данных

UDP hole punching — как это работает

Классический трюк для двух клиентов A и B оба за NAT:

  1. Оба клиента подключаются к серверу-рандеву (rendezvous). Сервер видит их внешние endpoint'ы A_ext и B_ext.
  2. Сервер сообщает A: «B на B_ext». Сообщает B: «A на A_ext».
  3. Оба одновременно шлют UDP-пакет: A → B_ext, B → A_ext.
  4. Первый пакет от A создаёт запись в NAT A: «исходящее на B_ext». NAT A теперь пропускает входящее с B_ext.
  5. Первый пакет от B аналогично для NAT B.
  6. Первый пакет от A к B может не дойти — B ещё не открыл дырку. Но B тоже отправляет к A, и его пакет попадает в открытую дырку.
  7. Дальше соединение работает симметрично, пока NAT-mapping не истечёт (обычно 30-120 сек, обновляется keepalive'ом).

Symmetric NAT — беда

Symmetric NAT назначает разный внешний порт для каждого назначения. Клиент шлёт к STUN — получает порт 54321. Шлёт к пиру — NAT назначает порт 54322. Пир пытается достучаться на 54321, но там ничего нет.

Способы:

По статистике 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 сек, чтобы дырка не закрылась.

См. также