ipfw+natd使用举例及注意事项
出处: http://blog.chinaunix.net/u1/38866/showart_339691.html ipfw+natd使用举例及注意事项
很多朋友使用FreeBSD的ipfw+natd来实现网络地址转换,但在使用的时候,可能或多或少都遇到过一些问题。本人就自己使用过程中遇到的问题作一个总结,希望能给新手们一些帮助。以下内容都是本人自己的理解,不一定正确,还请各位多多指教。

关于本文的说明:
1、本文以一些例子来说明ipfw+natd的实现,但由于手册上关于ipfw和natd的使用已经非常清楚,所以本文只给出相应的ipfw规则语句,不再讨论关于ipfw和natd的使用。
2、为什么不使用状态保持(keep-state):我经过测试,如果使用keep-state规则,写起来的确简单,但是连接数一大,就会出现效率 问题,要经常刷新状态链才行,否则会出现阻塞的现象。我用我的笔记本R40e做过实验,网卡用本机带的bge0和一个外3com509B的笔记本网卡,带 60台机器,同时上网。如果用keep-state,则五分钟内基本上都不通,而不用keep-state的时候,则跑一个星期也没事。但是不可否认,不 用keep-state的语句特别难写。我感觉这也应该是正常的,否则,在手册上就不会出现“IPFW 是为满足专业用户,以及掌握先进技术的电脑爱好者们对于高级的包选择需求而设计的。”之类的话了。
3、下文中,fxp0是内网网卡,地址是192.168.0.x;fxp1是外网网卡,地址是202.102.152.x(山东网通的,仅作为举例用,如果您觉得侵犯您的权利,请在后面跟贴说明,我再换一个)。
4、因为端口号在本文中并不是很重要,所以在大多数场合,不再写端口号。

任务一:我只想让一台机器(192.168.0.22)上网。
这个看起来可能很简单,但是做起来的时候,你就会发现,那并不是一件很容易的事!
很可能有人这样写,感觉应该可以通了,并且很完美。(实际上,我以前就是这么做的)
ipfw add 10 divert natd ip from 192.168.0.128/25 to any
ipfw add 20 allow ip from me to any
ipfw add 30 allow ip from any to any via fxp1
ipfw add 40 allow ip from any to any via lo0
ipfw add 100 deny ip from any to any
觉得完美的理由如下:
1、第一句(10),是先让这些机器可以NAT上网。
2、第二句(20),要允许自己访问,否则本机上网的时候就不通了。
3、30,40两句,是要允许局域网可以用,否则的话,你就不能通过局域网管理你的机器了。其中fxp1代表局域网网卡。lo0是本地回路,一般情况可以不要,但是有些应用是要用到它的。
乍一看,感觉也没有什么问题,但是,如果你把这个规则写到机器的时候,就会发现它根本不工作。
这个时候,你既不要抱怨机器有问题,也不要抱怨网络有问题,更不要抱怨FreeBSD不好用。仔细想一数
据包的走向,你就会发现你在犯错误!

先让我们来看一下数据到底是怎么走的,在这当中,都经过了哪些地方,IP地址(因为我们最关心的是NAT,也就是网络地址转换)到底是怎么改变的。现 在,以一个主机192.168.0.77联接到www.chinaunix.com [60.28.166.84]为例,来看一下数据包的整个旅程和IP的改变情况。

(一)首先,192.168.0.77主机发起一个包,目标是60.28.166.84,我简单写作:

