天天看点

一个可以直接使用的可用iptables配置的stateless NAT实现

使用iptables配置stateless NAT?我没有搞错。

可能你根本不知道这么多NAT的实现细节,或者说根本不在乎,那么本文就当是一个“如何编写iptables模块”的练习了。

实 际上,我已经实现了一个可以配置stateless NAT的内核模块了,但是它的接口是基于procfs的,并不是说这个接口不好用,而是我觉得如果能集成到iptables就更加perfect了,难道 不应该这样吗?有谁能忍受通过iptables和echo的方式配置两种NAT呢?难道不应该在一个iptables -L或者iptables-save命令中展示所有的配置吗?

       想法是一回事,实现是另一回事,二者之间需要有一个促成的动力,这个动力来自于一个陈年的帖子,大概是2004年的吧,那是我还在上学,那个帖子就是在讨 论关于Linux下stateless NAT的问题,时隔多年,如果你搜索相关的主题,依然没有太好的答案。也许是没有这方面的需求,但真的没有吗?我觉得不。在2.4内核时期,还可以通过 iproute2来配置stateless NAT,在2.6/3.X内核时期就只能用tc来配置了,不管怎么说,总是可以做到的,但是你不觉得太麻烦了吗?难道就不能像下面这样子配置一条 stateless NAT吗?

iptables -t nat -A PREROUTING -j STATIC-2-WAY-NAT --mapaddr 192.168.184.250-192.168.184.154 --type src

不 过请注意,iptables本身有一个默认的事实,那就是它是基于match和target的,它的句法是“如果...那么...”,这个句法在 static staleless NAT便不适合了,因为对于这种NAT而言,每添加一条规则,就会自动生成一条反转的规则,这样的话,“如果...那么...”就不行了,要想用 iptables配置stateless NAT,就必须仅仅将规则设置进内核,其它的都由独立的模块来做,换句话说,我们仅仅利用iptables的接口,而不使用它的match机制,因此我只 需要注册一个target,在这个target的checkentry回调中完成“设置规则(正反两个方向)”的操作,在destroy回调中完成“删除 规则”的操作,而target回调本身则什么都不做。幸亏target有这么两个回调函数,否则的话估计玩的还真的有点大。

       但是,千万别把checkentry/destroy这两个回调函数想得太简单,事实上,它们的调用机制远比你想象的要复杂得多,如果我说每添加一条规 则,该target的所有的规则都要重新checkentry一次,这可能有点抽象又不合情理,那么下面我就先说一下iptables规则的添加机制。 iptables添加/删除规则事实上操作的是两个规则集,即新的规则集replace旧的规则集,新旧规则集的区别在于对于添加操作新的规则集中多了一 条要添加的规则,对于删除操作新的规则集中少了一条要删除的规则。每一个规则集都是一块连续的内存,正是因为内存的连续性是一个要求(内存连续可以完全基 于offset来寻址,不必引入指针,完美利用局部性原理),才会出现上述那种replace机制,因为每添加/删除一条规则规则集的大小就会发生变化。 以下的文字摘自我的代码注释:

       给出代码前,先看一下用法,这个STATIC-2-WAY-NAT模块目前一共以下几个参数:

--mapaddr a.b.c.d-A.B.C.D

必须参数。它将地址a.b.c.d转换为A.B.C.D,至于是转换源地址还是目标地址,就看该配置的类型是src还是dst以及数据的方向了。

--type [src|dst]

必须参数。指示转换的类型,src表示源地址转换,它将--mapaddr参数中的源地址为a.b.c.d的数据包的源地址转换为A.B.C.D,目标地址为A.B.C.D的数据包的目标地址转换为a.b.c.d;dst表示目标地址转换,解释类似src。

--proto [udp|tcp]

可选参数。指示转换的数据包协议类型,如果缺失这个参数,则代表所有协议。

--dev [ethX|...]

可选参数。指示参与地址转换的数据包的接收网卡和发送网卡。

--mapport p1-p2

可选参数。指示第四层协议的端口转换规则,仅仅针对udp和tcp,即如果有这个参数,则必须指定proto为udp或者tcp。

目 前的参数就以上这么多,日后会完善。使用方法很简单,为了好看那么一点点,我将我的NAT实现放在了nat表的PREROUTING和 POSTROUTING上,在写规则的时候,随便哪个ROUTING都行,实现并不关心,matches在理论上是没有用的,但是它可以对精确匹配到的数 据包屏蔽基于conntrack的原生NAT操作,这也算是一种副作用吧。

代码一共三个文件,可用但不完美,一条日志都没有打,也算一种拆弹方式...:

xt_STATIC-2-WAY-NAT.c:这是一个内核模块,实现了NAT的核心逻辑和iptables target接口

libxt_STATIC-2-WAY-NAT.c:这是用户态的iptables target模块的实现文件

xt_STATIC-2-WAY.h:这是一个头文件,内核模块和用户态文件公用

最后是一个Makefile:

关 于备份不得不多说几句,我现在有时候在工作中碰到问题的时候,参考的最多的就是我自己的博客,因为总是隐隐约约觉得自己曾经搞定过某件事,但只是曾经而 已,只要有迹可寻,找到那个曾经的方案即可,当然,如果现在重新从零开始最终也是可以搞定的,但是那将浪费很多时间。以前我喜欢在纸上做笔记,但是几乎不 做索引,随着本子越来越多越来越厚,就很难找到要找的东西了,后来就改成了在电脑里用Word,OneNote甚至记事本做笔记,可是最终的结果和用纸和 笔的效果一样,后来我觉得互联网上现成的索引做的不错,为何不让搜索引擎替我搜索呢?于是就改成博客的方式了,至于代码,我倒不是很看重,自娱自乐而已, 我主要想记录的是当时想了些什么而不是怎么做的。其实你有没有想过,你参考的最多还是自己以往的经验,而不是别人的,因此干嘛不把自己以往的想法录下来 呢?以前是写日记,现在是写博客,零散的想法以前可以随身带个小本子写随笔,现在有微博和朋友圈,其实万变不离其中。

 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1596647

继续阅读