教科书告诉你互联网是端到端的——发送方和接收方直接通信,中间的路由器只负责转发 IP 包。但现实是:你的手机发出去的包,源地址 192.168.1.105 在离开家用的路由器时就被改成了运营商分配的公网 IP;你想在家搭建一个 Web 服务,外网根本连不进来;你的 P2P 下载永远卡在”寻找节点”——这一切的幕后推手,就是 NAT 和各种中间盒。
NAT(Network Address Translation,网络地址翻译)是互联网对 IPv4 地址耗尽问题的工程妥协。它打破了端到端原则,让数十亿设备躲在少量公网 IP 后面上网——代价是接收方无法主动连接你,P2P 通信变得困难,协议设计者不得不发明 STUN、TURN、ICE 等穿透机制来绕过 NAT。
而 NAT 只是中间盒(Middlebox)的一种。防火墙过滤流量、DPI 检查应用层内容、负载均衡器分发请求、CDN 缓存内容——这些设备都不在”路由器只转发 IP 包”的教科书模型里,但它们构成了互联网的”隐形基础设施”。
本章从 NAT 为什么存在开始,深入 NAPT 的工作原理和连接追踪,分析 NAT 如何破坏端到端,然后讲解 STUN/TURN/ICE 穿透机制,最后扩展到防火墙、DPI、负载均衡器等中间盒,理解为什么”干净分层”是理想而非现实。
一、NAT 为什么存在
1.1 IPv4 地址不够了
在 IP地址与子网 中看到,IPv4 只有 32 位地址空间,理论上限约 43 亿个地址。但实际可用地址远少于此——IANA 在 2011 年就分配完了最后的 /8 地址块,APNIC 在 2011 年、RIPE NCC 在 2012 年、LACNIC 在 2014 年、AFRINIC 在 2020 年也相继耗尽了各自的地址池。
到 2026 年,全球联网设备数量已超过 300 亿——手机、电脑、IoT 设备、智能家电——远超 IPv4 地址空间。但互联网并没有因为地址耗尽而崩溃,原因就是 NAT。
1.2 NAT 如何节省地址
NAT 的核心思想很简单:多台设备共享一个(或少数几个)公网 IP。内部网络使用私有地址(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16),这些地址在公网上不可路由。当内部设备访问公网时,NAT 设备将私有地址替换为公网地址,并在返回时反向替换。
# 查看本机的私有 IP 和公网 IPip addr show | grep "inet " # 私有 IP(通常是 192.168.x.x 或 10.x.x.x)
# 查看公网 IPcurl -s ifconfig.me # 通过外部服务获取公网 IP
# 典型场景:# 内网 IP: 192.168.1.105# 公网 IP: 203.0.113.42(NAT 设备的外网地址)# 比例:整个家庭网络(可能 20+ 设备)共享 1 个公网 IP一个典型的家庭网络:路由器 WAN 口获得运营商分配的 1 个公网 IP,LAN 口使用 192.168.1.0/24 私有网段。家里 20 台设备共享这 1 个公网 IP 上网——地址节省了 20 倍。
1.3 四种 NAT 类型
RFC 2663 定义了四种 NAT 类型,它们的区别在于翻译的粒度:
| NAT 类型 | 翻译方式 | 地址节省 | 端到端影响 | 典型场景 |
|---|---|---|---|---|
| 静态 NAT(1:1) | 一个私有 IP 固定映射到一个公网 IP | 不节省 | 几乎无 | 服务器发布、云主机 |
| 动态 NAT(池) | �私有 IP 从公网 IP 池中动态选取 | 有限 | 中等 | 企业出口(地址池) |
| NAPT(PAT) | 私有 IP + 端口映射到公网 IP + 端口 | 大幅节省 | 严重 | 家用路由器、企业出口 |
| 双向 NAT | 同时改写源和目的地址 | 视场景 | 严重 | 网络合并、IPv4-IPv6 翻译 |
日常说的”NAT”几乎都是 NAPT(Network Address Port Translation),也叫 PAT(Port Address Translation)。静态 NAT 和动态 NAT 不改写端口号,一个公网 IP 只能映射一台内部设备,实际上并不节省地址。NAPT 通过同时改写 IP 和端口,让数千个内部连接复用同一个公网 IP——这才是 NAT 能解决地址危机的根本原因。
1.4 为什么 NAPT 是主流
NAPT 成为事实标准有三个原因:
- 地址节省效果最好:一个公网 IP 可以同时支持约 65000 个并发连接(端口号 16 位),对家庭网络绰绰有余
- 隐式安全:外部无法主动连接内部设备——NAPT 转换表里没有的映射,包会被丢弃。这虽然不是真正的安全机制,但客观上挡掉了大量扫描和攻击
- 对应用透明:大部分客户端-服务器应用(浏览网页、发邮件)不需要任何修改就能工作——NAPT 自动处理出站连接的地址翻译
# 查看家用路由器的 NAT 配置(OpenWrt 示例)uci show firewall | grep nat
# 典型输出:# firewall.@nat[0]=nat# firewall.@nat[0].name='masquerade'# firewall.@nat[0].src='lan'# firewall.@nat[0].dest='wan'# firewall.@nat[0].target='MASQUERADE'# MASQUERADE = 动态 NAPT,公网 IP 由 DHCP 分配时使用二、NAPT 工作原理
2.1 转换表结构
NAPT 的核心数据结构是转换表(Translation Table),每条记录映射一个”内部 IP:端口”到”外部 IP:端口”:
# NAPT 转换表示例# 格式:内部地址:端口 ←→ 外部地址:端口 ←→ 目标地址:端口## 内部地址 外部地址 目标地址 协议# 192.168.1.105:54321 203.0.113.42:54321 93.184.216.34:80 TCP# 192.168.1.105:54322 203.0.113.42:54322 140.82.121.3:443 TCP# 192.168.1.106:54321 203.0.113.42:54323 93.184.216.34:443 TCP# 192.168.1.107:54321 203.0.113.42:54324 151.101.1.69:443 TCP注意第三条:内部两台设备使用了相同的内部端口号 54321,但 NAPT 为它们分配了不同的外部端口号 54321 和 54323——外部端口号是区分不同内部连接的关键。
2.2 完整连接转换追踪
以一个 HTTP 请求为例,追踪数据包从内网到外网的完整转换过程:
出站时,NAPT 改写源 IP 和源端口;入站时,改写目的 IP 和目的端口。服务器只知道它在和 203.0.113.42:54321 通信,完全不知道后面还有一台 192.168.1.105:54321 的主机。
2.3 源 NAT vs 目的 NAT
NAT 不只改写源地址。根据改写的对象不同,NAT 分为两大类:
| 类型 | 改写内容 | 方向 | 典型场景 | iptables 链 |
|---|---|---|---|---|
| 源 NAT(SNAT) | 源 IP + 源端口 | 出站 | 内网上网(MASQUERADE) | POSTROUTING |
| 目的 NAT(DNAT) | 目的 IP + 目的端口 | 入站 | 端口转发、服务器发布 | PREROUTING |
# Linux iptables 配置源 NAT(MASQUERADE)# 出站时改写源地址为外网接口地址iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# Linux iptables 配置目的 NAT(端口转发)# 将外网 203.0.113.42:8080 转发到内网 192.168.1.100:80iptables -t nat -A PREROUTING -d 203.0.113.42 -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
# 查看 NAT 规则iptables -t nat -L -n -v
# 典型输出:# Chain POSTROUTING (policy ACCEPT)# target prot opt source destination# MASQUERADE all -- 192.168.1.0/24 0.0.0.0/0
# Chain PREROUTING (policy ACCEPT)# target prot opt source destination# DNAT tcp -- 0.0.0.0/0 203.0.113.42 tcp dpt:8080 to:192.168.1.100:80源 NAT 让内网设备能访问外网;目的 NAT 让外网能访问内网服务。两者经常配合使用——家用路由器做源 NAT 让全家上网,同时做目的 NAT 把特定端口转发到内网的 NAS 或游戏主机。
2.4 Conntrack:连接追踪
NAPT 要正确改写返回包,必须记住每个连接的映射关系——这就是连接追踪(Connection Tracking)。Linux 内核的 nf_conntrack 模块负责这项工作:
# 查看当前连接追踪表cat /proc/net/nf_conntrack
# 典型输出:# ipv4 2 tcp 6 299 ESTABLISHED src=192.168.1.105 dst=93.184.216.34 sport=54321 dport=80 src=93.184.216.34 dst=203.0.113.42 sport=80 dport=54321 [ASSURED] mark=0 zone=0 use=2# ipv4 2 udp 17 29 src=192.168.1.105 dst=8.8.8.8 sport=54322 dport=53 src=8.8.8.8 dst=203.0.113.42 sport=53 dport=54322 [ASSURED] mark=0 zone=0 use=2
# 用 conntrack 工具查看(更可读)conntrack -L
# 查看连接追踪统计conntrack -S
# 查看最大追踪连接数cat /proc/sys/net/netfilter/nf_conntrack_max# 典型值:65536(家用路由器)或 262144+(服务器)
# 查看当前追踪连接数cat /proc/sys/net/netfilter/nf_conntrack_count
# 当 conntrack_count 接近 conntrack_max 时,新连接会被丢弃# 症状:网络间歇性断开,dmesg 出现 "nf_conntrack: table full, dropping packet"Conntrack 表满是一个常见但隐蔽的故障。当并发连接数超过 nf_conntrack_max 时,内核直接丢弃新连接的包——不是拒绝,而是静默丢弃。症状是网络间歇性不可用,但已有连接正常。在高并发服务器上,务必调大 nf_conntrack_max 或使用无状态防火墙规则绕过 conntrack。
Conntrack 的超时机制也很重要——TCP 连接在 ESTABLISHED 状态默认 432000 秒(5 天)才过期,UDP 只需 30 秒。这意味着:
- 长连接(SSH、数据库连接池)会长期占据 conntrack 表项
- UDP 应用(DNS、游戏)需要频繁发送心跳包维持映射
- NAT 设备重启后,所有连接映射丢失,TCP 连接会卡住直到超时
# 调整 conntrack 超时参数sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=7200 # TCP 已建立:2小时sysctl -w net.netfilter.nf_conntrack_udp_timeout=60 # UDP:60秒sysctl -w net.netfilter.nf_conntrack_max=262144 # 最大连接数
# 持久化配置echo "net.netfilter.nf_conntrack_tcp_timeout_established = 7200" >> /etc/sysctl.confecho "net.netfilter.nf_conntrack_max = 262144" >> /etc/sysctl.confsysctl -p三、NAT 如何破坏端到端
3.1 接收方无法主动连接
端到端原则的核心假设是:任何一方都能主动向对方发起连接。NAT 打破了这个假设。
# 场景:外部主机想连接内部服务器## 外部主机发送:Dst: 192.168.1.100:80# → 192.168.1.100 是私有地址,在公网上不可路由,包被丢弃## 外部主机发送:Dst: 203.0.113.42:80# → NAT 设备收到,查转换表:没有 203.0.113.42:80 的映射# → 包被丢弃(NAT 不知道该转发给哪台内部设备)除非你在 NAT 设备上配置了端口转发(DNAT),否则外部无法主动连接内部任何设备。这就是为什么你在家搭了 Web 服务器,外网却访问不了。
3.2 IP 地址不可达
NAT 后面的设备没有公网 IP,这意味着:
- 服务器无法识别客户端身份:服务器看到的源 IP 是 NAT 设备的公网 IP,不是客户端的真实 IP。同一 NAT 后面的 100 台设备,在服务器看来都是同一个 IP
- 地理位置判断失准:IP 地理定位服务只能定位到 NAT 设备的位置,而非客户端的实际位置
- IP 黑名单失效:封禁一个 NAT 公网 IP 可能影响该 NAT 后面的所有用户
# 用 tcpdump 观察 NAT 改写前后的包# 在 NAT 设备的内网接口抓包tcpdump -i eth1 -nn host 192.168.1.105 and port 80
# 在 NAT 设备的外网接口抓包tcpdump -i eth0 -nn host 93.184.216.34 and port 80
# 对比:内网接口看到 Src: 192.168.1.105:54321# 外网接口看到 Src: 203.0.113.42:54321# 服务器永远看不到 192.168.1.105 这个地址3.3 P2P 通信受影响
P2P 应用(BitTorrent、视频通话、在线游戏)需要双方都能主动向对方发送数据。但 NAT 后面的设备无法被外部主动连接,导致:
- BitTorrent:无法接收入站连接,只能主动连接他人,下载速度受限
- VoIP/视频通话:双方都在 NAT 后面时,无法直接建立媒体流
- 在线游戏:玩家之间的直接通信被 NAT 阻断,必须通过中继服务器
3.4 为什么是”必要的恶”
NAT 破坏了端到端原则,但它解决了 IPv4 地址耗尽这个更紧迫的问题。在 IPv6 全面普及之前(这可能还需要很多年),NAT 是互联网能继续运转的关键补丁。
NAT 还带来了一些”意外的”好处:
- 隐式防火墙:未请求的入站流量被 NAT 丢弃,客观上提供了安全防护
- 网络重编号:更换 ISP 时只需改 NAT 外网地址,内网无需任何变动
- 地址空间隔离:内网地址可以随意规划,不与公网地址冲突
但不要把 NAT 的隐式安全当作真正的安全——NAT 不是防火墙,它不做任何安全策略检查,只是碰巧丢弃了没有映射的包。专业的防火墙在 第五节 讨论。
四、NAT 穿透:STUN / TURN / ICE
既然 NAT 阻断了入站连接,应用如何绕过 NAT 建立直接通信?这就是 NAT 穿透(NAT Traversal)要解决的问题。
4.1 STUN:发现公网地址
STUN(Session Traversal Utilities for NAT,RFC 8489)的工作原理很简单:让客户端发现自己的公网 IP 和端口。
客户端向 STUN 服务器发送请求,STUN 服务器看到的是 NAT 改写后的公网 IP:端口,将其返回给客户端。客户端现在知道了自己的”公网身份”,可以将这个地址告诉对端,让对端尝试直接连接。
# 使用 stunclient 测试 NAT 类型apt install stuntman-clientstunclient stun.l.google.com:19302
# 典型输出:# Binding test: success# Local address: 192.168.1.105:54321# Mapped address: 203.0.113.42:54321
# 用 nmap 检测 NAT 类型nmap -sU -p 3478 --script stun-info stun.l.google.com
# Python 实现 STUN 客户端pip install pystun3pystun3
# 输出:# NAT Type: Full Cone NAT# External IP: 203.0.113.42# External Port: 54321STUN 的局限在于:它只能发现映射地址,不能保证对端能通过这个地址连进来。NAT 的行为决定了穿透是否可行——RFC 3489 定义了四种 NAT 行为:
| NAT 行为类型 | 入站过滤规则 | STUN 穿透 | P2P 可行性 | 典型设备 |
|---|---|---|---|---|
| 完全锥形(Full Cone) | 任何源都可连接映射端口 | 容易 | 高 | 少见,部分企业 NAT |
| 受限锥形(Restricted Cone) | 仅限客户端发过包的 IP | 需配合 | 中 | 部分家用路由器 |
| 端口受限锥形(Port Restricted) | 仅限客户端发过包的 IP:端口 | 需配合 | 低 | 大部分家用路由器 |
| 对称型(Symmetric) | 同一内部端口对不同目标分配不同外部端口 | 不可行 | 极低 | 运营商级 NAT(CGNAT) |
对称型 NAT 是穿透的噩梦——STUN 发现的映射地址只对 STUN 服务器有效,对其他目标会分配不同的外部端口。双方都是对称型 NAT 时,STUN 穿透彻底失败。
4.2 TURN:中继兜底
当 STUN 穿透失败时,TURN(Traversal Using Relays around NAT,RFC 8656)提供兜底方案——通过中继服务器转发所有流量。
# TURN 工作流程## 客户端A ←→ TURN服务器 ←→ 客户端B## 1. 客户端A 向 TURN 服务器申请一个中继地址# 2. TURN 服务器分配 relayed-address: 203.0.113.200:50000# 3. 客户端A 将这个地址告诉客户端B(通过信令通道)# 4. 客户端B 向 203.0.113.200:50000 发送数据# 5. TURN 服务器将数据转发给客户端A## 所有流量都经过 TURN 服务器,延迟增加,带宽成本高# 但保证在任何 NAT 类型下都能工作TURN 的代价是明显的:所有流量都经过中继,延迟增加一跳,带宽成本翻倍。因此 TURN 只作为最后的兜底手段——ICE 框架会优先尝试直接连接(STUN),只有全部失败才回退到 TURN。
4.3 ICE:综合框架
ICE(Interactive Connectivity Establishment,RFC 8445)不是一种新的穿透技术,而是一个综合框架——它系统地收集所有可能的连接方式(候选地址),按优先级逐一尝试,选择第一个成功的。
ICE 的候选地址有三类:
| 候选类型 | 来源 | 优先级 | 说明 |
|---|---|---|---|
| Host 候选 | 本地网卡 | 最高 | 直接使用本地 IP,仅同局域网有效 |
| Server Reflexive 候选 | STUN | 中 | NAT 映射的公网 IP:端口 |
| Peer Reflexive 候选 | 连接检查中发现 | 中 | ICE 检查过程中新发现的映射 |
| Relay 候选 | TURN | 最低 | TURN 中继地址,保证可达但延迟高 |
4.4 WebRTC 穿透流程
WebRTC 是 ICE 框架最典型的应用。一个完整的 WebRTC 连接建立过程:
# WebRTC 连接建立的关键步骤## 1. 信令交换(通过 WebSocket/HTTP,非 WebRTC 本身)# - 交换 SDP(Session Description Protocol)# - SDP 包含媒体格式、ICE 候选等信息## 2. ICE 候选收集# - Host 候选:枚举本地网卡# - Srflx 候选:查询 STUN 服务器# - Relay 候选:申请 TURN 中继## 3. ICE 连接检查# - 对每个候选对发送 STUN Binding Request# - 成功的候选对标记为 valid# - 选择优先级最高的 valid 候选对## 4. DTLS 握手# - 在 ICE 选定的连接上建立加密通道## 5. 媒体流传输# - SRTP 加密的音视频数据通过选定的连接传输# 使用 ss 查看套接字统计ss -s
# 典型输出:# Total: 234 (kernel 0)# TCP: 56 (estab 30, closed 12, orphaned 0, timewait 12)# Transport Total IP IPv6# RAW 1 0 1# UDP 12 8 4# TCP 44 36 8# INET 57 44 13# FRAG 0 0 0
# 查看 UDP 套接字(WebRTC 使用 UDP)ss -ulnp
# 查看特定连接的详细信息ss -ti dst 203.0.113.42
# Cisco IOS 配置 NAT(企业级设备)# ip nat inside source static tcp 192.168.1.100 80 203.0.113.42 8080# ip nat inside source list 1 interface GigabitEthernet0/0 overload# access-list 1 permit 192.168.1.0 0.0.0.255WebRTC 的信令交换不属于 ICE 本身——ICE 只负责连接检查和候选选择。信令通常通过 WebSocket、HTTP 等方式实现,交换 SDP offer/answer 和 ICE 候选。这种分离设计让 ICE 可以适配任何信令机制。
五、中间盒:互联网的”隐形基础设施”
NAT 只是中间盒的一种。互联网中还有大量设备不在”路由器只转发 IP 包”的教科书模型里——防火墙、DPI、负载均衡器、CDN——它们构成了互联网的”隐形基础设施”。
5.1 中间盒的位置
数据包从客户端到服务器,可能经过 5-6 种不同的中间盒。每个中间盒都在做”路由器不该做的事”——改写地址、过滤包、检查内容、分发流量、缓存数据。
5.2 防火墙
防火墙是最常见的中间盒,它根据规则决定哪些包可以通过、哪些包被丢弃。
包过滤防火墙(无状态)只看单个包的头部信息:
# Linux iptables 包过滤规则# 允许已建立的连接和相关连接iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 允许 SSH 入站iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 允许 HTTP/HTTPS 入站iptables -A INPUT -p tcp --dport 80 -j ACCEPTiptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 丢弃其他所有入站流量iptables -A INPUT -j DROP
# 查看规则iptables -L -n -v --line-numbers状态检测防火墙(有状态)跟踪连接状态,能识别属于同一连接的包:
# 状态检测依赖 conntrack# 查看连接状态conntrack -L -p tcp --state ESTABLISHED
# 典型输出:# tcp 6 431999 ESTABLISHED src=192.168.1.105 dst=93.184.216.34 sport=54321 dport=80
# 只允许内网主动发起的连接(类似 NAT 的隐式安全,但更灵活)iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTiptables -A FORWARD -i eth1 -o eth0 -j ACCEPTiptables -A FORWARD -j DROP应用层网关(ALG)能理解应用层协议,为特定协议做特殊处理:
- FTP ALG:FTP 使用控制通道和数据通道分离,NAT 需要改写 FTP PORT 命令中的 IP 地址
- SIP ALG:VoIP 的 SIP 协议在消息体中嵌入 IP 地址,NAT 需要改写
- DNS ALG:某些 NAT 会缓存 DNS 响应,对后续查询返回缓存结果
ALG 是 NAT 和应用层协议冲突的产物——应用层协议把 IP 地址嵌在数据载荷里,NAT 只改写 IP 头部,导致载荷中的地址与实际不符。ALG 的存在本身就说明 NAT 破坏了协议的层次分离。
5.3 DPI:深度包检测
DPI(Deep Packet Inspection)不只看包头,还检查包的载荷内容。它的用途包括:
- 流量分类:识别应用类型(视频、文件下载、P2P),用于 QoS 或限速
- 入侵检测:匹配已知攻击特征(Snort、Suricata 规则)
- 内容过滤:根据关键词或 URL 过滤特定内容
- 协议识别:识别加密流量的应用层协议(如通过 TLS SNI 识别访问的域名)
# nmap NAT 检测脚本(检测 NAT 类型和外部地址)nmap -sU -p 3478 --script stun-version stun.l.google.com
# 用 tcpdump 抓取经过 NAT 的包,观察地址改写tcpdump -i any -nn -vv host 93.184.216.34
# 典型输出(在 NAT 设备上同时抓内外接口):# 内网接口:IP 192.168.1.105.54321 > 93.184.216.34.80: Flags [S]# 外网接口:IP 203.0.113.42.54321 > 93.184.216.34.80: Flags [S]# 注意源地址从 192.168.1.105 变为 203.0.113.42DPI 的技术挑战在于加密——TLS 1.3 加密了大部分握手信息,ESNI/ECH 加密了 SNI,使得 DPI 越来越难以识别加密流量的具体内容。这是隐私保护和流量管理的持续博弈。
5.4 负载均衡器
负载均衡器是数据中心中最关键的中间盒之一,它将流量分发到多台后端服务器:
| 层级 | 工作方式 | 改写内容 | 典型产品 | 适用场景 |
|---|---|---|---|---|
| L4(传输层) | 基于 IP:端口分发 | 目的 IP + 端口(DNAT) | LVS、IPVS | 高吞吐、协议无关 |
| L7(应用层) | 基于 HTTP 头/URL/Cookie 分发 | 全部(反向代理) | Nginx、HAProxy、Envoy | 智能路由、会话保持 |
L4 负载均衡器本质上就是一个高性能的 DNAT——它不改写应用层内容,只改写 IP 和端口,因此速度极快。L7 负载均衡器是反向代理——它终止客户端连接,建立到后端的新连接,可以检查和改写 HTTP 头部。
# IPVS(L4 负载均衡器)配置示例ipvsadm -A -t 203.0.113.42:80 -s rr # 创建虚拟服务,轮询调度ipvsadm -a -t 203.0.113.42:80 -r 10.0.0.1:80 -m # 添加后端服务器(NAT 模式)ipvsadm -a -t 203.0.113.42:80 -r 10.0.0.2:80 -mipvsadm -a -t 203.0.113.42:80 -r 10.0.0.3:80 -m
# 查看连接统计ipvsadm -L -n --stats
# Nginx(L7 负载均衡器)配置示例# upstream backend {# server 10.0.0.1:80;# server 10.0.0.2:80;# server 10.0.0.3:80;# }# server {# listen 80;# location / {# proxy_pass http://backend;# proxy_set_header X-Real-IP $remote_addr;# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;# }# }5.5 CDN 作为应用层中间盒
CDN(Content Delivery Network)从网络架构的角度看,也是一种中间盒——它在客户端和源站之间插入了一层缓存节点。当用户请求 https://example.com/image.jpg 时,DNS 将域名解析到最近的 CDN 边缘节点,CDN 节点如果有缓存就直接返回,没有则回源获取。
CDN 与传统中间盒的区别在于:它工作在应用层,通过 DNS 任播或 HTTP 重定向将流量引导到边缘节点,而不是在网络层拦截流量。更多细节在 CDN与内容分发 中展开。
六、现实互联网架构:分层 + 中间盒
6.1 干净分层是理想
教科书里的互联网是干净分层的——每一层只关心自己的职责,路由器只转发 IP 包,交换机只转发以太网帧。这个模型优雅、简洁、可推理。
但现实不是这样。NAT 打破了网络层的端到端,防火墙打破了”路由器只转发”的假设,DPI 打破了层次隔离,负载均衡器同时操作 L4 和 L7,CDN 改变了 DNS 解析和 HTTP 路由。每一层都有”越界”的设备在做不该做的事。
6.2 分层 + 中间盒是现实
Pamela Zave 和 Jennifer Rexford 在《The Misfortunes of Network Architecture》中精辟地总结了这种张力:互联网的架构不是教科书描述的干净分层,而是分层 + 中间盒的混合体。中间盒的存在不是偶然的,而是因为纯粹的分层模型无法满足现实需求:
- NAT:分层模型说地址应该全局唯一,但 IPv4 地址不够用
- 防火墙:分层模型说路由器只转发,但安全需要过滤
- DPI:分层模型说层次隔离,但运营商需要管理流量
- 负载均衡器:分层模型说端到端,但服务器需要横向扩展
- CDN:分层模型说路由器选路,但延迟需要边缘缓存
这些需求是真实的,中间盒是对真实需求的工程回应。问题不在于中间盒存在,而在于中间盒的实现方式——它们通常是专有设备,配置复杂,难以编程,与路由器的交互不透明。
6.3 可编程化趋势
中间盒的未来不是消灭它们,而是让它们可编程:
- eBPF/XDP:在 Linux 内核中运行自定义包处理程序,替代专有防火墙和负载均衡器
- P4:可编程数据平面语言,让交换机/网卡执行自定义包处理逻辑
- SDN:集中控制网络设备,通过控制器编程转发规则
- Service Mesh:将中间盒功能(负载均衡、加密、观测)从网络设备移到应用层的 Sidecar 代理
这些趋势的共同点是:将中间盒的功能从专有硬件中解放出来,变成可编程的软件。这不改变”互联网是分层 + 中间盒”的现实,但让中间盒更透明、更可控、更易演进。
七、动手实践:观察 NAT 和中间盒
7.1 观察本机 NAT 配置
# 查看本机 IP 地址(判断是否在 NAT 后面)ip addr show
# 如果你的 IP 是以下范围,你在 NAT 后面:# 10.0.0.0 - 10.255.255.255 (10.0.0.0/8)# 172.16.0.0 - 172.31.255.255 (172.16.0.0/12)# 192.168.0.0 - 192.168.255.255 (192.168.0.0/16)
# 查看公网 IPcurl -s ifconfig.meecho
# 如果公网 IP ≠ 本机 IP,你在 NAT 后面# 如果公网 IP = 本机 IP,你有公网地址(罕见)7.2 检测 NAT 类型
# 安装 STUN 客户端pip3 install pystun3
# 检测 NAT 类型pystun3
# 可能的输出:# NAT Type: Full Cone NAT → 穿透容易# NAT Type: Restricted Cone NAT → 穿透需要配合# NAT Type: Port Restricted Cone NAT → 穿透困难# NAT Type: Symmetric NAT → 穿透极难,需要 TURN# NAT Type: No NAT (Public IP) → 无 NAT,直连
# 用 nmap 检测nmap -sU -p 3478 --script stun-info stun.l.google.com7.3 观察 conntrack 表
# 查看 conntrack 表cat /proc/net/nf_conntrack | head -20
# 用 conntrack 工具查看conntrack -L | head -20
# 统计连接数conntrack -C
# 按协议统计conntrack -L -p tcp | wc -lconntrack -L -p udp | wc -l
# 查看特定目标的连接conntrack -L -d 93.184.216.34
# 删除特定连接(调试用)conntrack -D -d 93.184.216.347.4 抓包观察 NAT 改写
# 在 NAT 设备上同时抓内外接口# 内网接口tcpdump -i eth1 -nn not port 22 -w /tmp/nat_inside.pcap &
# 外网接口tcpdump -i eth0 -nn not port 22 -w /tmp/nat_outside.pcap &
# 产生一些流量curl -s http://example.com > /dev/null
# 停止抓包kill %1 %2
# 对比两个 pcap 文件# 内网:Src: 192.168.1.105:54321 → Dst: 93.184.216.34:80# 外网:Src: 203.0.113.42:54321 → Dst: 93.184.216.34:80# 源地址被 NAT 改写了八、本章小结
| 概念 | 要点 |
|---|---|
| NAT 存在原因 | IPv4 地址耗尽,NAPT 让多台设备共享一个公网 IP |
| 四种 NAT 类型 | 静态 NAT(1:1)、动态 NAT(池)、NAPT(端口复用)、双向 NAT |
| NAPT 工作原理 | 转换表映射 内部IP:端口 ↔ 外部IP:端口,出站改写源地址,入站改写目的地址 |
| Conntrack | 连接追踪表记录每个 NAT 映射,表满会导致新连接被丢弃 |
| NAT 破坏端到端 | 接收方无法主动连接、IP 不可达、P2P 受影响 |
| STUN | 发现 NAT 映射的公网 IP:端口,但对称型 NAT 无法穿透 |
| TURN | 通过中继服务器转发流量,保证可达但延迟高、带宽成本大 |
| ICE | 综合框架,按优先级尝试 Host/Srflx/Relay 候选,选择最优连接 |
| 防火墙 | 包过滤(无状态)、状态检测(有状态)、ALG(应用层网关) |
| DPI | 深度包检测,检查载荷内容,受加密流量挑战 |
| 负载均衡器 | L4(DNAT 分发)和 L7(反向代理),数据中心核心中间盒 |
| CDN | 应用层中间盒,通过边缘缓存降低延迟 |
| 现实架构 | 干净分层是理想,分层 + 中间盒是现实,可编程化是趋势 |
NAT 和中间盒是互联网对现实需求的工程妥协。下一章进入 域内路由,看看组织内部的路由器如何通过 OSPF 和 IS-IS 计算最短路径——这次,路由器终于只做路由器该做的事了。
参考
- RFC 2663 — IP Network Address Translator (NAT) Terminology and Considerations
- RFC 3489 — STUN - Simple Traversal of User Datagram Protocol (UDP)
- RFC 8445 — Session Traversal Utilities for NAT (STUN)
- RFC 8489 — Traversal Using Relays around NAT (TURN)
- RFC 8656 — TURN Extension for IPv6
- IANA Service Name and Transport Protocol Port Number Registry
- RIPE NCC Stat API
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






