解决有趣的问题
实验目的
测试 packetdrill socket 基础参数。
此实验纯属个人研究,基本是想到什么测试什么,简单记录一下。
基础脚本
# 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
实验测试一
测试脚本
...
0 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
// domain,指定 AF_INET
或
0 socket(..., SOCK_STREAM, 0) = 3
// protocol,指定值 0
...
测试结果
抓包结果均一样,无区别。
实验测试二
测试脚本
# cat tcp_3hs_003.pkt
...
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [0], 4) = 0
// SO_REUSEADDR 设置为 0 的情况
...
测试结果
测试抓包结果一样,无区别,但个人理解和测试脚本有关。在上述基础测试脚本下,
一方面是脚本执行完毕后即RST了连接,并无TIME_WAIT状态,另一方面脚本每次执行所模拟出的服务器端IP基本都不相同,
综合起来看SO_REUSEADDR暂时没有起到相应作用。
但正常情况下,对于packetdrill,设置SO_REUSEADDR的主要作用,应该是在重新执行测试脚本时,可以快速重复绑定之前的地址,
避免bind错误,以方便快速重新执行测试脚本。
实验测试三
内核版本 5.15.0,简单测试下 SO_REUSEADDR 选项
SO_REUSEADDR 测试脚本
# cat tcp_3hs_004.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
// SO_REUSEADDR 设置为 1 的情况
+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
+0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 5
+0 setsockopt(5, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
// SO_REUSEADDR 设置为 1 的情况
+0 bind(5, ..., ...) = 0
+0 listen(5, 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(5, ..., ...) = 6
+0 `sleep 1000000`
测试结果,出现bind错误 Address already in use ,SO_REUSEADDR 未起作用。
# packetdrill tcp_3hs_004.pkt
tcp_3hs_004.pkt:16: runtime error in bind call: Expected result 0 but got -1 with errno 98 (Address already in use)
#
# ss -anto | grep 8080
FIN-WAIT-1 0 1 192.168.201.65:8080 192.0.2.1:50579 timer:(on,1.280ms,3)
#
抓包结果,仅有一个连接的TCP三次握手数据包,而由于脚本的错误退出,最后也只有一个服务器发送的 FIN/ACK 数据包,无之前文章提到的另一个 RST/ACK 数据包。
# 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
20:29:21.760392 ? In IP 192.0.2.1.50579 > 192.168.201.65.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
20:29:21.760411 ? Out IP 192.168.201.65.8080 > 192.0.2.1.50579: Flags [S.], seq 58476127, ack 1, win 65535, options [mss 1460], length 0
20:29:21.770507 ? In IP 192.0.2.1.50579 > 192.168.201.65.8080: Flags [.], ack 1, win 10000, length 0
20:29:21.771384 ? Out IP 192.168.201.65.8080 > 192.0.2.1.50579: Flags [F.], seq 1, ack 1, win 65535, length 0
实验测试四
内核版本 5.15.0,简单测试下 SO_REUSEPORT 选项
SO_REUSEPORT 测试脚本
# cat tcp_3hs_005.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
// SO_REUSEPORT 设置为 1 的情况
+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
+0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 5
+0 setsockopt(5, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
// SO_REUSEPORT 设置为 1 的情况
+0 bind(5, ..., ...) = 0
+0 listen(5, 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(5, ..., ...) = 6
+0 `sleep 1000000`
测试结果,服务端相同 IP 和端口 bind 成功。
# packetdrill tcp_3hs_005.pkt
#
# ss -anto | grep 8080
LISTEN 0 1 192.168.115.238:8080 0.0.0.0:*
LISTEN 1 1 192.168.115.238:8080 0.0.0.0:*
ESTAB 0 0 192.168.115.238:8080 192.0.2.1:50743
ESTAB 0 0 192.168.115.238:8080 192.0.2.1:38365
#
抓包结果,分别有两个连接的TCP三次握手数据包。
# 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
20:47:34.980397 tun0 In IP 192.0.2.1.38365 > 192.168.115.238.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
20:47:34.980418 tun0 Out IP 192.168.115.238.8080 > 192.0.2.1.38365: Flags [S.], seq 1668991635, ack 1, win 65535, options [mss 1460], length 0
20:47:34.990514 tun0 In IP 192.0.2.1.38365 > 192.168.115.238.8080: Flags [.], ack 1, win 10000, length 0
20:47:34.990556 tun0 In IP 192.0.2.1.50743 > 192.168.115.238.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
20:47:34.990561 tun0 Out IP 192.168.115.238.8080 > 192.0.2.1.50743: Flags [S.], seq 3447047094, ack 1, win 65535, options [mss 1460], length 0
20:47:35.000629 tun0 In IP 192.0.2.1.50743 > 192.168.115.238.8080: Flags [.], ack 1, win 10000, length 0
实验测试五
基于实验测试四的继续,因为脚本中的 +0 `sleep 1000000` 语句,实际上 packetdrill tcp_3hs_005.pkt 执行后并未退出,仍保持运行状态。
这时候通过 CTRL+C 终止执行,会出现两种情况,如下。
一、
测试结果,可见脚本已执行到 sleep 语句后,被中止,ss 查询两条连接状态均为 FIN-WAIT-1。
# packetdrill tcp_3hs_005.pkt
^Ctcp_3hs_005.pkt:24: error executing `sleep 1000000` command: got signal 2 (Interrupt)
#
# ss -anto | grep 8080
FIN-WAIT-1 0 1 192.168.104.148:8080 192.0.2.1:37455 timer:(on,1.108ms,4)
FIN-WAIT-1 0 1 192.168.104.148:8080 192.0.2.1:40257 timer:(on,1.108ms,4)
#
抓包结果,因为仅服务器端发出发 FIN,所以 FIN-WAIT-1 状态正确。
22:59:35.247616 ? Out IP 192.168.104.148.8080 > 192.0.2.1.40257: Flags [F.], seq 1, ack 1, win 65535, length 0
22:59:35.247633 ? Out IP 192.168.104.148.8080 > 192.0.2.1.37455: Flags [F.], seq 1, ack 1, win 65535, length 0
二、
测试结果,对比第一种,可见脚本并未执行到 sleep 语句,被中止时无任何 Interrupt ,ss 查询仅有一条连接状态为 FIN-WAIT-1。
# packetdrill tcp_3hs_005.pkt
^C
#
# ss -anto | grep 8080
FIN-WAIT-1 0 1 192.168.142.124:8080 192.0.2.1:53475 timer:(on,060ms,5)
抓包结果,通过 CTRL+C 终止执行后,53475 连接服务器端发出 FIN,所以进入 FIN-WAIT-1 状态,而 56315 连接服务器端直接由 RST 结束,因此 SS 无信息。
19:41:34.166521 ? Out IP 192.168.142.124.8080 > 192.0.2.1.53475: Flags [F.], seq 1, ack 1, win 65535, length 0
19:41:34.166529 ? Out IP 192.168.142.124.8080 > 192.0.2.1.56315: Flags [R.], seq 1, ack 1, win 65535, length 0
总结:
上述脚本测试时,运行脚本--CTRL+C中止,不断重复的过程中,第一种和第二种情况会随机出现,并无规律。
(注:个人能力有限,目前暂时没法解释相关现象,待继续研究看看能不能有新的发现)
实验测试六
测试脚本
# cat tcp_3hs_006.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, 0) = 0
// backlog 设置为 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
// 删除 accept()语句
+0 `sleep 1000000`
测试结果
测试抓包结果一样,无区别
root@hecs-132451:~# 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
21:17:32.528394 tun0 In IP 192.0.2.1.56835 > 192.168.137.78.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
21:17:32.528417 tun0 Out IP 192.168.137.78.8080 > 192.0.2.1.56835: Flags [S.], seq 1596170924, ack 1, win 65535, options [mss 1460], length 0
21:17:32.538509 tun0 In IP 192.0.2.1.56835 > 192.168.137.78.8080: Flags [.], ack 1, win 10000, length 0
原本实验是想测试 backlog 参数值为 0 的情况下,对于再一个新连接被拒绝的现象和结果。
但是尝试过几个方式都没有成功,一是受限于模拟的tun0网卡,再次运行另一个packetdrill脚本无效,同时就算是能启用,也是不同的IP+端口;
二是同一个packetdrill脚本,暂时没找到如何实现再次发起一个新连接,如果仍是以SO_REUSEPORT测试方式,仍是两个独立的socket,并不作用于受限于一个socket里的backlog 0。
<对于packetdrill测试,backlog大小并不重要,这里设置为1仅是约定俗成的惯例。>,
这句话忘了是在哪个文章中看到的,但既然是个人研究,也就测试了下,么有想到还是有坑,查询了很多资料,暂时还没有相关解决方法,个人对于packetdrill
也才刚起步,希望之后的实验中能找到答案,先记录之,也希望有做过相关测试成功的同学告知方法,不甚感激,谢谢。
实验参考
Linux内核中reuseport的演进
https://segmentfault.com/a/1190000020524323
listen函数的backlog参数的本质
https://blog.csdn.net/qq_39781096/article/details/107934922
TCP 半连接队列和全连接队列
https://www.xiaolincoding.com/network/3_tcp/tcp_queue.html
TCP协议细节系列(9):深入解析Linux下so_reuseaddr和so_reuseport选项
https://zhuanlan.zhihu.com/p/632831436
往期推荐
原文始发于微信公众号(Echo Reply):Wireshark & Packetdrill | Socket 基础参数测试
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论