5. 控制哪些要 NAT
您需要建立一些 NAT 规则﹐来告诉核心哪些联机要改变﹐同时如何去改变它们。要做到这点﹐我们需要一个非常多用� 的 iptables
工具﹐同时指定 `-t nat' 选项告诉它去修改 NAT 表格。
NAT 规则的表格含有三个列表叫做`chains' ﹕每一条规则都按顺序检查﹐直到找到一个相符的比对。该三个链就叫做 PREROUTING (对 Destination NAT 来说﹐因为封包首先是传入的)﹑POSTROUTING (对 Source NAT 来说﹐因为封包是离开的)﹑以及 OUTPUT (对 Destination NAT 来说﹐是指那些由本机产生的封包)。
假如我够艺术天份的话﹐下面的图示将准确仿真出上面所说的概念。
_____ _____
/ \ / \
PREROUTING -->[Routing ]----------------->POSTROUTING----->
\D-NAT/ [Decision] \S-NAT/
| ^
| __|__
| / \
| | OUTPUT|
| \D-NAT/
| ^
| |
--------> Local Process ------
于前述的每一点﹐当一个封包通过我们要查看的相关联机之时﹐如果它是一个新建联机﹐我们查看它在 NAT 表格里对应的链﹐看看能对之做些什么动作。而由此获得的答案就应用于该联机将来的所有封包。
5.1 用 iptables 做简单的选择
iptables
具有如后所列的许多标准选项。所有那些带双减号的选项都是可以缩写的﹐只要 iptables
仍可将之与其它可能的选项区分开来就行。如果您的核心以模块形式来支持 iptables ﹐您就需要首先加载 ip_tables.o ﹕ `insmod ip_tables'。
这里﹐最重要的一个选项是表格选择选项﹕ `-t' 。对于所有的 NAT 操作﹐您会想用 `-t nat' 来表示 NAT 表格。第二个重要的选项是以 `-A' 增加一条新规则至链的末端 (如﹕`-A POSTROUTING')﹐或以 `-I' 插入至前端(如﹕`-I PREROUTING')。
您可以指定您要做 NAT 的封包来源地址 (`-s' 或 `--source') 与目的地 (`-d' or `--destination')。这两个选项后面可以后接一个单一的 IP 地址 (如﹕192.168.1.1)﹐或一个名称 (如﹕ www.gnumonks.org)﹐或一个网络地址 (如﹕192.168.1.0/24 或 192.168.1.0/255.255.255.0)。
您也可以指定要比对的传入 (`-i' 或 `--in-interface') 和传出 (`-o' or `--out-interface') 界面﹐但哪一个界面可以指定则取决于您要将规则写入哪一个链去﹕对于 PREROUTING ﹐您可以选择传入界面﹐但对于 POSTROUTING (以及 OUTPUT)﹐您可以选择传出界面。如果您不小心用错了﹐ iptables
就会给您一个错� 。
5.2 关于挑选哪些封包来 mangle 的细节
我前面已经说过﹐您可以指定来源和目的地地址。如果您省略来源地址的选项﹐那么就泛指任何来源。如果您省略目的地地址﹐则泛指所有目的地地址。
您还可以指定一个特定协议 (`-p' or `--protocol')呢﹐例如 TCP 或 UDP﹕只有这些协议的封包� 符合该规则。其主要原因是﹐指定 tcp 或 udp 协议可以允许更多选项﹕尤其是 `--source-port' 与 `--destination-port' 选项 (缩写为 `--sport' 与 `--dport' )。
这些选项可以让您指定只有哪些特定来源和目的地埠口的封包� 符合该规则。这在您要重导 web 请求 (TCP port 80 或 8080) 但又怕影响其它封包的时候﹐就很好用了。
这些选项必须接在 `-p' 选项的后面(这会在为该协定加载共享函式库时有副作用)。您可以使用端口口号码﹐或者是在 /etc/services 档中的名称。
所有这些您能选择的封包之不同� 质﹐都详细列在那个详细得有点恐怖的 manual page 中了(man iptables
)。
6. 谈谈要怎样 Mangle 封包
现在﹐我们知道如何去挑选那些我们要 mangle 的封包。为了要完善我们的规则﹐我们需要准确无� 的告诉核心﹐什么� 是我们要对封包做的。
6.1 Source NAT
您想要做 Source NAT﹐是要去将联机的来源地址换成别的什么的。这就要在它最后要送出去之前﹐于 POSTROUTING 链中完成了﹔这是一个非常重要的细节﹐因为它意味着所有在 Linux 主机本身上的其它东西 (routing, packet filtering) 都只看见那个还没改变的封包。同时﹐这也就是说﹐`-o' (传出界面) 选项可以派上用场了。
Source NAT 是用 `-j SNAT' 来指定的﹐同时﹐ `--to source' 则指定一个 IP 地址﹑或一段 IP 地址﹑以及一个可配选的埠口或一段值域的埠口(仅适用于 UDP 和 TCP 协议)。
## Change source addresses to 1.2.3.4.
# iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 1.2.3.4
## Change source addresses to 1.2.3.4, 1.2.3.5 or 1.2.3.6
# iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 1.2.3.4-1.2.3.6
## Change source addresses to 1.2.3.4, ports 1-1023
# iptables -t nat -A POSTROUTING -p tcp -o eth0 -j SNAT --to 1.2.3.4:1-1023
封包伪装 (Masquerading)
有一个 Source NAT 之特例﹐叫做封包伪装﹕它只用于动态分配的 IP 地址﹐例如标准的拨接(如果用静态 IP 地址﹐则使用前述之 SNAT)。
您无需明确地将 masquerading 放进来源地址那里去﹕它将会使用封包传出界面作为来源地址。但更重要的是﹐如果该连接(link)断掉的话﹐那么联机 (connections﹐无可避免的将失掉) 也会被忘掉﹐当联机用新的 IP 地址回来的时候就会有问题了。
## Masquerade everything out ppp0.
# iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
6.2 Destination NAT
一旦封包进入﹐会由 PREROUTING 链完成处理﹔也就是说﹐除了该主机自己的其它东西(诸如﹕路由﹑封包过滤) 都将封包看成要送到 `真正' 目的地。另� ﹐那个 `-i' (传入界面) 选项也可以在这里使用。
需要修改本机产生的封包之目的地的话﹐那么 OUTPUT 链就可以用上了﹐不过这并不常碰到。
Destination NAT 必须以 `-j DNAT' 来指定使用﹐同时用 `--to destination' 选项指定一个 IP 地址﹑或一段 IP 地址﹐以及可以配选一个埠口或一段埠口值域(只能用于 UDP 和 TCP 协定上面)。
## Change destination addresses to 5.6.7.8
# iptables -t nat -A PREROUTING -i eth1 -j DNAT --to 5.6.7.8
## Change destination addresses to 5.6.7.8, 5.6.7.9 or 5.6.7.10.
# iptables -t nat -A PREROUTING -i eth1 -j DNAT --to 5.6.7.8-5.6.7.10
## Change destination addresses of web traffic to 5.6.7.8, port 8080.
# iptables -t nat -A PREROUTING -p tcp --dport 80 -i eth1 \
-j DNAT --to 5.6.7.8:8080
## Redirect local packets to 1.2.3.4 to loopback.
# iptables -t nat -A OUTPUT -d 1.2.3.4 -j DNAT --to 127.0.0.1
重导向 (Redirection)
在 Destination NAT 有一个特别的情形﹕它是一个简单的便利﹐完全等同于给传入界面地址做 DNAT 一样。
## Send incoming port-80 web traffic to our squid (transparent) proxy
# iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 \
-j REDIRECT --to-port 3128
6.3 进一步的映对(Mappings)
还有许多 NAT 上面的解决方案是大多数人无需用到的。这里不妨和那些有兴趣的朋友探讨一下﹕
同一范围内的复合地址(Multiple Addresses)之选择。
如果您已经指定了一段 IP 地址﹐ 而 IP 地址的使用选择是基于机器所知联机目前最少使用之 IP。它可以提供最原始的平衡负载(load-balancing)。
建立空 NAT 映对
您可以使用 `-j ACCEPT' 目标来让一个联机通过﹐而绕过 NAT 的处理。
标准的 NAT 行为(Behaviour)
默认的行为是在用户制定的规则限制内﹐尽可能少的改变联机。换而言之﹐非不得已不要重映对(remap)埠口。
绝对来源埠口映对
如果其它联机已经被映对到新的联机﹐就算对于一个无需 NAT 的联机来说﹐来源埠口的转换有时或是必须绝对存在的。让我们假设一个封包伪装的情形﹐这已经非常普遍了﹕
1. 一个网页联机由一台 192.1.1.1 的机器从 port 1024 建立﹐要连接到www.netscape.com port 80。
2. 它被封包伪装主机以其自己的 IP 地址(1.2.3.4)进行伪装。
3. 该封包伪装主机尝试由 1.2.3.4 (它的� 部界面地址) port 1024 来做一个网页联机至www.netscape.com port 80。
4. 然后 NAT 程序改变第二个联机的来源埠口为 1025﹐所以这两个联机不至于相冲(clash)。
当这个绝对来源映对存在之时﹐埠口被拆分为三个等级﹕
- 512 以下的埠口
- 512 到 1023 之间的埠口
- 1024 以上的埠口
任何一个埠口都不会被绝对映对到不同的等级去。
当 NAT 失效时会怎样﹖
如果没有办法如用户要求那样独一无二地映对联机﹐那么联机就会被挡掉。当一个封包不能够界定为任何联机的时候﹐结果也一样﹐因为它们可算是畸形的﹐或者是该机器内存耗光了﹐诸如此类。
复合映对﹑重迭﹑和相冲(clash)
您可以设定 NAT 规则在同一个范围之上映对封包﹔NAT 程序足以聪明的去避免相冲。比方说﹐用两条规则将 192.168.1.1 和 192.168.1.2 这两个来源地址分别映对到 1.2.3.4﹐是完全可行的。
再来﹐您可以映对到真实的﹑已用的 IP 地址﹐只要这些地址通过这个映对主机就行。所以﹐如果您获得一个网络(1.2.3.0/24)﹐但有一个内部网络使用这些地址﹐而另一个使用私有地址 192.168.1.0/24 ﹐您就可以 NAT 那些 192.168.1.0/24 的来源地址到 1.2.3.0 网络之上﹐而无需担心相冲﹕
# iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 \
-j SNAT --to 1.2.3.0/24
这同样适用于那些 NAT 主机自己使用的地址﹕这其实就是封包伪装如何工作的了(分享伪装封包地址和来自主机本身封包之 `真实' 地址。 )
更甚者﹐您还可以映对相同的封包到许多不同的目标(targets)上去﹐而且它们都是共享的。例如﹐如果您不想映对任何东西到 1.2.3.5 上去﹐您可以这样做﹕
# iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 \
-j SNAT --to 1.2.3.0-1.2.3.4 --to 1.2.3.6-1.2.3.254
改变本机产生的联机之目的地
如果本机产生的封包之目的地改变了(例如﹐用 OUTPUT 链)﹐而这样会导致封包由不同的界面送出去﹐这样来源地址也跟着变为那个界面。举例子说﹐改变一个环回(loopback)封包之目的地由 eth0 送出﹐会让来源地址也由 127.0.0.1 变成 eth0 的地址﹔而不像其它来源地址映对那样﹐这是立即完成的。当然﹐所有这些映对在响应封包进入时是颠倒过来的。