您当前的位置:首页 > 电脑百科 > 网络技术 > 网络知识

动图图解!收到RST,就一定会断开TCP连接吗?

时间:2021-09-13 10:28:34  来源:微信公众号  作者:寒蝉鸣泣之时QAQ

想必大家已经知道我的niao性,搞个标题,就是不喜欢立马回答。

就是要搞一大堆原理性的东西,再回答标题的问题。

说这个是因为我这次会把问题的答案就放到开头吗?

不!

我就不!

但是大家可以直接根据目录看自己感兴趣的部分。

之所以要先铺垫一些原理,还是希望大家能先看些基础的,再慢慢循序渐进,这样有利于建立知识体系。多一点上下文,少一点gap。

好了,进入正题。

下面是这篇文章的目录。

动图图解!收到RST,就一定会断开TCP连接吗?

 

收到RST就一定会断开连接吗

 

什么是RST

我们都知道TCP正常情况下断开连接是用四次挥手,那是正常时候的优雅做法。

异常情况下,收发双方都不一定正常,连挥手这件事本身都可能做不到,所以就需要一个机制去强行关闭连接。

RST 就是用于这种情况,一般用来异常地关闭一个连接。它是一个TCP包头中的标志位

正常情况下,不管是发出,还是收到置了这个标志位的数据包,相应的内存、端口等连接资源都会被释放。从效果上来看就是TCP连接被关闭了。

而接收到 RST的一方,一般会看到一个 connection reset 或 connection refused 的报错。

动图图解!收到RST,就一定会断开TCP连接吗?

 

TCP报头RST位

 

怎么知道收到RST了?

我们知道内核应用层是分开的两层,网络通信功能在内核,我们的客户端或服务端属于应用层。应用层只能通过 send/recv 与内核交互,才能感知到内核是不是收到了RST。

当本端收到远端发来的RST后,内核已经认为此链接已经关闭。

此时如果本端应用层尝试去执行 读数据操作,比如recv,应用层就会收到 Connection reset by peer 的报错,意思是远端已经关闭连接

动图图解!收到RST,就一定会断开TCP连接吗?

 

ResetByPeer

如果本端应用层尝试去执行写数据操作,比如send,那么应用层就会收到 Broken pipe 的报错,意思是发送通道已经坏了。

动图图解!收到RST,就一定会断开TCP连接吗?

 

BrokenPipe

这两个是开发过程中很经常遇到的报错,感觉大家可以把这篇文章放进收藏夹吃灰了,等遇到这个问题了,再打开来擦擦灰,说不定对你会有帮助。

 

出现RST的场景有哪些

RST一般出现于异常情况,归类为 对端的端口不可用 和 socket提前关闭

 

端口不可用

端口不可用分为两种情况。要么是这个端口从来就没有"可用"过,比如根本就没监听(listen)过;要么就是曾经"可用",但现在"不可用"了,比如服务突然崩了。

端口未监听

动图图解!收到RST,就一定会断开TCP连接吗?

 

TCP连接未监听的端口

服务端listen 方法会创建一个sock放入到全局的哈希表中。

此时客户端发起一个connect请求到服务端。服务端在收到数据包之后,第一时间会根据IP和端口从哈希表里去获取sock。

动图图解!收到RST,就一定会断开TCP连接吗?

 

全局hash表

如果服务端执行过listen,就能从全局哈希表里拿到sock。

但如果服务端没有执行过listen,那哈希表里也就不会有对应的sock,结果当然是拿不到。此时,正常情况下服务端会发RST给客户端。

 

端口未监听就一定会发RST吗?

不一定。上面提到,发RST的前提是正常情况下,我们看下源码。

// net/ipv4/tcp_ipv4.c  
// 代码经过删减
int tcp_v4_rcv(struct sk_buff *skb)
{
    // 根据ip、端口等信息 获取sock。
    sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
    if (!sk)
        goto no_tcp_socket;

no_tcp_socket:
    // 检查数据包有没有出错
    if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
        // 错误记录
    } else {
        // 发送RST
        tcp_v4_send_reset(NULL, skb);
    }
}

内核在收到数据后会从物理层、数据链路层、网络层、传输层、应用层,一层一层往上传递。到传输层的时候,根据当前数据包的协议是TCP还是UDP走不一样的函数方法。可以简单认为,TCP数据包都会走到 tcp_v4_rcv()。这个方法会从全局哈希表里获取 sock,如果此时服务端没有listen()过 , 那肯定获取不了sock,会跳转到no_tcp_socket的逻辑。

