爱悠闲 > 实现透明防火墙的必备知识-Bridge Filter半景

实现透明防火墙的必备知识-Bridge Filter半景

标签: 防火墙,filter,hook,linux内核,框架,output  |  作者: dog250 相关  |  发布日期 : 2012-03-13  |  热度 : 955°
Netfilter是一个可以高度定制协议栈的优良框架,早就已经被包含于Linux内核了,关于它的设计,可以在其官方网站上找到N多可以一看的东西。被讨论最多的就是HOOK点的位置的设计(人家老外关注的核心的设计,而不是如何去实现它,以及实现了之后的源码分析),最好还是看一下这些讨论。
    若想实现一个透明的防火墙,那么对Netfilter几本框架的理解是少不了的,除此之外还会有一些问题,比如下面的几个问题:
1.为何OUTPUT设计在路由之后?
2.为何FORWARD设计在路由之后?
3.桥接层的Netfilter钩子函数为何有那么多的Stolen?

对于问题1,我觉得不难回答,因为Linux的路由模块直接区分了本地和非本地的概念,且路由结果中包含了目标设备的很多信息,包括网卡信息,发送回调函数等,并且此时已经选择了源IP地址(对于TCP,UDP而言,非tso情形下,因为在进入IP层之前要计算校验码,需要一个伪头,而伪头中有一个源IP地址,所以在进入IP层之前就需要先路由一下),OUTPUT这个HOOK点需要的信息基本都在路由之后被确定,DNAT是该HOOK点上比较特殊的,因为它改变了目标地址,那么就需要被重新路由,当然对于DNAT之后的重新路由责任上由DNAT模块自己负责。回答了问题1,问题2基本就不用回答了,至于问题3,则比较复杂,因为这涉及到了Bridge Netfilter和IP Netfilter的联动问题。
    Bridge和IP在透明防火墙中是需要联动的,因为IP层可以进行更多的控制,比如更多的策略(string match)进行Filter,比如可以进行基于五元素的连接追踪等,虽然这些都可以在Bridge层完成,但是模块化以及KISS原则将Bridge从这些复杂的策略当中分离了,使它仅仅处理它应该处理的事,如果需要IP层的帮助,那么直接call即可,Netfilter机制使用NF_HOOK宏就可以让一个任意层的数据包被任意层的任意HOOK处理,并且每个HOOK点的钩子函数遍历处理还可以在任意点被中断(NF_STOP),然后在任意时间的任意其它地点重新在被中断的HOOK点的被中断的钩子函数处继续遍历下去,这是通过NF_HOOK_THRESH宏来完成的,另外任意的HOOK点钩子函数都可以将数据包偷走(NF_STOLEN),如下图所示:


正是这样的灵活性带来了复杂。Linux的Netfilter在Bridge层的执行使用了IP的Netfilter钩子,在使能了net.bridge.bridge-nf-call-iptables的情况下,情况更加复杂,进入桥的数据包需要经过IP层的PREROUTING和POSTROUTING这两个HOOK,而在IP-PREROUTING上可能被DNAT,而被DNAT后的数据包需要被IP层路由,因此就不能继续在Bridge层向前走了,此时必须进入本地IP层,被路由的结果可能是从同一个Bridge设备发出,也可能被从其它网卡发出,如果从同一个Bridge发出,那么就会进入Bridge的LOCAL-OUT,否则数据包将会被导入本地的IP层处理,由于在Bridge处理时已经经过IP-PREROUTING了,那么这次将直接绕过IP-PREROUTING...这一切到底是怎么发生的?文字描述可不容易,直接看代码之前我先展示一幅图,可能看过此图后,就没有必要再去看代码了:



该图展示了一个数据包如何经过Bridge的每一个HOOK点以及如何与IP层的HOOK点进行互动,非常复杂,这张图本应该是横向的,然而限于显示器的宽度,还是画成了纵向的,起码滚动条是几乎没有限制的,并且纵向的滚动条要比横向的滚动条好用(当然使用Apple的360度滚轮鼠标那是例外)。处理很复杂,然而从这幅图看来,有很有条理。如果要实现透明防火墙的话,理解这幅图是必须的。当然,如果看代码也是很不错的,不过初看代码总觉得有些凌乱,你很难抽出哪些是主要路径,哪些是次要路径,也很难有耐心先将Netfilter的基本框架熟透了之后再去看代码,基本上,Netfilter的核心框架就是:
1.几个HOOK点
2.每一个HOOK上按照优先级排序的钩子函数
3.NF_HOOK_THRESH宏
4.nf_hook_ops结构体
5.几个nf_XX_hook_priorities优先级枚举