(十)洞悉linux下的Netfilter&iptables:网络地址转换原理之SNAT

源地址转换:SNAT

    SNAT 主要应用于下列场景:

    这种情况下,我们只有一个公网地址A,而又有三台主机需要同时上网,这时就需要SNAT了。它的主要作用是将那些由私网发来的数据包skb的源地址改成防火墙的公网地址A,这是因为目的主机在响应源地址为私网地址的数据包时,私网地址不能在网络上路由的缘故。

    SNAT仅可以在LOCAL_OUTPOSTROUTING点生效,这也说明了为什么用户空间的iptables命令在配置SNAT规则时只能配置到nat表的OUTPUTPOSTROUTING链的缘由。

    我们现在假设的情形是:其他四个HOOK点都没配任何规则,且内置链的缺省处理策略为ACCEPT,然后在防火墙上配置了一条SNAT规则,私网地址B1要访问公网地址C的情况。

    和前面DNAT类似,当第一个由B1发往C的数据包到达POSTROUTING点时,在连接跟踪阶段就已经为该连接建立好了ip_conntrack实例并进行过适当的初始化。SNAT主要是在ip_nat_out()函数中完成,而该函数本质上也调用了ip_nat_fn(),所以流程和DNAT一样,但执行的操作有所差别。

     还是先回顾一下 ip_nat_fn() 的流程图:

    注意,当执行ip_nat_out()函数时,该skb已经被正确路由过了。此时,在ip_nat_fn()里执行的是ip_nat_rule_find()分支,然后进入ip_nat_setup_info()函数中。

    接下来我们简单说一下get_unique_tuple()函数。

     如果属于某条连接的数据包之前已经被执行过 NAT 了,则其连接跟踪记录会被添加到 bysource 链表中。对于 SNAT 操作,如果是第一个数据包,其流程和 DNAT 非常相似,在函数 find_best_ips_proto() 中完成对临时 tuple 的源地址的修改。然后,以修改后的 tuple 计算其响应 tuple ,最终用该响应 tuple 替换掉连接跟踪记录中原来的响应 tuple 。替换的结果是:

    此时,连接跟踪记录中响应tuple的源地址已经被替换成防火墙的公网地址A了。等会儿当服务器C回应时,它所发出的报文目的地址就是A,这样防火墙就可以正确接收到服务器C的回应报文。最后,会根据该连接跟踪记录实例的初始tuple来计算一个hash值,然后将其插入到bysource里,并对ip_conntrack.status状态进行适当设置。

    DNAT类似,最后也是在manip_pkt()函数中完成对skb源地址、IP校验和进行修改。对skb里端口的修改也是在manip_pkt()里完成的。

 

    OK,关于SNAT我们第一阶段的分析就完成了,接下来我们来看一下当服务器C收到这个请求报文后,在对其响应的后续流程里防火墙是如何实现所谓的自动De-SNAT功能。

 

    服务器的响应报文其源地址为自己的公网地址C,目的地址为防火墙的公网地址A。当该响应报文到达防火墙后,连接跟踪系统可以准确地识别该数据包所欲的连接跟踪记录,因为存在一个源地址是C,目的地址是A的响应tuple(如上图所示)与其匹配。该响应数据包还是会先到达PREROUTING点的ip_nat_in()函数,因为没有配置任何DNAT规则,同时ct.status字段又设置了IPS_SRC_NATIPS_SRC_NAT_DONE_BIT标志位,所以在进入到ip_nat_in()函数的ip_nat_fn()里时就直接调用ip_nat_packet()接口。

    ip_nat_packet()中以连接跟踪记录的初始tuple计算原来旧的响应tuple:源地址是C,目的地址是B1。因为在PREROUTING点上要做DNAT,所以此时skb中的目的地址A就被改成了原来的响应tuple中的目的地址B1了。后面的流程和DNAT完全一样。

    当该响应报文来到POSTROUTING点时,又调用ip_nat_out(),和前面分析SNAT一阶段时的流程一样。

 

    现在我们就明白了,De-SNAT功能是基于manip_pkt()函数实现的。在结尾之际,我们来解释一下上篇博文中关于NAT的一段描述:“只有每条连接的第一个数据包才会经过nat表,而属于该连接的后续数据包会按照第一个数据包则会按照第一个报所执行的动作进行处理,不再经过nat”。

    从用户空间的iptables规则来看,每条规则都有一个counter计数器,该计数器记录的是被该规则成功匹配了数据包的数目。而内核中对该计数器的修改是在ipt_do_table()函数。从ip_nat_fn()函数的执行流程可以看出,当连接跟踪记录项被设置了IPS_SRC_NAT_DONE_BIT状态位,或者连接跟踪的状态不再是IP_CT_NEW状态时,ipt_do_table()函数就不再被调用了,反应在用户空间所看到的直观现象就是,nat表的规则计数器不再增长了。

     基于连接跟踪的 NAT ,其特殊之处就在于初始和响应 tuple 不再一致了,而这也是 NAT 得以正确运行的关键所在。通过如下这张图,让大家再复习一下 NAT 的初始和响应 tuple 之间的关系:

    至此,LinuxNetfilter框架下的nat子系统我们就全部学习完了。用了十个章节基本将Netfilter框架的各个功能子模块、架构、原理、流程等作了初步简单的分析了解。由于知识有限,本人的分析难免存在疏漏,还请各位大侠不吝指正。在接下来后续的文章中,主要探讨以下主题:

