初探BSD的ipfw防火墙

使用Mac OS X已经半年多了,很炫是真的,很悬也不假!Mac有太多太多的特性值得让你花大量的时间仔细琢磨,用起来让你自豪的找不着北,最终你会深信自己是一个很时尚的人...我承认自己很土,也不是什么果粉,更不是什么达人,因此我除了使用Mac最基本的功能外,几乎没有去深究其它。突然间,我发现Mac居然是一个BSD,虽然这个事实我早就知道,但是也只有在我在命令行敲入man ipfw的时候,我才真的想深究一下这个手边的Unix,于是乎,不再觉得Unix很遥远,它居然就在手边,于是乎,我的MacBook也就成了我的Unix试验机了。
伟大的Unix初看很庞大,然而用起来还真的比Linux简单,Cisco IOS这样的网络操作系统内核也是基于BSD的,在BSD之上出现了两个ios,一个是Cisco的IOS,一个是Apple的iOS,足见其伟大!

一.防火墙软件学习历程

关于防火墙,最开始学习的是华为VRP系统的命令,那是2004年,听讲师说和Cisco命令差不多,当时我们都一准认为有抄袭的内幕,于是自学了Cisco,发现还真的很像,几乎是一模一样...2006年参加工作第一次看到有人在Linux终端前敲命令,我开口就给同事说,我们配路由器配防火墙就是那样的...太丢人了,其实那是Linux而已,于是就自学了Linux,学习iptables是08年的事了。
现在想想,但凡命令行,差别都不大,如果你在windows命令行输入netsh,结果也差不多。因此我不认为那是抄袭了,充其量是拿来主义,共同点的底层一定是相同的组件,这种组件其实就是BSD内核。最终如果你比较一下各种包过滤机制,就会发现,Cisco,H3C,Windows的都差不多,只有Linux的Netfilter是个例外。Mac OS基于BSD,因此只要懂得Cisco,Mac OS上的命令行防火墙命令也就很容易驾驭了。

二.ipfw简介以及与Netfilter的异同

想要简介,最好就是man一下ipfw,这里就不列举了。最关键的是一个章节,那就是“PACKET FLOW”这一章节。只有你知道数据包在协议栈的哪些地方被过滤,你才能设计好良好的规则集。过滤点如下图所示:

相比于Netfilter的5个HOOK点,这幅图看起来清爽多了,一个包最多经过4个HOOK点,而Netfilter则复杂的多,按照路由前,路由后以及路由结果三个要素将HOOK点划分为5个,任何点的规则都需要你仔细考虑路由的结果以及是否可以过滤,ip_conntrack机制以及由于conntrack机制内置的动态规则加重了事情的复杂度。
现摘录一段ipfw命令的man手册内容:
Also note that each packet is always checked against the complete ruleset, irrespective of the place where the check occurs, or the source of the packet. If a rule contains some match patterns or actions which are not valid for the place of invocation (e.g. trying to match a MAC header within ip_input() ), the match pattern will not match, but a not operator in front of such patterns will cause the pattern to always match on those packets. It is thus the responsibility of the programmer, if necessary, to write a suit-able ruleset to differentiate among the possible places.
这段话说了两个意思,第一个意思就是ruleset是全局的,不区分HOOK点的,每一个HOOK点都要遍历所有的ruleset中的rule;另一个意思是说需要配置者来完成一切。我们看一个简单的规则,禁掉icmp:
ipfw add deny icmp from any to any
是不是和Cisco的很像,然而却和iptables的一点都不像。

三.ipfw的动态规则

Netfilter有ip_conntrack机制可以追踪每一个流。这个ip_conntrack机制让人欢喜让人愁,于是可以在PREROUTING的raw表上配置notrack。ipfw就不一样了,它可以在任意地方针对任意流进行track,这就是其state机制,ipfw通过keep-state来追踪一个流,并且建立针对该包反向包的动态规则,通过check-state来匹配keep-state建立的动态规则,ipfw的state可以在任意匹配的地方被keep,所谓的keep就是建立一条动态规则,其动作就是keep-state的动作,这个比较类似Netfilter的ip_conntrack和state match的联动机制。以下是一个man手册中的实例,我在前面加上了注释:
对每一个包check所有的动态创建的rule:
ipfw add check-state
对本地子网始发的TCP流量放行且保持连接,创建动态rule:
ipfw add allow tcp from my-subnet to any setup keep-state
禁用其它地方始发的TCP连接:
ipfw add deny tcp from any to any
上述实例的分析:
内网始发一个TCP,由于没有任何动态rule,因此直接匹配到第二个rule并且保持了这个conntrack创建了反向动态rule,该连接的返回包到达这个Box时,由于匹配到了动态rule,因此通过。如果是外网始发的TCP到达内网,将直接匹配到第三条rule。如果使用iptables的话,则如下配置:
iptables -t raw -A PREROUTING -p tcp -s ! my-subbet -j NOTRACK
iptables -A FORWARD -p tcp -m state --state ESTABLISHED -j ACCEPT
iptables -A FORWARD -p tcp -s my-subnet -j ACCEPT
iptables -A FORWARD -p tcp -j DROP