注意这里会先走一个 tcp_checksum_complete(),目的是看看数据包的校验和(Checksum)是否合法。

校验和可以验证数据从端到端的传输中是否出现异常。由发送端计算,然后由接收端验证。计算范围覆盖数据包里的TCP首部和TCP数据。

如果在发送端到接收端传输过程中,数据发生任何改动,比如被第三方篡改,那么接收方能检测到校验和有差错,此时TCP段会被直接丢弃。如果校验和没问题,那才会发RST。

所以,只有在数据包没问题的情况下,比如校验和没问题,才会发RST包给对端。

 

为什么数据包异常的情况下,不发RST?

一个数据包连校验都不能通过,那这个包,多半有问题

动图图解!收到RST,就一定会断开TCP连接吗?

 

有可能是在发送的过程中被篡改了,又或者,可能只是一个胡乱伪造的数据包。

五层网络,不管是哪一层,只要遇到了这种数据,推荐的做法都是默默扔掉而不是去回复一个消息告诉对方数据有问题。

如果对方用的是TCP,是可靠传输协议,发现很久没有ACK响应,自己就会重传。

如果对方用的是UDP,说明发送端已经接受了“不可靠会丢包”的事实,那丢了就丢了。

因此,数据包异常的情况下,默默扔掉,不发RST,非常合理。

动图图解!收到RST,就一定会断开TCP连接吗?

 

还是不能理解?那我再举个例子

正常人喷你,他说话条理清晰,主谓宾分明。此时你喷回去,那你是个充满热情,正直,富有判断力的好人。

而此时一个憨憨也想喷你,但他思维混乱,连话都说不清楚,一直阿巴阿巴的,你虽然听不懂,但大受震撼,此时你会?

  • A:跟他激情互喷
  • B:不跟他一般见识,就当没听过

一般来说最优选择是B,毕竟你理他,他反而来劲。

这下,应该就懂了。

 

程序启动了但是崩了

端口不可用的场景里,除了端口未监听以外,还有可能是从前监听了,但服务端机器上做监听操作的应用程序突然崩了,此时客户端还像往常一样正常发送消息,服务器内核协议栈收到消息后,则会回一个RST。在开发过程中,这种情况是最常见的

比如你的服务端应用程序里,弄了个空指针,或者数组越界啥的,程序立马就崩了。

动图图解!收到RST,就一定会断开TCP连接吗?

 

TCP监听了但崩了

这种情况跟端口未监听本质上类似,在服务端的应用程序崩溃后,原来监听的端口资源就被释放了,从效果上来看,类似于处于CLOSED状态。

此时服务端又收到了客户端发来的消息,内核协议栈会根据IP端口,从全局哈希表里查找sock,结果当然是拿不到对应的sock数据,于是走了跟上面"端口未监听"时一样的逻辑,回了个RST。客户端在收到RST后也释放了sock资源,从效果上来看,就是连接断了

RST和502的关系

上面这张图,服务端程序崩溃后,如果客户端再有数据发送,会出现RST。但如果在客户端和服务端中间再加一个Nginx,就像下图一样。

动图图解!收到RST,就一定会断开TCP连接吗?

 

RST与502

nginx会作为客户端和服务端之间的"中间人角色",负责转发请求和响应结果。但当服务端程序崩溃,比如出现野指针或者OOM的问题,那转发到服务器的请求,必然得不到响应,后端服务端还会返回一个RST给nginx。nginx在收到这个RST后会断开与服务端的连接,同时返回客户端一个502错误码。

所以,出现502问题,一般情况下都是因为后端程序崩了,基于这一点假设,去看看监控是不是发生了OOM或者日志是否有空指针等报错信息。

 

socket提前关闭

这种情况分为本端提前关闭,和远端提前关闭。

本端提前关闭

如果本端socket接收缓冲区还有数据未读,此时提前close() socket。那么本端会先把接收缓冲区的数据清空,然后给远端发一个RST。

动图图解!收到RST,就一定会断开TCP连接吗?

 

recvbuf非空

 

远端提前关闭

远端已经close()了socket,此时本端还尝试发数据给远端。那么远端就会回一个RST。

动图图解!收到RST,就一定会断开TCP连接吗?

 

close()触发TCP四次挥手

大家知道,TCP是全双工通信,意思是发送数据的同时,还可以接收数据。

Close()的含义是,此时要同时关闭发送和接收消息的功能。

