想必大家已经知道我的niao性,搞个标题,就是不喜欢立马回答。
就是要搞一大堆原理性的东西,再回答标题的问题。
说这个是因为我这次会把问题的答案就放到开头吗?
不!
我就不!
但是大家可以直接根据目录看自己感兴趣的部分。
之所以要先铺垫一些原理,还是希望大家能先看些基础的,再慢慢循序渐进,这样有利于建立知识体系。多一点上下文,少一点gap
。
好了,进入正题。
下面是这篇文章的目录。
收到RST就一定会断开连接吗
我们都知道TCP正常情况下断开连接是用四次挥手,那是正常时候的优雅做法。
但异常情况下,收发双方都不一定正常,连挥手这件事本身都可能做不到,所以就需要一个机制去强行关闭连接。
RST 就是用于这种情况,一般用来异常地关闭一个连接。它是一个TCP包头中的标志位。
正常情况下,不管是发出,还是收到置了这个标志位的数据包,相应的内存、端口等连接资源都会被释放。从效果上来看就是TCP连接被关闭了。
而接收到 RST的一方,一般会看到一个 connection reset
或 connection refused
的报错。
TCP报头RST位
我们知道内核跟应用层是分开的两层,网络通信功能在内核,我们的客户端或服务端属于应用层。应用层只能通过 send/recv
与内核交互,才能感知到内核是不是收到了RST
。
当本端收到远端发来的RST
后,内核已经认为此链接已经关闭。
此时如果本端应用层尝试去执行 读数据操作,比如recv
,应用层就会收到 Connection reset by peer 的报错,意思是远端已经关闭连接。
ResetByPeer
如果本端应用层尝试去执行写数据操作,比如send
,那么应用层就会收到 Broken pipe 的报错,意思是发送通道已经坏了。
BrokenPipe
这两个是开发过程中很经常遇到的报错,感觉大家可以把这篇文章放进收藏夹吃灰了,等遇到这个问题了,再打开来擦擦灰,说不定对你会有帮助。
RST一般出现于异常情况,归类为 对端的端口不可用 和 socket提前关闭。
端口不可用分为两种情况。要么是这个端口从来就没有"可用"过,比如根本就没监听(listen)过;要么就是曾经"可用",但现在"不可用"了,比如服务突然崩了。
TCP连接未监听的端口
服务端listen
方法会创建一个sock
放入到全局的哈希表
中。
此时客户端发起一个connect
请求到服务端。服务端在收到数据包之后,第一时间会根据IP和端口从哈希表里去获取sock
。
全局hash表
如果服务端执行过listen
,就能从全局哈希表
里拿到sock
。
但如果服务端没有执行过listen
,那哈希表
里也就不会有对应的sock
,结果当然是拿不到。此时,正常情况下服务端会发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包给对端。
一个数据包连校验都不能通过,那这个包,多半有问题。
有可能是在发送的过程中被篡改了,又或者,可能只是一个胡乱伪造的数据包。
五层网络,不管是哪一层,只要遇到了这种数据,推荐的做法都是默默扔掉,而不是去回复一个消息告诉对方数据有问题。
如果对方用的是TCP,是可靠传输协议,发现很久没有ACK
响应,自己就会重传。
如果对方用的是UDP,说明发送端已经接受了“不可靠会丢包”的事实,那丢了就丢了。
因此,数据包异常的情况下,默默扔掉,不发RST
,非常合理。
还是不能理解?那我再举个例子。
正常人喷你,他说话条理清晰,主谓宾分明。此时你喷回去,那你是个充满热情,正直,富有判断力的好人。
而此时一个憨憨也想喷你,但他思维混乱,连话都说不清楚,一直阿巴阿巴的,你虽然听不懂,但大受震撼,此时你会?
一般来说最优选择是B,毕竟你理他,他反而来劲。
这下,应该就懂了。
端口不可用的场景里,除了端口未监听以外,还有可能是从前监听了,但服务端机器上做监听操作的应用程序突然崩了,此时客户端还像往常一样正常发送消息,服务器内核协议栈收到消息后,则会回一个RST。在开发过程中,这种情况是最常见的。
比如你的服务端应用程序里,弄了个空指针,或者数组越界啥的,程序立马就崩了。
TCP监听了但崩了
这种情况跟端口未监听本质上类似,在服务端的应用程序崩溃后,原来监听的端口资源就被释放了,从效果上来看,类似于处于CLOSED
状态。
此时服务端又收到了客户端发来的消息,内核协议栈会根据IP端口,从全局哈希表里查找sock
,结果当然是拿不到对应的sock
数据,于是走了跟上面"端口未监听"时一样的逻辑,回了个RST
。客户端在收到RST后也释放了sock资源,从效果上来看,就是连接断了。
上面这张图,服务端程序崩溃后,如果客户端再有数据发送,会出现RST
。但如果在客户端和服务端中间再加一个nginx
,就像下图一样。
RST与502
nginx
会作为客户端和服务端之间的"中间人角色",负责转发请求和响应结果。但当服务端程序崩溃,比如出现野指针或者OOM的问题,那转发到服务器的请求,必然得不到响应,后端服务端还会返回一个RST
给nginx
。nginx
在收到这个RST
后会断开与服务端的连接,同时返回客户端一个502
错误码。
所以,出现502问题,一般情况下都是因为后端程序崩了,基于这一点假设,去看看监控是不是发生了OOM或者日志是否有空指针等报错信息。
这种情况分为本端提前关闭,和远端提前关闭。
如果本端socket
接收缓冲区还有数据未读,此时提前close()
socket。那么本端会先把接收缓冲区的数据清空,然后给远端发一个RST。
recvbuf非空
远端已经close()
了socket
,此时本端还尝试发数据给远端。那么远端就会回一个RST。
close()触发TCP四次挥手
大家知道,TCP是全双工通信,意思是发送数据的同时,还可以接收数据。
Close()
的含义是,此时要同时关闭发送和接收消息的功能。
客户端执行close()
, 正常情况下,会发出第一次挥手FIN,然后服务端回第二次挥手ACK。如果在第二次和第三次挥手之间,如果服务方还尝试传数据给客户端,那么客户端不仅不收这个消息,还会发一个RST消息到服务端。直接结束掉这次连接。
我们知道TCP是可靠传输,意味着本端发一个数据,远端在收到这个数据后就会回一个ACK
,意思是"我收到这个包了"。
而RST,不需要ACK确认包。
因为RST
本来就是设计来处理异常情况的,既然都已经在异常情况下了,还指望对方能正常回你一个ACK
吗?可以幻想,不要妄想。
但问题又来了,网络环境这么复杂,丢包也是分分钟的事情,既然RST包不需要ACK来确认,那万一对方就是没收到RST,会怎么样?
RST丢失
RST丢了,问题不大。比方说上图服务端,发了RST之后,服务端就认为连接不可用了。
如果客户端之前发送了数据,一直没等到这个数据的确认ACK,就会重发,重发的时候,自然就会触发一个新的RST包。
而如果客户端之前没有发数据,但服务端的RST丢了,TCP有个keepalive机制,会定期发送探活包,这种数据包到了服务端,也会重新触发一个RST。
RST丢失后keepalive
先说结论,不一定会断开。我们看下源码。
// 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包的seq不在这个窗口范围内,那就会被丢弃。
正常情况下客户端服务端双方可以通过RST来断开连接。假设不做seq校验,如果这时候有不怀好意的第三方介入,构造了一个RST包,且在TCP和IP等报头都填上客户端的信息,发到服务端,那么服务端就会断开这个连接。同理也可以伪造服务端的包发给客户端。这就叫RST攻击。
RST攻击
受到RST攻击时,从现象上看,客户端老感觉服务端崩了,这非常影响用户体验。
如果这是个游戏,我相信多崩几次,第二天大家就不来玩了。
实际消息发送过程中,接收窗口是不断移动的,seq也是在飞快的变动中,此时第三方是比较难构造出合法seq的RST包的,那么通过这个seq校验,就可以拦下了很多不合法的消息。
不是,只是增加了攻击的成本。但如果想搞,还是可搞的。
以下是面向监狱编程的环节。
希望大家只了解原理就好了,不建议使用。
相信大家都不喜欢穿着蓝白条纹的衣服,拍纯狱风的照片。
从上面可以知道,不是每一个RST包都会导致连接重置的,要求是这个RST包的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
。
challenge ack抓包上图是抓包的结果,用scapy
随便伪造一个seq=5
的包发到服务端(端口9090
),服务端回复一个带有正确seq值的challenge ack
包给客户端(端口8888
)。
上面提到的这个challenge ack ,仿佛为盲猜seq的老哥们打开了一个新世界。
在获得这个challenge ack
后,攻击程序就可以以ack值为基础,在一定范围内设置seq,这样造成RST攻击的几率就大大增加了。
利用ChallengeACK的RST攻击
TCP旁路攻击分析与重现 - https://www.cxyzjd.com/article/qq_27446553/52416369
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/S1UoFQcXa0nvCXpos2c51Q
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。