使用ipfw,就不用非要在特定的HOOK点来进行track了,它可以随时进行track,随时进行check。ipfw可以将track和target结合在一条rule中,可以针对特定的filter rule进行track,而Netfilte的iptables却必须将这些关联解除。

四.ipfw和natd

使用iptables,我们可以使用nat表来配置NAT,然而使用ipfw却不能做到这一点,在BSD中,nat只是一个target,存在用户态的natd进程来进行nat,也可以使用ipnat来完成,不管哪种方式,都使用了divert这个动作,所谓divert,其实就是其字面含义,将控制转发到其它的逻辑,对于nat,很常见的就是将控制转发到natd进程。我在Mac OS上,配置以下的nat:
ipfw add divert natd all from any to any via en1
natd -interface en1

其中divert natd将匹配的数据包“路由”到natd进程。至于natd进程,也可以man一下,有下列关键的叙述:
The natd normally runs in the background as a daemon. It is passed raw IP packets as they travel into and out of the machine, and will possibly change these before re-injecting them back into the IP packet stream.
It changes all packets destined for another host so that their source IP number is that of the current machine. For each packet changed in this manner,an internal table entry is created to record this fact. The source port number is also changed to indicate the table entry applying to the packet. Packets that are received with a target IP of the current host are checked against this internal table. If an entry is found, it is used to determine the correct target IP number and port to place in the packet.

可见natd本身就是保持状态的,不需要keep-state和check-state,然而如果你想小实验一把keep-state和check-state,那么你可能会注意到ipfw有一via/recv/xmit系列match,上例中是使用via,如果使用xmit的话,那么只能适用于out方向的数据包,man ipfw中有下述:
The recv interface can be tested on either incoming or outgoing packets, while the xmit interface can only be tested on outgoing packets. So out is required (and in is invalid) whenever xmit is used.
注意,in/out匹配的是数据包方向,而via/recv/xmit匹配的是接口。xmit时已经确定了接口,对于in方向的数据包,由于还没有路由,所以不能确定出口设备,进而不能用于xmit。如果我们创建以下的rule:
ipfw add divert natd all from any to any xmit en1
natd -interface en1

如此一来,对于返回的数据包将全部放过,虽然natd是保持状态的,然而并没有rule将数据包divert到natd,所以仅仅为了将数据包divert到natd,需要使用keep-state:
ipfw add check-state
ipfw add divert natd all from any to any xmit en1 keep-state

这样就可以对付返回的数据包了。然而问题是,内核保持的state状态可能会和natd中的状态相冲突,因此对于有状态的协议比如TCP,此方式工作的很好,对于UDP和ICMP会有不稳定的问题。

五.最后再来点比较

ipfw和Cisco一样,毕竟都是BSD的缘故,都是将数据包分为了in和out两个方向,并且都可以认为应用于接口,虽然ipfw在内核中保存了一份全局的ruleset。不管怎样,Netfilter绝不是这样,另外,Netfilter的iptables区分了INPUT和FORWARD,所以FORWARD的数据包无需经过INPUT链,而ipfw则是全局的rule。ipfw和natd可以在任意位置相互插入,任意动作包括divert natd都可以和state联动,实现了动作的stateful,也就是说,ipfw的机制可以记住一个流的头包或者任意包的第一次匹配的动作,而Netfilter的conntrack和rule则是分离的,conntrack仅仅追踪了连接,filter动作需要自己去匹配,没有filter的动态rule。最后,ipfw保存一份ruleset,在man ipfw所示的图的一路最多4个HOOK点重复check。

你可能感兴趣的:(操作系统,网络)