网络地址转换通常涉及在IP包经过路由器或防火墙的时候,修改其源和/或目的地址
Linux 内核中有一个数据包过滤框架(packet filter framework),叫做 netfilter,这个框架使得 Linux 机器可以像路由器一样工作。接下来我们将使用一个命令行工具 iptables 来创建复杂的规则,用于修改和过滤 数据包。毫无意外,和 NAT 相关的最重要的规则,都在 nat 这个(iptables)table 里。这个表有三个预置的 chain:PRETOUTING, OUTPUT 和 POSTROUTING。
PREROUTING 和 POSTROUTING 是最重要的 chain。如名字所示,PRETOUTING chain 负责处理刚刚到达网络接口的数据包,这时还没有做路由判断,因此还不知道这个包是发往本机(local),还是网络内的其他主机。包经过 PRETOUTING chain 之后,将进行路由判 断。如果目的是本机,那接下来的过程将不涉及NAT;如果目的是网络内的其他机器,那包 将会被转发到那台机器,前提是这台机器配置了允许转发。
- 子网内的所有主机(客户端)通过socket将数据包发送到一个特定路由器(通过将路由器地址设置为所有机器的网关来实现,数据会通过以太网 或其他底层协议传输)
- 路由器将发送方的socket 替换为自己的一个(还未使用的)socket
- 这个socket收到的数据,路由器将socket地址修改为对应的客户端socket,并转发给它 // 收到数据的响应,这个是反向规则
我们假设客户端的网关设置是正确的,那剩下的就是如何配置路由器了幸运的是,netfilter 框架会对设置的每条一条(出向或入向)规则,自动设置它的反向规则,理解这句话的意思是,比如,我们的期望的结果出规则是,从本地网络发出的、目的是因特网的包,将发送方地址修改为路由器的地址**,反规则是从因特网接收的本地网络包,目的地址修改为本地地址。
因此我们只需要设置一个方向的规则即可。选择哪个方向来设置规则?通常是选择不确定性小的一个方向。 例如,“替换所有从本地网络发出的数据包的地址”比“如果客户端发送过一些东西给服务端 ,那将服务端发送的数据进行某种方式的修改”要简单的多。
如何通过iptables来设置规则
# 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 服务器。
iptables [-t table] command [match pattern] [action]
对于nat -t 的参数value必须是nat, 默认的是 table是filter table。
几个重要的命令
$> 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
选择匹配模式
# 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 中。
# 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>]
NAT chain
几个hook的点
从上图可以看出,如果我们要让某台Linux主机充当路由和负载均衡角色的话,我们显然应该在该主机的nat表的prerouting链中对数据包做DNAT操作。
什么是链和表
防火墙的作用就在于对经过的报文匹配规则,然后执行对应的动作,所以,当报文经过这些关卡的时候,则必须匹配这个关卡上的规则,但是,这个关卡上可能不止有一条规则,而是有很多条规则,当我们把这些规则串到一个链条上的时候,就形成了链,所以,我们把每一个"关卡"想象成如下图中的模样 ,这样来说,把他们称为"链"更为合适。

表
我们把具有相同功能的规则的集合叫做"表",所以说,不同功能的规则,我们可以放置在不同的表中进行管理,而iptables已经为我们定义了4种表,每种表对应了不同的功能,而我们定义的规则也都逃脱不了这4种功能的范围
filter表:负责过滤功能,防火墙;内核模块:iptables_filter,set policies for the type of traffic allowed into, through and out of the computer
nat表:network address translation,网络地址转换功能;内核模块:iptable_nat,used with connection tracking to redirect connections for networking addresss translation。
mangle表:拆解报文,做出修改,并重新封装 的功能;iptable_mangle,特殊用途,比如strip off ip options。
raw表:关闭nat表上启用的连接追踪机制;iptable_raw
表的概念意味着它们的规则类似,是一类统称。
并不是所有的表,都可以在某个链中使用
iptables为我们定义了4张"表",当他们处于同一条"链"时,执行的优先级如下。优先级次序(由高而低):
匹配规则
基本匹配条件
上述内容都可以作为基本匹配条件。
扩展匹配条件:
除了上述的条件可以用于匹配,还有很多其他的条件可以用于匹配,这些条件泛称为扩展条件,这些扩展条件其实也是netfilter中的一部分,只是以模块的形式存在,如果想要使用这些条件,则需要依赖对应的扩展模块。
上述内容都可以作为扩展匹配条件
处理动作
理动作在iptables中被称为target(这样说并不准确,我们暂且这样称呼),动作也可以分为基本动作和扩展动作。
此处列出一些常用的动作,之后的文章会对它们进行详细的示例与总结:
ACCEPT:允许数据包通过。
DROP:直接丢弃数据包,不给任何回应信息,这时候客户端会感觉自己的请求泥牛入海了,过了超时时间才会有反应。
REJECT:拒绝数据包通过,必要时会给数据发送端一个响应的信息,客户端刚请求就会收到拒绝的信息。
SNAT:源地址转换,解决内网用户用同一个公网地址上网的问题。
MASQUERADE:是SNAT的一种特殊形式,适用于动态的、临时会变的ip上。
DNAT:目标地址转换。
REDIRECT:在本机做端口映射。
LOG:在/var/log/messages文件中记录日志信息,然后将数据包传递给下一条规则,也就是说除了记录以外不对数据包做任何其他操作,仍然让下一条规则去匹配。