• Freebsd+ipfw2+Apache解决fin_wait_2太多问题
    时间:2008-12-06   作者:regshot   出处:互联网

    Freebsd+ipfw2+Apache有时会有一些问题: 当系统运行一段时间后,比如几天或者几周后,用命令netstat -an会看到许多处于fin_wait_2状态的死连接,这些连接随着时间增长会越来越多,而且似乎无法消除,无论用ipfw进行防火墙规则的重新设定 (比如ipfw flush一下),或者用ifconfig对网卡进行down或者up都无法做到消除这些连接,(不知道有没有清除tcp状态表的工具),所以只有重新启 动系统才行.而如果系统不设置动态防火墙rule,则不会有此现象.

     我在2005年的时候在ivan的一台FreeBSD 64位机器上碰到过这个情况,当时是非常头大的,虽然不影响使用,但总是感觉不爽, 从google上找到一些东西,但都没有说清楚,其中有一篇文章,最后找到freebsd ipfw的专家Luigi Rizzo写在一个maillist上面的留言,我看了这个留言后才对这个问题有稍微清楚的认识:

    By Luigi
    Rizzo: http://lists.freebsd.org/pipermail/freebsd-ipfw/2003-May/000206.html

    i imagine the following happens:
    + the client does not properly close the connection;
    + when a keepalive is sent (every 5 minutes), the the server's TCP responds (thus refreshing the rule), and the TCP timeout is reset so it stays in the FIN_WAIT[2] state for another cycle, whereas the client does not bother to send back a RST (which would cause the timeout for the dynamic rule go down to very low values).

    This would explain why the phenomenon is relatively rare (500 entries in 5 days).

    Maybe i should change the logic in the dynamic rules so that further keepalives are not sent unless a reply has been received from both sides.

    ---------------------------------

     当时在那台机器上产生这些连接的基本上都是Apache服务,它作为一个server端,能够进入fin_wait_2状态,只能是因为它发出了 active close,即主动结束tcp连接. 这在网页传输过程中应该很正常的,因为数据传完了,它必须要告诉客户端:我传完了,我要结束连接,它先向client发个fin,然后进入fin_wait_1状态,等待client发来ack。一旦接受到ack,马上进入 fin_wait_2状态,等待client也发个fin过来,然后ack,然后进入time_wait状态等待2个MSL,然后消失。这种是正常的结 束。但是假设server进入fin_wait_2状态后client端没有进行graceful close,即它根本不向服务端发fin,或者client发的fin被某种通道上的东西挡住了(或者丢失了),那么server端就会以 fin_wait_2状态等待下去,等待的时间长短由tcp的一些参数 决定,不同平台不同,超过这个时间连接就会自动消亡,不会一直赖着不走的. 但是当系统里面加载了ipfw动态规则就不一样了 ipfw默认对创建dynamic rules(动态规则)会发keepalive packets,即它会保持这个连接!!在ipfw的man说明里面有这么一段:

    net.inet.ip.fw.dyn_keepalive: 1

    Enables generation of keepalive packets for keep-state rules on TCP sessions. A keepalive is generated to both sides of the connection every 5 seconds for the last 20 seconds of the lifetime of the rule.

    也就是说,ipfw默认在动态规则的生存期(lifetime)的最后20秒里面,每隔5秒会给Client,Server分别发送keepalive信号.

    我们来看看FreeBSD系统的默认lifetime,摘自ipfw's man:

    net.inet.ip.fw.dyn_ack_lifetime: 300 //????? Why this? 注1
    net.inet.ip.fw.dyn_syn_lifetime: 20
    net.inet.ip.fw.dyn_fin_lifetime: 1 // ?? Not this ??
    net.inet.ip.fw.dyn_rst_lifetime: 1
    net.inet.ip.fw.dyn_udp_lifetime: 5
    net.inet.ip.fw.dyn_short_lifetime: 30 //but not always the
    same on all system

    这个就是问题的根本了,对于fin_wait_2,根据参数推测,会等net.inet.ip.fw.dyn_ack_lifetime: 300 秒左右(也就是上文说的5minutes)<-见注1 而它在最后20秒又自动给client,server发keepalive信号,server的Tcp回应了,timeout被重置,又会等300 秒,而client端对这个"无故发来"的keepalive信号不理会,因为它认为上次连接已经结束,它没有义务对这个信号回应(极有可能是装了防火墙 了), 哪怕它发个RST也好,这个连接也能结束,但是它没有发, 于是Server端就在那里一直傻等.......就像谈恋爱分手后一方对另一方的感觉, 可见,明确通知对方"我不爱你了" 有多么重要阿!!!

    (注1:这个到底要等多少秒,我没有查到结果,可能要看源代码才能看到是哪个rule的lifetime在起作用. 根据apache网站的一篇文章:http://httpd.apache.org/docs/1.3/misc/fin_wait_2.html,里面说到:在标准的RFC里面是没有FIN_WAIT_2 的timeout设定的,所以造成某些client如果不发fin的话,一些老的OS的server端就永远不退出,因为它没有这个timeout概念. FreeBSD从2.0后已经加了这个fin_wait_2 timeout,所以应该没有事,问题就是ipfw2搀和进来了,它似乎overwrite了系统的timeout(或者小于系统的), 所以造成上面的情况,上面我引用的Rizzo的帖子后面还有一贴:http://lists.freebsd.org/pipermail/freebsd-ipfw/2003-May/000207.html ,这是Gregory Neil Shapiro对Rizzo 的话的评论,其中有一个疑问和我的一样:"But wouldn't a dyn_fin_lifetime of 1 mean it wouldn't reach 5 minutes?" ,看来ipfw2在这里采用的并不是字面上的意思,它lifetime是指上一个包的类型,上一个包过来后这条动态规则还要存在相应的延时, 具体到这里的dyn_ack_lifetime指的是上一次client发来的用于结束fin_wait_1状态的是ACK 包,而不是将要接收的Fin包,所以这个规则离消亡又要等一个300秒(dyn_ack_lifetime)...)

     

    The solution:
    那么解决问题的方案之一就是不要发keepalive

    Found TOO many fin_wait_2 connections ? Solution is :
    ipfw diable dyn_keepalive

    或者(or:)

    sysctl net.inet.ip.fw.dyn_keepalive=0

    注意,要等一会,大概在5分钟以后,你会慢慢地看到netstat -an |grep
    FIN_WAIT 消下去.

    And wait for 5min or more (depending on your settings of *_lifetime), the [netstat -an | grep FIN_WAIT ] output result would slowly goes down...
    (请原谅我一段英文,一段中文的,老毛病了,写regshot多语言版的时候养成的,便于交流)

    Free BSD 专家:Luigi Rizzo的最后一段话是最好的解决办法,我一直没有用新
    版本的freebsd和ipfw,不知道现在改进没有.

    网友留言/评论

    我要留言/评论