Wireshark & Packetdrill | TCP 三次握手之 TCP Options 字段 MSS

admin 2023年11月27日10:39:10评论49 views字数 8088阅读26分57秒阅读模式

也许离答案又近了一步




实验目的

基于 packetdrill TCP 三次握手脚本,测试 TCP options 中 MSS 字段的由来。

对于 TCP options,各类介绍资料已经数不胜数,本篇就不再赘述。


实验脚本

# cat tcp_3hs_000.pkt // TCP 基础之三次握手0   socket(..., SOCK_STREAM, IPPROTO_TCP) = 3+0  setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0+0  bind(3, ..., ...) = 0+0  listen(3, 1) = 0
+0 < S 0:0(0) win 10000 <mss 1460>+0 > S. 0:0(0) ack 1 <...>+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4


实验测试一

因为 < 属于构造数据包,所以 SYN 中 TCP Options 的 MSS 理解较为简单。

+0  < S 0:0(0) win 10000 <mss 1460>// <> 表示 TCP options,mss 1460,表示设置mss 1460字节大小。

尝试 MSS 是否可以不用定义,也就是不包含任何 Options,修改脚本去除 <> 并执行,结果成功。

# cat tcp_3hs_options_mss_001.pkt

0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0+0 bind(3, ..., ...) = 0+0 listen(3, 1) = 0
+0 < S 0:0(0) win 10000+0 > S. 0:0(0) ack 1 <...>+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4# # packetdrill tcp_3hs_options_mss_001.pkt #

观察 tcpdump 抓包结果可以看到确实  SYN TCP Options 中不包含 MSS ,之后三次握手也可以建立成功,这也意味着 MSS 不是个必须存在的字段,尽管通常 TCP 三次握手中的前两次基本都带有 MSS 字段。

# tcpdump -i any -nn port 8080tcpdump: data link type LINUX_SLL2tcpdump: verbose output suppressed, use -v[v]... for full protocol decodelistening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes23:07:37.821195 ?     In  IP 192.0.2.1.58949 > 192.168.192.79.8080: Flags [S], seq 0, win 10000, length 023:07:37.821224 ?     Out IP 192.168.192.79.8080 > 192.0.2.1.58949: Flags [S.], seq 3289638781, ack 1, win 65535, options [mss 1460], length 023:07:37.831350 ?     In  IP 192.0.2.1.58949 > 192.168.192.79.8080: Flags [.], ack 1, win 10000, length 023:07:37.831423 ?     Out IP 192.168.192.79.8080 > 192.0.2.1.58949: Flags [F.], seq 1, ack 1, win 65535, length 023:07:37.831434 ?     In  IP 192.0.2.1.58949 > 192.168.192.79.8080: Flags [R.], seq 1, ack 1, win 10000, length 0

Wireshark 打开捕获文件,如下可以看到 SYN 中确实没有 TCP Options 字段。

Wireshark & Packetdrill | TCP 三次握手之 TCP Options 字段 MSS


实验测试二

熟悉 TCP 三次握手的同学,会清楚一般常看到的 TCP SYN 中的 TCP Options 实际上最少都会包含有 MSS 字段,而此外像是 Timestamps、Window Scale、SACK 等,会随着系统 TCP 参数的选项打开而随之带有相关字段。

MSS 一般会携带,但不代表必须携带,通过上面实验测试一 SYN 构造数据包时并没有写 MSS 而三次握手成功,也验证了这个结论。但是客户端 SYN 如果没有携带 MSS 字段,那么服务器收到 SYN 后如何判断客户端所能接收的最大报文长度呢?这实际上在接收端也定义好了默认值,也就是 536 字节

如何验证呢,仍然可以通过 packetdrill 测试得知,执行脚本如下,通过 getsockopt() 系统调用获取了这个 Socket 的 TCP_MAXSEG,也就是 MSS,判断值为 536,返回成功。

