连接重置常见原因及排查方法

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,内容安全 1000次 1年
对象存储 OSS,恶意文件检测 1000次 1年
简介: 与 SYN/FIN 类似,TCP RST 报文也是控制类报文的一种,可以改变TCP 状态也可以用于响应未预期的报文,在TCP Header 中的Flags 字段内标记。相比于其他报文,RST 包是专门为了处理一些异常状态而设计的,通常由协议栈本身使用,业务应当只在“不得不”的情况下使用RST强行终止连接,那么RST 的场景到底有哪些呢,该如何排查RST 问题,其实都是有套路的,请看下文。

1 RST 简介

与 SYN/FIN 类似,TCP RST 报文也是控制类报文的一种,可以改变TCP 状态也可以用于响应未预期的报文,在TCP Header 中的Flags 字段内标记。相比于其他报文,RST 包是专门为了处理一些异常状态而设计的,通常由协议栈本身使用,业务应当只在“不得不”的情况下使用RST强行终止连接。

image.png

2 RST 报文的作用及出现场景

2.1 终止 ESTAB 状态连接

正常情况下连接关闭之后主动断开连接的一方会进入TIME_WAIT 状态持续TW_TIMEOUT时间(通常是2MSL,默认120s),另外一方直接进入CLOSE 状态。如果是使用RST 来终止连接的话,连接会从当前状态跳过所有流程直接进入CLOSE 状态,不会经历连接正常关闭过程中的各类状态的转换过程。

2.2 终止TIME_WAIT 状态连接

在连接处于TIME_WAIT 状态时,其实当前连接的一系列状态都是在的,甚至处于TIME_WAIT 状态的连接协议栈里还堆的有未被应用读掉的数据。此时收到RST 报文会立即从TIME_WAIT 状态进入CLOSE状态,协议栈内如果有数据的话也会被丢弃,上层应用也能感知到连接被RST。

2.3 应答未预期报文

主机收到某个TCP 报文后:

首先会看是不是建联过程中的SYN包,如果是并且发现主机并未Listen 相应端口时会回一个RST 包,如下图所示,实际上服务端回复了RST 报文,curl 软件逻辑包掉输出 Connection refused。

image.png

19:33:56.810709 IP a11348.cloud.xx21.58630 > a11348.cloud.xx21.ddi-tcp-1: Flags [S], seq 201987671, win 43690, options [mss 65495,sackOK,TS val 1621745206 ecr 0,nop,wscale 9], length 119:33:56.810722 IP a11348.cloud.xx21.ddi-tcp-1 > a11348.cloud.xx21.58630:Flags [R.], seq 0, ack 201987672, win 0, length 0

如果非SYN包,会根据其四元组在本地查找对应的连接,如果找到则继续处理,如果未匹配到则返回RST报文。

典型的未预期场景主要是如上两种,那么为什么要回复RST呢,而不是静默丢弃或者返回其他类型报文呢。这个很少看到有解释,实际上可以这么理解,为什么会收到这些异常包呢,在不考虑这些包是伪造出来的情况下,那么要么这个包是之前连接上游荡在网络中"迟到" 的报文,要么就是对端的连接状态还在,而本地连接状态已经结束,此时在确认本机连接已经断开的情况下对端连接存在已经没有意义了,因此有义务发给RST 重置对端连接。其次是发出去的包总要有个回应,这有利于网络问题的排查及应用程序的处理,遇到异常能及时返回而不是等待超时。

2.4 业务特殊场景强制终止连接

TCP 是全双工的协议,正常情况下单边只能单向关闭,先主动发FIN(EOF),告诉对方我这边数据发完了,如果对方持续不关闭连接,并且持续要发数据过来,(不考虑buffer 满zero window的情况)是阻止不了的,此时要终止连接只能强制重置连接,现实中很少发生这样的情况是因为应用层协议之间配合的较好,而攻击场景则不一定。

典型的是 “Nginx 的linering close 场景”,可自行搜索。

