Saki's 研究记录

Linux 服务器下查看网络连接及抓包分析

字数统计: 2k阅读时长: 8 min
2023/12/03

环境信息

OS: macOS 14.1.1 (23B81)
Docker: 24.0.5
Image: mysql(8.0.21)

Linux 服务器下查看网络连接的状态

Linux 服务器下查看网络连接的状态, 通过 netstat 命令查看了当前tcp链接的情况(本地测试,线上实际值大的多)

1
2
3
4
5
# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
SYN_SENT 106
CLOSE_WAIT 1
TIME_WAIT 27
ESTABLISHED 60

常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT表示被动关闭。

TCP建立连接和断开连接的整个过程

  • CLOSED: 初始状态, socket连接未被使用
  • LISTENING: 正在监听进入的连接
  • SYN_SENT: 正在试着建立连接
  • SYN_RECEIVED: 进行连接初始同步
  • ESTABLISHED: 连接已被建立
  • CLOSE_WAIT: 远程服务器关闭连接,正在等待socket连接的关闭
  • FIN_WAIT_1: socket连接关闭,正在关闭连接
  • CLOSING: 先关闭本地socket连接,然后关闭远程socket连接,最后等待确认信息(特殊的状态,也比较少见,正常情况下不会出现)
  • LAST_ACK: 远程服务器关闭后,等待确认信号
  • FIN_WAIT_2: socket连接关闭后,等待来自远程计算器的关闭信号
  • TIME_WAIT: 连接关闭后,等待远程服务器关闭重发

可以使用 netstat -an 命令查看链接状态,进行了检查。
例如,查看所有 CLOSE_WAIT 的链:

1
2
3
# netstat -an | grep TIME_WAIT
tcp 0 0 127.0.0.1:4000 127.0.0.1:52844 TIME_WAIT
tcp 0 0 10.0.20.2:80 123.183.224.77:34770 TIME_WAIT

抓自己的服务器的包

Linux 系统有很多块网卡,可以用 ifconfig -a(显示或设置网络设备) 打出来,自己写的服务器一定要监听 lo(本地环路接口)这块网卡,不然监听上网的那块网卡是监听不到的。

eth0eth1eth2…代表网卡一,网卡二,网卡三…
lo 代表 127.0.0.1,即 localhost

查找本地 lo 网卡:

1
2
3
4
5
# ifconfig -a | grep lo
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0

另外也可以使用nmcli dev status命令查看网卡状态:

1
2
3
4
# nmcli dev status
DEVICE TYPE STATE CONNECTION
eth0 ethernet 已连接 System eth0
lo loopback 未托管 --

TCP 抓包分析下

tcpdump 是对网络上的数据包进行截获的包分析工具,它支持针对网络层、协议、主机、网络或端口的过滤,并提供 andornot 等逻辑语句来去掉无用的信息。
例如,监听本地Docker运行的MySQL服务(端口 13306 )的数据包:

1
sudo tcpdump  -i lo0 host 127.0.0.1 and port 13306

参数说明:
X: 包的内容也可以查看的到
i: 指定要监听的网卡
-n: 指定将每个监听到数据包中的域名转换成 IP 地址后显示,不把网络地址转换成名字
-nn: 指定将每个监听到的数据包中的域名转换成 IP、端口从应用名称转换成端口号后显示
-v: 输出一个稍微详细的信息,例如在 IP 包中可以包括 TTL 和服务类型的信息
-S: 打印绝对时间戳
-w mysql.pcap: 将抓到的包写入文件,生成的 pcap 文件可以用 tcpdump 或者 wireshark 之类的网络流量分析工具打开
host: 是 IP 地址
port: 是端口号

抓包结果:

1
2
3
21:29:40.148304 IP localhost.59994 > localhost.13306: Flags [S], seq 1224152147, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 2485129604 ecr 0,sackOK,eol], length 0
21:29:40.148352 IP localhost.13306 > localhost.59994: Flags [S.], seq 2879060989, ack 1224152148, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 1613321693 ecr 2485129604,sackOK,eol], length 0
21:29:40.148358 IP localhost.59994 > localhost.13306: Flags [.], ack 1, win 6379, options [nop,nop,TS val 2485129604 ecr 1613321693], length 0

FLAG[S]意思是包中的标志是SYN,这是一个请求包。FLAG[.]一般是指明这是一个单一的 ack 确认包。

NOTE:Flags are some combination of S (SYN), F (FIN), P (PUSH), R (RST), W (ECN CWR) or E (ECN-Echo), or a single ‘.’ (no flags)
options 表示选项
mss 表示是发送端通告的最大报文长度
sackOK 表示发送端支持 SACK 选项,SACK 选项是为了更好的确定数据的准确接收的
TS val 发送端的时间戳 ecr 接收端的时间戳
wscale 表示窗口因子大小