1、  用户空间的iptables是怎样识别传递给它的每个参数?

2、  通过iptables配置的每条规则是如何进到内核里的?它们又有什么关系?

3、  内核是如何判断数据包到底匹配还是不匹配某条具体规则?

4、  如何自己动手扩展iptables的功能模块?

    未完,待续

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(8341) | 评论(6) | 转发(47) |
给主人留下些什么吧!~~

gaopeiliang2014-07-30 13:49:23

wjlkoorey258:做完策略路由之后,再配置两组规则看看?可以用源地址伪装来实现,比如所有去55端口的数据走ppp0,所有去22的数据走eth1就可以这样设置:
iptables -I POSTROUTING -t nat -o ppp0 -p tcp --dport 55 -j MASQUERADE
iptables -I POSTROUTING -t nat -o ppp0 -p udp --dport 55 -j MASQUERADE
iptables -I POSTROUTING -t nat -o eth1 -p tcp --dport 22 -j MASQUERADE
iptables -I POSTROUTING -t nat -o eth1 -p udp --dport 22 -j&n

感谢楼主的回复: 我发现了点问题,应该是rp_filter 这个特性造成的,,,谢谢!!!

回复 | 举报

wjlkoorey2582014-07-28 22:58:37

gaopeiliang:怎么搞的,关键的数据怎么丢了那,,出现的问题是

从ppp0上走出的数据,正确的SNAT出去了,同时也得到了响应,但是数据却没有转发到eth1上,看来看去好像是回来的数据包没有被自动的NAT回来,导致没有转发到eth1上。

这个没有什么道理啊,,希望博主帮忙分析一下,是哪个环节可能有问题,还是这种用法就有问题???

做完策略路由之后,再配置两组规则看看?可以用源地址伪装来实现,比如所有去55端口的数据走ppp0,所有去22的数据走eth1就可以这样设置:
iptables -I POSTROUTING -t nat -o ppp0 -p tcp --dport 55 -j MASQUERADE
iptables -I POSTROUTING -t nat -o ppp0 -p udp --dport 55 -j MASQUERADE
iptables -I POSTROUTING -t nat -o eth1 -p tcp --dport 22 -j MASQUERADE
iptables -I POSTROUTING -t nat -o eth1 -p udp --dport 22 -j&n

回复 | 举报

gaopeiliang2014-07-25 09:50:12

gaopeiliang:博主你好,你的文章写的很好,对深入的理解机制原理很有帮助,我拜读了几篇,受益匪浅。

之所以找到搜索到博主的文章学习,是因为我遇到了一个关于SNAT的奇怪的问题,不知道怎么办了,希望博主能指点一下;

问题是这样的:

一台机器  3个网卡    eth0 可正常上网,  ppp0 3G拨号可上网, eth1接内网,通过这台机器的转发使内网的机器可以正常上网。

eth1 上的内网数据转发到eth0或者ppp0的其中一个时,就可以正常的上网;

但是我想根据不同的端口,将eth1上的指定端口的数据导到ppp0 上去,如https(443端口),其他的数据走eth0, 我利用的是 标记 + 策略路由的方式

这时就出现问题了, 
                
    &

怎么搞的,关键的数据怎么丢了那,,出现的问题是

从ppp0上走出的数据,正确的SNAT出去了,同时也得到了响应,但是数据却没有转发到eth1上,看来看去好像是回来的数据包没有被自动的NAT回来,导致没有转发到eth1上。

这个没有什么道理啊,,希望博主帮忙分析一下,是哪个环节可能有问题,还是这种用法就有问题???

回复 | 举报

gaopeiliang2014-07-25 01:07:42

博主你好,你的文章写的很好,对深入的理解机制原理很有帮助,我拜读了几篇,受益匪浅。

之所以找到搜索到博主的文章学习,是因为我遇到了一个关于SNAT的奇怪的问题,不知道怎么办了,希望博主能指点一下;

问题是这样的:

一台机器  3个网卡    eth0 可正常上网,  ppp0 3G拨号可上网, eth1接内网,通过这台机器的转发使内网的机器可以正常上网。

eth1 上的内网数据转发到eth0或者ppp0的其中一个时,就可以正常的上网;

但是我想根据不同的端口,将eth1上的指定端口的数据导到ppp0 上去,如https(443端口),其他的数据走eth0, 我利用的是 标记 + 策略路由的方式

这时就出现问题了, 
                
    &

wjlkoorey2582014-01-05 21:42:04

loler_zuan:想咨询一个问题。
我使用layer7对数据包标记,如果是http协议使用路由表2,发送到eth1,然后对eth1出去的报文做snat到IP2。
然而每次使用wireshark在eth1抓包,http协议的src地址都是默认的IP1。
我改用80端口做标记,也是使用路由表2,其他不变,就可以成功的nat到IP2。这是什么情况啊?

我看了上面的原理,感觉我的设置没有什么问题啊?

不好意思,前段时间刚出差回来,有点忙,没能及时回复你。
对于您的问题,我实在没看明白,还希望您能提供进一步的描述,比如使用场景、环境、目的,然后再来描述您的问题,不然光是你上面提到的IP1,eth1,路由表xxx的,就已经让人很眩晕了

回复 | 举报
评论热议
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页