NAT 的原理是什么?

什么是 NAT

NAT (Network Address Translation, 网络地址转换) 是一个函数,能够将一个 IP 地址转换为另一个 IP 地址。

NAPT (Network Address/Port Translation,网络地址端口转换) 是我们通常真正所指的 NAT,能够将一个 (地址,端口) 二元组转换为另一个 (地址,端口) 二元组。

NAPT 的类型

为了更高效地理解,我举一个例子。假设在客户端看来,有如下三个连接:

192.168.0.106:1414 <-> 6.7.8.9:8000
192.168.0.106:3210 <-> 8.8.8.8:53
192.168.0.106:3210 <-> 1.1.1.1:66

每个连接可以看作一个四元组:<src_addr, src_port, dest_addr, dest_port>

nat.drawio2

那么由于 Client 位于局域网中,Server A/B/C 是无法直接与之通信的。需要借助网关来通信。

对于 Client 而言,网关是透明的,也即 Client 会认为自己直接连接了 Server

简单而言,网关可以理解为一个拥有公网 IP 的转发者。它负责将 Client 发出或收到的数据重新封包,将其中的 Client IP:Port 替换为自己的 Gateway IP:Port.

此处不同的替换策略就对应了不同的 NAPT 类型。

对称型

nat-sym.drawio2

对称 NAT 的特点是一一对应。从 Gateway 的视角来看,既然你 Client 与外界有三条连接,那么我就对这三条连接分别分配一个端口进行转发。其内部的转发规则表是这样的:

192.168.0.106:1414 <-> 221.21.12.2:4356  <-> 6.7.8.9:8000
192.168.0.106:3210 <-> 221.21.12.2:31234 <-> 8.8.8.8:53
192.168.0.106:3210 <-> 221.21.12.2:4324  <-> 1.1.1.1:66

锥形(不对称)

nat-cone.drawio

对称型的缺点是客户端有多少连接,我 Gateway 就要开多少端口。那不需要太多的 Client 就会耗尽我的端口。

有没有办法解决这个问题?其实我们可以把 Client 的 3210 端口的各个连接都当成一个连接。于是映射规则变成这样:

192.168.0.106:1414 <-> 221.21.12.2:4356  <-> 6.7.8.9:8000
192.168.0.106:3210 <-> 221.21.12.2:31234 <-> 8.8.8.8:53, 1.1.1.1:66

而基于安全策略,锥形又分为以下几种:

全锥型(Full Cone)

这是最为宽松的类型。只要 192.168.0.106:3210 <-> 221.21.12.2:31234 这条规则一建立,则任何外部消息均可通过 221.21.12.2:31234 转发给 192.168.0.106:3210。比如上面的 8.8.8.8:53,但并不止于此,假设突然来了个 233.233.233.233:233221.21.12.2:31234 发送消息,那么 192.168.0.106:3210 都能收到。

nat-fullcone.drawio

简言之:可以不请自来。外面的机器比如 Host X (红色) 不需要和你打招呼,直接就能给 Client 发信息。因此安全性比较差。

受限锥型(Restricted Cone)

也称 IP 受限锥型,要求必须 Client 和外网的 Server IP 发起过连接,才能允许该 Server 向 Client 发送信息。比如上面的 Server A/B/C 可以,但 Host X (红色) 不行。

简言之:不能不请自来,必须 Client 主动先邀请。

端口受限锥型(Port Restricted Cone)

在受限锥形的基础上增加了端口限制,要求 <IP:Port> 均有过历史连接,才能连进来。

参考

Network address translation - Wikipedia