文章目录
一、Packet Drill 基本原理
TUN 网络设备
TUN 是Linux 下的虚拟网络设备, 可以直通到网络层。使得应用程序可以直接收发IP报文。
二、Packet Drill 脚本解析/执行引擎
首先 Packet Drill 脚本必须要被解析和分解为 通过传统socket 接口收发报文的部分和通过TUN接口收发报文的部分
在传统socket 接口执行对应的动作。
在TUN接口执行对应的动作,并对收到的数据进行比对。
在本文中 socket 接口主要扮演的是 server side的角色。TUN接口扮演的是client的角色。因而我们可以通过TUN接口完全掌控我们将要发送出去的IP报文,并受到TCP协议栈的反馈。并和预设数据进行比对。
三、Packet Drill 语法简介
1. 相对时间顺序
Packet Drill 每一个事件(发送/接收/发起系统调用)都有相对前后事件的时间便宜。一般使用+number 来表达。例如+0 就是在之前的事件结束之后立即发起。+.1 表示为在之前时间结束0.1秒之后发起。以此类推
2. 系统调用
Packet Drill 中集成了系统调用, 可以通过脚本来完成例如 socket,bind, read,write,getsocketoption 等等系统调用。熟悉socket 编程的同学很容易理解并使用。
3. 报文的发送与接受
通过内核栈侧。可以通过调用系统调用 read/write 来完成报文的发送与接受。但是因为tcp是有状态的协议栈,所以内核栈本身也会根据协议栈所处状态发送报文(例如ACK/SACK).
TUN 设备侧. Packet Drill 使用 < 表示发送报文, 使用 > 表示接收报文。
4. 报文的格式描述
报文格式的表达比较类似tcpdump。例如 S 0:0(0) win 1000表示syn包 win大小为1000, 同时tcp的选项 mss (max segment size)为1000. 如果不熟悉报文格式, 可以先复习一下《TCP/IP协议详解》 卷1.
进一步的信息请参考 Drilling Network Stacks with packetdrill:
https://storage.googleapis.com/pub-tools-public-publication-data/pdf/41848.pdf
四、实战示例
下面我们通过2个例子来进一步学习
Handshake and Teardown
我们通过packet drill的脚本 复习一下这个经典的流程。
首选来回顾一下 TCP协议标准的 handshake 和 treardown 流程
接下来我们结合packet drill 的脚本来重现 整个过程
至此, 我们纯手动的完成了全部的发起和关闭连接的过程。然后我们用wireshark 来验证一下
通过结合packetdrill与wireshark 使得每一步都在我们的掌控之中,
SACK
我们将使用packet drill 来探索一些更为复杂的案例。例如内核协议栈对于 SACK中各种排列组合的响应。
SACK 是TCP协议中优化重传机制的一个重要选项(该选项一般都在报头的options部分)。
最原始的情况下如果发送方对于 每一个报文接受到ACK之后再发送下一个报文, 效率将是极为低下的。引入滑动窗口之后允许发送方一次发送多个报文 但是如果中间某个报文丢失(没有收到其对应的ACK)那么从那个报文开始,其后所有发送过的报文都要被重新发送一次。造成了极大的浪费。
SACK 是一种优化措施, 用来避免不必要的重发, 告知发送方那些报文已经收到,不用再重发。tcp 的选项中允许带有最多3个SACK的options。也就是三个已经收到了得报文区间信息。说了这么多, 还是有一些抽象, 我们来看一个具体的示例。
示例说明
在下面的这个例子中, 我们需要发送报文的顺序是 1,3,5,6,8,4,7,2 也就是测试一下内核tcp协议栈的SACK逻辑是否如同RFC中所描述的一样。
随后我们再来用wireshark 验证一下。
果然完全匹配。
Packet Drill 其实还有非常复杂而且更精巧的玩法, 可以充分测试各种边界条件。以后有机会再和大家进一步分享
五、参考信息
例子脚本的链接:
https://gitee.com/block_chainsaw/linux-kernel-tcp-study.git