## 绪论
网络地址转换通常涉及在**IP包经过路由器或防火墙的时候,修改其源和/或目的地址**

## 资料来源
* https://arthurchiao.github.io/blog/nat-zh/

## netfilter
Linux 内核中有一个数据包过滤框架(packet filter framework),叫做 netfilter,这个框架使得 **Linux 机器可以像路由器一样工作**。接下来我们将使用一个命令行工具 iptables 来创建复杂的规则,用于修改和过滤 数据包。毫无意外,和 NAT 相关的最重要的规则,都在 nat 这个(iptables)table 里。这个表有三个预置的 chain:**PRETOUTING, OUTPUT 和 POSTROUTING**。

PREROUTING 和 POSTROUTING 是最重要的 chain。如名字所示,PRETOUTING chain 负责处理**刚刚到达网络接口的数据包,这时还没有做路由判断**,因此还不知道这个包是发往**本机(local),还是网络内的其他主机**。包经过 PRETOUTING chain 之后,将进行路由判 断。如果目的是本机,那接下来的过程将不涉及NAT;如果目的是网络内的其他机器,那包 将会被转发到那台机器,前提是这台机器配置了允许转发。

## 私有网络通过NAT连接到互联网

* 子网内的所有主机(客户端)通过socket将数据包发送到一个**特定路由器**(通过将路由器地址设置为所有机器的网关来实现,数据会通过以太网 或其他底层协议传输)
* 路由器将发送方的**socket 替换为自己的一个(还未使用的)socket**
* 这个socket收到的数据,路由器将socket地址修改为对应的客户端socket,并转发给它 // 收到数据的响应,这个是反向规则

我们假设客户端的**网关设置是正确**的,那**剩下的就是如何配置路由器了**幸运的是,netfilter 框架会对设置的**每条一条(出向或入向)规则,自动设置它的反向规则**,理解这句话的意思是,比如,我们的期望的结果出规则是,**从本地网络发出的、目的是因特网的包,将**发送方地址修改为路由器的地址**,反规则是从因特网接收的本地网络包,目的地址修改为本地地址。


因此我们只需要**设置一个方向的规则即可**。选择哪个方向来设置规则?通常是**选择不确定性小的一个方向**。 例如,“替换所有从本地网络发出的数据包的地址”比“如果客户端发送过一些东西给服务端 ,那将服务端发送的数据进行某种方式的修改”要简单的多。


**如何通过iptables来设置规则**
```sh
# Connect a LAN to the internet
$> iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
```

* iptables - 配置内核的工具
* -t nat - 指定对名为 nat 的 iptables table 配置 NAT 规则
* -A POSTROUTING - 追加(A: Append)规则到 iptables 的 POSTROUTING chain
* -o eth1 - 指定只对从 eth1 发出的数据包做操作(o: output)
* -j MASQUERADE - 规则匹配成功后的动作是 masquerade (伪装)数据包,例如将**源地址修改为路由器地址**。

另外需要说明的是,除了客户端过来的包,路由器自己的包也会涉及以上处理逻辑,因为它们也经过 POSTROUTING chain(见下面的图)。然而,因为路由器为客户端做 socket (IP+Port) 转换的时候,会从它的未使用端口中挑选,因此它自身的包所使用的 port 与做 NAT 的 port 肯定是不同的。因此,虽然它自身的包也会经过以上规则,但并不会被修改。

## 缺点
本地计算机可以**访问因特网**,但反过来,我们看看**因特网上的机器访问本地机器会是什么情况**。

因特网上的机器要和本地机器建立连接的话,它唯一能利用的信息就是用**路由器的IP地址加一个端口号**。大部分情况下,这个端口都是没有被使用的,因此过来的包会被拒绝。即使运气比较好,这个端口是**路由器做NAT的一个端口**,包仍然很可能会被拒绝,因为这个端口已经和因特网上的其他主机建立连接了。

对于常规服务,可以静态地将路由器的端口映射到本地服务,例如,将路由器的**80端口收到**的包转发到本地机器的 HTTP 服务器。


## 解析iptable

```shell
iptables [-t table] command [match pattern] [action]

```
对于nat -t 的参数value必须是nat, 默认的是 table是**filter table**。