192.168.0.77>>60.28.166.84,这个过程,和我们的NAT服务器没有关系,也不会进入ipfw。
(二)然后这个数据包到了第一站,进入我们的NAT服务器,记住,这个是从内网网卡,也就是fxp0进入的,那么,她必然要进入到防火墙接受申查,以便好 进入到我们的服务器内部,因为只有进入到我们服务器内部,FreeBSD能才对她进行处理。所以,这儿必须设置一条对她的规则,我们可以简单地设置一条:
ipfw add 10 allow ip from any to any in via fxp0
意思就是,允许内网上的所有数据进入。其实就相当于一个人到了机场,机场门口的保安人员对本地人非常放心,所以统统放行。
(三)数据进入到服务器内部了,现在FreeBSD可以对她进行打扮了,可以把她打扮地漂亮些,并且发上护照,毕竟是快要出国的人了嘛!现在进来了,又要 和防火墙打交道了,就象进了飞机场,还要接受检查人员的检查一样。这次检查人员来检查她的机票,看她是到哪儿去的,然后把她领到相应的候机处。所以这个规 则就是:
ipfw add 20 divert natd ip from 192.168.0.77 to any out via fxp1
检查员检查的结果是:她从192.168.0.77来,要通过fxp1这架航班去60.28.166.84,所以,检查员就divert她到natd 办公室,而natd对她进行了重新打扮,以便看上去和外国人差不多。natd做的工作,就是把数据包的源IP地址和端口改写为服务器自己的外网IP (fxp1上的)地址和端口,一般来说,这是个合法的IP地址,以便在因特网上传播。
现在,这个数据包有了一个新的合法IP地址和端口,202.102.152.X>>60.28.166.84,可以在因特网上路由了。
(四)检查完了,可以上飞机了,飞机的检票员还要检查机票。
ipfw add 30 allow ip from me to any out via fxp1
(五)飞机起飞了,到了外国,这期间内,不是我们服务器所负责的范围,他所能做的,就只有静静地等待,但是,她临走时的留的她在本国的住址和电话(192.168.0.77和端口号)还要保留一段时间,否则,她回来的时候,就找不到自己的家了。
(六)经过一段时间,她回来了。这个时候,她是这种形式了:60.28.166.84>>202.102.152.x。下飞机了,要经过检查才能进入大厅。
ipfw add 40 allow ip from any to me in via fxp1
(七)她要先回到natd的办公室,根据取回她的地址和电话。所以就又要和防火墙打交道了:
ipfw add 50 divert natd ip from any to me in via fxp1
于是,她又变了个样,成为本国人了。60.28.166.84>>192.168.0.77。
(八)但是,回来后还要隔离一段时间才能出natd的办公室,因为怕来自外国的病毒,所以还要检查身体:
ipfw add 60 allow ip from any to any in via fxp1
(九)回家时,还要打个的士,因为家里人没空来接她:
ipfw add 70 allow ip from any to any via lo0
(十)要出飞机场了,机场门口的保安人员这次更马虎,因为不管是外国人还要本国人,一样欢迎。
ipfw add 80 allow ip from any to any out via fxp0
(十一)终于,她可以回家休息了。休息完后,再准备下个旅程。


好了,故事讲完了,该总结一下了:
一个包要做好几次检查才行:
(1)进入防火墙。(2)natd给改包头。(其实这地方还有个防火墙间的传输)(3)出口检查。(4)回来检查。(5)natd回来的包。(6)修改后的包检查。(7)防火墙内网卡间传输。(8)从防火墙到内网的检查。

现在,把所有的事情都归结到一起,我们得到:

ipfw add 10 allow ip from any to any in via fxp0
ipfw add 20 divert natd ip from 192.168.0.77 to any out via fxp1
ipfw add 30 allow ip from me to any out via fxp1
ipfw add 40 allow ip from any to me in via fxp1
ipfw add 50 divert natd ip from any to me in via fxp1
ipfw add 60 allow ip from any to any in via fxp1
ipfw add 70 allow ip from any to any via lo0
ipfw add 80 allow ip from any to any out via fxp0

我们还可以对上面的语句进一步简化,在更多的时候,我们可以把出和入两个方向的规则合写在一起,比如:10和80,30、60和40,则可以写作:
ipfw add 10 allow ip from any to any via fxp0
ipfw add 20 divert natd ip from 192.168.0.77 to any out via fxp1
ipfw add 30 divert natd ip from any to any in via fxp1
ipfw add 40 allow ip from any to any via fxp1
ipfw add 50 allow ip from any to any via lo0
ipfw add 60 deny ip from any to any

