本文的名字取的比较有意义,因为本文并不是真的要讨论如何在Linux上使用iptables实现static nat!之所以这么命名本文,是想引起别人的注意,因为中文资料,以及国内的搜索引擎,基本上没有人关注这个问题,也几乎没有什么人讨论这个问题,而我却由于项目原因以及平时的折腾,切身体会到了Linux的NAT实现的缺陷。如果你使用google,你会看到大量的国外的技术宅男宅女们在讨论这个问题,虽然也并没有给出一个明确的解答(但是我相信,解答很快就会有的,xtables-addons-2.4?or later...)!我希望,即使你问的是度娘,“Static NAT with iptables on Linux”也能给出一个答案!如果本文不幸被某位大牛看到,加之这哥们儿再有点影响力,那么搞出来一个真正的Static Stateless的NAT实现就指日可待了!
我相信,国内很多人也碰到了这个问题,也觉得Linux的NAT无法实现纯净的Static,Stateless,千万别扯RAWNAT,对于这个东西以及背后的想法,我可能比你知道的多些,RAWNAT不也得配两条规则吗?何况,即使你不把配置两条规则当成缺点-UNIX哲学笃信,能让人多个动作实现的,它绝不合并成一个,更加本质的原因在于Linux NAT规则的查找方式上。
Linux的NAT附着于ip_conntrack之上,我再说一遍。这是一个优点,可以在一个流内部,一次查找,多次使用。然而我们看看这个查找的方式与代价,它是顺序查找的,也就是按照用户配置的顺序遍历规则表,假设我们已近实现了可以用iptables配置的static NAT,那么在10000个地址需要转换的情况下,让你失望的效果是多么明显,以至于你马上就由于其不可扩展的缺陷而改用BSD或者直接购买Cisco了,幸运的是,这种事从来没有发生,因为这只是一个假设,真正的可以用iptables配置的static NAT还没有实现,也许你会说:
iptables -t nat ....-s 1.1.1.1 -j SANT --to-source 2.2.2.2
iptables -t nat ....-d 2.2.2.2 -j DANT --to-destination 1.1.1.1
这算什么?配置工作量直接扩大两倍,虽不经常,也不绝对,这两条规则配置在了最后,赶紧抱着你的UNIX哲学跳楼吧!
这么说吧,Linux没有实现一种可扩展的NAT规则查找机制,也许你会这么反驳我,Cisco的NAT不也有基于ACL的吗?ACL不也是按照配置顺序配置的吗?好吧,我承认你有点文化,并且带着怀疑论者的论调而不是妄下结论:“不,你错了!”。这实际上是我要说的话,并且很直接:不,你错了!首先基于ACL的匹配是针对于动态NAT的,它将匹配任务交给了ACL;其次,Cisco有自己的static NAT机制,在配置的时候就将映射注入系统,然后针对任意使能了NAT的接口进来的数据包执行统一高效的NAT表查询。
现在我们抽离出NAT的操作步骤,首先要决定哪些包需要NAT,其次是查找NAT规则以便知道如何NAT,最后就是执行NAT。以上第三步执行NAT想必都是一样的,改一下包头即可,重点在前两个步骤。
先看第一步,对于动态NAT,需要基于match来匹配,因为只有在符合一定条件的情况下,才能NAT,对于Linux,使用用户配置iptables NAT的顺序来匹配,对于Cisco,使用ACL,二者是一样的。然而针对static NAT,则这一点是确定的,在配置的时候就是确定的,然而Linux根本就没有实现,即使可以使用两条iptables实现,那也是使用用户的配置顺序来匹配的,这就不妥了,对于Cisco则没有这个问题。Cisco将高优先权匹配赋予了静态NAT,而Linux则全部依赖用户配置iptables规则的顺序。虽然依附于ip_conntrack的NAT可以提高效率,不必每次都遍历iptables NAT规则,但那也是在一个连接内部有效,起码对static的NAT不公平,试想大量的短连接的情况...
再看第二步,第一步知道了哪些包要NAT,现在来看如何NAT,对于Linux而言,这个步骤和第一个步骤是合在一起的,即在你知道了它要NAT的时候就同时知道如何来NAT,前者就是matches,后者是target,而对于Cisco-也是本应该的做法,动态NAT情况,ACL和ip nat pool是分来的,并且一旦知道了如何NAT,该条NAT映射就会被注入系统内部,后续的包就可以直接在系统中查找NAT映射了而不必每次都匹配ACL了,静态NAT情况,该步骤和第一步是合在一起的。Linux也不是每次都匹配iptables的NAT规则,而是将“如何NAT”的结果附着于ip_conntrack的反向五元组上,后面的包直接查ip_conntrack即可,而这个查询是快速的。
虽然我自己修改了一些代码已经可以应对这个问题,但是还是希望能有更加“官方”,更加健壮的代码出自业界大牛之手。另外令我费解的是,难道真的就没有碰到这个问题吗?肯定有人碰到过,虽然Linux作为纯网络设备的情况很少,但是对于中小型机构还是有的,宁可配置两条策略也不去想想怎么能一条搞定?好吧,我承认网上有很多封装,一条命令就能搞定,我在项目中也做过这种垃圾至极的封装,试问这些封装者,你们怎么也不去想想怎么去改进,问题被规避了而不是解决了。封装只是为了使用上的方便,但你一定要明白冰山下面有什么。我特别恶心那些介绍某种库的文字:xxx可以专注于业务逻辑,而不必xxx关注它的实现xxx。不关注并不代表不懂,不关注的意思是你不用自己去实现,但是你一定要理解为什么要这么实现!另外,并不是所有的事情都能靠封装解决的,Static NAT with iptables on Linux就是一例!
我相信,国内很多人也碰到了这个问题,也觉得Linux的NAT无法实现纯净的Static,Stateless,千万别扯RAWNAT,对于这个东西以及背后的想法,我可能比你知道的多些,RAWNAT不也得配两条规则吗?何况,即使你不把配置两条规则当成缺点-UNIX哲学笃信,能让人多个动作实现的,它绝不合并成一个,更加本质的原因在于Linux NAT规则的查找方式上。
Linux的NAT附着于ip_conntrack之上,我再说一遍。这是一个优点,可以在一个流内部,一次查找,多次使用。然而我们看看这个查找的方式与代价,它是顺序查找的,也就是按照用户配置的顺序遍历规则表,假设我们已近实现了可以用iptables配置的static NAT,那么在10000个地址需要转换的情况下,让你失望的效果是多么明显,以至于你马上就由于其不可扩展的缺陷而改用BSD或者直接购买Cisco了,幸运的是,这种事从来没有发生,因为这只是一个假设,真正的可以用iptables配置的static NAT还没有实现,也许你会说:
iptables -t nat ....-s 1.1.1.1 -j SANT --to-source 2.2.2.2
iptables -t nat ....-d 2.2.2.2 -j DANT --to-destination 1.1.1.1
这算什么?配置工作量直接扩大两倍,虽不经常,也不绝对,这两条规则配置在了最后,赶紧抱着你的UNIX哲学跳楼吧!
这么说吧,Linux没有实现一种可扩展的NAT规则查找机制,也许你会这么反驳我,Cisco的NAT不也有基于ACL的吗?ACL不也是按照配置顺序配置的吗?好吧,我承认你有点文化,并且带着怀疑论者的论调而不是妄下结论:“不,你错了!”。这实际上是我要说的话,并且很直接:不,你错了!首先基于ACL的匹配是针对于动态NAT的,它将匹配任务交给了ACL;其次,Cisco有自己的static NAT机制,在配置的时候就将映射注入系统,然后针对任意使能了NAT的接口进来的数据包执行统一高效的NAT表查询。
现在我们抽离出NAT的操作步骤,首先要决定哪些包需要NAT,其次是查找NAT规则以便知道如何NAT,最后就是执行NAT。以上第三步执行NAT想必都是一样的,改一下包头即可,重点在前两个步骤。
先看第一步,对于动态NAT,需要基于match来匹配,因为只有在符合一定条件的情况下,才能NAT,对于Linux,使用用户配置iptables NAT的顺序来匹配,对于Cisco,使用ACL,二者是一样的。然而针对static NAT,则这一点是确定的,在配置的时候就是确定的,然而Linux根本就没有实现,即使可以使用两条iptables实现,那也是使用用户的配置顺序来匹配的,这就不妥了,对于Cisco则没有这个问题。Cisco将高优先权匹配赋予了静态NAT,而Linux则全部依赖用户配置iptables规则的顺序。虽然依附于ip_conntrack的NAT可以提高效率,不必每次都遍历iptables NAT规则,但那也是在一个连接内部有效,起码对static的NAT不公平,试想大量的短连接的情况...
再看第二步,第一步知道了哪些包要NAT,现在来看如何NAT,对于Linux而言,这个步骤和第一个步骤是合在一起的,即在你知道了它要NAT的时候就同时知道如何来NAT,前者就是matches,后者是target,而对于Cisco-也是本应该的做法,动态NAT情况,ACL和ip nat pool是分来的,并且一旦知道了如何NAT,该条NAT映射就会被注入系统内部,后续的包就可以直接在系统中查找NAT映射了而不必每次都匹配ACL了,静态NAT情况,该步骤和第一步是合在一起的。Linux也不是每次都匹配iptables的NAT规则,而是将“如何NAT”的结果附着于ip_conntrack的反向五元组上,后面的包直接查ip_conntrack即可,而这个查询是快速的。
虽然我自己修改了一些代码已经可以应对这个问题,但是还是希望能有更加“官方”,更加健壮的代码出自业界大牛之手。另外令我费解的是,难道真的就没有碰到这个问题吗?肯定有人碰到过,虽然Linux作为纯网络设备的情况很少,但是对于中小型机构还是有的,宁可配置两条策略也不去想想怎么能一条搞定?好吧,我承认网上有很多封装,一条命令就能搞定,我在项目中也做过这种垃圾至极的封装,试问这些封装者,你们怎么也不去想想怎么去改进,问题被规避了而不是解决了。封装只是为了使用上的方便,但你一定要明白冰山下面有什么。我特别恶心那些介绍某种库的文字:xxx可以专注于业务逻辑,而不必xxx关注它的实现xxx。不关注并不代表不懂,不关注的意思是你不用自己去实现,但是你一定要理解为什么要这么实现!另外,并不是所有的事情都能靠封装解决的,Static NAT with iptables on Linux就是一例!