客户端执行close(), 正常情况下,会发出第一次挥手FIN,然后服务端回第二次挥手ACK。如果在第二次和第三次挥手之间,如果服务方还尝试传数据给客户端,那么客户端不仅不收这个消息,还会发一个RST消息到服务端。直接结束掉这次连接。

 

对方没收到RST,会怎么样?

我们知道TCP是可靠传输,意味着本端发一个数据,远端在收到这个数据后就会回一个ACK,意思是"我收到这个包了"。

而RST,不需要ACK确认包

因为RST本来就是设计来处理异常情况的,既然都已经在异常情况下了,还指望对方能正常回你一个ACK吗?可以幻想,不要妄想。

问题又来了,网络环境这么复杂,丢包也是分分钟的事情,既然RST包不需要ACK来确认,那万一对方就是没收到RST,会怎么样?

动图图解!收到RST,就一定会断开TCP连接吗?

 

RST丢失

RST丢了,问题不大。比方说上图服务端,发了RST之后,服务端就认为连接不可用了。

如果客户端之前发送了数据,一直没等到这个数据的确认ACK,就会重发,重发的时候,自然就会触发一个新的RST包。

而如果客户端之前没有发数据,但服务端的RST丢了,TCP有个keepalive机制,会定期发送探活包,这种数据包到了服务端,也会重新触发一个RST。

动图图解!收到RST,就一定会断开TCP连接吗?

 

RST丢失后keepalive

 

收到RST就一定会断开连接吗?

先说结论,不一定会断开。我们看下源码。

// net/ipv4/tcp_input.c
static bool tcp_validate_incoming()
{
    // 获取sock
    struct tcp_sock *tp = tcp_sk(sk);

    // step 1:先判断seq是否合法(是否在合法接收窗口范围内)
    if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
        goto discard;
    }

    // step 2:执行收到 RST 后该干的事情
    if (th->rst) {
        if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt)
            tcp_reset(sk);
        else
            tcp_send_challenge_ack(sk);
        goto discard;
    }
}

收到RST包,第一步会通过tcp_sequence先看下这个seq是否合法,其实主要是看下这个seq是否在合法接收窗口范围内。如果不在范围内,这个RST包就会被丢弃。

至于接收窗口是个啥,我们先看下面这个图。

动图图解!收到RST,就一定会断开TCP连接吗?

 

接收窗口

这里黄色的部分,就是指接收窗口,只要RST包的seq不在这个窗口范围内,那就会被丢弃。

 

为什么要校验是否在窗口范围内

正常情况下客户端服务端双方可以通过RST来断开连接。假设不做seq校验,如果这时候有不怀好意的第三方介入,构造了一个RST包,且在TCP和IP等报头都填上客户端的信息,发到服务端,那么服务端就会断开这个连接。同理也可以伪造服务端的包发给客户端。这就叫RST攻击

动图图解!收到RST,就一定会断开TCP连接吗?

 

RST攻击

受到RST攻击时,从现象上看,客户端老感觉服务端崩了,这非常影响用户体验。

如果这是个游戏,我相信多崩几次,第二天大家就不来玩了。

实际消息发送过程中,接收窗口是不断移动的,seq也是在飞快的变动中,此时第三方是比较难构造出合法seq的RST包的,那么通过这个seq校验,就可以拦下了很多不合法的消息。

 

加了窗口校验就不能用RST攻击了吗

不是,只是增加了攻击的成本。但如果想搞,还是可搞的。

以下是面向监狱编程的环节。

希望大家只了解原理就好了,不建议使用

相信大家都不喜欢穿着蓝白条纹的衣服,拍纯狱风的照片。

从上面可以知道,不是每一个RST包都会导致连接重置的,要求是这个RST包的seq要在窗口范围内,所以,问题就变成了,我们怎么样才能构造出合法的seq

 

盲猜seq

窗口数值seq本质上只是个uint32类型。

struct tcp_skb_cb {
    __u32       seq;        /* Starting sequence number */
}

如果在这个范围内疯狂猜测seq数值,并构造对应的包,发到目的机器,虽然概率低,但是总是能被试出来,从而实现RST攻击。这种乱棍打死老师傅的方式,就是所谓的合法窗口盲打(blind in-window attacks)

觉得这种方式比较?那有没有聪明点的方式,还真有,但是在这之前需要先看下面的这个问题。

 

已连接状态下收到第一次握手包会怎么样?

我们需要了解一个问题,比如服务端在已连接(ESTABLISHED)状态下,如果收到客户端发来的第一次握手包(SYN),会怎么样?

以前我以为服务单会认为客户端憨憨了,直接RST连接。