3 能正确重置连接的条件

并非随意发一个四元组正确的RST 包就能重置掉连接,那这个条件也太容易了,连接非常容易被三方恶意重置,因此除基本的四元组正确外要求序列号也必须正确才能RST掉连接,否则RST 报文会被静默丢弃。

因此我们发现,如果连接被无端RST 了,要么是对端干的,要么是中间数据包经过的网络设备干的(包括本机),毕竟别人只有看到你的数据包才能拿到当前正确的序列号,才能构造出正确的RST报文重置连接。这也是为什么TCP 连接系列号不直接从 1 开始而是随机生成的原因之一。

注:Wireshark 有时看到的序列号从1 开始的原因是开启了相对序列号显示,Wireshark 为了方便分析自动根据SYN 包中的初始序列号计算了偏移,实际上是随机的数字,可以关闭此功能。

4 如何分析

上述简单介绍RST 的出现及应用场景,了解上述信息后大部分基础场景的RST 问题不需要通过抓包就可以大致推测出原因,但是遇到一些复杂的RST 场景,还是需要借助工具深入分析

4.1 中间网络伪造RST报文场景

这里用实际遇到的一例被中间网络RST 的案例来分析介绍。

image.png

4.1.1 根据TTL 确认RST 的来源

分析RST 的第一步是先看RST 包的来源,是对端发出来的还是中间设备发出,此时只需要看TTL 即可。TTL 是Time to live 的缩写,为了防止配置错误导致数据包一直在互联网中游荡,报文每经过一个设备TTL 都会减1,直到TTL 为0 时被丢弃同时返回相应的 ICMP 报文,TTL 初始值通常是64/128 这类值,当然也有其他特殊设置的值。比如OSS回包TTL 初始值是64 而ALB 出包初始值为102。

对比RST 方向正常报文和RST 报文的TTL,如果是对端发出来的RST包,那么TTL 也应该相同(至少接近)。

看下正常SYN ACK 报文的TTL为 46:

image.png

RST 报文TTL 为 85:

image.png

根据上述TTL 分析,一个TTL 是46 一个是85,可以断定这个RST 报文一定不是OSS 发出来的,不过不确定是中间网络伪造的还是ALB回复的,毕竟两个包一个经历18 跳一个经历17 跳,如此接近还是有可能是ALB 发的。

4.1.2 裸ping 确认到ALB 设备的TTL

客户端裸ping VIP,TTL 持续是87,此时可以确定客户端到ALB 的跳数是102 - 87 = 15 跳。而且从客户端给的报文中没发现TTL 为87 的,此时可基本确定RST 也不是来自ALB。

有时候客户端配合的话能拿到Ping 截图,如果客户端不配合也没关系,网络上还是会有其他蛛丝马迹。

image.png

4.1.3 确定RST 的目标报文

上文所述能正常RST 掉连接的一个必要条件是序列号要正确,根据序列号可以推断出RST 针对的是哪个报文,或者说这个报文触发了防火墙的规则。

通常情况下中间防火墙设备会针对建联的SYN 包、TLS ClientHello 中的SNI 、HTTP Host、数据包中的特殊串 进行RST 拦截,因此在RST 发生时找到RST 的目标报文或许能得出连接被RST 的背后逻辑。

这里序列号有三个:

47 号SYN ACK 包的序列号,3788899386

48 号 ACK 包,要ACK 的序列号,3788899387

49 号 RST 包,序列号是,3788899387 = 47号包的seq + 1

由此可以看出来,这个RST 包是针对48 号SYN ACK 包重置的,但是由于47 号SYN ACK触发还是由于48 号ACK 包不得而知,但是从常识上来说,ACK 包除了携带ACK 信息外没有什么特殊参考价值,因此这里基本可以确定是针对47 号报文SYN 部分进行。

image.png

4.1.4 通过确定RST 时间,分析RST 位置

