有时候我们抓取网络包发现TCP RESET帧,我们想知道此时网络出了什么问题。仅看到TCP RESET帧不能说明网络出现问题,因为RESET帧发送的原因有很多,并不是所有的原因都是网络出问题导致的。事实上,RESET是个好东西,它可以用于关闭之前打开的连接。举个例子,我们的应用建立了很多短连接,但我们不想在服务端time wait
状态时继续保持连接,所以,客户端通过RESET重置连接。
三次握手
先说下tcp连接。当网络中的一个节点通过TCP协议向另一个节点通信,它们就会建立TCP连接。此时,客户端节点向服务端节点发送Synchronization(SYN)帧。该数据包中包含了建立连接和传送数据所需的所有信息,但这儿我们感兴趣的是端口信息,连接通常在客户端的源端口和服务端的目标端口之间发生。SYN帧中会包含发送者的源端口和节点想要连接到的目标端口。
下图就是一个SYN帧数据包,你可以看到TCP:Flags= .......S
,表示这是一个SYN帧。SrcPort是源端口,这是客户端用来建立连接的客户端端口。DstPort是目标端口,本例是445(Direct SMB端口)。服务端会监听该端口以便接收SYN数据包和后续通信。
接下来的两帧会完成连接的建立。第二个帧是ACK+SYN帧,服务端确认接收第一个SYN帧,并发送自己的SYN帧。这两个动作在同一个帧中发生。注意,此时源和目标端口与第一帧SYN中的源和目标端口是对换的。
最后一帧是客户端收到服务端的SYN后巷服务端发送的确认帧,此后,两节点之间的连接建立。
time wait状态
什么是time wait状态?为什么说它很重要?当TCP连接关闭(gracefully)时,主动关闭一端会向对端发送FIN帧。表示主动关闭端不再有数据发送。对端会发送ACK帧。当对端不再有数据发送,也会主动发送FIN帧给这一端,这一端也会向对端发送ACK帧。当两端都发送了FIN帧,并且都收到了ACK帧,此时,TCP连接会进入time wait状态。
默认情况下,连接会保持time wait状态4分钟。这保证了仍然在网络中的数据包可以使用该连接继续传输。
现在我们知道了如何建立和优雅关闭TCP连接,接下来让我们讨论一下如何/为什么我们会重置TCP连接。
resets
什么是reset?TCP reset表示立即关闭
TCP连接。这保证了之前连接分配的资源能够得以释放,并为系统所用。以下是一些发生TCP重置的场景。
SMB reset(客户端主动reset)
有的客户端与服务端建立TCP连接时发送两个SYN帧,分别使用不同的目标端口。服务端收到两个SYN帧后,分别对两个帧发送ACK+SYN。客户端收到ACK+SYN后选择一个发送ACK建立连接,另一个发送RESET关闭连接。
ACK+RESET(服务端主动reset)
客户端发送SYN帧,服务端由于某些原因无法与客户端建立连接,结果发送ACK+RESET帧。这些原因包括:
- 服务端没有监听客户端想要连接的端口;
- 服务端资源不足,不能分配连接所需要的资源等。
由于没有响应导致的TCP重置
假设我们已经经过三次握手建立了一个TCP连接。当一个网络数据包连续发送了六次都没有收到响应,此时发送端会主动重置TCP连接。重置前的重传次数是可以配置的,默认情况下是5。(默认情况下,建立连接时重传SYN帧的最大值是2,但也是可配的)。
这里有几个要点需要牢记,初学者很容易忽略并认为发生了TCP重置,而实际上没有。注意重传次数。在上例中,发送端发送帧,并且没有收到确认,此时TCP发送重传,每次都没有收到确认。当数据包第五次重传以后,发送端等待一定时间确认。如果仍然没有收到确认,发送RESET帧重置连接。需要注意的要点:
- 同一个数据包重传5次;
- 发送端发送了其他帧并收到了响应的确认没有关系,我们关注的是重传的帧;
- late acknowledgement不会导致该重置现象。
应用重置
如果我们观察网络通信状况,但找不到TCP发送重置的原因,那么重置一定是来自应用程序本身。这在建立大量TCP短连接的应用程序里很常见。由于大量端口出在time wait状态,这可能导致服务端端口枯竭。尽管如此,在重置所有连接之前,应用开发人员仍需要了解为什么time wait状态的存在。
Note:看一下程序代码里有没有调用close(socket)。如果在发送数据的连接数调用了close,会产生一个RESET
数据帧。如果在三次握手建立连接后,直接调用close,而没有数据传输,这会产生一个FIN
数据帧来优雅关闭连接。
另一种可能性就是目标节点上的其他进程已经监听了该目标端口,这也可能导致应用重置的发生。
对于高级用户和网络管理员
在网络传输中发生的问题是最难以解决的问题。如果对reset的发生理解不深,很难跟踪调试。网络中的很多设备,如路由器、防火墙等,都可能重置网络连接。解决这种特殊重置行为的唯一办法就是跟踪从源到目的节点的整个网络路径。比如,从一个节点捕获到了RESET帧,并且期望在另一个节点也能捕获到,而实际上没有捕获到,说明这两个节点直接存在问题。
另一个有趣的现象是中间设备可以重置客户端和服务端的连接。举个例子,在两个节点之间建立了TCP连接。源IP10.10.10.20,目的IP10.10.10.30,在TCP端口2301和445之间建立了连接。我们可能捕获到了发往10.10.10.20:2301的重置帧和发往10.10.10.30:445的重置帧。
端口重用
如果应用程序试图重用出在time wait状态的端口,这可能导致Reset。当客户端和服务端之间的连接已经经由优雅关闭进入time wait状态时,同一个客户端通过发送SYN帧(相同的源和目标端口)试图重用同一个端口对。根据RFC1122,这是允许的。但请注意,这样做是有风险的,别忘了端口保持time wait是有原因的。
警告:SYN帧中的序列号(被发送以通过已有的连接建立新连接)应该大于之前连接中最后帧的序列号。如果不是,会导致连接重置。
总结
TCP重置是个好东西。如果没有它们,当TCP遇到网络连接问题时,会出现大量问题。请记住,连接重置可能发生自网络栈和应用程序。仅仅因为存在重传数据包并不能推断连接会自动重置。重要的是,确定数据帧并理解发送重传的原因。