NAT原理

最近在做一个关于NAT的需求,之前对于NAT只是有个大概的了解,对于一些细节还不够清楚,正好学习一下。

NAT简介

静态 NAT(Static NAT)

  • 一对一映射:一个私有 IP 地址固定映射到一个公网 IP 地址。
  • 适用于需要从外部访问内部服务器的情况,例如 Web 服务器或邮件服务器。

动态 NAT(Dynamic NAT)

  • 多个私有 IP 地址动态映射到一个或多个公网 IP 地址,但每个私有 IP 只能临时获得一个公网 IP。
  • 适用于公网 IP 资源有限的情况,但不支持多个私有 IP 同时共享同一个公网 IP 进行外部通信。

端口地址转换(PAT,Port Address Translation,NAPT)

  • 也称为 “多对一 NAT”“NAT Overload”,是最常见的 NAT 类型。
  • 通过修改 TCP/UDP 端口号,使多个私有 IP 地址共享一个公网 IP 地址访问互联网。
  • 典型应用:家庭路由器(路由器使用一个公网 IP,所有内网设备通过该 IP 访问互联网)。

NAT实现

单纯从数据包角度,现在一个请求到达服务器G(网关),但是实际提供服务的是另外的内网服务器S,现在就要进行转发了。对于数据包,现在的请求源IP是客户端1.1.1.1,请求目的IP是G的IP8.8.8.8,现在进行NAT转换,G根据自己的NAT规则,重新构造数据包,将数据包的源IP改成自己的IP,也就是8.8.8.8,对于目的IP改成实际服务器S的IP,比如是2.2.2.2,现在请求数据包就能成功到达提供服务的S。这其中就包含了NAT了。

但是上面说的是从服务器的角度考虑的,在现在的大内网环境了,内网客户端也会在出口网络进行NAT,原理也差不多吧。

配置NAT

对于Linux服务器来说,通过iptables就能配置简单的NAT规则了

1
2
3
4
5
# SNAT规则,指定1.1.1.1为出站IP
iptables -t nat -A POSTROUTING -o ethX -j SNAT --to-source 1.1.1.1

# DNAT规则,入站请求8080的会映射成 192.168.1.100:80
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80

端口策略

SNAT 端口转换

阶段 源 IP:端口 目标 IP:端口 NAT 处理
内网请求 192.168.1.100:1001 8.8.8.8:8080 客户端发出请求
经过 NAT 203.0.113.1:1001(如果可用) 或 203.0.113.1:56789 8.8.8.8:8080 可能修改端口
服务器响应 8.8.8.8:8080 203.0.113.1:1001203.0.113.1:56789 服务器回复
NAT 还原 192.168.1.100:1001 8.8.8.8:8080 NAT 设备转换回去
  1. 尝试保持源端口(如 1001)。
  2. 如果端口冲突(如多个设备同时使用 1001),则 分配新的端口(如 56789)。
  3. NAT 设备建立映射表,确保外部服务器的返回流量能正确转换回去。

DNAT 端口转换

外部请求 NAT 设备修改 内网服务器看到
8.8.8.8:34567 → 203.0.113.1:8080 8.8.8.8:34567 → 192.168.1.100:80 8.8.8.8:34567 → 192.168.1.100:80
  1. 如果外部端口与内网端口相同(如 8080 → 8080),则 不修改端口
  2. 如果端口不同(如 8080 → 80),则 修改目标端口
  3. NAT 设备建立映射表,确保内网服务器返回数据时,NAT 设备能转换回原始端口。

端口耗尽

有个场景,存在大量内网设备,但是出口只有一个公网IP,在并发的场景下自然会出现端口耗尽的问题:

NAT 设备在 SNAT(源地址转换) 时,需要为每个内部连接分配一个唯一的 (公网 IP, 端口号),但是:

  • 每个公网 IP 只有 64511 个可用端口1024-65535)。
  • 高并发连接可能很快耗尽端口资源,导致新连接失败

示例

内部 IP NAT 设备 目标服务器 端口映射
192.168.1.100:5000 171.1.1.1:20000 8.8.8.8:443
192.168.1.101:5001 171.1.1.1:20001 8.8.8.8:443
192.168.1.102:5002 171.1.1.1:20002 8.8.8.8:443
192.168.1.5000:6000 171.1.1.1:65535 8.8.8.8:443 ❌ 端口耗尽!

解决方案

  1. 使用多个公网 IP(SNAT 轮换 IP)(最有效)
  2. 优化 NAT 超时,回收端口更快
  3. 限制单个 IP 的 NAT 连接数
  4. 使用代理(HTTP / SOCKS)减少 NAT 连接
  5. 考虑 CGNAT(运营商级 NAT)

NAT与代理

NAT看着看着感觉似曾相识啊,SNAT与正向代理,DNAT与反向代理,感觉很多点都是相似的啊。

稍微了解一下,他们之间还是有很大区别的,总结来说是几点:

  1. NAT工作在三层,代理工作在七层
  2. NAT是转发流量,不建立实际连接,代理会建立请求实际连接
功能 NAT 代理
请求发起 不发起实际的请求,只是修改数据包的地址。 代理服务器发起实际的请求,作为客户端或服务器与目标建立连接。
连接建立 不会建立新的连接,只是修改地址并转发数据包。 代理服务器与目标服务器建立连接,处理请求并转发响应。
应用层交互 不涉及应用层的协议处理。 代理可以在应用层进行修改,例如缓存、负载均衡、加密解密等。
透明性 对外部设备透明,仅在数据包层进行地址修改。 代理处理所有的请求和响应,客户端和目标服务器都通过代理进行交互。

举个最明显的例子,Nginx负载的时候,可以配置负载策略,比如根据路径进行转发,这就是明显的七层,同时对于数据数据,Http代理的时候通常会加上X-Forwarded-For: clint IP这样的请求头,这就更是七层的内容了。