找到RST 报文和RST 目标报文,找到二者的时间差,就是RST 和当前抓包主机的距离,通过这个时间差的量级及这个时间差和链路RT的对比,就可以大致得出RST 的位置。


首先看整条链路的延时,从syn ack 的时序分析,或者4.1.2 中的ping 可以确认链路RT 在26ms 左右。

image.png

再看RST 报文和触发此RST 报文的时间间隔,可以确认无论是47 号SYN ACK包触发的还是48号ACK 包触发,间隔时间都是0.04ms,这个时间基本可以确定是靠近用户侧主机的设备发出的RST 包,甚至可能是客户端本机。

image.png

接下来继续分析,找到最终的RST 的点。

4.1.5 根据TTL 判断发送RST 设备位置

如果知道RST 包的TTL 初始值,那么可以推算出RST 是在客户端多少跳的设备发出的,利用traceroute 也就能大致定位RST 的设备位置。即使不知道TTL 的初始值,如果按照常规套路出牌的话,TTL 的初始值通常是64 或128,可定位出大致RST 位置。

遗憾的是这个Case 不同RST包的终态TTL 值是不一样的,大概率是在构造这个RST是TTL 在某个范围内随机了,因此此Case 场景下该方法很难奏效,后文服务端抓包后也有针对性分析。

4.1.6 IP package Identification

这个字段作为IP 的唯一标识(可能存在冲突),结合报文发送的时间可大致确定该报文,在转发过程中这个字段不应该被修改,包括经过NAT 设备。如果服务端的特殊报文设置了这个值,那么在调查过程中可根据此字段判断是什么设备RST 的连接,比如ALB 触发的RST 此字段会设置为一个特殊的值。

image.png

可以选中该字段,单机右键 选择Apply as Column 就可以显示在上面报文以单独一列展示,其他字段也一样。

4.1.7 另外一个有趣的现象

我们发现这里其实不止一个RST,有两个RST,而且另外一个RST 从TTL 看确实是由服务端发出来的,而且这个RST 的时间相比目标报文间隔正好是26ms,也可以说两个RST 相对目标报文的间隔时间和是26ms,也就是一个链路RT 值。

image.png

分析到这里,基本确定和防火墙的行为匹配,中间防火墙设备在RST 连接时,会根据捕获到的报文,分别向客户端可服务端构造两个RST 分别发送,同时干掉客户端及服务端的连接(这里分析是有问题的,实际上是分别根据SYN 和SYN ACK 分别构造的两个RST,见4.1.8 分析)。第一个RST 报文在客户端三次握手的第三个ACK 包之前发送到服务端,RST 掉服务端连接,此时服务端连接状态已经消失,紧接着客户端的ACK 报文到达,由于没匹配到任何TCP 连接,服务端也回复了一个RST,这是第二个RST 包出现的成因。

至此,上述分析都是基于客户端的包,至于对不对我们拿服务端的抓包验证一下。

4.1.8 服务端抓包分析

从上面分析看,原因已经基本明确了,服务端抓包主要是为了验证下前面的一系列调查结论是否正确。

1、服务端确实也收到了一个RST,ip identification 和客户端看到的一样都是1536.

2、服务端的抓包,并非SYN ACK 触发,而是有SYN 包触发,由此可见,RST 是由各自方向的SYN 包(包括SYN ACK)触发。

注:RST 要拿到正确的序列号,因此纯SYN只能构造出同向RST,而SYN ACK包可以根据seq 和 ack 构造出双向RST。

3、4.1.6 中的多出来的RST 分析是对的,在RST 三次握手第三个包到达之前到达,触发了服务端回RST。

image.png

4.1.5 中的TTL 分析:

TTL 到OSS这边剩余82,到用户主机剩余85,看起来到双方的跳数差不多,但是从时间上看物理距离客户侧更近。客户主机到OSS侧的跳数是21 跳,初始TTL 是 (82 + 85 + 21)/2 = 94,综合看应该是距离客户侧主机9 跳的位置。如果是在同设备上监控包并发RST 的话,上述分析是正确的,但是网络上大多数是旁路嗅探,并在其他设备上构造RST发送出来,因此上述分析有道理但是只能作为辅助。

