暗影中的麻烦制造者
某金融行业重要Web业务服务器发生多次 CPU使用率接近 100%。对系统性能分析发现,CPU异常时段业务网卡上进入流量有明显异常,约为正常情况的 5 倍。一开始,看到大量的 FIN包,不禁令人联想到了安全事件,有人开始怀疑这是一次攻击,后来,查出在不久前刚给web 服务器软件iplanet打过补丁,人们怀疑的目光又投向了iplanet……正在大家漫无目标地寻找着问题根源的时候,故障又重现了……
其中F5采用单臂连接,即进出均通过一个口,而对外的 Virtual IP为 73,后台 Web服务器地址为70,71,72。因此利用 InfiniStream监控F5的单臂连接,通过区分访问 Vip 73和访问后台70,71,72的流量来区分F5 内侧外侧的流量。
问题分析:
- 异常发生时,分别观察服务器端和出口路由器端的流量,结果发现出口路由器上并无流量增长。而服务器端则捕获到大量 Window=0的FIN与ACK包。因此怀疑异常的流量产生在内部,特别是F5前后。因此通过InfiniStream 监控 F5单臂连接的出入流量。
- 异常再次发生时,观察到的时间点是 15:44分,抽取当时的原始数据包进行分析,并且逐步回溯,发现异常的Window=0的FIN+ACK包出现在14:24 左右。因此重点对14:24 前后的数据包进行分析。
- 对F5外侧流量的分析:
如图,客户端来自互联网,访问 F5的虚地址73(为保护用户,详细地址信息作了技术处理),在这一端的通讯中,F5对客户端维持了 4.4k左右的TCP Window大小。TCP Window在每条 flow中略有小幅变化,但是单条flow中window size几乎没变化。
- 对F5内侧流量的分析:
如图,在 F5将这个客户端代理到内侧后,TCP Window 的行为发生了很大变化,服务器的 window size稳定在32768,但F5的 window size先是从6k多一直在变小直到 0,然后过若干秒才逐渐恢复到12k左右,然后再进入下降通道。在 window size=0的时候,服务器按照默认TCP机制,分别相隔1秒、2秒和3秒发出 zero window probe包,以探测对方 window 是否恢复。从这一端来看,F5 的处理性能较为紧张。
- Flow的结束情况
再进一步查看大多数flow的结束情况:
可以看到大多数flow都以 RST结束,这符合面向互联网的应用的特征。
然而我们也观察到一些以FIN四次握手拆链的flow:
图中的Flow由服务器发起 FIN请求,然后F5代理客户端 ACK了 FIN后发出第二个FIN,服务器ACK后,该条flow正常结束。
对比这条flow在F5外侧的情况也是一样。
- 异常flow分析
这是14:24左右找到的一条异常 flow,图中关键的几个包都用颜色框标出并编号
- 第一个,是来自 Web服务器的FIN包
- 第二个,是F5作为客户端 ACK了第一个FIN包,注意此时window size正好降到 0
- 第三个,F5继续作为客户端发送了第二个 FIN给服务器
- 第四个,服务器很快 ACK了第二个FIN包,拆链所需的四次握手全部完成
- 此时F5不知为何又发了一个 FIN包给服务器,服务器只好再次ACK,接下来便反复发生这个动作,陷入死循环
这里有两个值得关注的细节,一是 F5 在ACK第一个 FIN包时 TCP Window正好降为 0,因此这个 ACK包还起到了zero window通告的作用。二是F5在拆链四次握手完成后再次发送了 FIN包,这时的服务器并未完全关闭连接,而是处于Close_wait2状态,该状态会持续2 个MSL 时间,大约1分多钟,用以应对最后一个 ACK对方未收到而重发FIN 的情况,因此这里服务器以为F5没收到前面那个ACK,于是继续ACK了这个异常的 FIN包,并最终陷入循环。
而找到这条flow在F5 外侧的情况进行对比发现,与内侧有很大区别:
在F5外侧,客户端发出了 FIN后,F5仍然向客户端发送数据,客户端于是直接 RST了连接,之后便再无数据包发出。
因此,异常位置已被确认,是 F5 与服务器之间产生了异常的通讯。
- 异常的持续与恶化
在14:24这条flow产生了这种异常现象后,FIN<>ACK 循环一直在持续,直到15:44被管理人员发现,此时该循环产生的流量相对刚开始已经呈几何级数增长,经统计,在大约1.8 秒的时间内,共产生了约37万个FIN<>ACK 包,形成了类似风暴的规模,也直接导致了服务器流量大大增长,当时统计到的服务器流量约120Mb/s,其中约有100Mb/s是由这场风暴贡献的,以及CPU 占用率几乎100%,如图:
此时在F5的外侧已经找不到这台客户端与 73的任何通讯了,完全是 F5内侧在产生流量。
- 对正常flow的观察
对比正常结束的flows,发现无一例外的是在整个FIN 握手过程中TCP Window未达到 0,因此初步怀疑在拆链时TCP window降到0可能是异常的起因。
- 分析小结
- F5 在整个路径中起到 proxy的作用,在内外两侧分别维护不同的TCP窗口大小
- 异常发生时,F5 内外侧的行为不一致
- 异常发生时,F5 内侧TCP window正好降为0,发了一个 zero window通告后紧接着发了FIN 包
- 在服务器正常响应后,F5又发了个FIN包,使双方陷入 FIN/ACK死循环
- 此时F5外侧连接已经结束,没有额外流量产生
- 该现象持续80分钟后,FIN/ACK的数量级猛增,形成风暴并直接影响服务器性能。
- 问题根源
既然将问题定位到F5上了,经过查询,在F5的知识库中找到了这个问题,原来这是 F5的一个已知bug,并且具有一定的普遍性,据调查有不少用户遇到过此类情况。
F5的这个bug的触发条件是,当 F5发出一个TCP zero window 通告后紧跟着一个客户端 FIN包,则会引发 FIN/ACK风暴。这与我们分析的现象完全一致。
F5已经针对这个bug放出了补丁。





Recent Comments