_____ _____
/ \ / \
PREROUTING -->[Routing ]----------------->POSTROUTING----->
\D-NAT/ [Decision] \S-NAT/
| ^
| |
| |
| |
| |
| |
| |
--------> Local Process ------
上述每一点,当我们查看连接(中)的包时,如果是一个新的连接,我们查看 NAT表中相对应的链,看看需要做些什么。其结果就会作为对这个连接后面所有包 的反应。
(译者注:此处的连接是指一个HTTP会话之类的连接,而非物理上的线路、节点)
5、1 用iptables做简单的选择
下面列出了iptables的一些标准选项。所有双横杠(--)的选项都是可以缩写的。只要iptables可以将它们与其他选项区分开来就够了。如果你的内核是以模块方式支持iptables的,你需要先用命令:insmod ip_tables来加载ip_tables.o
这是最重要的选项表格选择,“-t”。对所有NAT操作,你都需要使用'-t nat'以指定NAT表。其次重要的是'-A',添加一个新的规则到链的末尾(例如'-A POSTROUTING'到POSTROUTING链),或者'-I'从最开始插入一个规则(例如'-I PREROUTING')。
你可以指定你想要进行NAT的包的源(地址)('-s' 或者'--source')和目的(地址)('-d' 或 '--destination')。这两个选项可以跟单个IP地址(例如192.168.1.1),一个名字(例如www.gnumonks.org),或者一个网络地址(例如192.168.1.0/24或192.168.1.0/255.255.255.0[译者:这二者是等价的,只是表示方法不同])。
你可以指定进入或送出的匹配接口。不过能否指定取决于你想要写入规则的那个链:PREROUTING链你只能选择进入接口,POSTROUTING你只能选择送出接口。如果用错了,iptables会给出一个错误。
5、2 关于应当挑选哪些包来拆分(mangle)的要点
我上面说了你可以指定源地址和目标地址。如果忽略源地址选项,那么所有源地址都会被匹配,同样,如果忽略目标地址,所有目标地址都将被匹配。
你还可以标出一个指定的协议('-p'或'--protocol'),诸如TCP或者UDP;那么只有这类协议的包会被匹配。这么做的主要原因是指定了协议,就可以增加额外的选项:指定'--source-port'源端口和'--destination-port'目的端口选项(可缩写为'-sport'和'-dport')。
这些选项让你可以只匹配那些特定源端口及目标端口的包。这些对于重定向Web请求(TCP 80或8080端口)而不影响其他包非常有用。
这些选项必须跟在'-p'选项后面(这可能会对加载该协议的连接库有一定影响)。你可以使用端口号,或者来自/etc/serverices文件的(端口)名。
所有这些你可以对一个包作出的不同选择都详细的列在那详细得可怕的使用手册中了(man iptables)。(译者注:参见iptables man page中文版)
6、说说如何拆分包吧
现在我们知道如何选择我们想要拆分的包了。为完成我们的规则,我们需要准确的告诉内核我们想要它如何做。
6、1 Source NAT
你想要进行Source NAT,改变连接的源地址。这在POSTROUIING链中完成,就在它将送出去的最后一刻。这是一个重要的细节,所有Linux本机上的其他任何东西(路由、包过滤)都会看见那个尚未改变的包。也意味着'-o'(送出接口)选项可用了。
用指定'-j SNAT'来进行Source NAT,'--to-source'选项指定一个或一段IP地址,(加上)一个或一段可选的端口号(只能用于UDP和TCP协议)。
# 改变源地址为1.2.3.4
# iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 1.2.3.4
# 改变源地址为1.2.3.4、1.2.3.5或者1.2.3.6
# iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 1.2.3.4-1.2.3.6
# 改变源地址为1.2.3.4,端口1-1023
# iptables -t nat -A POSTROUTING -p tcp -o eth0 -j SNAT --to 1.2.3.4:1-1023
6、1、1 伪装
Source NAT的一个特例被称作伪装。它只能被用于动态分配IP地址的情况。例如标准拨号服务(静态IP地址请用SNAT)。
你无需为IP伪装明确指定源地址。它会使用包送出的那个接口(地址)作为源地址。不过更重要的是,如果那个线路关闭了的话,连接(无论如何都会丢失了)会被忘掉,意味着启用新的IP后返回的包就会有点问题了(指那些响应掉线前发出的包的包)。
# 伪装所有由ppp0送出的东西
# iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
6、2 Destination NAT
用于PREROUTING链,包刚刚进入的时候。意味着本机上的任何东西看见的都是“真正”的目的地(译者注:即已修改过的目的地址)。也意味着'-i'(进入接口)可用了。
用指定'-j DNAT'来进行Destination NAT,'--to-destination'选项指定定一个或一段IP地址,(加上)一个或一段可选的端口号(只能用于UDP和TCP协议)。
# 改变目标地址为5.6.7.8
# iptables -t nat -A PREROUTING -i eth0 -j DNAT --to 5.6.7.8
# 改变目标地址为5.6.7.8、5.6.7.9或5.6.7.10
# iptables -t nat -A PREROUTING -i eth0 -j DNAT --to 5.6.7.8-5.6.7.10
# 改变Web传送的目标地址为5.6.7.8,8080端口
# iptables -t nat -A PREROUTING -p tcp --dport 80 -i eth0 -j DNAT --to 5.6.7.8:8080
6、2、1 重定向
Destination NAT的一个特例被称为重定向。它相当于对进入接口进行DNAT的简单方便的一种形式。
# 发送进入的80端口的Web传输到我们的Squid(透明)代理
# iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 3128
注意Squid需要被配置为透明代理。
6、3 深层次的映射
还有一些可能大部分人不会用到的NAT的细节。
6、3、1 一个范围内多地址的选择
如果指定了IP地址的范围,那么机器会选择当前使用最少的那个IP地址。这就实现了最俭朴的负载均衡。
6、3、2 建立空的NAT映射。
你可以使用 '-j ACCEPT' 目标让连接通过,不需要NAT参与。
6、3、3 标准NAT行为
默认行为是根据用户给定的内在约束规则,对连接作最小的改动。也就是除非必要 不要进行端口重映射。
6、3、4 内部源端口映射
如果其他的连接覆盖了一个连接,即使这个连接不需要使用NAT,源地址转换仍会发生。考虑IP伪装,这种情况就非常普遍。
1、一个从192.168.1.1 1024端口到www.netscape.com 80端口的Web连接已建立
2、它被伪装成IP伪装服务器的IP地址(1.2.3.4)
3、IP伪装服务器试图建立一个从www.netscape.com 80端口到1.2.3.4 1024端口的Web连接(它自己的外部接口IP地址)
4、NAT代码会修改第二个连接的源地址到1025,这样两个(连接)就不会冲突了。
当这种内部源地址映射发生时,端口分为三级:
512以下的端口
512至1023之间的端口
1024以上的端口
内部端口映射决不会被映射到(除此之外的)其他种类。
6、3、5 如果NAT失败会怎样?
如果无法按照用户请求的那样,为连接建一个单独的映射,(包)会被删除。这也适用于那些无法被归为任何连接的包,因为它们是畸形的,或者是主机内存溢出了。
6、3、6 多重映射,重叠和冲突
你的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服务器本身的地址。这就是得以伪装工作的原因(由伪装后的包和来自本身的“真实”的包共享一个接口地址)。
甚至,你可以映射相同的包到许多不同的目标,它们会被共享。例如,如果你不希望映射任何东西到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
6、3、7 修改本地生成的连接的目标地址
NAT代码允许你插入DNAT规则到OUTPUT链,不过这在2.4中尚未完全支持(可以使用,但必须用新的配置选项,某些测试中的代码。所以除非有人在疯狂的写这部分代码,我不相信它会很快实现)。
当前的限制是你只能修改目标地址到本机(例如'-j DNAT --to 127.0.0.1'), 不能到任何其他机器,否则相应可能不能够被正确转换。
7、 特定的协议
有些协议不希望被NAT。这些协议,两种延伸必须指明:一个是协议的连接跟踪,一个是真实NAT。
在发布的netfilter中,有可用的FTP模块:ip_conntrack_ftp.o和ip_nat_ftp.o。如果你加载了任一种模块到你的内核(或者编译进去),那么任何关于FTP连接的NAT都是可行的。如果没有,那么只能使用被动FTP(passive ftp),而且如果做了一些Source NAT,它(指FTP)也许不能可靠的工作。
8、 关于NAT的警告
如果你对连接做NAT,所有双向传送的包(进入和送出网络的)必须通过NAT服务器,否则NAT服务器的工作可能不可靠。特别是,连接跟踪代码重组了分片,也就意味着不光是连接跟踪不能可靠工作,甚至所有包都无法通过,因为分片被丢弃。
9、 Source NAT 和路由
如果你要做SNAT,你必须注意所有机器被SNAT的包的回应都将发送到NAT服务器。例如,如果你映射了一些送出的包的源地址为1.2.3.4,那么外部的路由器必须知道发送回应包的地址到NAT服务器。可以这样做:
1、如果你对本机地址做SNAT(路由等所有事情都正常),你不需要做任何事。
2、如果你在本地LAN上做SNAT到未用地址(例如,你映射为1.2.3.99,你的1.2.3.0/24网络中未用的IP),你的NAT服务器需要像那个地址(99)一样正确响应ARP请求。最简单的办法是建立一个IP别名,例如:
# ip address add 1.2.3.99 dev eth0
3、如果你对完全不同的地址做SNAT,你必须保证被SNAT的包到达的机器会返回NAT服务器。如果NAT服务器是它们的默认网关,那么就已经行了,否则你需要发布一个路由(如果运行了路由协议)或者对每个机器手工添加路由。
10、 同一个网络内的Destination NAT。
如果你要对同一个网络做端口转发,你需要确认所有以后的包和回应包都通过NAT服务器(这样它们才能被修改)。NAT代码现在(自2.4.0-test6),会屏蔽掉同组的被NAT的包送出的ICMP重定向,不过收到的服务器会继续尝试直接响应客户。(不会理解这个回应)
经典的情况是,内部人员试图访问你的“公用”Web服务器,而它实际上从公用地址(1.2.3.4)被DNAT到内部机器(192.168.1.1),例如:
# iptables -t nat -A PREROUTING -d 1.2.3.4 -p tcp --dport 80 -j DNAT --to 192.168.1.1
一种办法是运行一台内部DNS服务器,它知道你的公用(外部)Web服务器的真实的(内部)IP地址,并转发所有其他请求到外部DNS服务器。也就是说你的Web服务器能够记录真实的内部IP地址。
另一种办法是让NAT服务器映射那些连接的源地址到它自己,让服务器通过它发出响应。例如,我们可以这样做(假设NAT服务器内部IP地址是192.168.1.250):
# iptables -t nat -A POSTROUTING -d 192.168.1.1 -s 192.168.1.0/24 -p tcp --dport 80 -j SNAT --to 192.168.1.250
因为PREROUTING规则会首先运行,对内部Web服务器来说,包的去向早已确定。我们可以确定好源IP地址。
11. Thanks
Thanks first to WatchGuard, and David Bonn, who believed in the netfilter idea enough to support me while I worked on it. And to everyone else who put up with my ranting as I learnt about the ugliness of NAT, especially those who read my diary.
首先感谢在我工作期间相信netfilter设想并支持我的WatchGuard和David Bonn。以及所有对NAT提出指正的朋友,尤其是读过了我的日记的。
Rusty.
All pages ended here.
本文地址:http://com.8s8s.com/it/it35439.htm