5 四七层典型连接异常现象

5.1 物理网络异常

现实中各类防火墙封禁从物理网络上看无非三种现象:

1 黑洞

完全不通,所有经过某链路的报文全部静默丢弃。

2 丢包

访问变慢,尤其在主流拥塞控制算法下,TCP 连接性能会极其低下。(实际上有些情况是想黑洞,但是控制不了所有路径导致)。

3 连接重置

就是本文4 中描述的现象,通常是针对SYN 包进行,让连接都建不起来。

这里只是列举一些常见的现象,当然还有一些其他方式

5.2 业务异常

从内容及业务侧看就丰富了,主流如下:

1 按IP 封禁

特定IP 异常,特定网段异常,通常表现访问特定IP/网段不通,连接或被RST。

2 按内容封禁

特定HTTP 明文URL 异常,比如访问特定的资源会被RST,访问同域名下的其他资源正常

3 按 Host 封禁

特定HTTP 明文Host 异常。

比如当前网络环境下会对 www.example.com 进行封禁,则:

curl www.example.com 被RST,而curl www.example.com -H "Host: anotherhost" 则正常。

4 按HTTPS SNI (Server Name Indication) 封禁

     HTTPS 握手Client Hello 中携带特定SNI 异常。

比如当前网络环境下会对SNI 为 www.example.com 的进行封禁,则:

openssl s_client -servername www.example.com -connect www.example.com:443 会被RST,而 openssl s_client -servername anothersni -connect www.example.com:443 则正常。

5 抢先应答/数据篡改

只能针对HTTP 明文生效,应答错误的数据或者插入一段非预期数据,比如插入一段弹窗广告代码。

6 跨运营商访问

比如移动网络访问联通的IP,则可能通也可能不通,有可能是丢包,也可能是连接被重置。

6 总结

OSS 作为基础互联网对象存储服务,经常遇到各类网络问题,网络对于大部分开发者而言是个黑盒,但是协议是公开的,把握协议细节,推测背后真正的根因,当真正遇到连接RST 时不妨先自行按照上述步骤排查,如仍未找出原因可工单联系OSS 一起协助排查。

相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
目录
相关文章
|
Arthas 前端开发 Java
问题排查---应用程序不在接收新请求
关键词:springboot,jstack,Arthas
144 1
|
4月前
|
存储 安全 测试技术
网络中的状态检查是什么?
【8月更文挑战第24天】
120 0
|
数据库连接 API Nacos
Nacos在启动时会检查数据库连接,如果连接失败,它会抛出一个异常并退出
Nacos在启动时会检查数据库连接,如果连接失败,它会抛出一个异常并退出
262 7
|
数据安全/隐私保护
阿里云 RPA 在与服务器连接断开时会显示这个警告
阿里云 RPA 在与服务器连接断开时会显示这个警告
184 3
|
SQL 关系型数据库 MySQL
错误代码:2013查询期间丢失与MySQL服务器的连接
错误代码:2013查询期间丢失与MySQL服务器的连接
148 0
|
网络安全 数据安全/隐私保护
解决 SSH 无操作自动断开 | pychram 超时无响应
SSH 是用于与远程服务器建立加密通信通道的,因此配置涉及服务端和客户端
508 0
|
数据库 C#
C#如何检查MySqlConnection是否连接成功
    检测的方法之一是使用异常捕获方式来辨别是否已打开了MySqlConnection.如下: using MySql.Data; using MySql.Data.MySqlClient; private bool isConnectedOK() ...
2536 0
开机显示被调用的对象已与其客户端断开连接,解决方案亲测有效
开机显示被调用的对象已与其客户端断开连接,解决方案亲测有效
1818 0
开机显示被调用的对象已与其客户端断开连接,解决方案亲测有效