但实际,并不是

static bool tcp_validate_incoming()
{
    struct tcp_sock *tp = tcp_sk(sk);

    /* 判断seq是否在合法窗口内 */
    if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
        if (!th->rst) {
            // 收到一个不在合法窗口内的SYN包
            if (th->syn)
                goto syn_challenge;
        }
    }

    /* 
     * RFC 5691 4.2 : 发送 challenge ack
     */
    if (th->syn) {
syn_challenge:
        tcp_send_challenge_ack(sk);
    }
}

当客户端发出一个不在合法窗口内的SYN包的时候,服务端会发一个带有正确的seq数据ACK包出来,这个ACK包叫 challenge ack。

动图图解!收到RST,就一定会断开TCP连接吗?

 

challenge ack抓包

上图是抓包的结果,用scapy随便伪造一个seq=5的包发到服务端(端口9090),服务端回复一个带有正确seq值的challenge ack包给客户端(端口8888)。

 

利用challenge ack获取seq

上面提到的这个challenge ack ,仿佛为盲猜seq的老哥们打开了一个新世界。

在获得这个challenge ack后,攻击程序就可以以ack值为基础,在一定范围内设置seq,这样造成RST攻击的几率就大大增加了。

动图图解!收到RST,就一定会断开TCP连接吗?

 

利用ChallengeACK的RST攻击

 

总结

  • RST其实是TCP包头里的一个标志位,目的是为了在异常情况下关闭连接。
  • 内核收到RST后,应用层只能通过调用读/写操作来感知,此时会对应获得 Connection reset by peer 和Broken pipe 报错。
  • 发出RST后不需要得到对方的ACK确认包,因此RST丢失后对方不能立刻感知,但是通过下一次重传数据或keepalive心跳包可以导致RST重传。
  • 收到RST包,不一定会断开连接,seq不在合法窗口范围内的数据包会被默默丢弃。通过构造合法窗口范围内seq,可以造成RST攻击,这一点大家了解就好,千万别学!

来源:
https://mp.weixin.qq.com/s/Fr6o6gRiIUIspV9-jR9snw



