简介

TCP状态不一致是一种常见故障。双方的机器上列出连接,其中一方连接存在,另一方则没有任何连接。如果连接存在的一方有数据正在发送,则存在两种可能性。一种是对方收到数据报文后发现这个报文没有socket对应,于是返回一个RST,导致连接存在的一方连接直接消失。这种情况下问题很难察觉。另一种是对方(或者中间方)收到报文后直接丢弃。发送方经过一定次数(tcp_retries2,一般来说就是15分钟左右)的重试后,认为连接已死,连接会自动消失。如果tcp连接打开了keep-alive,经过7200s(具体值看socket设定),即等于有数据发送。后同。

一般来说,有三种常见理由:

  • RST报文丢失。在RST报文丢失时,发送方连接直接消失,接收方由于没有任何消息,因此连接始终存在。
  • FIN报文丢失。FIN报文丢失和RST报文丢失情况类似,但是发送方会进入FIN_1状态,经过一定次数重发(tcp_orphan_retries)无回应后连接消失。
  • 内核错误。

排障时,应当首先考虑RST报文和FIN报文丢失的情况。

排障

首先应排除三种常见场景:

场景一:接收缓冲区满。

当接收缓冲区满时,FIN报文会直接丢失。这常见于接收方处理上下文堵死导致不再处理新数据的情况。特征是发送方可以观察到一定量的FIN_1状态链接(常见于接收方不处理fin报文的情形下),接收方缓冲区满,可作为特异性标识。

确证方法:在接受方使用ss观察接收缓冲区。如果接收缓冲区极大而且基本不下降,配合发送方可见FIN_1状态,可以断定此场景。

场景二:RST报文丢失。

当中间有IDS之类(F5/SDN/firewall)的网络设备时,RST报文可能丢失。特征是发送方socket直接消失,接收方socket无任何异常。检查可见netstat -s中reset的发出和接收状况不一致,但是无法作为确证标准。

确证方法:双方抓RST包,无法对齐。

场景三:FIN报文丢失。

情况和场景二类似,网络中有IDS类设备。但是丢失的报文是FIN。这导致发送方可见FIN_1状态,接收方缓冲区没有异常。

确证方法:双方抓FIN包,无法对齐。

内核错误导致的TCP状态不一致很少见,建议升级到最新内核后重测。如无法复现可以合理推测这种可能性,但是无法确证。确证需要systemtap。

PS:FIN_1状态同时也受到tcp_max_orphans的限制。如果tcp_max_orphans被设定为0以清理TIME_WAIT状态的情况下,可能导致FIN_1状态很难察觉。可考虑恢复tcp_max_orphans或者干脆直接抓包诊断。