说明:
1、注意40语句,在大多数的时候,会把他忘掉。因为在回来的时候并且能过natd之后,包变成了60.28.166.84>> 192.168.0.77,并且方向是in via fxp1,如果没有这句的话,这种形式的包就不好知道去向了,最后被60截获,被禁止掉了。这好象有点问题,两个方向上包检查缺少对称性。但是我跟踪了几 次包,确实就是这样。
这样还带来一个问题,就是相当于本机所有端口都对外开放了。所以,更多的时候,还是分开写更好些,把出和入的包分开写,在入的地址上再加上本地LAN的地址。如:
ipfw add 40 allow ip from any to any out via fxp1
ipfw add 41 allow ip from any to 192.168.0.0/24 in via fxp1
2、好象20和30语句也可以合并,但是我们要注意,当包从外面回来的时候,她的形式是:60.28.166.84>> 202.102.152.x,和内部地址192.168.0.77是没有任何关系的。还要注意,我这地方写的是any to any ,而上面(七)中写的是any to me,或者可以干脆写:any to 202.102.152.x。
3、最后一句,就是禁止其他的所有机器发出的包。
4、其实,在本例中,真正起限制作用的,只有第二句,你可以在第二句上扩展范围。例如:
ipfw add 20 divert natd ip from 192.168.0.77,192.168.0.88,192.168.0.100-120,192.168.0.128/25 to any out via fxp1
地址范围用短横线表示,子网用IP/掩码位数表示,地址间用逗号隔开。


任务二:我想限制所有机器只能上一个网站。

这个任务和上个任务看起来好象应该差不多,但是有个问题,就是要禁止掉所有其他网站的时候,把DNS也禁止掉了。所以,要先把DNS开放开来。(下面两个地址是山东省的DNS)

ipfw add 10 allow ip from any to any via fxp0
ipfw add 20 allow ip from any to any via lo0
ipfw add 30 divert natd ip from any to 202.102.152.3,202.102.154.3 53 out via fxp1
ipfw add 40 divert natd ip from any to 60.28.166.84 out via fxp1
ipfw add 50 divert natd ip from any to any in via fxp1
ipfw add 60 allow ip from any to any via fxp1
ipfw add 70 deny ip from any to any

说明:
1、这个规则还是利用出包限制来实现访问控制,但是要注意一点,要考虑双向的包,但是50规则,已经把所有回来的包处理,所以就不用专门写语句来处理这些回来的包了。
2、DNS正常情况是用UDP协议,所以你可以把30规则中的ip换成udp,但是,它有时也走tcp协议,所以,有时候,可能用ip比udp保险。


新手常见Q&A:

Q1、在设置ipfw的时候,我总是要忘掉一些规则,有什么好的办法吗?  
A1、在考虑规则的时候,在脑子中要有一个原则:关卡,变身要规则;双向路径分开写。意思就是:每过一个关卡(网卡介面),每次数据包的地址和端口改变, 就要有一条规则和它相适应;在写规则的时候,一定要考虑出去和回来的路径,并且尽量不要合起来写,是in的就是in,是out的就是out,起初的时候, 最好不要写在一块,出现问题时好分析。最后都写完,并且可以执行的时候,再总起来看一下,可以把能合的规则合在一起。
强烈建议把所有的规则都分开写,因为这样出现总是时好排查,并且,分地越细,你能干的事就越多。

Q2、该考虑的全考虑了,还是不行,还有什么方法吗?
A2、还可以看一下顺序,由于ipfw是按照规则号从小到大的顺序执行的,如果匹配了一个,那么它就略过下面所有的规则。比如,如果你allow ip from any to any放在最前面的话,那么后面的所有规则都不会被检查。

Q3、我想检查一下我的规则在哪儿出错,怎么样做才好呢?
A3、ipfw消息可以发送到syslogd去。
a、修改/etc/syslog.conf,在后面加上两行:
!ipfw
*.*   /var/log/ipfw.log
b、touch /var/log/ipfw.log
c、在你想查看的规则上面加上log关键字。比如
ipfw add 40 divert natd log ip from any to 60.28.166.84 out via fxp1
这样,你便可以在/var/log/ipfw.log里找到关于这条规则的信息,它详细地记录了所有通过这条规则的包。但是要注意,它不是记录所有的包,只记录有限的包,记录的数量靠参数logamount,一般默认值是30。
但是,更多的时候,你也许会把这个log放到拒绝的语句中(logamount设置为500了)。 
ipfw add 70 deny log logamount 500 ip from any to any

Q4、还有什么可以说的吗?
A4、对不起,我也是新手,其它的我也不会了。如果想知道详情,去问CU上的老大们吧。