破事多多,喝杯浓缩
实验目的
通过实验简单了解 packetdrill 时间模型中的绝对时间。
时间模型
由于许多协议对时间非常敏感,所以在「packetdrill」 脚本中增加了对时间灵活性的支持。每个语句都有一个时间戳,由 「packetdrill」 强制执行:如果事件没有在指定的时间发生,则 「packetdrill」 会标记一个错误并报告实际时间。
「packetdrill」时间模型
Model |
Syntax |
Description |
Absolute |
0.750 |
Specifies the specific time at which an event should occur. |
Relative |
+0.2 |
Specifies the interval after the last event at which an event should occur. |
Wildcard |
* |
Allows an event to occur at any time. |
Range |
0.750~0.9 |
Requires the given event to occur with in the timer ange. |
Loose |
--tolerance_usecs=800 |
Allows all events to happen with in a range(from the command line). |
Blocking |
0.750...0.9 |
Specifies a blocking system call that starts/returns at the given times. |
避免伪造失败
使用 --tolerance_usecs 参数值 4ms,使得事件只要在期望时间的 4ms 范围内发生,即认为测试是成功的。
基础脚本
# 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
绝对时间
以 TCP 三次握手为基础,测试时间模型中的绝对时间。所谓的绝对时间,就是指定事件发生的具体时间。
实验测试一
初步修改的脚本,第一行创建 socket 的绝对时间为0,之后每一行的脚本均指定发生的具体时间,第二行脚本的执行时间是在 10ms 左右,第三行脚本的执行时间是在 20ms 左右,以此类推。
# cat tcp_3hs_time_001.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
0.01 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
0.02 bind(3, ..., ...) = 0
0.03 listen(3, 1) = 0
0.04 < S 0:0(0) win 10000 <mss 1460>
0.05 > S. 0:0(0) ack 1 <...>
0.06 < . 1:1(0) ack 1 win 10000
0.07 accept(3, ..., ...) = 4
测试结果,实际测试执行失败,提示了相关错误。时间错误:脚本期望的出向数据包在绝对时间0.05秒发出,但实际上协议栈在0.040077秒就发出了(需要记住的是,> 代表预期,表示预期协议栈会响应的数据包),即使默认偏移时间值在0.004秒,也就是[ts-tolerance,ts+tolerance]内,也不符合,因此执行失败。
# packetdrill tcp_3hs_time_001.pkt
tcp_3hs_time_001.pkt:8: error handling packet: timing error: expected outbound packet at 0.050000 sec but happened at 0.040077 sec; tolerance 0.004000 sec
script packet: 0.050000 S. 0:0(0) ack 1
actual packet: 0.040077 S. 0:0(0) ack 1 win 65535 <mss 1460>
#
抓包结果,可以看到仅抓到两个包,也就是 SYN 和 SYN/ACK,因为在之后脚本错误停止了。
另外也可以看到捕获时间,11:34:44.628409 和 11:34:44.628433 也仅是相差 24 微秒,和 packetdrill 所记录的相差 77 微秒有所差距。实际也查找了不少资料,但没有找到很明确的说法,通过反复实验测试,暂时目前我理解:一是 packetdrill 和 tcpdump 工具各自执行的偏差,二是 packetdrill 所嗅探数据包的时间与 tcpdump 在 tun0 上捕获的时间,也就是捕获发生的点可能不一样,三是微秒级精度问题。
# 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
11:34:44.628409 ? In IP 192.0.2.1.40013 > 192.168.119.164.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
11:34:44.628433 ? Out IP 192.168.119.164.8080 > 192.0.2.1.40013: Flags [S.], seq 4212406018, ack 1, win 65535, options [mss 1460], length 0
实验测试二
考虑到系统执行本身的偏差,那么在实验测试一的基础上,保持脚本不变,反复执行脚本,查找下适合的绝对时间。
执行了五次脚本,分别是 0.040078 sec、0.040075 sec、0.040072 sec、0.040076 sec、0.040074 sec,可见基本都在 7x 范围内。(注:当然多执行几次,也会发现有 6x 或 8x 的间隔)
# packetdrill tcp_3hs_time_001.pkt
tcp_3hs_time_001.pkt:7: error handling packet: timing error: expected outbound packet at 0.050000 sec but happened at 0.040078 sec; tolerance 0.004000 sec
script packet: 0.050000 S. 0:0(0) ack 1
actual packet: 0.040078 S. 0:0(0) ack 1 win 65535 <mss 1460>
# packetdrill tcp_3hs_time_001.pkt
tcp_3hs_time_001.pkt:7: error handling packet: timing error: expected outbound packet at 0.050000 sec but happened at 0.040075 sec; tolerance 0.004000 sec
script packet: 0.050000 S. 0:0(0) ack 1
actual packet: 0.040075 S. 0:0(0) ack 1 win 65535 <mss 1460>
# packetdrill tcp_3hs_time_001.pkt
tcp_3hs_time_001.pkt:7: error handling packet: timing error: expected outbound packet at 0.050000 sec but happened at 0.040072 sec; tolerance 0.004000 sec
script packet: 0.050000 S. 0:0(0) ack 1
actual packet: 0.040072 S. 0:0(0) ack 1 win 65535 <mss 1460>
# packetdrill tcp_3hs_time_001.pkt
tcp_3hs_time_001.pkt:7: error handling packet: timing error: expected outbound packet at 0.050000 sec but happened at 0.040076 sec; tolerance 0.004000 sec
script packet: 0.050000 S. 0:0(0) ack 1
actual packet: 0.040076 S. 0:0(0) ack 1 win 65535 <mss 1460>
# packetdrill tcp_3hs_time_001.pkt
tcp_3hs_time_001.pkt:7: error handling packet: timing error: expected outbound packet at 0.050000 sec but happened at 0.040074 sec; tolerance 0.004000 sec
script packet: 0.050000 S. 0:0(0) ack 1
actual packet: 0.040074 S. 0:0(0) ack 1 win 65535 <mss 1460>
#
考虑到协议栈大都在 0.04006x-0.04008x 秒就做出了响应,再加上默认偏移时间值在0.004秒内,因此绝对时间如果写成0.044090秒的情况下,也就是 [0.040090,0.048090] 内,脚本应该也不会成功。
# cat tcp_3hs_time_002.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
0.01 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
0.02 bind(3, ..., ...) = 0
0.03 listen(3, 1) = 0
0.04 < S 0:0(0) win 10000 <mss 1460>
0.044090 > S. 0:0(0) ack 1 <...>
0.06 < . 1:1(0) ack 1 win 10000
0.07 accept(3, ..., ...) = 4
测试脚本依然报错执行失败,测试结果正确。
# packetdrill tcp_3hs_time_002.pkt
tcp_3hs_time_002.pkt:7: error handling packet: timing error: expected outbound packet at 0.044090 sec but happened at 0.040075 sec; tolerance 0.004000 sec
script packet: 0.044090 S. 0:0(0) ack 1
actual packet: 0.040075 S. 0:0(0) ack 1 win 65535 <mss 1460>
# packetdrill tcp_3hs_time_002.pkt
tcp_3hs_time_002.pkt:7: error handling packet: timing error: expected outbound packet at 0.044090 sec but happened at 0.040074 sec; tolerance 0.004000 sec
script packet: 0.044090 S. 0:0(0) ack 1
actual packet: 0.040074 S. 0:0(0) ack 1 win 65535 <mss 1460>
# packetdrill tcp_3hs_time_002.pkt
tcp_3hs_time_002.pkt:7: error handling packet: timing error: expected outbound packet at 0.044090 sec but happened at 0.040081 sec; tolerance 0.004000 sec
script packet: 0.044090 S. 0:0(0) ack 1
actual packet: 0.040081 S. 0:0(0) ack 1 win 65535 <mss 1460>
实验测试三
那么如何修改脚本呢?考虑到协议栈大都在 0.04006x-0.04008x 秒就做出了响应,再加上默认偏移时间值在 0.004 秒内,因此绝对时间写成 0.044050 秒的情况下,也就是 [0.040050,0.048050] 内,脚本肯定会执行成功。
# cat tcp_3hs_time_003.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
0.01 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
0.02 bind(3, ..., ...) = 0
0.03 listen(3, 1) = 0
0.04 < S 0:0(0) win 10000 <mss 1460>
0.044050 > S. 0:0(0) ack 1 <...>
0.06 < . 1:1(0) ack 1 win 10000
0.07 accept(3, ..., ...) = 4
测试脚本连续运行五次均通过,结果正确。
# packetdrill tcp_3hs_time_003.pkt
# packetdrill tcp_3hs_time_003.pkt
# packetdrill tcp_3hs_time_003.pkt
# packetdrill tcp_3hs_time_003.pkt
# packetdrill tcp_3hs_time_003.pkt
#
因为是本地模式运行,再加上是很基础的三次握手测试,默认的偏移时间 4ms 绝对满足使用。当然随着不同模式下不同测试脚本,对于时间上的需要,也可以修改默认的 --tolerance_usecs 参数值,使得事件只要在期望时间的 xxms 范围内发生,即认为测试是成功的。
实验测试四
考虑到脚本执行会去执行校验 > (代表预期,表示预期协议栈会响应的数据包)的情况,所以一般注意 > 处即可,而 < 代表注入数据包的时间,相对来说灵活一些。以下结合 tcpdump 捕获结果再说明下测试情况。
最终脚本,> SYN/ACK的绝对执行时间修改成了 0.041 秒。
# cat tcp_3hs_time_004.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
0.01 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
0.02 bind(3, ..., ...) = 0
0.03 listen(3, 1) = 0
0.04 < S 0:0(0) win 10000 <mss 1460>
0.041 > S. 0:0(0) ack 1 <...>
0.06 < . 1:1(0) ack 1 win 10000
0.07 accept(3, ..., ...) = 4
抓包结果,第一个 SYN 的时间 16:53:42.500410,第二个 SYN/ACK 的时间 16:53:42.500431,协议栈自动回复,只要落在预期的绝对时间加减偏移即可,第三个 ACK 的时间 16:53:42.520402,相较第一个 SYN 时间间隔了大约 20ms,这也是脚本中 0.04 SYN 和 0.06 ACK 的 20ms 的时间间隔体现。
# 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
16:53:42.500410 ? In IP 192.0.2.1.36527 > 192.168.23.15.8080: Flags [S], seq 0, win 10000, options [mss 1460], length 0
16:53:42.500431 ? Out IP 192.168.23.15.8080 > 192.0.2.1.36527: Flags [S.], seq 3751296785, ack 1, win 65535, options [mss 1460], length 0
16:53:42.520402 ? In IP 192.0.2.1.36527 > 192.168.23.15.8080: Flags [.], ack 1, win 10000, length 0
16:53:42.530443 ? Out IP 192.168.23.15.8080 > 192.0.2.1.36527: Flags [F.], seq 1, ack 1, win 65535, length 0
16:53:42.530462 ? In IP 192.0.2.1.36527 > 192.168.23.15.8080: Flags [R.], seq 1, ack 1, win 10000, length 0
简单来说,< 执行时间相对精准可控,> 执行时间预期范围可控。
往期推荐
原文始发于微信公众号(Echo Reply):Wireshark & Packetdrill | 理解时间模型之绝对时间
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论