TCP断开时的状态与Linux nf_conntrack

时间:2022-07-05 15:12:24
题目有点大了,但是难免有一些愤怒!
我们的网关产品目前处在系统测试阶段,不太顺利,是太不顺利!各方面都在懈怠,包括我!我除了懈怠,还在找机会逆袭!顺便蔑视一下测试者,希望产生一种想象,即他发现的问题其实不是问题,而是因为他的无知所导致!就在昨天,机会来了,我便气扬了!
       很多人觉得我是下三层网络的专家,对于TCP之类的无权问津,但是我对TCP除了辱骂还是辱骂!因为它太复杂了,作为一个低层的设施,如果太复杂,应用层的复杂空间便小了!编程的人,特别是socket编程的人,总是将IP以及网卡作为一个黑盒子,而着重看待TCP,他们对UDP是不屑一顾的,也不会关注什么非合作UDP流量。这是搞通讯网络和人和编程的人的本质区别,毕竟分工不同。但是我也是从研究TCP开始的,起初我并无法触及路由器之类的东西,更不晓得什么流量工程,可是到了后来当我懂了IP以后,我发现IP是多么的美妙,可以让人瞬间高潮!而TCP,就是一团乱麻,在产品系统测试的时候,一个TCP的疑难杂症可以让一个研发人员折腾一下午甚至好几天,而这几天时间,测试人员爽了,没什么事,可以聊天,看新闻,这是多么悲哀,让人没法释怀!真TMD想给测试人员上椅刑!旋转升降座椅一定会爆炸,菊花残,满地伤,花落人断肠!
       只可惜,昨天的事在我愤怒状态时,彻底灭了这种格局!事情是这样的。
客户端A:128.129.1.2
网关外网口(连接客户端):128.129.1.1
网关内网口(连接服务器):192.168.220.223
服务器:192.168.30.75

网关上做NAT的redirect转换,将访问服务器的流量重定向到本地的apache服务器,apache服务器行使正向代理功能(正向代理是一个重要概念,请深入理解)。规则如下:
iptables -t nat -A PREROUTING -d 192.168.30.75 -j REDIRECT --to-ports 80
多么简单的规则,然而多么痛的领悟。

测试人员,严格说是黑盒测试人员(非白盒),测试功能就算了,不影响业务就可以了,偏偏要搞什么抓包,关键是抓包还误会我们!问题如下:
连接的建立以及数据的传输,均经过了代理,但是连接的拆除却没有被NAT,直接forward出去了,导致192.168.30.75服务器直接接收到了源IP地址为128.129.1.2的数据包。这是怎么回事?!
       实际上,这是毫无影响的,只要应用程序能够坚持足够长的时间!
       我发现,这是服务器30.75主动断开的连接,也就是它主动发送了FIN,此后客户端发送了ACK,然而客户端迟迟没有发送自己方面的FIN,过了两分钟才发送了FIN/ACK,此时连接只是单方面断开了。此时,在客户端没有发送FIN之前,它处在CLOSE_WAIT状态。由于目前的产品是基于前一个产品构建的,在前一个产品中,我由于一些特殊的原因将conntrack的和TCP相关的timeout都减到了足够小,比如我将TCP的conntrack的establish的timeout减少到了120秒(默认是5天),因此断开连接时的各状态timeout更小,因此在两分钟内,conntrack早就删除了!
       此时客户端的FIN来了,由于conntrack已经删除,网关会为其建立一个新的conntrack吗?这要看有没有设置loose,我当然设置了,也就是不再以syn为建立新conntrack的依据,按理说,Linux网关会创建一个新的conntrack,然而它携带了fin标志!这就意味着Linux不会创建新的conntrack!因此数据包就直接forward了。但是这不会造成什么影响!因为携带fin的数据包拥有自己的控制通道timeout期限。到期后会自动转换,这是和establish状态的截然不同!这是为什么呢?
       这样从TCP/IP的设计说起。有一种设计方案叫做带内控制,也就是控制通道和数据通道共享一条网络路径,TCP协议就是这种设计的典型,另一种就是IP,比如ICMP就是IP的控制协议,然而你不能说IP不是一种完全的带内控制,它虽然和TCP有所区别,那只是因为它的无状态所致!
       TCP的控制通道用标志来区分,携带SYN,FIN的数据段都是控制段,Linux的conntrack对TCP的行为就是以这种控制标志为依据的,如果来了一个establish的数据包-没有syn,没有fin,没有查找到conntrack,在设置loose标志的情况下,Linux为创建一个新的conntrack项,然后对于控制信号,比如fin段,就不同了,Linux不会创建。然而这并不影响终端的结果,虽然由于Linux没有NAT成功导致了数据不会正确到达服务器,可是由于已经fin了,终端会自动用timeout处理。
       但是一定要注意方向,这可能会稍微带来一些问题。如果主动发出的FIN被拦截,会有大问题吗?不会的,因为主动发出FIN的一端不管它会不会被拦截,都会更改状态,而被动关闭的那一端虽然收不到FIN,也会行使超时重传限制权利,虽然可能慢一点。
       持续饥饿!保持饥饿!