Почему вообще существует NAT
IPv4 даёт 2^32 ≈ 4.3 млрд адресов. Устройств в мире -- десятки миллиардов. Решение -- Network Address Translation: миллионы устройств делят один публичный IP, переписывая заголовки пакетов на границе сети.
NAT ломает "конец-в-конец" принцип интернета, создаёт проблемы для P2P, но купил IPv4 ещё 25 лет жизни. Параллельно с IPv6 (который не нуждается в NAT) большинство современных сетей -- NAT-based.
Private IP ranges (RFC 1918)
Три диапазона зарезервированы для частных сетей и не маршрутизируются в интернете:
| CIDR | Range | Hosts | Обычное применение |
|---|---|---|---|
| 10.0.0.0/8 | 10.0.0.0 – 10.255.255.255 | 16.7M | Крупные корпоративные сети, AWS VPC default |
| 172.16.0.0/12 | 172.16.0.0 – 172.31.255.255 | 1M | Docker default bridge (172.17.0.0/16) |
| 192.168.0.0/16 | 192.168.0.0 – 192.168.255.255 | 65K | Домашние роутеры |
| 100.64.0.0/10 | 100.64.0.0 – 100.127.255.255 | 4M | CGNAT (Carrier-Grade) |
| 169.254.0.0/16 | link-local | 65K | Auto-config, cloud metadata (169.254.169.254) |
Пакет с source=10.0.0.5 не выйдет в публичный интернет -- провайдеры дропают такие (или должны).
CIDR notation
Classless Inter-Domain Routing (RFC 4632) записывает подсеть через префикс:
10.0.0.0/24
Бинарно:
10.0.0.0 = 00001010.00000000.00000000.00000000
Mask /24 = 11111111.11111111.11111111.00000000 (255.255.255.0)
Network = 10.0.0.0
Broadcast = 10.0.0.255
First host = 10.0.0.1
Last host = 10.0.0.254
Usable = 254
Таблица быстрого расчёта
| Prefix | Netmask | Total IPs | Usable hosts | Пример |
|---|---|---|---|---|
| /32 | 255.255.255.255 | 1 | 1 | Конкретный хост |
| /30 | 255.255.255.252 | 4 | 2 | Point-to-point link |
| /29 | 255.255.255.248 | 8 | 6 | Мини-сеть для 4-6 устройств |
| /28 | 255.255.255.240 | 16 | 14 | AWS VPC minimum subnet |
| /24 | 255.255.255.0 | 256 | 254 | Типичный LAN |
| /20 | 255.255.240.0 | 4096 | 4094 | Крупная подсеть VPC |
| /16 | 255.255.0.0 | 65536 | 65534 | Класс B, docker bridge |
| /8 | 255.0.0.0 | 16.7M | 16.7M | Класс A, 10/8 |
Для сабнеттинга важно: hosts = 2^(32-prefix) - 2 (минус network и broadcast).
AWS VPC дополнительно забирает 3 IP в начале и 1 в конце каждой subnet (VPC router, DNS, future use, broadcast).
Типы NAT
SNAT (Source NAT, Masquerade)
Типичный домашний роутер: исходящие пакеты от 192.168.1.x переписываются с source IP = публичный IP роутера.
Client 192.168.1.5:51234 → Router → Internet
SNAT:
rewrite src = 203.0.113.7:51234
Server 93.184.216.34:80
← reply to 203.0.113.7:51234
Router NAT table:
203.0.113.7:51234 ↔ 192.168.1.5:51234
rewrite dst = 192.168.1.5:51234 → Client
Роутер держит NAT table -- маппинг наружного соединения на внутреннее. Entry живёт, пока идёт трафик + timeout (60-600 секунд для TCP).
DNAT (Destination NAT, Port Forwarding)
Обратное -- пакет из интернета приходит на публичный IP:port, роутер перенаправляет на внутренний хост.
# iptables: forward public port 443 to internal web server
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 \
-j DNAT --to-destination 192.168.1.100:8443
# Plus masquerade back
iptables -t nat -A POSTROUTING -p tcp -d 192.168.1.100 --dport 8443 \
-j SNAT --to-source 192.168.1.1
# Enable kernel IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
Full Cone / Restricted / Symmetric NAT
RFC 3489 (STUN original) классифицировал NAT по поведению:
- Full Cone -- любой внешний IP:port может слать на открытую дырку. P2P-friendly.
- Restricted Cone -- только тот внешний IP, куда клиент уже слал. Порт любой.
- Port-Restricted Cone -- только тот внешний IP:port.
- Symmetric -- для каждого dst маппинг разный. Убивает P2P.
Symmetric NAT (типичный carrier-grade) -- главный враг WebRTC и игровых P2P.
NAT traversal: STUN и TURN
STUN (Session Traversal Utilities for NAT)
Клиент за NAT хочет узнать, какой у него public IP:port. Он стучит на STUN-сервер, сервер отвечает: "я видел тебя с 203.0.113.7:51234". Дальше клиент сообщает пиру этот адрес.
Client (10.0.0.5:51234) → NAT (203.0.113.7:45678) → STUN server (74.125.250.129:19302)
STUN response:
Your reflexive address: 203.0.113.7:45678
Client share это peer'у через signaling.
Peer пытается подключиться напрямую по 203.0.113.7:45678.
Если NAT симметричный -- маппинг для другого dst будет другим, не сработает.
TURN (Traversal Using Relays around NAT)
Когда STUN не помогает (symmetric NAT, CGNAT), используется TURN-сервер как relay:
Peer A ───→ TURN server ←─── Peer B
(10.0.0.5) (203.0.113.20) (10.1.2.3)
Весь трафик идёт через TURN-сервер. Дороже, но всегда работает.
WebRTC обычно пробует по порядку: host (LAN) → srflx (STUN) → relay (TURN). Это называется ICE -- Interactive Connectivity Establishment.
Hairpinning
Сценарий: два устройства за одним NAT хотят общаться через публичный IP этого NAT.
Device A (192.168.1.5) → 203.0.113.7:443 (публичный адрес своего же роутера) → Device B (192.168.1.10)
Hairpin NAT должен завернуть пакет обратно в LAN. Многие дешёвые роутеры этого не умеют -- соединение умирает. Решение -- split-horizon DNS (отдавать внутренний IP изнутри, внешний -- извне).
CGNAT: double NAT
Провайдерам не хватает публичных IPv4, они ставят свой NAT между абонентом и интернетом:
Home device → Home router NAT → CGNAT (ISP) → Internet
10.0.0.5 192.168.1.1 100.64.0.1 203.0.113.7
Абонент получает адрес из 100.64.0.0/10 (CGNAT range). Два слоя NAT ломают ещё больше P2P, inbound connection становится невозможен без провайдерского cooperation.
VPC subnet design
AWS VPC
VPC -- изолированная виртуальная сеть в облаке. Типичная топология:
VPC: 10.0.0.0/16 (65K адресов)
│
├── Public subnet A: 10.0.0.0/20 (AZ-1, IGW route)
├── Public subnet B: 10.0.16.0/20 (AZ-2, IGW route)
│
├── Private subnet A: 10.0.32.0/20 (AZ-1, NAT Gateway)
├── Private subnet B: 10.0.48.0/20 (AZ-2, NAT Gateway)
│
├── DB subnet A: 10.0.64.0/24 (AZ-1, no internet)
└── DB subnet B: 10.0.65.0/24 (AZ-2, no internet)
Правила
- Не использовать
192.168.0.0/16для VPC -- дома тоже используется, VPN ломается. - Резервировать
/16(минимум) -- потом не расширить. - Подсети должны не пересекаться между VPC, если планируется peering.
- Одна subnet = одна AZ (нельзя растянуть).
- Public vs Private определяется маршрутом: public имеет route
0.0.0.0/0 → IGW, private → NAT Gateway.
Docker bridge network = NAT
Docker создаёт Linux bridge docker0 с адресом 172.17.0.1/16. Каждый контейнер получает IP в 172.17.0.0/16 и общается с миром через masquerade NAT.
# Смотрим bridge
ip addr show docker0
# inet 172.17.0.1/16 scope global docker0
# NAT правила для исходящих
iptables -t nat -L POSTROUTING -n
# MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
# Port publishing = DNAT
docker run -p 8080:80 nginx
# → iptables DNAT: host:8080 → 172.17.0.2:80
iptables -t nat -L DOCKER -n
# DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80
Overlay-сети в Swarm/K8s работают поверх VXLAN и шифруются, NAT -- только на границе (ingress).
Kubernetes networking
K8s имеет три слоя IP:
Node network: 10.0.0.0/16 (реальные VM)
Pod network: 10.244.0.0/16 (каждый pod свой IP, flat)
Service network: 10.96.0.0/12 (виртуальные ClusterIP)
Pod IP
Каждый Pod получает уникальный IP в cluster. Pods могут общаться напрямую без NAT (это K8s model).
ClusterIP
Service с type: ClusterIP -- виртуальный IP в service-subnet, не принадлежит ни одному Pod. kube-proxy реализует балансировку через iptables/IPVS rules:
Client → 10.96.23.45:80 (ClusterIP)
iptables:
dst=10.96.23.45:80
→ DNAT to one of: 10.244.1.2:8080, 10.244.1.3:8080, 10.244.2.1:8080
NodePort / LoadBalancer / ExternalIP
NodePort-- открывает порт 30000-32767 на всех Node, извне доступно черезany-node-ip:nodeport.LoadBalancer-- облачный LB (AWS ELB, GCP LB), маршрутизирует на NodePorts.ExternalIP-- Pod видит приходящий трафик с real client IP (если сохранять черезexternalTrafficPolicy: Local).
Практика: iptables NAT пример
Домашний шлюз с port forwarding
#!/bin/bash
# /usr/local/bin/setup-nat.sh
# Run on the gateway/router
WAN_IF=eth0
LAN_IF=eth1
LAN_NET=192.168.1.0/24
# Enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
# Flush NAT table
iptables -t nat -F
iptables -t nat -X
# Masquerade outbound traffic from LAN
iptables -t nat -A POSTROUTING -s $LAN_NET -o $WAN_IF -j MASQUERADE
# Forward incoming HTTPS to internal web server
iptables -t nat -A PREROUTING -i $WAN_IF -p tcp --dport 443 \
-j DNAT --to-destination 192.168.1.100:443
# Allow forwarding between interfaces
iptables -A FORWARD -i $LAN_IF -o $WAN_IF -j ACCEPT
iptables -A FORWARD -i $WAN_IF -o $LAN_IF \
-m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i $WAN_IF -o $LAN_IF -p tcp --dport 443 -j ACCEPT
# Show NAT rules
iptables -t nat -L -n -v --line-numbers
Linux routing
# Показать таблицу маршрутизации
ip route
# default via 192.168.1.1 dev eth0
# 10.0.0.0/8 via 192.168.1.1 dev eth0
# 169.254.0.0/16 dev eth0 scope link metric 1000
# 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.50
# Добавить статический маршрут для VPN подсети
ip route add 10.0.100.0/24 via 192.168.1.254 dev eth0
# Показать, какой маршрут выберется для конкретного destination
ip route get 8.8.8.8
# 8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.50
# Policy-based routing (несколько таблиц)
ip rule add from 192.168.1.100/32 lookup 100
ip route add default via 10.0.0.1 table 100
# Проверить NAT connection tracking
conntrack -L | head
# tcp 6 ESTABLISHED src=192.168.1.5 dst=93.184.216.34 sport=51234 dport=443
# src=93.184.216.34 dst=203.0.113.7 sport=443 dport=45678 [ASSURED]
Conntrack: сердце NAT на Linux
NAT работает поверх conntrack -- per-connection state tracker в ядре. Таблица имеет лимит (net.netfilter.nf_conntrack_max, обычно 262144). Переполнение приводит к drop новых соединений.
# Current usage
cat /proc/sys/net/netfilter/nf_conntrack_count
wc -l /proc/net/nf_conntrack
# Max
sysctl net.netfilter.nf_conntrack_max
# Tune для busy gateway
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.nf_conntrack_tcp_timeout_established=600
Когда NAT мешает
- Real client IP: приложение за NAT видит только адрес балансера. Решение --
X-Forwarded-For(HTTP), PROXY protocol (L4). - Long-lived connections: TCP keepalive должен быть чаще NAT timeout (~180 сек), иначе roadrmap тихо умирает.
- P2P: WebRTC, BitTorrent, игры требуют STUN/TURN или UPnP.
- FTP, SIP: протоколы, передающие IP в payload, нуждаются в ALG (Application Layer Gateway).
- IPsec: ломается NAT, требует NAT-T (UDP encapsulation).
- Port exhaustion: один публичный IP = 65K портов на destination. CGNAT с 1000 абонентов на IP быстро кончается.
Выводы
- Private ranges: 10/8, 172.16/12, 192.168/16, плюс 100.64/10 для CGNAT.
- CIDR
/prefix: hosts = 2^(32-prefix) - 2. AWS ещё вычитает 3 служебных адреса. - NAT типы: SNAT (outbound), DNAT (port forwarding). Симметричный NAT ломает P2P.
- STUN даёт public reflexive address, TURN relay'ит трафик когда STUN бессилен.
- CGNAT -- двойной NAT у провайдеров, абонент получает адрес из
100.64/10. - Docker = bridge + MASQUERADE. K8s = flat pod network + virtual ClusterIPs.
- Проектируя VPC: не пересекаться с офисными сетями, резервировать
/16, одна subnet = одна AZ. - conntrack table -- узкое место busy NAT-gateway, tune
nf_conntrack_max.