Tags:TCP连接   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
TCP握手的时候维护的队列 半连接队列(SYN队列) 全连接队列(accepted队列)半连接队列是什么?服务器收到客户端SYN数据包后,Linux内核会把该连接存储到半连接队列中,并响应SYN+ACK报...【详细内容】
2021-12-21  Tags: TCP连接  点击:(9)  评论:(0)  加入收藏
想必大家已经知道我的niao性,搞个标题,就是不喜欢立马回答。就是要搞一大堆原理性的东西,再回答标题的问题。说这个是因为我这次会把问题的答案就放到开头吗?不!我就不!但是大家可...【详细内容】
2021-09-13  Tags: TCP连接  点击:(71)  评论:(0)  加入收藏
下面是一个简单的echo服务程序:#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <unistd.h>#include...【详细内容】
2021-06-21  Tags: TCP连接  点击:(116)  评论:(0)  加入收藏
这明显是进入了思维的误区,65535是指可用的端口总数,并不代表服务器同时只能接受65535个并发连接。举个例子:我们做了一个网站,绑定的是TCP的80端口,结果是所有访问这个网站的用...【详细内容】
2021-04-08  Tags: TCP连接  点击:(308)  评论:(0)  加入收藏
在互联网领域,客户端和服务端之间通常需要建立和保持TCP长连接。所谓长连接,就是通信双方在建立TCP连接后进行数据通信,一次或若干次通信交互完成之后,不主动断开连接,而是保持TC...【详细内容】
2020-06-21  Tags: TCP连接  点击:(88)  评论:(0)  加入收藏
一道经典的面试题是从 URL 在浏览器被输入到页面展现的过程中发生了什么,大多数回答都是说请求响应之后 DOM 怎么被构建,被绘制出来。 图片来自 Pexels 但是你有没有想过,收到...【详细内容】
2020-02-16  Tags: TCP连接  点击:(70)  评论:(0)  加入收藏
之前我写了篇关于 HTTP 的文章,文章中讲述了 HTTP的特点,HTTP 的报文,HTTP 的请求方式等知识,接下来,深入了,我们就关于 HTTP 引发的面试题来进行入手,一起来看一下吧!1. HTTP 是如...【详细内容】
2020-01-28  Tags: TCP连接  点击:(72)  评论:(0)  加入收藏
前言1:负载均衡技术简介 2:谈谈HTTP优化和加速特性带来的益处 负载均衡技术简介现代企业信息化应用越来越多的采用B/S应用架构来承载企业的关键业务,因此,确保这些任务的可靠运...【详细内容】
2019-12-31  Tags: TCP连接  点击:(67)  评论:(0)  加入收藏
一道经典的面试题是从 URL 在浏览器被输入到页面展现的过程中发生了什么,大多数回答都是说请求响应之后 DOM 怎么被构建,被绘制出来。 图片来自 Pexels 但是你有没有想过,收到...【详细内容】
2019-12-23  Tags: TCP连接  点击:(113)  评论:(0)  加入收藏
1、什么是HOLHOL是Head of line blocking的意思,在互联网领域,包括HTTP head of line blocking和 TCP head of line blocking。1.1 HTTP Head of line blockingWikipedia对HTT...【详细内容】
2019-08-20  Tags: TCP连接  点击:(345)  评论:(0)  加入收藏
▌简易百科推荐
HTTP 报文是在应用程序之间发送的数据块,这些数据块将通过以文本形式的元信息开头,用于 HTTP 协议交互。请求端(客户端)的 HTTP 报文叫做请求报文,响应端(服务器端)的叫做响应...【详细内容】
2021-12-27  程序员蛋蛋    Tags:HTTP 报文   点击:(0)  评论:(0)  加入收藏
一 网络概念:1.带宽: 标识网卡的最大传输速率,单位为 b/s,比如 1Gbps,10Gbps,相当于马路多宽2.吞吐量: 单位时间内传输数据量大小单位为 b/s 或 B/s ,吞吐量/带宽,就是网络的使用率...【详细内容】
2021-12-27  码农世界    Tags:网络   点击:(2)  评论:(0)  加入收藏
1.TCP/IP 网络模型有几层?分别有什么用? TCP/IP网络模型总共有五层 1.应用层:我们能接触到的就是应用层了,手机,电脑这些这些设备都属于应用层。 2.传输层:就是为应用层提供网络...【详细内容】
2021-12-22  憨猪哥08    Tags:TCP/IP   点击:(28)  评论:(0)  加入收藏
TCP握手的时候维护的队列 半连接队列(SYN队列) 全连接队列(accepted队列)半连接队列是什么?服务器收到客户端SYN数据包后,Linux内核会把该连接存储到半连接队列中,并响应SYN+ACK报...【详细内容】
2021-12-21  DifferentJava    Tags:TCP   点击:(9)  评论:(0)  加入收藏
你好,这里是科技前哨。 随着“元宇宙”概念的爆火,下一代互联网即将到来,也成了互联网前沿热议的话题,12月9日美国众议院的听证会上,共和党议员Patrick McHenry甚至宣称,要调整现...【详细内容】
2021-12-17  王煜全    Tags:Web3   点击:(14)  评论:(0)  加入收藏
一、demopublic static void main(String[] args) throws Exception { RetryPolicy retryPolicy = new ExponentialBackoffRetry( 1000, 3);...【详细内容】
2021-12-15  程序员阿龙    Tags:Curator   点击:(20)  评论:(0)  加入收藏
一、计算机网络概述 1.1 计算机网络的分类按照网络的作用范围:广域网(WAN)、城域网(MAN)、局域网(LAN);按照网络使用者:公用网络、专用网络。1.2 计算机网络的层次结构 TCP/IP四层模...【详细内容】
2021-12-14  一口Linux    Tags:网络知识   点击:(30)  评论:(0)  加入收藏
无论是在外面还是在家里,许多人都习惯了用手机连接 WiFi 进行上网。不知道大家有没有遇到过这样一种情况, 明明已经显示成功连接 WiFi,却仍然提示“网络不可用”或“不可上网”...【详细内容】
2021-12-14  UGREEN绿联    Tags:WiFi   点击:(25)  评论:(0)  加入收藏
拉了千兆宽带,买了标称 1300Mbps 的无线路由器,为什么 WiFi 还是跑不满千兆?要回答这个问题,我们先得知道这个 1300Mbps 是怎么来的。开始回答之前先说明一下,这期只讲 802.11ac,...【详细内容】
2021-12-14  Ubiquiti优倍快    Tags:WiFi   点击:(77)  评论:(0)  加入收藏
问题背景IPv6环境下,在浏览器中通过http://[vip:port]访问web业务,提示无法访问此网站,[vip]的响应时间过长。分析过程之前碰到过多次在PC浏览器上无法访问vip的情况,排查方法也...【详细内容】
2021-12-13  云原生知识星球    Tags:网络问题   点击:(27)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条