# cat tcp_3hs_options_mss_002.pkt  +0  socket(..., SOCK_STREAM, IPPROTO_TCP) = 3+0  setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0+0  bind(3, ..., ...) = 0+0  listen(3, 1) = 0
+0 < S 0:0(0) win 10000+0 > S. 0:0(0) ack 1 <...>+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4
+0.01 getsockopt(4, SOL_TCP, TCP_MAXSEG, [536], [4]) = 0
# # packetdrill tcp_3hs_options_mss_002.pkt #

当然也可以通过 TCP_INFO 查询 TCP 连接的状态信息得知,修改脚本如下,可知服务器 advmss 的值为 1460(服务器端通告的 MSS 仍为 1460),snd_mss 的值为 536(服务器端实际发送的 MSS 大小为 536,因为本地 MSS 1460 大于 SYN 的 MSS 536,需要取小值)。

# cat tcp_3hs_options_mss_003.pkt +0  socket(..., SOCK_STREAM, IPPROTO_TCP) = 3+0  setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0+0  bind(3, ..., ...) = 0+0  listen(3, 1) = 0
+0 < S 0:0(0) win 10000+0 > S. 0:0(0) ack 1 <...>+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4
+0 %{print (tcpi_advmss)}%+0 %{print (tcpi_snd_mss)}%# # packetdrill tcp_3hs_options_mss_003.pkt 1460536#

而 tcpdump 抓包结果是很有意思的,你不会看到任何带有 MSS 536 值的地方,甚至 SYN/ACK 中 options 的 MSS 为 1460,但是客户端和服务器之间最大传输单元确实只有 536 字节,这在后面的测试脚本中增加数据包传输时也可以得到结果。

# tcpdump -i any -nn port 8080tcpdump: data link type LINUX_SLL2tcpdump: verbose output suppressed, use -v[v]... for full protocol decodelistening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes15:59:13.088792 ?     In  IP 192.0.2.1.43217 > 192.168.227.110.8080: Flags [S], seq 0, win 10000, length 015:59:13.088813 ?     Out IP 192.168.227.110.8080 > 192.0.2.1.43217: Flags [S.], seq 2917547041, ack 1, win 64240, options [mss 1460], length 015:59:13.099317 ?     In  IP 192.0.2.1.43217 > 192.168.227.110.8080: Flags [.], ack 1, win 10000, length 015:59:13.109436 ?     Out IP 192.168.227.110.8080 > 192.0.2.1.43217: Flags [F.], seq 1, ack 1, win 64240, length 015:59:13.109446 ?     In  IP 192.0.2.1.43217 > 192.168.227.110.8080: Flags [R.], seq 1, ack 1, win 10000, length 0


这实际上在以前 Wireshark 案例文章中不少地方都有提到过,
TCP 三次握手中的 MSS 是通告,而非协商

事实上你可以打开一些包含有 TCP 三次握手的数据包文件,可以发现会有不少 SYN 和 SYN/ACK 中 MSS 不一致的地方,举两个例子说下相关过程:

  1. 客户端发送 SYN,SYN 中的 MSS 为 1200,服务器端收到了 SYN,之后回复 SYN/ACK,SYN/ACK 中的 MSS 是 1460。如果是协商,那么理论服务器所发的 SYN/ACK 中的 MSS 应为 1200(取小,因为 SYN MSS 1200 比本地 MSS 1460 小),但实际 SYN/ACK 中的 MSS 仍为 1460,这个现象说明了不是协商,而是通告,即通告本端的 MSS,而重点是此时服务器端清楚客户端所能接收的 MSS 为 1200,之后服务器发送的数据分段会按 1200 大小来分。之后服务器端的 SYN/ACK 到了客户端,客户端接收以后再发送 ACK 完成三次握手,此时客户端根据 SYN/ACK 中 MSS 1460,对比本端 MSS 1200 取小值,仍为 1200,之后客户端发送的数据分段也会按 1200 大小来分,因为 MSS 实际上是有发送和接收两方面的限制。

  2. 反过来说,客户端发送 SYN,SYN 中的 MSS 为 1460,服务器端收到了 SYN,之后回复 SYN/ACK,如果服务器端本地 MSS 为 1200,那么通告出去的 SYN/ACK 中的 MSS 为 1200 ,这个过程是无法说清是协商还是通告之后服务器发送的数据分段会按 1200 大小来分,因为 MSS 实际上是有发送和接收两方面的限制。之后服务器端的 SYN/ACK 到了客户端,客户端接收以后再发送 ACK 完成三次握手,此时仍然是没有什么现象能说明协商还是通告,但实际客户端根会据 SYN/ACK 中 MSS 1200,对比本端 MSS 1460 取小值,即 1200,在之后客户端发送的数据分段大小中会有所体现。

  3. 另外注意的是,考虑到大多客户端和服务器都是 MTU 1500 和 MSS 1460 标准,因此很多数据包文件  SYN 和 SYN/ACK 中 MSS 都是 1460 ,在两个一样值的情况下也是不好理解到底是通告还是协商这个说法的。

