这篇文章,我不想展开,本来我并不想写这么一篇文章,由于前段时间,我写了一个static-stateless-2-way-NAT-on-Linux-with-iptables发现自己重新造了轮子之后,我不得不讴歌一下自己。
下载xtables-addons,编译,安装,然后man xtables-addons,你会发现:
   DNETMAP
       The DNETMAP target allows dynamic two-way 1:1 mapping of IPv4 subnets.  Single rule can map  private  subnet  to  shorter
       public  subnet  creating and maintaining unambigeous private-public ip bindings. Second rule can be used to map new flows
       to private subnet according to maintained bindings. Target allows efficient public IPv4 space usage and  unambigeous  NAT
       at the same time.
...
       --prefix addr/mask
...
       --reuse
...
       --persistent
...
       --static
...
       --ttl seconds
...
       * /proc interface

       Module creates following entries for each new specified subnet:

       /proc/net/xt_DNETMAP/subnet_mask
...
       /proc/net/xt_DNETMAP/subnet_mask_stat
...
       Following write operations are supported via proc interface:

       echo "+prenatIP:postnatIP" > /proc/net/xt_DNETMAP/subnet_mask
先说下这是个什么东西吧。这是一个扩展iptables自带NAT功能的NAT模块,具体的功能自己自行去man。
       这里有一个功能是双向的NAT,如果你不知道什么是双向NAT,那么看看Cisco的static NAT配置手册,所谓的双向NAT就是和数据从哪个方向发起无关,如果你在一个方向配置了一条DNAT,那么如果有一个流的第一个数据包从另一个方向而 来,NAT模块会自动执行SNAT,这就是双向NAT。我们来看一下使用DNETMAP模块是怎么配置的:
3. Map 192.168.0.0/24 to subnets 20.0.0.0/26 bidirectional way:

iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 20.0.0.0/26
iptables -t nat -A PREROUTING -j DNETMAP

If host 192.168.0.10 generates some traffic, it gets bound to first free  IP  in  subnet  -  20.0.0.0.  Now  any  traffic
directed to 20.0.0.0 gets DNATed to 192.168.0.10 as long as there's an active (ttl>0) binding. There's no need to specify
--prefix parameter in PREROUTING rule, because this way it DNATs traffic to all active prefixes. You could specify prefix
it you'd like to make DNAT work for specific prefix only.
首 先,我不认为这是一个真正的双向NAT,然而作者的本意真的就是想实现一个真正的双向NAT。如果想主动在20.0.0.0/26段到 192.168.0.0/24段的方向上打开一个口子,必须首先由内向外主动先打一个口,且全部的反向流量都DNAT到了192.168.0.10,这合 理吗?我想,钳制这个实现达到最终目的的并非nf_conntrack,而是iptables。我再三强调,iptables的模式是“条件语句”而非 “祈使语句”,这就是说同一组matches不可能或者退一步说很难同时匹配到两个方向的数据流/数据包。因此:
1.要么你就写两条NAT规则,这也是标准的做法;
2.要么你就写一个target,并且在规则中不设置任何match,这相当于一个祈使句,if(true);do sth...
很显然,如果不是走火入了魔,基本都会使用第一种做法,DNETMAP也是这样。然而在我的static-stateless-2-way-NAT-on-Linux-with-iptables实现中,我采用了第二种方式,事实证明这也是非常不错的。
       其次,我们来看第二个问题,如果DNATMAP的作者也采用了上述第二种方式,那么会怎样。我的观点是,如果DNETMAP也采用了仅使用单一 target的方式,那么它也达到的效果要比我的static-stateless-2-way-NAT-on-Linux-with-iptables 更加perfect,这个more在于nf_conntrack。由于DNETMAP使用了nf_conntrack的接口,它这个NAT依然是基于 conntrack实现的,而conntrack和iptables规则的联动点在conntrack结构体创建的当时,只要规则中不写任何match, 那么任何方向到来的数据流的第一个数据包都会匹配到这个规则,在target中做完全部的事,之后conntrack机制将会记住这个NAT结果,而不必 每次执行target。
       我当时设计stateless-2-way-NAT-on-Linux-with-iptables的时候,最初的想法是stateless,也许这是我 对nf_conntrack的极端偏见导致,这种偏见的形成来自多个方面,比如配置的NAT规则不能即时生效,这个局限导致我在2012-2014年的时 间段中无数次的深夜呆在华丽丽银行大厦里的机房,还比如我在2004年初始接触网络的时候,对H3C/Cisco设备的那种特殊的情感,再比如我对 iptables模式的不认同等,所以我希望能有一个毫无状态的NAT,一个可以即时生效,类似Cisco的双向静态的NAT。在敲定了我的NAT一定得 是stateless的之后,我想它一定得是双向的,这样就不用配置两条iptales规则了,这种想法还是来自对iptables的偏见,我热爱 iptables,但是觉得它总是那么美中不足。抛开所有这些偏见,xtables-addons的DNETMAP让我看到了曙光,然而它还是没有突破 iptables的“条件语句”模式带来的那种可能造就深入人心的局限。新东西都是组合出来的,我的偏见给我带来了一个stateless 2-way NAT实现,当我发现stateless仅仅就是极端带来的偏见后,我决定抛弃它,保留2-way NAT,DNETMAP是statefull的,然而它在NAT操作中使用了match,如果将-s 192.168.0.0/24作为一个target的参数,问题就解决了。于是将我的2-way NAT的target only思想以及DNETMAP的statefull with nf_conntrack相结合,一个完美的NAT就出炉了。
       最后,我想说一下我的自我讴歌。看看我们的接口设计是多么的类似吧,同时有iptables接口和procfs接口(虽然我决定封闭procfs接口,但 这也是逆反心理带来的好的或者不好的结果),procfs接口的操作方式,+/-方式添加/删除,另外,DNETMAP也注意到了xt_target结构 体里面的checkentry问题,这事实上是iptables规则的连续排布导致的,DNETMAP在checkentry中进行了NAT映射数据结构 的管理工作...
--------------------------------------------------
是 时候修剪我的stateless-2-way-NAT-on-Linux-with-iptables了,我将之重新命名为2-way-NAT-on- Linux-with-iptables,意味着它也是基于nf_conntrack的,因此它的iptables规则就可以安心配置在nat表中接受匹 配了。我在stateless-2-way-NAT-on-Linux-with-iptables中除了注册了一个xt_target之外,还注册了单 独的nf_hook_ops用来执行实际操作,事实上,我的xt_target什么都不做,仅仅是接收参数,将其配置到NAT映射容器中,由于重新拥抱了 nf_conntrack,因此这个单独的nf_hook_ops将不再需要,一切都可以回到xt_target中来执行。
       好了,事情就是这样,很晚了,碎碎念小小...我还是那样,自我否定,以对耻感的拒绝营建罪感的孤岛,并最终从罪感中获取力量,得以重生,事情不分大小,事情没大也没小。