Files
cs-note/hhs/NETWORK/03-网络层/08-NAT原理与应用.md
T
2026-05-24 11:42:38 +08:00

211 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 记录