Linux 2.4 NAT HOWTO 作者:Rusty Russell, mailing list netfilter@lists.samba.org 译者:网中人 netmanforever@yahoo.com v1.0.1 Mon May 1 18:38:22 CST 2000 _________________________________________________________________ 本文件描述如何用 2.4 Linux 核心去做 masquerading、transparent proxying 、port forwarding、和其它类型的 Network Address Translations 。 _________________________________________________________________ 1. Introduction 2. 官方的网站和通信论坛何处觅? * 2.1 何为 Network Address Translation? * 2.2 为什麽我要做 NAT 呢? 3. 两种类型的 NAT 4. 从 2.0 到 2.2 核心的快速转变 * 4.1 救命啊!我只想要封包伪装而已! * 4.2 那 ipmasqadm 怎麽了? 5. 控制哪些要 NAT * 5.1 用 iptables 做简单的选择 * 5.2 关於挑选哪些封包来 mangle 的细节 6. 谈谈要怎样 Mangle 封包 * 6.1 Source NAT * 6.2 Destination NAT * 6.3 进一步的映对(Mappings) 7. 特殊协定 8. NAT 的一些限制 (caveats) 9. Source NAT 与路由 10. 在同一网路上的 Destination NAT 11. 感谢 _________________________________________________________________ 1. Introduction 亲爱的读者,欢迎您! 您将要探索的是引人入胜(有时蛮恐怖)的 NAT(Network Address Translation) 世界,同时,您甚至可以把这份 HOWTO 当成 Linux 2.4 核心及以後版本的精确 指南呢。 在 Linux 2.4 里面,有一个叫 `netfilter' 的东东,它是专门撕裂 (mangling* )封包的。在它再上一个层级,就是提供 NAT 功能的了,则是完全由 以往的核心实作而成的。 (译者注:很奇怪,原作者用 mangle 这一词,似乎在过往的中文文件中都没碰到 过,查过好多字典都不知道怎麽翻译好。这里暂时勉强用‘撕裂’这个词代替, 不过後面我就不尝试翻译这词了,让读者自己去理解吧。) 2. 官方的网站和通信论坛何处觅? 目前有三个官方网站可供浏览: * 感谢 [1]Filewatcher (http://netfilter.filewatcher.org). * 感谢 [2]The Samba Team and SGI (http://www.samba.org/netfilter). * 感谢 [3]Jim Pick (http://netfilter.kernelnotes.org). 而官方的 netfilter 邮件论坛,则可以到这里看: [4]Samba's Listserver (http://lists.samba.org). 2.1 何为 Network Address Translation? 一般来说,在网路上封包从其来源(比方您家中的电脑)出去,然後到达目的地(比 方www.kernelnotes.org),会经过许许多多个不同的连接(links):就我所在的澳 洲来说就大约有 19 个之多。没有任何一个连接会真的去更改您的封包:他们仅 仅是将之传送出去而已。 假如其中一个连接会做 NAT 的话,然後它们就会更改那些经它而过的封包之来源 或目的地地址。诚如您能想像得到的,这并非系统被设计成这样的,而是 NAT 所 做的手脚而已。通常要做 NAT 的连线会记住它如何 mangled 封包的,然後当回 应封包从另一方向过来的时候,然後就反过来 mangling 那个回应封包,所以所 有东西都工作起来了。 2.2 为什麽我要做 NAT 呢? 在完美的世界里,您无需这样做啦。在目前来说,还是有其理由的: 用 modem 拨接上网 大多数的 ISP 在您连上去的时候只会给您一个单一 IP 地址。您喜欢的 话,以任何来源地址把封包送出去都行,但只有回应到这个来源地址的封 包才可以回到您那里。如果您想用多台不同主机(例如家中网路)透过该连 接上 internet 的话,那您就要 NAT 了。 这也就是今天 NAT 最常用之处,而在 Linux 世界最为人知的就是所谓的 `masquerading(封包伪装术)' 了。我称之为 SNAT,因为您改变了第一个 封包的 source(来源) 地址的缘故。 多重伺服器 有时候,您会想去改变那些进入您网路的封包之路向。这最常是因为(如 上述)您只有一个 IP 地址,但您却想让别人能够连接到 `真实' IP 地址 後面的主机去。如果您重写这些内送封包的目的地址,这样您就可以管理 它们了。 一个常见的变动是负载分担(load-sharing),也就是在一组机器上面为封 包做映对(mapping)的动作。这类型的 NAT ,在以前的的 Linux 版本中 也就被称为 port-forwarding 。 透明代理(Transparent Proxying) 有时候,您或许想要每一个经过您的 Linux 主机的封包送至主机本身的 一个程式去。这就需要进行透明代理的动作了:一个代理就是一个位於您 的网路和外部网路的程式,为彼此双方负起沟通的任务。而所谓的透明, 则是因为您的网路甚至无须知道在和一个代理对讲,当然了,除非代理不 再工作了吧。 Squid 可以配置成这样的工作方式,这就是在过往的 Linux 版本中所谓 的重导向(redirection)、或透明代理了。 3. 两种类型的 NAT 我将 NAT 分为两种不同的类型: Source NAT (SNAT) 与 Destination NAT (DNAT) Source NAT 就是您将改变第一个封包的来源地址:例如,您为传入的连线做 caching 的动作。Source NAT 永远会在封包传出网线之前就做好 post-routing 的动作。封包伪装(Masquerading)就是一个 SNAT 特例。 Destination NAT 就是您将改变第一个封包的目的地地址:例如您要为传出的连 线做 caching 的动作。Destination NAT 永远会在封包从网线进入之後就马上做 好 pre-routing 的动作。Port forwarding、负载分担、以及透明代理,都属於 DNAT。 4. 从 2.0 到 2.2 核心的快速转变 非常抱歉,假如您仍然忙於从 2.0(ipfwadm) 到 2.2(ipchains) 的转型的话。不 过,这也是个喜忧参半的消息啦。 首先,您可以轻易的一如往昔地使用 ipchains 和 ipfwadm。要这样做的话,您 需要将最新的 netfilter 套件中的 `ipchains.o' 或 `ipfwadm.o' 核心模组载 入。它们是相互排斥的(您应已获警告了),同时也不能和其它 netfilter 模组同 时整合在一起。 一旦其中一个模组被载入,您就可以如常使用 ipchains 和 ipfwadm 了,但也有 如下一些变化啦: * 用 ipchains -M -S,或是 ipfwadm -M -s 作伪装逾时将不再有效。因为逾 时设定已经移至新的 NAT 架构中,所以这里也就没什麽所谓了。 * 在伪装列表中显示的 ini_seq、delte、和 previous_delta 栏位,将永远为 零。 * 同时归零(zeroing)和列示记数器(counter)的 `-Z -L' 已无作用:记数器将 不能再归零了。 Hacker 们仍要留意之处: * 您现在可以捆绑 61000-65095 之间的埠口,而无需理会您是否使用封包伪装 技术。在过去,封包伪装程式会把此值域内的所有东西捕获进来,所以其它 程式就不可用之了。 * 至於(尚未成文之) getsockname 破解,在过去,透明代理程式可以找出那些 不再有效连线之真正目的地。 * 至於(尚未成文之) bind-to-foreign-address 破解,同样尚未实作;这在过 去用以完善透明代理的构想。 4.1 救命啊!我只想要封包伪装而已! 没错,这也是大多数朋友之需。如果您用 PPP 拨接获得的动态 IP (如果您不了 解的话,那您应该是了),您或许只想单纯告诉您的主机让所有来自您内部网路的 封包,看起来如来自该 PPP 拨接主机一样。 # Load the NAT module (this pulls in all the others). modprobe iptable_nat # In the NAT table (-t nat), Append a rule (-A) after routing # (POSTROUTING) for all packets going out ppp0 (-o ppp0) which says to # MASQUERADE the connection (-j MASQUERADE). iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE # Turn on IP forwarding echo 1 > /proc/sys/net/ipv4/ip_forward 注:您这里并没做任何封包过滤:如要的话,请参考 Packet Filtering HOWTO: 将 NAT 和封包过滤合并起来就是了。 4.2 那 ipmasqadm 怎麽了? 这个其实取决於使用者而已,所以我并不是很为向後兼容问题而担心。您可以单 纯使用 iptables -t nat 做 port forwarding 的动作。例如,在 Linux 2.2 您 或许已经这样做了: # Linux 2.2 # Forward TCP packets going to port 8080 on 1.2.3.4 to 192.168.1.1's port 80 ipmasqadm portfw -a -P tcp -L 1.2.3.4 8080 -R 192.168.1.1 80 而现在,如此则可: # Linux 2.4 # Append a rule pre-routing (-A PREROUTING) to the NAT table (-t nat) that # TCP packets (-p tcp) going to 1.2.3.4 (-d 1.2.3.4) port 8080 (--dport 8080) # have their destination mapped (-j DNAT) to 192.168.1.1, port 80 # (--to 192.168.1.1:80). iptables -A PREROUTING -t nat -p tcp -d 1.2.3.4 --dport 8080 \ -j DNAT --to 192.168.1.1:80 假如您想让这条规则同时修改本机连线的话(如,即使在 NAT 主机本身,要连接 1.2.3.4 的 8080 埠口之 telnet 连线,会帮您连接至 192.168.1.1 的 80 埠 口),您就可以插入相同的规则至 OUTPUT 链中(它只适用於本机传出的封包): # Linux 2.4 iptables -A OUTPUT -t nat -p tcp -d 1.2.3.4 --dport 8080 \ -j DNAT --to 192.168.1.1:80 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.kernelnotes.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 的地址;而不像其它来源地址映对那样,这是立即完成的。当然,所有这些 映对在回应封包进入时是颠倒过来的。 7. 特殊协定 有些协定是并不想要做 NAT 的。对於每一个这样的协定而言,有两个延伸设 定(extension)是必须要写清楚的:一个是关於协定之连线追踪,另一个关於实际 的 NAT。 在 netfilter 发行套件里面,有一些关於 ftp 的现行模组 :ip_conntrack_ftp.o 与 ip_nat_ftp.o 。如果您把这些插入到您的核心里面( 或您永久性的编译它们),那麽要在 ftp 连线上做任何种类的 NAT 都是可行的。 如果您不这样的话,那您可以使用被动模式 ftp,不过如果您要做一些动作甚於 简单 Source NAT 的话,这就可能不那麽可靠了。 8. NAT 的一些限制 (caveats) 如果在一个连线上做 NAT,所有 双向 (传出和传入) 的封包,都必须要通过 NAT 主机才行,否则并不可靠。尤其在连线追踪程式重组碎片 (fragments)的时 候,也就是说,不但连线追踪会不可能,而且您的封包根本就不能通过,因为碎 片会被挡下。 9. Source NAT 与路由 如果您要做 SNAT,您会想要确定经过 SNAT 封包所传给的主机会将回应送回给 NAT 主机。例如,如果您映对某些传出封包到来源地址 1.2.3.4 之上,那麽外部 的路由器就必须知道要将回应封包(目的地为 1.2.3.4 )送回给该主机。这可以用 如下方法做到: 1. 如果您要在主机自己的地址(路由和其它所有运作皆正常)上面做 SNAT,您无 需做任何动作。 2. 如果您要在一个在本机网路上尚未使用的地址做 SNAT(例如,映对到在 1.2.3.0/24 网路上的一个可用 IP 1.2.3.99),您的 NAT 主机就需要回应关 於该地址的 ARP 请求,一如它自己本身的一样:最简单的方法就是建立 IP alias,例如: # ip address add 1.2.3.99 dev eth0 3. 如果您要在一个完全不同的地址上做 SNAT,您就要确定 SNAT 封包抵达的机 器能够路由回该 NAT 主机。如果 NAT 主机是它们的预设闸道器的话,是可 以做到的,否则,您就要广告(advertize )一个路由(如果跑路由协定的话) ,或是手工的在每一台参与机器上增加路由。 10. 在同一网路上的 Destination NAT 如果您要做 portforwarding 回到同一个网路,您要确定前向和回应封包双方都 经过该 NAT 主机(这样它们才能被修改)。NAT 程式从现在开始(2.4.0-test6以 後),会挡掉後面情形所产生的传出 ICMP 重导向:那些已经 NAT 的封包以它所 进入的相同界面传出,而接收端伺服器仍尝试直接回应到客户端(不认可该回应) 。 经典的情形是内部人员尝试连接到您的 `公有(public)' 网站伺服器,实际上是 从公有地址(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 伺服器,它知道您的公有网站的真正(内部) IP 地址 ,而将其它请求转传给外部的 DNS 伺服器。换而言之,关於您网站伺服器的记录 会正确地显示为内部 IP 地址。 而另一个方法是同时让这台 NAT 主机将该等连线之来源 IP 地址映对为它自己的 地址,我们可以像如下那样做(假设 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 规则是最先执行的,对内部网站伺服器而言,封包就已经被定 向好了:我们可以内定好哪个为来源 IP 地址。 11. 感谢 首先感谢在我工作期间相信 netfilter 的构想并支持我的 WatchGuard 和 David Bonn。 以及所有其他帮我指正 NAT 之不足的朋友,尤其是那些读过我的日记的。 Rusty. References 1. http://netfilter.filewatcher.org/ 2. http://www.samba.org/netfilter 3. http://netfilter.kernelnotes.org/ 4. http://lists.samba.org/