跳至主要內容

基于WireShark的网络分析

PPLong大约 9 分钟

基于WireShark的网络分析

一直对网络抓包这块很感兴趣,并且WireShark也一直吃灰,借这个机会通过WireShark分析一下传输层和网络层(TCP、IP)具体到数据包的封装细节和具体的工作细节

介绍

WireShark

前称Ethereal,是一个网络封包分析软件,截取网络封装包,尽可能详细地显示网络封包信息 开源开源开源!

网络分析

三次握手

主要看传输层的分析

Hello

报文首部结构在这里,可以一一对照

image-20210914163318606
image-20210914163318606
image-20220331162210966
image-20220331162210966

解释一些之前不太懂的含义:

  • 源端口号是从Linux较靠后的“客户端端口”范围随机提供的(为什么有源端口?->找得到回来的路),到哪去,肯定是到为HTTP开放的80端口上。
  • 序列号:客户端系统随机产生的32位,这里wireshark优化了一下 提供了相对的值
  • 确认序列号:为0,因为是我请求,不需要确认什么
  • Flag:
    • SYN打开表示建立连接,仅SYN为1的包为SYN包,只有服务端受到客户端发来的SYN包,才可以建立联系
  • 接受窗口大小:指定窗口大小
  • 检验和:TCP整体检验(包括载荷)[12字节伪首部] TCP首部校验和计算三部分:TCP首部+TCP数据+TCP伪首部
  • 选项
    • MSS:[限定载荷数据长度]设置了自己的MSS值,以便与服务端进行沟通,选择最小值
    • SACK:允许优化的重传方式
    • Window Scale:表明自己的窗口扩大因子

I hear u

image-20220331165900512
image-20220331165900512

服务器返回的,从服务端的80端口到客户端指定的端口。这里Sequence Number是服务端选定的,确认号是在客户端的序列号的+1;由于是希望建立连接,所以SYN=1,且表示确认收到信息,所以ACK=1;表明了自己支持的接受窗口大小(这里<客户端);表明了自己不同的MSS。其他信息意义都相同。也在Option中表明自己是否支持SACK

Let's talk

image-20220331170950616
image-20220331170950616

客户端向服务端表明我知道你能听到我了

序列号在之前自己发的原有基础+1(与学的不同?)确认号在对方发来的序列号基础上加1;Window size为正确的factor * size值(为什么是这个值?)

理解Ghost Byte

正常传输

发送HTTP请求

image-20220331171820581
image-20220331171820581

客户端向服务器发送HTTP GET请求,请求某URL下的资源;从这里开始就有payload了;确认号为之前服务端发来的序列号 +1;PSH(表明是该请求的最后一个数据包,方便通知应用程序及时从网络缓存中去拿数据)和ACK(表示响应,我收到你之前的消息了)都打开;Window Size 采用之前规定好的(但没有factor了,为啥?怎样能确保最终为size*factor?)

响应

服务端先回应我收到你HTTP请求的响应,序列号在服务端之前发送给客户端的基础上+1,同时还会有window size的调整。然后就另起数据包,开始发数据了

image-20220331190932943
image-20220331190932943

可以看到,确认号和序列号都不变,主要是有了payload,开始负载数据。且之后的传输中,确认号都等于对方的序列号+payload长度

窗口的应用

发现了有趣之处image-20220331191642232,此时Server同时传了两个TCP PDU给Client,但Client只用了一个ACK包就表明受到,这里就涉及到TCP的窗口知识了

最终在传完一整个请求的数据包时,Client返回最后一个数据包的ACK包,随之Server向Client传HTTP 200的响应包,(PSH和ACK = 1)到这里,对服务器来说,才算是把一个请求响应结束!。Client返回ACK包表示收到,(猜测可能会在这里浏览器会初步加载没有css js等的页面)。

然后Client可以开始发送新的HTTP请求,比如get css文件等

TCP Stream

在这里,对一个返回数据量较大的HTTP请求,WireShark会显示"TCP segment of a reassembled PDU",这里其实对TCP的知识还不太熟悉。今天看了下先勉强解释下,与IP分片不同,因为一个返回数据量大的请求会把数据从TCP层拆分,他们具有不同的确认号和序列号,以类似流的形式持续达到目的端口,可在WireShark里“Follow TCP Streams”能够看到完整的数据包(对于一个网页请求,完整数据就是一个网页的html代码)。
🔗 WireShark的对TCP PDU的解释open in new window 🔗 What is Packet Reassemblyopen in new window

对之前的数据包来说,WireShark会标注[Reassembled PDU in frame:xxx],其中xxx就代表的是HTTP 200等响应的回复,代表这是一个完整的数据

一个ACK包确认多个发送包

例如这里便用一个ACK确认了两个发送的包

