人人都是求知的探索者
前言
起因是之前在和同事讨论 TCP 虚假重传的场景,说到了重复数据以及 DSACK 机制,但到现在回想起来,扩展思考一下,还是有些许疑惑。
如果拿 TCP 虚假重传的场景来说,相同的 TCP Seq 序列号,相同的 TCP Payload ,对于 TCP 接收端,像是 DSACK 这都属于正常的基操处理,但如果说是异常重复数据,譬如相同的 TCP Seq 序列号,但不同的 TCP Payload 呢,TCP 如何处理。
基础信息
首先拿 TCP 虚假重传场景为例,脚本如下。
# cat tcp_ex_dup_001.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 < S 0:0(0) win 10000 <mss 1460,nop,nop,sackOK>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000
+0 accept(3, ..., ...) = 4
+0.01 < P. 1:101(100) ack 1 win 10000
+0.01 < P. 1:101(100) ack 1 win 10000
+0 `sleep 1`
#
通过 tcpdump 捕获数据包如下,可以看到在收到重复数据 Seq 1:101 后,服务器端响应了 ACK 数据包,其中包含 SACK 块信息,且第一个块信息范围 Seq 1:101 被 ACK Num 101 覆盖,即 ACK Num 大于等于第一个 SACK 块范围的起始序列号和结束序列号,说明是 DSACK 。
# packetdrill tcp_ex_dup_001.pkt
#
# 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:20:19.832499 tun0 In IP 192.0.2.1.45531 > 192.168.191.130.8080: Flags [S], seq 0, win 10000, options [mss 1460,nop,nop,sackOK], length 0
22:20:19.832531 tun0 Out IP 192.168.191.130.8080 > 192.0.2.1.45531: Flags [S.], seq 2759867484, ack 1, win 64240, options [mss 1460,nop,nop,sackOK], length 0
22:20:19.842665 tun0 In IP 192.0.2.1.45531 > 192.168.191.130.8080: Flags [.], ack 1, win 10000, length 0
22:20:19.853005 tun0 In IP 192.0.2.1.45531 > 192.168.191.130.8080: Flags [P.], seq 1:101, ack 1, win 10000, length 100: HTTP
22:20:19.853248 tun0 Out IP 192.168.191.130.8080 > 192.0.2.1.45531: Flags [.], ack 101, win 64140, length 0
22:20:19.862866 tun0 In IP 192.0.2.1.45531 > 192.168.191.130.8080: Flags [P.], seq 1:101, ack 1, win 10000, length 100: HTTP
22:20:19.862914 tun0 Out IP 192.168.191.130.8080 > 192.0.2.1.45531: Flags [.], ack 101, win 64140, options [nop,nop,sack 1 {1:101}], length 0
22:20:20.868869 ? Out IP 192.168.191.130.8080 > 192.0.2.1.45531: Flags [R.], seq 1, ack 101, win 64140, length 0
22:20:20.868935 ? In IP 192.0.2.1.45531 > 192.168.191.130.8080: Flags [R.], seq 101, ack 1, win 10000, length 0
#
在 tcp_data_queue() 函数中,该数据包的 end_seq 完全在 tp->rcv_nxt 之前,说明是个疑似重传数据包,定义为 SKB_DROP_REASON_TCP_OLD_DATA,进行 DSACK 发送,并且直接 drop 该数据包。
if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
tcp_rcv_spurious_retrans(sk, skb);
/* A retransmit, 2nd most common case. Force an immediate ack. */
reason = SKB_DROP_REASON_TCP_OLD_DATA;
NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOST);
tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
out_of_window:
tcp_enter_quickack_mode(sk, TCP_MAX_QUICKACKS);
inet_csk_schedule_ack(sk);
drop:
tcp_drop_reason(sk, skb, reason);
return;
}
问题扩展
基于以上正常重复数据的场景,扩展一下,如果只是部分 Seq Num 重复呢。
# cat tcp_ex_dup_002.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 < S 0:0(0) win 10000 <mss 1460,nop,nop,sackOK>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000
+0 accept(3, ..., ...) = 4
+0.01 < P. 1:101(100) ack 1 win 10000
+0.01 < P. 51:151(100) ack 1 win 10000
+0 `sleep 1`
#
通过 tcpdump 捕获数据包如下,可以看到在收到部分重复数据 Seq 51:151 后,服务器端仍是立马响应了 ACK 数据包,其中包含 SACK 块信息,且第一个块信息范围 Seq 51:101 被 ACK Num 151 覆盖,即 ACK Num 大于等于第一个 SACK 块范围的起始序列号和结束序列号,说明是 DSACK 。
# packetdrill tcp_ex_dup_002.pkt
#
# 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
09:38:16.853028 tun0 In IP 192.0.2.1.38787 > 192.168.138.236.8080: Flags [S], seq 0, win 10000, options [mss 1460,nop,nop,sackOK], length 0
09:38:16.853135 tun0 Out IP 192.168.138.236.8080 > 192.0.2.1.38787: Flags [S.], seq 376354095, ack 1, win 64240, options [mss 1460,nop,nop,sackOK], length 0
09:38:16.863687 tun0 In IP 192.0.2.1.38787 > 192.168.138.236.8080: Flags [.], ack 1, win 10000, length 0
09:38:16.874428 tun0 In IP 192.0.2.1.38787 > 192.168.138.236.8080: Flags [P.], seq 1:101, ack 1, win 10000, length 100: HTTP
09:38:16.874509 tun0 Out IP 192.168.138.236.8080 > 192.0.2.1.38787: Flags [.], ack 101, win 64140, length 0
09:38:16.884144 tun0 In IP 192.0.2.1.38787 > 192.168.138.236.8080: Flags [P.], seq 51:151, ack 1, win 10000, length 100: HTTP
09:38:16.884227 tun0 Out IP 192.168.138.236.8080 > 192.0.2.1.38787: Flags [.], ack 151, win 64090, options [nop,nop,sack 1 {51:101}], length 0
09:38:17.891506 tun0 Out IP 192.168.138.236.8080 > 192.0.2.1.38787: Flags [R.], seq 1, ack 151, win 64090, length 0
09:38:17.892061 tun0 In IP 192.0.2.1.38787 > 192.168.138.236.8080: Flags [R.], seq 151, ack 1, win 10000, length 0
#
与基础 TCP 虚假重传场景不一样的地方是,之前重复数据是一样的,而这里是只有部分重复数据,TCP 是如何处理。可能和想得不太一样是,在收到第二个数据包 Seq Num 51:151 后,并没有什么裁剪一说,也就是不会直接丢弃 Seq Num 51:101 的数据,而是正常保留完整数据。
之后在应用层读取数据阶段,通过序列号+偏移机制跳过重复部分,得到可用数据,确保应用层获得连续无重复的数据,相关函数可以研究下 tcp_recvmsg_locked() 函数。
found_ok_skb:
/* Ok so how much can we use? */
used = skb->len - offset;
if (len < used)
used = len;
...
if (!(flags & MSG_TRUNC)) {
err = skb_copy_datagram_msg(skb, offset, msg, used);
if (err) {
/* Exception. Bailout! */
if (!copied)
copied = -EFAULT;
break;
}
}
WRITE_ONCE(*seq, *seq + used);
copied += used;
len -= used;
tcp_rcv_space_adjust(sk);
以下应用层实际读取数据内容的验证,如下脚本,连续模拟输入两个 10 字节大小的数据包,并通过 read() 读取 15 字节的数据。
# cat tcp_ex_dup_003.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 < S 0:0(0) win 10000 <mss 1460,nop,nop,sackOK>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000
+0 accept(3, ..., ...) = 4
+0.01 < P. 1:11(10) ack 1 win 10000
+0.01 < P. 6:16(10) ack 1 win 10000
+0.01 read(4,...,15) = 15
+0 `sleep 1`
#
执行脚本,预期运行正常。
# packetdrill tcp_ex_dup_003.pkt
#
# 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
14:36:36.492723 tun0 In IP 192.0.2.1.37799 > 192.168.217.66.8080: Flags [S], seq 0, win 10000, options [mss 1460,nop,nop,sackOK], length 0
14:36:36.492802 tun0 Out IP 192.168.217.66.8080 > 192.0.2.1.37799: Flags [S.], seq 1335152261, ack 1, win 64240, options [mss 1460,nop,nop,sackOK], length 0
14:36:36.502975 tun0 In IP 192.0.2.1.37799 > 192.168.217.66.8080: Flags [.], ack 1, win 10000, length 0
14:36:36.513120 tun0 In IP 192.0.2.1.37799 > 192.168.217.66.8080: Flags [P.], seq 1:11, ack 1, win 10000, length 10: HTTP
14:36:36.513146 tun0 Out IP 192.168.217.66.8080 > 192.0.2.1.37799: Flags [.], ack 11, win 64230, length 0
14:36:36.523125 tun0 In IP 192.0.2.1.37799 > 192.168.217.66.8080: Flags [P.], seq 6:16, ack 1, win 10000, length 10: HTTP
14:36:36.523171 tun0 Out IP 192.168.217.66.8080 > 192.0.2.1.37799: Flags [.], ack 16, win 64225, options [nop,nop,sack 1 {6:11}], length 0
14:36:37.539779 ? Out IP 192.168.217.66.8080 > 192.0.2.1.37799: Flags [F.], seq 1, ack 16, win 64225, length 0
14:36:37.539806 ? In IP 192.0.2.1.37799 > 192.168.217.66.8080: Flags [R.], seq 16, ack 1, win 10000, length 0
#
当然也可以修改脚本,尝试 read() 读取 20 字节,脚本自然运行失败,Expected result 20 but got 15 。
# cat tcp_ex_dup_004.pkt
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0
+0 < S 0:0(0) win 10000 <mss 1460,nop,nop,sackOK>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000
+0 accept(3, ..., ...) = 4
+0.01 < P. 1:11(10) ack 1 win 10000
+0.01 < P. 6:16(10) ack 1 win 10000
+0.01 read(4,...,20) = 20
+0 `sleep 1`
#
# packetdrill tcp_ex_dup_004.pkt
tcp_exdup_004.pkt:15: runtime error in read call: Expected result 20 but got 15 with errno 2 (No such file or directory)
#
再次运行 tcp_ex_dup_003.pkt ,连续模拟输入两个 10 字节大小的数据包,第一个数据包 Seq 1:11 内容为 "0123456789",第二个数据包 Seq 6:16 内容同样为 "0123456789",即模拟前言中所描述的异常重复数据,相同的 TCP Seq 序列号,但不同的 TCP Payload 。
通过 tcpdump 捕获数据包如下,增加 -x,打印数据,以十六进制显示,No.4 和 No.6 数据包均为 3031 3233 3435 3637 3839 。
# packetdrill tcp_ex_dup_003.pkt
#
# tcpdump -i any -nnx 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
14:48:33.584544 tun0 In IP 192.0.2.1.50435 > 192.168.243.162.8080: Flags [S], seq 0, win 10000, options [mss 1460,nop,nop,sackOK], length 0
0x0000: 4500 0030 0000 0000 ff06 457b c000 0201
0x0010: c0a8 f3a2 c503 1f90 0000 0000 0000 0000
0x0020: 7002 2710 012f 0000 0204 05b4 0101 0402
14:48:33.584629 tun0 Out IP 192.168.243.162.8080 > 192.0.2.1.50435: Flags [S.], seq 787815103, ack 1, win 64240, options [mss 1460,nop,nop,sackOK], length 0
0x0000: 4500 0030 0000 4000 4006 c47b c0a8 f3a2
0x0010: c000 0201 1f90 c503 2ef5 1abf 0000 0001
0x0020: 7012 faf0 766f 0000 0204 05b4 0101 0402
14:48:33.595028 tun0 In IP 192.0.2.1.50435 > 192.168.243.162.8080: Flags [.], ack 1, win 10000, length 0
0x0000: 4500 0028 0000 0000 ff06 4583 c000 0201
0x0010: c0a8 f3a2 c503 1f90 0000 0001 2ef5 1ac0
0x0020: 5010 2710 e42d 0000
14:48:33.605276 tun0 In IP 192.0.2.1.50435 > 192.168.243.162.8080: Flags [P.], seq 1:11, ack 1, win 10000, length 10: HTTP
0x0000: 4500 0032 0000 0000 ff06 4579 c000 0201
0x0010: c0a8 f3a2 c503 1f90 0000 0001 2ef5 1ac0
0x0020: 5018 2710 df11 0000 3031 3233 3435 3637
0x0030: 3839
14:48:33.605347 tun0 Out IP 192.168.243.162.8080 > 192.0.2.1.50435: Flags [.], ack 11, win 64230, length 0
0x0000: 4500 0028 d8c2 4000 4006 ebc0 c0a8 f3a2
0x0010: c000 0201 1f90 c503 2ef5 1ac0 0000 000b
0x0020: 5010 fae6 7667 0000
14:48:33.615556 tun0 In IP 192.0.2.1.50435 > 192.168.243.162.8080: Flags [P.], seq 6:16, ack 1, win 10000, length 10: HTTP
0x0000: 4500 0032 0000 0000 ff06 4579 c000 0201
0x0010: c0a8 f3a2 c503 1f90 0000 0006 2ef5 1ac0
0x0020: 5018 2710 df0c 0000 3031 3233 3435 3637
0x0030: 3839
14:48:33.615605 tun0 Out IP 192.168.243.162.8080 > 192.0.2.1.50435: Flags [.], ack 16, win 64225, options [nop,nop,sack 1 {6:11}], length 0
0x0000: 4500 0034 d8c3 4000 4006 ebb3 c0a8 f3a2
0x0010: c000 0201 1f90 c503 2ef5 1ac0 0000 0010
0x0020: 8010 fae1 7673 0000 0101 050a 0000 0006
0x0030: 0000 000b
14:48:34.628416 ? Out IP 192.168.243.162.8080 > 192.0.2.1.50435: Flags [F.], seq 1, ack 16, win 64225, length 0
0x0000: 4500 0028 d8c4 4000 4006 ebbe c0a8 f3a2
0x0010: c000 0201 1f90 c503 2ef5 1ac0 0000 0010
0x0020: 5011 fae1 7667 0000
14:48:34.628586 ? In IP 192.0.2.1.50435 > 192.168.243.162.8080: Flags [R.], seq 16, ack 1, win 10000, length 0
0x0000: 4500 0028 0000 0000 ff06 4583 c000 0201
0x0010: c0a8 f3a2 c503 1f90 0000 0010 2ef5 1ac0
0x0020: 5014 2710 e41a 0000
#
而当应用层读取数据时,最终获得的内容是:"012345678956789" 。
# packetdrill tcp_ex_dup_001.pkt
#
# 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:20:19.832499 tun0 In IP 192.0.2.1.45531 > 192.168.191.130.8080: Flags [S], seq 0, win 10000, options [mss 1460,nop,nop,sackOK], length 0
22:20:19.832531 tun0 Out IP 192.168.191.130.8080 > 192.0.2.1.45531: Flags [S.], seq 2759867484, ack 1, win 64240, options [mss 1460,nop,nop,sackOK], length 0
22:20:19.842665 tun0 In IP 192.0.2.1.45531 > 192.168.191.130.8080: Flags [.], ack 1, win 10000, length 0
22:20:19.853005 tun0 In IP 192.0.2.1.45531 > 192.168.191.130.8080: Flags [P.], seq 1:101, ack 1, win 10000, length 100: HTTP
22:20:19.853248 tun0 Out IP 192.168.191.130.8080 > 192.0.2.1.45531: Flags [.], ack 101, win 64140, length 0
22:20:19.862866 tun0 In IP 192.0.2.1.45531 > 192.168.191.130.8080: Flags [P.], seq 1:101, ack 1, win 10000, length 100: HTTP
22:20:19.862914 tun0 Out IP 192.168.191.130.8080 > 192.0.2.1.45531: Flags [.], ack 101, win 64140, options [nop,nop,sack 1 {1:101}], length 0
22:20:20.868869 ? Out IP 192.168.191.130.8080 > 192.0.2.1.45531: Flags [R.], seq 1, ack 101, win 64140, length 0
22:20:20.868935 ? In IP 192.0.2.1.45531 > 192.168.191.130.8080: Flags [R.], seq 101, ack 1, win 10000, length 0
#
0
往期推荐
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...