相较于 1 协商和通告的区别,2 理解上可能不是那么清晰,所以重点还是要理解 1。


实验测试三

基于上述 TCP 三次握手中的 MSS 是通告,而非协商的说明,来简单测试下 1 中的数据包传输现象,SYN 仍不写 MSS,以默认 536 字节测试。

修改脚本如下,write 写入 3000 字节的数据包,执行脚本成功。

# cat tcp_3hs_options_mss_004.pkt 0  socket(..., SOCK_STREAM, IPPROTO_TCP) = 3+0  setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0+0  bind(3, ..., ...) = 0+0  listen(3, 1) = 0
+0 < S 0:0(0) win 10000+0 > S. 0:0(0) ack 1 <...>+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4
+0.01 write(4, ..., 3000) = 3000# # packetdrill tcp_3hs_options_mss_004.pkt#

通过 tcpdump 抓包的结果显示,服务器端发送 3000 字节的数据,实际上是分成了三个分段,大小分别为 1072、1072、856。为什么在 snd_mss 值为 536 的情况下,看到的仍是大于 MSS 的分段?这个现象实际在日常的抓包分析中也经常会碰到,是因为网卡 offload 的缘故,而在本地发送数据包时,tcpdump 是在网卡分段之前执行,所以看到的仍是大数据分段。

# tcpdump -i any -nn port 8080tcpdump: data link type LINUX_SLL2tcpdump: verbose output suppressed, use -v[v]... for full protocol decodelistening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes20:33:53.097040 tun0  In  IP 192.0.2.1.47833 > 192.168.150.138.8080: Flags [S], seq 0, win 10000, length 020:33:53.097064 tun0  Out IP 192.168.150.138.8080 > 192.0.2.1.47833: Flags [S.], seq 3233791097, ack 1, win 64240, options [mss 1460], length 020:33:53.107162 tun0  In  IP 192.0.2.1.47833 > 192.168.150.138.8080: Flags [.], ack 1, win 10000, length 020:33:53.117251 ?     Out IP 192.168.150.138.8080 > 192.0.2.1.47833: Flags [P.], seq 1:1073, ack 1, win 64240, length 1072: HTTP20:33:53.117255 ?     Out IP 192.168.150.138.8080 > 192.0.2.1.47833: Flags [P.], seq 1073:2145, ack 1, win 64240, length 1072: HTTP20:33:53.117256 ?     Out IP 192.168.150.138.8080 > 192.0.2.1.47833: Flags [P.], seq 2145:3001, ack 1, win 64240, length 856: HTTP20:33:53.117298 ?     Out IP 192.168.150.138.8080 > 192.0.2.1.47833: Flags [F.], seq 3001, ack 1, win 64240, length 020:33:53.117307 ?     In  IP 192.0.2.1.47833 > 192.168.150.138.8080: Flags [R.], seq 1, ack 1, win 10000, length 0

通过关闭 Tun0 网卡 TSO 以及 GSO 的方式,修改脚本后执行成功。