image-20220405130940538
image-20220405130940538

异常情况分析

Sack

结构:在Option中,1字节表示类型,1字节表示该SACK部分占用字节长度,剩下的字节分别为左右edge的范围 一般总共为10个字节,最多携带3个SACK(留10个字节要给TimeStamp)

解释一下某些情况下Bytes in flight的猛增现象

刚看的时候一直被不按 未被ACK的包造成的Bytes in flight猛增现象搞混,分析了一下 。

image-20220405194323205
image-20220405194323205

例如这里,在经历两次Dup ACK 后,Bytes in flight从4194激增到50328,为什么?依次做减法:1122595 - 50328 = 1072267,正是考虑到其实有最初DUP ACK包的最小SACK的left edge值,说明了什么?其实是到上一个包发送的Seq + length 到 SACK left edge的差,令人惊讶的是,它把没有尚未接受的包也考虑在内

image-20220405195326028
image-20220405195326028

接下来也类似,尽管有两个SACK,但还是将最近包的Seq+length 与最初SACK的left edge做差,表明这之间的包没有收到。尽管两个SACK之间的包。

这里实践总结就是如果服务端在传大于ACK的其他包,则值为当前Seq+length - 最初SACK left edge;如果正在传当前要的ACK,则SACK最大值的left edge - Seq不过这里还未完全理解

**其实就是Server发送的,但Client还未接收到的数据的总和,不要搞得太复杂了 **

Dup Packet

TCP Duplicate Packet :指示与之前已ack的包有相同的序列号和确认号,通常是带有SACK的。比如这里:

image-20220405144319645
image-20220405144319645

Server的637693、639091的数据包过快到达(之前的包可能因时延还未到达),但Client无法根据顺序接受这个包,TCP顺序不匹配,所以Client再发送一条ACK 包,确认号和序列号都与上一条相同,所以被标注为TCP Duplicate ACK (Packet)。
更有意思的是,其TCP Option中带有637693-640489的SACK,表明之前的两个包我有了,但是顺序不对,还没接到从我ACK开始的包

过多的Duplicate Packet指明可能当前网络丢包率较高

TCP Retransmission

一个典型的案例:image-20220405151110207

这里就比较清楚,之前传的15379的包Client一直没接收到,然后Client就不断的给Server发带有更新SACK的Dup ACK Packet,Server后来接收到这个包,知道自己之前发的15379的包可能loss了,所以重新发了一个15379的包

Congestion Control

拥塞控制:服务端负荷过重,请求过多

TCP Keep-Alive

长连接 保活,一段时间内一方未回应或未发包,就会发送一个Keep-Alive包 无载荷Seq与上一个包重合,如果对方在线,就会回一个包,来确保连接继续

Window Size

遇到一个坑点,自己用的梯子,所以去访问自己的服务器时,Window Size总是1398,并且访问其他的网站时,也都显示的是从某一个指定的网站上返回过来的数据,难怪去filter host却找不到对应的数据包,原来是我代理的问题,后来把代理改了应该就没有问题了

但有时候机器性能太好、网络 太好,导致无法捕获到Window Full情况,就需要手动调整Linux TCP的窗口大小:

Linux设置Window Size
  1. route -ne查看默认路由表。
  2. sudo route del default删除默认路由表
  3. sudo route add default gw 172.20.10.1 window xxx dev wlo1,增加新的路由表,带有window约束

(会发现网页的加载都变慢许多)还有一些方法是临时修改/etc/sys/net下的文件,没试过怕改错。

四次挥手

为了直观了解挥手情况,不受其他因素影响,这里我采用的是从ssh断开连接的情况

输入exit后,敲入回车,Client给Server发回车的数据包,Server发相应的数据包在terminal中显示;Client发ack报表示已经接受

第一次挥手

Client - > Server:客户端完成了所有请求,要求断开。FIN 和 ACK均置为1,确认号还是对方发的上一个包的序列号+payload.length

image-20220331200819661
image-20220331200819661

第二次挥手

Server -> Client:表明我已经接收到你的结束请求,ACK=1

image-20220331220424436
image-20220331220424436

第三次挥手

Server->Client:表明“那我也走了”,与Client类似,设置FIN和ACK=1,这时确认号要在第二次的基础上+1

image-20220331221931572
image-20220331221931572

第四次挥手

Client -> Server:客户端说我也知道你要走了,发送后进入2MSL后,进入CLOSED状态。Server接受到这个数据包时,不再发数据包,进入CLOSED状态

对四次挥手的细节及原由 移步《计网》

Telnet过程

SSH过程

基于docker的SSH过程

FTP过程

网络拥塞控制分析

DNS过程

TLS过程

为什么在三次握手后会有Client Hello