Files
cs-note/hhs/NETWORK/03-网络层/08-NAT原理与应用.md
T

211 lines
7.7 KiB
Markdown
Raw Normal View History

2026-05-24 11:42:38 +08:00
---
tags: [计算机网络, NAT, SNAT, DNAT, PAT, STUN, TURN]
create time: 2026-05-18 01:20
---
# NAT 原理与应用
## 概述
NAT(Network Address Translation,网络地址转换)是 IPv4 时代最重要的"续命"技术之一——它让大量私有 IP 主机通过少数几个公网 IP 访问互联网。理解 NAT 的工作原理对于排查连接问题、设计分布式系统和部署 VPN 至关重要。
> [!QUESTION] NAT 为什么能工作?
> NAT 的本质是一个中间人:它在修改经过的数据包的源或目的地址/端口。因为 TCP/IP 协议的端到端原则要求只有通信两端才应看到完整地址,而 NAT 在中间偷偷改了——这破坏了理论模型的"纯洁性",但解决了现实中的地址短缺问题。
## NAT 分类速览
| 类型 | 全称 | 方向 | 修改字段 | 用途 |
|------|------|------|---------|------|
| **SNAT** | Source NAT | 出向 | 源 IP | 内网 → 外网 |
| **DNAT** | Destination NAT | 入向 | 目的 IP+Port | 外网 → 内网服务器 |
| **PAT** | Port Address Translation | 双向 | 同时改 IP+Port | 多主机共享单公网 IP |
| **静态 NAT** | Static NAT | 双向一对一 | 固定映射 | 内网服务器暴露到公网 |
## SNAT(源地址转换)— 内网上网的钥匙
### 工作流程
```mermaid
sequenceDiagram
participant PC as 内网主机<br/>192.168.1.100:45678
participant NAT as 路由器/NAT网关<br/>eth0: 203.0.113.5<br/>eth1: 192.168.1.1
participant Server as 外部 Web 服务器<br/>93.184.216.34:80
PC->>NAT: SYN src=192.168.1.100:45678 dst=93.184.216.34:80
Note over NAT: NAT Table Entry:<br/>192.168.1.100:45678 ↔ 203.0.113.5:50001
NAT->>Server: SYN src=203.0.113.5:50001 dst=93.184.216.34:80
Server-->>NAT: SYN-ACK dst=203.0.113.5:50001
Note over NAT: 反向查找 NAT Table<br/>203.0.113.5:50001 → 192.168.1.100:45678
NAT-->>PC: SYN-ACK
PC->>NAT: ACK src=192.168.1.100:45678
NAT->>Server: ACK src=203.0.113.5:50001
Note over PC,Server: ← 双方都不知道 NAT 的存在 →
```
### iptables 实现 SNAT
```bash
# 内网接口 eth1, 外网接口 eth0
sudo iptables -t nat -A POSTROUTING \
-s 192.168.1.0/24 -o eth0 -j MASQUERADE
# MASQUERADE vs SNAT:
# MASQUERADE: 自动获取 eth0 的当前 IP(适合动态 IP,如 DHCP/拨号)
# SNAT: 指定固定 IP (适合静态 IP,性能略高)
# 等价写法:
sudo iptables -t nat -A POSTROUTING \
-s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.5
```
```bash
# 查看 NAT 表条目
$ sudo conntrack -L | head -10
tcp 6 119 SYN_SENT ... src=192.168.1.100 dst=93.184.216.34
src=203.0.113.5 dst=93.184.216.34
```
## DNAT(目的地址转换)— 反向代理
### 典型场景:将公网 IP 的 80 端口转发到内网 Web 服务器
```mermaid
sequenceDiagram
participant Client as 外部客户端<br/>x.x.x.x:12345
participant FW as 防火墙/NAT<br/>公网: 203.0.113.5:80
participant Web as 内网 Web<br/>192.168.1.10:80
Client->>FW: SYN src=x.x.x.x:12345 dst=203.0.113.5:80
Note over FW: PREROUTING 链: DNAT<br/>203.0.113.5:80 → 192.168.1.10:80
FW->>Web: SYN src=x.x.x.x:12345 dst=192.168.1.10:80
Note over Web: ← 注意: 源IP仍然是客户端真实IP (Full Cone)
Web-->>FW: SYN-ACK dst=x.x.x.x:12345
FW->>Client: SYN-ACK
Note over FW,Web: 回程路由需要配置: ip route add x.x.x.x via ...
```
### iptables 实现 DNAT
```bash
sudo iptables -t nat -A PREROUTING \
-d 203.0.113.5 -p tcp --dport 80 -j DNAT \
--to-destination 192.168.1.10:80
# 还需允许转发
sudo iptables -A FORWARD -p tcp -d 192.168.1.10 --dport 80 -j ACCEPT
# 如果希望 Web 能看到客户端真实 IP(不启用 SNAT)
sudo sysctl net.ipv4.conf.all.accept_local=1
```
### Docker 的端口映射本质就是 DNAT + SNAT
```bash
# docker run -p 8080:80 nginx
# = DNAT: host:8080 → container_ip:80
# + SNAT: container reply 时把源 IP 改回 host IP
iptables -t nat -A PREROUTING \
-d <host_ip> -p tcp --dport 8080 -j DNAT --to-destination <container_ip>:80
```
## PAT / NAPT(端口级 NAT)— 解决地址枯竭的关键
### 核心机制
```
内网主机 NAT设备 公网IP:Port池
───────── ─────── ─────────────
192.168.1.10:45678 ──→ 203.0.113.5:50001 203.0.113.5
192.168.1.11:45678 ──→ 203.0.113.5:50002 ← 多个内网IP
192.168.1.12:45678 ──→ 203.0.113.5:50003 ← 复用同一个公网IP
192.168.1.10:45679 ──→ 203.0.113.5:50004
```
一个公网 IP 最多可提供 **65535 个端口**,理论上支持约 6.5 万并发内网用户。实际受限于内核文件描述符限制(通常 ~6.5 万)。
## NAT 对 TCP 的影响
### TIME_WAIT 放大效应
由于所有内网主机的连接都映射到同一个公网 IP,NAT 设备的 TIME_WAIT 连接数会远超单机。当 NAT 重启时,所有半关闭的连接都会丢失。
### FTP 的问题
FTP 使用**独立的控制连接和数据连接**,且在 HTTP body 中嵌入 IP 和端口信息:
```
FTP 控制通道: PORT 192,168,1,100,180,13 ← "数据连接请发到这个IP和端口"
```
NAT 后的内网 IP 对公网不可达!解决方案:
| 方案 | 说明 |
|------|------|
| **FTP ALG** | NAT 设备深度解析 FTP 控制流,改写 PORT 命令中的 IP |
| **被动模式 PASV** | 服务器提供监听端口,由客户端发起数据连接 |
| **SFTP over SSH** | 完全绕过 FTP,用加密隧道传输文件 |
> [!warning] NAT ALG 的隐患
> ALG (Application Layer Gateway) 需要深度解析应用层协议内容,容易出错且增加安全风险。现代实践中更倾向于 SFTP/SCP 替代 FTP。
## NAT 穿透技术
### NAT 类型分类
| 类型 | 行为 | 连通性 |
|------|------|--------|
| Full Cone | 任何外部地址都能发送 UDP 到你的映射端口 | ⭐⭐⭐⭐⭐ 最佳 |
| Restricted Cone | 仅已知 IP 能发 | ⭐⭐⭐⭐ |
| Port Restricted Cone | 仅已知 IP+Port 能发 | ⭐⭐⭐⭐ |
| Symmetric | 不同目的地获得不同映射端口 | ⭐⭐ 最难穿透 |
### 常用穿透方案
```mermaid
flowchart TD
P1["STUN<br/>Session Traversal Utilities for NAT"] -->|"1. 告诉对方我的公网 IP:Port"| P2["TURN<br/>Traversal Using Relays around NAT"]
P2 -->|"2. 中继服务器转发电文"| P3["ICE<br/>Interactive Connectivity Establishment"]
P3 -->|"3. 尝试直连 → 失败则 fallback 到 TURN"| Final["WebSocket/WebRTC ✅"]
style P1 fill:#DDA0DD,color:#000
style P2 fill:#FFD700,color:#000
style P3 fill:#98FB98,color:#000
```
### ICE 流程
```
Step 1: Host Candidate — 本机直接 IP(局域网内直连最快)
Step 2: SRFLX Candidate — STUN 探测得到的公网映射地址
Step 3: Relay Candidate — TURN 服务器分配的 relay 地址
排序优先级: Host > SRFLX > Relay
尝试顺序: 先试直连,超时后走中继
```
## NAT64 / DNS64
IPv6 过渡的重要机制:IPv6-only 客户端访问 IPv4-only 服务器
```
客户端 (IPv6): 2001:db8::1
DNS64: 将 A 记录 93.184.216.34 → AAAA 记录 ::ffff:93.184.216.34
NAT64 网关: 拦截 IPv6 包 → 转换为 IPv4 包 → 发送到 Internet
回包: IPv4 → IPv6 转换 → 返回客户端
```
```bash
# Linux NAT64 内核模块
modprobe nf_nat_ipv6
sysctl -w net.ipv6.conf.all.forwarding=1
ip6tables -t nat -A POSTROUTING -s fd00::/64 -j SNAT --to-source 2001:db8:64::1
```
## 关联笔记
- [[hhs/NETWORK/CIDR与子网划分]] — NAT 使用 RFC1918 私有地址空间
- [[hhs/NETWORK/TCP状态机详解]] — NAT 影响 TCP 连接生命周期
- [[hhs/NETWORK/DNS原理与优化]] — DNS64 将 A 记录转为 AAAA 记录