# cat tcp_3hs_options_mss_005.pkt `ethtool -K tun0 tso off ethtool -K tun0 gso off`
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0+0 bind(3, ..., ...) = 0+0 listen(3, 1) = 0
+0 < S 0:0(0) win 10000+0 > S. 0:0(0) ack 1 <...>+0.01 < . 1:1(0) ack 1 win 10000+0 accept(3, ..., ...) = 4
+0.01 write(4, ..., 3000) = 3000# # packetdrill tcp_3hs_options_mss_005.pkt#

通过 tcpdump 抓包的结果显示,服务器端发送 3000 字节的数据,最终是分成了六个分段,大小分别为 5 个 MSS 536 字节和 1 个 320 字节。

# tcpdump -i any -nn port 8080tcpdump: data link type LINUX_SLL2tcpdump: verbose output suppressed, use -v[v]... for full protocol decodelistening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes21:59:41.037035 ?     In  IP 192.0.2.1.44835 > 192.168.68.146.8080: Flags [S], seq 0, win 10000, length 021:59:41.037050 ?     Out IP 192.168.68.146.8080 > 192.0.2.1.44835: Flags [S.], seq 812393905, ack 1, win 65535, options [mss 1460], length 021:59:41.047145 ?     In  IP 192.0.2.1.44835 > 192.168.68.146.8080: Flags [.], ack 1, win 10000, length 021:59:41.057230 ?     Out IP 192.168.68.146.8080 > 192.0.2.1.44835: Flags [.], seq 1:537, ack 1, win 65535, length 536: HTTP21:59:41.057233 ?     Out IP 192.168.68.146.8080 > 192.0.2.1.44835: Flags [P.], seq 537:1073, ack 1, win 65535, length 536: HTTP21:59:41.057238 ?     Out IP 192.168.68.146.8080 > 192.0.2.1.44835: Flags [.], seq 1073:1609, ack 1, win 65535, length 536: HTTP21:59:41.057238 ?     Out IP 192.168.68.146.8080 > 192.0.2.1.44835: Flags [P.], seq 1609:2145, ack 1, win 65535, length 536: HTTP21:59:41.057241 ?     Out IP 192.168.68.146.8080 > 192.0.2.1.44835: Flags [.], seq 2145:2681, ack 1, win 65535, length 536: HTTP21:59:41.057243 ?     Out IP 192.168.68.146.8080 > 192.0.2.1.44835: Flags [P.], seq 2681:3001, ack 1, win 65535, length 320: HTTP21:59:41.057281 ?     Out IP 192.168.68.146.8080 > 192.0.2.1.44835: Flags [F.], seq 3001, ack 1, win 65535, length 021:59:41.057288 ?     In  IP 192.0.2.1.44835 > 192.168.68.146.8080: Flags [R.], seq 1, ack 1, win 10000, length 0


Wireshark & Packetdrill | TCP 三次握手之 TCP Options 字段 MSS


往期推荐


1. Wireshark 提示和技巧 | 捕获点之 TCP 三次握手

2. Wireshark 提示和技巧 | a == ${a} 显示过滤宏

3. Wireshark TS | 防火墙空闲会话超时问题

4. Wireshark TS | HTTP 传输文件慢问题

5. 网络设备 MTU MSS Jumboframe 全解



后台回复「TT」获取 Wireshark 提示和技巧系列 合集
后台回复「TS」获取 Wireshark Troubleshooting 系列 合集
如需交流,可后台直接留言,我会在第一时间回复,谢谢!
Wireshark & Packetdrill | TCP 三次握手之 TCP Options 字段 MSS

原文始发于微信公众号(Echo Reply):Wireshark & Packetdrill | TCP 三次握手之 TCP Options 字段 MSS

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月27日10:39:10
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Wireshark & Packetdrill | TCP 三次握手之 TCP Options 字段 MSShttps://cn-sec.com/archives/2242657.html

发表评论

匿名网友 填写信息