**几个重要的命令**

```shell
$> iptables -t nat -A chain [...]

# list rules:
$> iptables -t nat -L

# remove user-defined chain with index 'myindex':
$> iptables -t nat -D chain myindex

# Remove all rules in chain 'chain':
$> iptables -t nat -F chain
```

**选择匹配模式**

```shell
# actions to be taken on matched packets
# will be abbreviated by '[...]'.
# Depending on the match pattern the appropriate chain is selected.

# TCP packets from 192.168.1.2:
$> iptables -t nat -A POSTROUTING -p tcp -s 192.168.1.2 [...]

# UDP packets to 192.168.1.2:
$> iptables -t nat -A POSTROUTING -p udp -d 192.168.1.2 [...]

# all packets from 192.168.x.x arriving at eth0:
$> iptables -t nat -A PREROUTING -s 192.168.0.0/16 -i eth0 [...]

# all packets except TCP packets and except packets from 192.168.1.2:
$> iptables -t nat -A PREROUTING -p ! tcp -s ! 192.168.1.2 [...]

# packets leaving at eth1:
$> iptables -t nat -A POSTROUTING -o eth1 [...] // [...]是命令

# TCP packets from 192.168.1.2, port 12345 to 12356
# to 123.123.123.123, Port 22
# (a backslash indicates contination at the next line)
$> iptables -t nat -A POSTROUTING -p tcp -s 192.168.1.2 \
   --sport 12345:12356 -d 123.123.123.123 --dport 22 [...]

```

**匹配后的动作**

```
# In the following the table selection, the command and the match pattern
# will be abbreviated using [...]

# Source-NAT: Change sender to 123.123.123.123
$> iptables [...] -j SNAT --to-source 123.123.123.123

# Mask: Change sender to outgoing network interface
$> iptables [...] -j MASQUERADE

# Destination-NAT: Change receipient to 123.123.123.123, port 22
$> iptables [...] -j DNAT --to-destination 123.123.123.123:22

# Redirect to local port 8080
$> iptables [...] -j REDIRECT --to-ports 8080
```


**SNAT - 修改源IP为固定新IP (静态)**

前面的将本地私有网络连接到因特网的例子中,我们已经使用了**Source NAT(SNAT )**。如名字所暗示,**发送方的地址会被静态地修改**。

在例子中我们选择MASQUERADE的原因在于:对于 SNAT,必须显式指定转换后的 IP。 如果路由器配置的是静态 IP 地址,那 SNAT 是最合适的选择,因为它比 MASQUERADE 更 快,后者对每个包都需要检查指定的输出端口上配置的 IP 地址。因为**SNAT只对离开路由器的包有意义**,因此它只用在 POSTROUTING chain 中。

```shell
# Options for SNAT (abstract of manual page)
--to-source <ipaddr>[-<ipaddr>][:port-port]
```

**MASQUERADE** - 修改源 IP为动态新 IP(动态获取网络接口 IP)

和 SNAT 类似,但是对每个包**都会动态获取指定输出接口(网卡)的IP**,因此如果接口的**IP地址发送了变化**,MASQUERADE规则不受影响,可以正常工作;而对于 SNAT 就必须重新调整规则。

和 SNAT 一样,MASQUERADE 只对**POSTROUTING chain**有意义。但和 SNAT 不同, MASQUERADE 不支持更详细的配置项了。


**DNAT - 修改目的 IP**
如果想修改包的目的 IP 地址,那需要使用 Destination NAT(DNAT)。DNAT 可以用于运行在**防火墙后面的服务器**。

显然,接收端修改必须在做路由决策之前,因此 DNAT 适用于**PRETOUTING**和**OUTPUT (本地生成的包)chain**。

```
# Options for DNAT (abstract of manual page)
--to-destination <ipaddr>[-<ipaddr>][:port-port] 
```

**REDIRECT - 将包重定向到本机另一个端口**

REDIRECT 是 DNAT 的一个特殊场景。包被重定向到路由器的另一个本地端口,可以实现, 例如透明代理的功能。和DNAT 一样,REDIRECT 适用于**PRETOUTING 和 OUTPUT chain**。

```
# Options for REDIRECT (abstract of manual page)
--to-ports <port>[-<port>] 
```