如果 tcpdump 无法抓到数据包,可以考虑以下几种原因:

  • 没有安装libpcap库: tcpdump需要使用libpcap库来抓取数据包,如果没有安装,请先安装。
  • 没有足够的权限: tcpdump需要使用root权限才能抓取数据包,使用sudo命令运行tcpdump
  • 网络配置问题: 如果网络配置不正确,tcpdump可能无法抓取数据包。请检查网络配置是否正确。
  • 无数据: 如果当前网络中没有数据流动,tcpdump也无法抓取数据包。

如果以上原因都已经排除,请尝试使用其他工具来检测网络是否正常。

抓包分析

TCP 三次握手

  • step 1:
    1
    21:29:40.148304 IP localhost.59994 > localhost.13306: Flags [S], seq 1224152147, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 2485129604 ecr 0,sackOK,eol], length 0
    客户端执行系统调用 connect() 发出 SYN 请求建立 TCP 连接,此时客户端的 TCP 端口状态为 SYN_SENT(表示请求连接)。
  • step 2:
    1
    21:29:40.148352 IP localhost.13306 > localhost.59994: Flags [S.], seq 2879060989, ack 1224152148, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 1613321693 ecr 2485129604,sackOK,eol], length 0
    服务端执行系统调用 listen() 监听到 SYN 请求,TCP 端口状态从 LISTEN 转为 SYN_RCVD 并第一次响应 ACK
  • step 3:
    1
    21:29:40.148358 IP localhost.59994 > localhost.13306: Flags [.], ack 1, win 6379, options [nop,nop,TS val 2485129604 ecr 1613321693], length 0
    客户端接收到 ACK 响应之后,TCP 端口状态变成 ESTABLISHED(已建立连接,表示通信双方正在通信),再给服务端发送 ACK。服务端接收到 ACK 之后,服务端的 TCP 端口状态为 ESTABLISHED

为什么要握手要三次?
防止失效的连接请求突然传到服务器端,让服务器端误认为要建立连接。

数据传输

双方建立通信之后,ClienServer 正式发出请求:

1
2
22:56:02.009399 IP localhost.13306 > localhost.58189: Flags [P.], seq 1:79, ack 1, win 6379, options [nop,nop,TS val 1886650872 ecr 1513261871], length 78
22:56:02.009420 IP localhost.58189 > localhost.13306: Flags [.], ack 79, win 6378, options [nop,nop,TS val 1513261884 ecr 1886650872], length 0

Client => Serverseq = x+1, ack = y+1 继承了第三次连接的 seqack number
请求长度 length: 78,seq 1:79 == seq 1:[1+length]

四次挥手

TCP 协议规定,对于已经建立的连接,收发双方要进行四次挥手才能成功断开连接,如果缺少了其中某个步骤,都会使连接处于假死状态,连接本身所占用的资源不会被释放。

  • step 1:

    1
    21:32:25.591866 IP localhost.59994 > localhost.13306: Flags [F.], seq 27, ack 1, win 6334, options [nop,nop,TS val 2485295047 ecr 1613321738], length 0

    Client 提出断开连接请求 FIN(Flags [F.]),ClientTCP 端口进入 FIN_WAIT_1 状态。

  • step 2,3:

    1
    2
    21:32:25.591885 IP localhost.13306 > localhost.59994: Flags [.], ack 28, win 6368, options [nop,nop,TS val 1613487136 ecr 2485295047], length 0
    21:32:25.592202 IP localhost.13306 > localhost.59994: Flags [F.], seq 1, ack 28, win 6368, options [nop,nop,TS val 1613487136 ecr 2485295047], length 0

    Server 发送 ACK 应答,ServerTCP 端口进入 CLOSE_WAIT 状态,ClientTCP 端口进入 FIN_WAIT_2 状态。几乎同时 Server 还发送了一个 FIN 端口连接请求,进入 LAST_ACK 状态。

  • step 4:

    1
    21:32:25.592247 IP localhost.59994 > localhost.13306: Flags [.], ack 2, win 6334, options [nop,nop,TS val 2485295047 ecr 1613487136], length 0

    Client 进行 ACK 应答(LAST ACK),进入 TIME_WAIT(两倍的分段最大生存期),并最终变成 CLOSED 状态。

为什么进入 TIME_WAIT 状态后必须等待 2MSL 后才能返回 CLOSED 状态?

  • 保证发送的最后一个 ACK 报文段能达到
  • 防止失效的报文段出现在连接中

参考

以上。

CATALOG
  1. 1. 环境信息
  2. 2. Linux 服务器下查看网络连接的状态
  3. 3. 抓自己的服务器的包
  4. 4. TCP 抓包分析下
  5. 5. 抓包分析
    1. 5.1. TCP 三次握手
    2. 5.2. 数据传输
    3. 5.3. 四次挥手
  6. 6. 参考