新年伊始,常安常乐。
实验目的
基于 packetdrill TCP 三次握手脚本,通过构造模拟客户端场景,测试客户端 SYN 超时重传现象。
基础脚本
# cat tcp_3hs_007.pkt
// TCP 基础之三次握手
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <...>
+0.01 < S. 0:0(0) ack 1 win 10000 <mss 1000>
+0 > . 1:1(0) ack 1
基础测试
测试脚本很简单,注释掉或者删除 SYN/ACK 和 ACK ,并使得程序不要退出即可,这样在客户端发起 connect(),协议栈发出 SYN 后,没有得到响应,即会进入超时重传现象。
# cat tcp_rto_001.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 connect(3, ..., ...) = -1 EINPROGRESS (Operation now in progress)
+0 > S 0:0(0) <...>
+0 `sleep 10000000`
#
执行脚本,保持不退出状态
# packetdrill tcp_rto_001.pkt
同时 tcpdump 捕获数据包,等待一段时间后,即可捕获到所有的 SYN 超时重传数据包。
# tcpdump -i any -nn port 8080
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
19:30:54.320911 tun0 Out IP 192.168.76.212.50560 > 192.0.2.1.8080: Flags [S], seq 1216637537, win 64240, options [mss 1460,sackOK,TS val 176847787 ecr 0,nop,wscale 7], length 0
19:30:55.352720 tun0 Out IP 192.168.76.212.50560 > 192.0.2.1.8080: Flags [S], seq 1216637537, win 64240, options [mss 1460,sackOK,TS val 176848819 ecr 0,nop,wscale 7], length 0
19:30:57.368698 tun0 Out IP 192.168.76.212.50560 > 192.0.2.1.8080: Flags [S], seq 1216637537, win 64240, options [mss 1460,sackOK,TS val 176850835 ecr 0,nop,wscale 7], length 0
19:31:01.624696 tun0 Out IP 192.168.76.212.50560 > 192.0.2.1.8080: Flags [S], seq 1216637537, win 64240, options [mss 1460,sackOK,TS val 176855091 ecr 0,nop,wscale 7], length 0
19:31:09.816700 tun0 Out IP 192.168.76.212.50560 > 192.0.2.1.8080: Flags [S], seq 1216637537, win 64240, options [mss 1460,sackOK,TS val 176863283 ecr 0,nop,wscale 7], length 0
19:31:25.944705 tun0 Out IP 192.168.76.212.50560 > 192.0.2.1.8080: Flags [S], seq 1216637537, win 64240, options [mss 1460,sackOK,TS val 176879411 ecr 0,nop,wscale 7], length 0
19:31:58.200703 tun0 Out IP 192.168.76.212.50560 > 192.0.2.1.8080: Flags [S], seq 1216637537, win 64240, options [mss 1460,sackOK,TS val 176911667 ecr 0,nop,wscale 7], length 0
期间 ss 输出的 TCP 状态
# ss -nto
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
SYN-SENT 0 1 192.168.76.212:50560 192.0.2.1:8080 timer:(on,1.980ms,2)
通过 tcpdump 捕获的数据包跟踪文件,再通过 Wireshark 打开如下,共计重传 6 次,间隔时间以 1s、2s、4s、8s、16s、32s 翻倍递增,最后一次超时重传后还会持续 64s,所以 SYN-SENT 状态也就维持了大约 127s 左右。
# date; nc 1.1.1.1 1111; date
Thu Jan 11 08:32:36 PM CST 2024
Thu Jan 11 08:34:46 PM CST 2024
#
对于不同的操作系统以及版本,初始超时重传的时间一般有 1s 和 3s 两种,且通常不能更改,本次测试系统的初始超时重传时间是 1s。
客户端的 SYN 报文最大重传次数由 tcp_syn_retries 内核参数控制,这个参数是可以自定义的,本次测试系统的 SYN 报文最大重传次数为 6 次,相关参数如下:
# uname -r
5.15.0-76-generic
其中HZ的值为1000,即初始值为1秒。
#define TCP_TIMEOUT_INIT ((unsigned)(1*HZ)) /* RFC6298 2.1 initial RTO value */
# cat /proc/sys/net/ipv4/tcp_syn_retries
6
# sysctl -a | grep syn_ret
=
6
#
扩展测试一
Linux ss -i 参数能展现出很多 TCP 信息,因此继续扩展测试一下。考虑到 TCP SYN 重传次数太多,总的超时时间太长,因此先把 SYN 重传次数改为 3。
# sysctl -q net.ipv4.tcp_syn_retries=3
#
仍然是通过 packetdrill 执行 tcp_rto_001.pkt 脚本,保持不退出状态,同时间通过 tcpdump 以及 ss 命令捕获的数据包和 TCP 信息如下。
一共4个SYN数据包,包括原始1个+超时重传3个。
# tcpdump -i any -nn port 8080
tcpdump
:
data link type LINUX_SLL2
tcpdump
:
verbose output suppressed, use -v[v]... for full protocol decode
listening
on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
17
:
45:06.971565 tun0 Out IP 192.168.230.225.33896 > 192.0.2.1.8080: Flags [S], seq 2346089904, win 64240, options [mss 1460,sackOK,TS val 1071063904 ecr 0,nop,wscale 7], length 0
17
:
45:07.992707 tun0 Out IP 192.168.230.225.33896 > 192.0.2.1.8080: Flags [S], seq 2346089904, win 64240, options [mss 1460,sackOK,TS val 1071064925 ecr 0,nop,wscale 7], length 0
17
:
45:10.008697 tun0 Out IP 192.168.230.225.33896 > 192.0.2.1.8080: Flags [S], seq 2346089904, win 64240, options [mss 1460,sackOK,TS val 1071066941 ecr 0,nop,wscale 7], length 0
17
:
45:14.104694 tun0 Out IP 192.168.230.225.33896 > 192.0.2.1.8080: Flags [S], seq 2346089904, win 64240, options [mss 1460,sackOK,TS val 1071071037 ecr 0,nop,wscale 7], length 0
1s),第一次超时重传(rto 2s),第二次超时重传(rto 4s),第三次超时重传(rto 8s),8秒之后退出SYN-SENT状态。
# for i in `seq 1 100`; do ss -i dport 8080 | grep rto | awk '{print $2}'; sleep 1; done
rto
:
1000
rto
:
2000
rto
:
2000
rto
:
4000
rto
:
4000
rto
:
4000
rto
:
4000
rto
:
8000
rto
:
8000
rto
:
8000
rto
:
8000
rto
:
8000
rto
:
8000
rto
:
8000
rto
:
8000
简单学习了下 ebpf bcc,有几个工具也可以观测到相关现象。
SYN
重传三次,状态 SYN_SENT
# ./tcpretrans.py
Tracing retransmits ... Hit Ctrl-C to end
TIME PID IP LADDR:LPORT T> RADDR:RPORT STATE
20
:
46
:
01
0
4
192.168.241.152:37628
R>
192.0.2.1:8080
SYN_SENT
20
:
46
:
03
0
4
192.168.241.152:37628
R>
192.0.2.1:8080
SYN_SENT
20
:
46
:
07
0
4
192.168.241.152:37628
R>
192.0.2.1:8080
SYN_SENT
#
# ./tcpretrans.py -c
Tracing retransmits ... Hit Ctrl-C to end
^C
LADDR:LPORT RADDR:RPORT RETRANSMITS
[
192.168.171.239
]
#59302 <-> [192.0.2.1]#8080 3
#
SYN_SENT到CLOSE状态变化,用时
15477
.
044ms
,也就是约
15
秒,
1
+
2
+
4
+
8
=
15
.
# ./tcpstates.py
SKADDR C-PID C-COMM LADDR LPORT RADDR RPORT OLDSTATE -> NEWSTATE MS
ffff8c7e6d6a2bc0
30738
packetdril
0.0.0.0
41135
0.0.0.0
0
CLOSE -> LISTEN
0
.
000
ffff8c7e6d6a3d40
30738
packetdril
192.168.231.85
0
192.0.2.1
8080
CLOSE -> SYN_SENT
0
.
000
ffff8c7e6d6a3d40
0
swapper/
0
192.168.231.85
39682
192.0.2.1
8080
SYN_SENT -> CLOSE
15477
.
044
ffff8c7e6d6a2bc0
30738
packetdril
0.0.0.0
41135
0.0.0.0
0
LISTEN -> CLOSE
26425
.
808
扩展测试二
上面提到说不同的操作系统的区别,正好手上有测试环境,顺便验证了一个 Windows 和 Mac 系统下的 SYN 超时重传现象。
Windows
Windows 10 企业版,使用 telnet 测试,初始超时重传的时间 3s ,重传两次,同样超时时间会翻倍。
λ
telnet
1
.1
.1
.1
1111
正在连接1
.1
.1
.1
...无法打开到主机的连接。在端口 1111: 连接失败
λ
通过 netsh 可查看 tcp 全局参数,可看到初始 RTO:3000,最大 SYN 重新传输次数 2。
λ netsh int tcp
show
global
查询活动状态...
TCP 全局参数
----------------------------------------------
接收端缩放状态 : enabled
接收窗口自动调节级别 :
normal
附加拥塞控制提供程序 :
default
ECN 功能 : disabled
RFC
1323
时间戳 : disabled
初始 RTO :
3000
接收段合并状态 : enabled
非 Sack Rtt 复原 : disabled
最大 SYN 重新传输次数 :
2
快速打开 : enabled
快速打开回退 : enabled
HyStart : enabled
节奏配置文件 :
off
Mac
macOS Ventura,版本 13.6.3,使用 nc 命令测试,初始超时重传的时间为 1s ,重传十次,间隔时间不太一样,前五次保持 1s 超时时间,后五次开始翻倍,2s、4s、8s、16s、32s。
nc 1.1.1.1 1111
貌似和 Linux 一样,初始超时重传的时间一般不能更改,而超时重传的次数由以下参数控制。
sysctl -a | grep rexmit
net.inet.tcp.broken_peer_syn_rexmit_thres: 10
原文始发于微信公众号(Echo Reply):Wireshark & Packetdrill | TCP 三次握手之 SYN 超时重传
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论