对某个问题是否乐于去解决,取决于该问题是否单调乏味。
前言
Packetdrill ,初识的时候简直了,奉为神器,但受限于懒癌,一直也没怎么好好研究。现在趁着一波热情上来,赶紧结合 Wireshark 好好做下内核协议栈的相关实验,加深理解数据包分析。
Packetdrill 相关的资料并不是很多,所以测试当中也是带着很多疑问,限于能力,有些结论也并不一定准确,纯瞎研究性质。
实验目的
通过 packetdrill 测试 TCP 基础之三次握手。
实验脚本
# 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(..., SOCK_STREAM, IPPROTO_TCP) = 3
// 0 是 创建 socket 的绝对时间。
// 创建 socket ,socket(int domain, int type, int protocol)
// domain,默认值... ,即 AF_INET 。
// type,SOCK_STREAM ,指定这个套接字为面向连接的流式套接字(TCP)。
// protocol,IPPROTO_TCP,指定TCP协议。
// 3,因为 stdin(0),stdout(1),stderr(2) 默认被定义,内核将会给此 socket 指定文件描述符 FD 为 3。
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
// +0 本行代码执行时间相对于上一行代码的偏移时间。
// setsockopt() 设置套接字选项的系统调用。
// 3,设置的套接字描述符。
// SOL_SOCKET,表示要设置的选项位于socket层面。
// SO_REUSEADDR,要设置的选项,表示重用本地地址,譬如快速重启服务程序。
// [1],表示选项值,1表示启用。
// 4,表示选项值的长度,4字节。
// 0,设置成功返回0。
+0 bind(3, ..., ...) = 0
// +0 本行代码执行时间相对于上一行代码的偏移时间。
// bind(),用来绑定套接字到本地地址的系统调用。
// 3,绑定的套接字描述符。
// ... ,省略的bind()的参数,包含绑定的本地IP地址和端口。
// 0,设置成功返回0。
+0 listen(3, 1) = 0
// +0 本行代码执行时间相对于上一行代码的偏移时间。
// listen(),用于启动对指定套接字的监听的系统调用。
// 3,监听的套接字描述符。
// 1,backlog参数,指定排队的全连接队列最大数(最多允许1个待处理的全连接)。
+0 < S 0:0(0) win 10000 <mss 1460>
// +0 本行代码执行时间相对于上一行代码的偏移时间。
// < ,表示输入或注入的数据包。
// S ,表示是 SYN 数据包。
// 0:0(0) ,表示开始序号:结束序号(数据包长度)。
// win 10000,表示接收窗口10000字节。
// <> 表示 TCP options,mss 1460,表示设置mss 1460字节大小。
+0 > S. 0:0(0) ack 1 <...>
// +0 本行代码执行时间相对于上一行代码的偏移时间。
// > ,表示预期协议栈会响应的数据包。
// S. ,表示是 SYN/ACK 数据包。
// 0:0(0) ,表示开始序号0:结束序号0(数据包长度0)。
// ack 1,表示确认号1,确认了客户端的SYN(seq + 1),因为SYN占1个字节。
// <> 表示 TCP options,... 表示默认值。
+0.01 < . 1:1(0) ack 1 win 10000
// +.1 本行代码执行时间相对于上一行代码的偏移时间,0.01s = 10ms。
// < ,表示输入或注入的数据包。
// . ,表示是 ACK 数据包。
// 1:1(0) ,表示开始序号1:结束序号1(数据包长度0),因为SYN占1个字节。
// ack 1 ,表示确认号1,确认了服务端的SYN(seq + 1),因为SYN占1个字节。
// win 10000,表示接收窗口10000字节。
+0 accept(3, ..., ...) = 4
// +0 本行代码执行时间相对于上一行代码的偏移时间。
// accept(),表示用来接收连接的系统调用。
// 3,监听的套接字描述符。
// ... ,省略的两个参数,分别为接收客户端地址和地址长度。
// 4,返回新连接的套接字描述符4。
实验测试
1.执行脚本
# packetdrill tcp_3hs_000.pkt
#
执行完成后退出。
2.捕获数据包
# 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
22:57:46.180400 ? In IP 192.0.2.1.37353 > 192.168.19.227.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
22:57:46.180424 ? Out IP 192.168.19.227.8080 > 192.0.2.1.37353: Flags [S.], seq 4169589518, ack 1, win 65535, options [mss 1460], length 0
22:57:46.190540 ? In IP 192.0.2.1.37353 > 192.168.19.227.8080: Flags [.], ack 1, win 10000, length 0
22:57:46.190624 ? Out IP 192.168.19.227.8080 > 192.0.2.1.37353: Flags [F.], seq 1, ack 1, win 65535, length 0
22:57:46.190635 ? In IP 192.0.2.1.37353 > 192.168.19.227.8080: Flags [R.], seq 1, ack 1, win 10000, length 0
^C
5 packets captured
7 packets received by filter
0 packets dropped by kernel
#
数据包说明
1. TCP三次握手 - SYN
22:57:46.180400 ? In IP 192.0.2.1.37353 > 192.168.19.227.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
// 对应于脚本 +0 < S 0:0(0) win 10000 <mss 1460> ,因为 < 表示输入数据包,所以 packetdrill 根据此行脚本各字段值构造的 SYN 数据包,注入到内核协议栈。
2. TCP三次握手 - SYN/ACK
22:57:46.180424 ? Out IP 192.168.19.227.8080 > 192.0.2.1.37353: Flags [S.], seq 4169589518, ack 1, win 65535, options [mss 1460], length 0
// 对应于脚本 +0 > S. 0:0(0) ack 1 <...> ,因为 > 表示预期内核协议栈会响应的数据包,所以如果内核协议栈响应的实际数据包与脚本所预期的数据包一致,则脚本正常执行,如果不一致,则脚本会报错停止运行,并指出相关错误。
3. TCP三次握手 - ACK
22:57:46.190540 ? In IP 192.0.2.1.37353 > 192.168.19.227.8080: Flags [.], ack 1, win 10000, length 0
// 对应于脚本 +0.01 < . 1:1(0) ack 1 win 10000,与上一行间隔10ms后 < 输入数据包,所以 packetdrill 根据此行脚本各字段值构造的 ACK 数据包,注入到内核协议栈。
4. FIN/ACK 和 RST/ACK
22:57:46.190624 ? Out IP 192.168.19.227.8080 > 192.0.2.1.37353: Flags [F.], seq 1, ack 1, win 65535, length 0
22:57:46.190635 ? In IP 192.0.2.1.37353 > 192.168.19.227.8080: Flags [R.], seq 1, ack 1, win 10000, length 0
// packetdrill 脚本之外产生的数据包,或者说预期之外的数据包。
// 因为未具体研究 packetdrill 代码,仅根据个人判断,在 packetdrill 脚本执行完毕后,socket 被强行关闭,如果有accept()的情况下,内核会发送 FIN/ACK 结束连接,没有accept()的情况下,则不会发送 FIN/ACK.
// 而最后一个客户端所发送的 RST/ACK 结束连接,也像是 packetdrill 脚本默认构造,用于快速重置连接使用的。
// 因此这两个数据包一般与 packetdrill 构建的测试脚本无关,分析时可省略。
扩展测试
# cat tcp_3hs_001.pkt
...
+0 `sleep 1000000`
脚本最后增加 sleep 语句,使得脚本执行完不要退出。
测试结果
# packetdrill tcp_3hs_001.pkt
脚本执行完后未退出,可见光标闪烁。
# ss -anto | grep 8080
LISTEN 0 1 192.168.68.247:8080 0.0.0.0:*
ESTAB 0 0 192.168.68.247:8080 192.0.2.1:44569
#
通过 ss 查询 TCP 连接建立状态。
# ip a
396: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 192.168.68.247/16 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::b034:651:cd73:73f2/64 scope link stable-privacy
valid_lft forever preferred_lft forever
#
ip a 查询可见多了一个虚拟的网卡tun0,这也是packetdrill实现的基本原理,可自行查询相关介绍文章。
抓包结果
# 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
22:05:59.240395 tun0 In IP 192.0.2.1.44569 > 192.168.68.247.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
22:05:59.240416 tun0 Out IP 192.168.68.247.8080 > 192.0.2.1.44569: Flags [S.], seq 1898366152, ack 1, win 65535, options [mss 1460], length 0
22:05:59.250513 tun0 In IP 192.0.2.1.44569 > 192.168.68.247.8080: Flags [.], ack 1, win 10000, length 0
Sleep 有无的情况下,数据包方面稍微的区别:
在无 sleep 语句的情况下,脚本执行完成后就正常退出,通过抓包结果,除了 TCP 三次握手数据包,最后会默认出现服务器发的一个 FIN/ACK 和客户端发的一个 RST/ACK 。
在有 sleep 语句的情况下,脚本执行完成后并不退出,因此抓包结果仅有 TCP 三次握手数据包。
而在有 sleep 语句的脚本执行后,通过 CTRL+C 可终止脚本运行,如下:
# packetdrill tcp_3hs_001.pkt
^Ctcp_3hs_001.pkt:12: error executing `sleep 1000000` command: got signal 2 (Interrupt)
#
此时 ip a 查询,已无 tun0 网卡,通过 ss 查询 TCP 连接状态已经变成 FIN-WAIT-1。
# ss -anto | grep 8080
FIN-WAIT-1 0 1 192.168.68.247:8080 192.0.2.1:44569 timer:(on,540ms,7)
#
抓包结果如下,仅出现一个服务器发送的 FIN 数据包,与上面的 TCP 连接 FIN-WAIT-1 对应。
22:27:27.263963 ? Out IP 192.168.68.247.8080 > 192.0.2.1.44569: Flags [F.], seq 1, ack 1, win 65535, length 0
往期推荐
原文始发于微信公众号(Echo Reply):Wireshark & Packetdrill | TCP 基础之三次握手
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论