Wireshark TS | TLP 超时时间

前言

说到 TLP 超时时间,之前曾经在《超时重传时间不翻倍》中讨论过一个案例,本篇再简单总结一下两种场景的现象。

TLP

Tail Loss Probe (TLP)是一个发送端算法,主要目的是使用快速重传取代RTO超时重传来处理尾包丢失场景。如果TCP尾包丢失,如果依靠RTO超时进行重传会带来比较大的延迟,进而影响用户体验。

那么如果一个TCP连接没有在一段时间内没有收到ACK报文,TLP会强制传输还没有收到ACK确认的报文里面的最后一个报文或者未发送的新报文(传输的这个报文就叫做loss probe)。

tcp_early_retrans - INTEGER

Enable Early Retransmit (ER), per RFC 5827. ER lowers the threshold for triggering fast retransmit when the amount of outstanding data is small and when no previously unsent data can be transmitted (such that limited transmit could be used). Also controls the use of Tail loss probe (TLP) that converts RTOs occurring due to tail losses into fast recovery (draft-dukkipati-tcpm-tcp-loss-probe-01).

Possible values:

0 disables ER

1 enables ER

2 enables ER but delays fast recovery and fast retransmit by a fourth of RTT. This mitigates connection falsely recovers when network has a small degree of reordering (less than 3 packets).

3 enables delayed ER and TLP.

4 enables TLP only.

Default: 3

场景一

首先是仅写入一个数据的场景,脚本如下。

plain 复制代码
# cat test-01.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 1000,nop,nop,sackOK>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000
+0 accept(3, ..., ...) = 4

+0.1 write(4,...,100) = 100

+0 `sleep 0.25`
#

执行脚本,同时通过 tcpdump 抓取数据包,现象如下。

可以看到发出数据段后,第一次重传间隔时间为214ms,而第二次重传间隔时间为215ms,第二次重传的超时时间并未翻倍。

plain 复制代码
# packetdrill test-01.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:07:11.284546 tun0  In  IP 192.0.2.1.34063 > 192.168.218.210.8080: Flags [S], seq 0, win 10000, options [mss 1000,nop,nop,sackOK], length 0
09:07:11.284595 tun0  Out IP 192.168.218.210.8080 > 192.0.2.1.34063: Flags [S.], seq 1519431825, ack 1, win 64240, options [mss 1460,nop,nop,sackOK], length 0
09:07:11.294933 tun0  In  IP 192.0.2.1.34063 > 192.168.218.210.8080: Flags [.], ack 1, win 10000, length 0
09:07:11.395432 tun0  Out IP 192.168.218.210.8080 > 192.0.2.1.34063: Flags [P.], seq 1:101, ack 1, win 64240, length 100: HTTP
09:07:11.610184 tun0  Out IP 192.168.218.210.8080 > 192.0.2.1.34063: Flags [P.], seq 1:101, ack 1, win 64240, length 100: HTTP
09:07:11.826079 tun0  Out IP 192.168.218.210.8080 > 192.0.2.1.34063: Flags [P.], seq 1:101, ack 1, win 64240, length 100: HTTP
09:07:11.897813 ?     Out IP 192.168.218.210.8080 > 192.0.2.1.34063: Flags [F.], seq 101, ack 1, win 64240, length 0
09:07:11.897874 ?     In  IP 192.0.2.1.34063 > 192.168.218.210.8080: Flags [R.], seq 1, ack 1, win 10000, length 0
#

重传超时时间的设置过程,简述如下:

  1. No.4 为发送端发送的第一个数据包,如果之前 RTO 定时器没有运行,则会重启 RTO 定时器,并设置定时时间为 RTO,也就是 212ms。
  2. 之后进入 TLP 机制,主要通过 tcp_schedule_loss_probe 函数(它用于调度"尾部丢失探测计时器",目的是为了更早地检测和修复数据包丢失,从而避免依赖于更慢的"重传超时"机制),timeout 值先为 224ms,而在 advancing_rto 为假的情况下,rto_delta_us 使用了 tcp_rto_delta_us(RTO 距离当前时间的剩余时间),实际上也就是 212ms,之后在 timeout = min_t(u32, timeout, usecs_to_jiffies(rto_delta_us)) 取小,仍是 212ms。因 RTO 和 TLP 共享同一个定时器 icsk_retransmit_timer,先 RTO ,后 TLP ,最后是通过 tcp_reset_xmit_timer(inet_csk_reset_xmit_timer)重置了重传时间,事件为 ICSK_TIME_LOSS_PROBE。
  3. 当最终超时并触发时,回调函数根据事件 ICSK_TIME_LOSS_PROBE 最终调用 tcp_send_loss_probe 发送探测,因为没有待发新数据包的情况,所以是发送了还没有收到 ACK 确认的报文里面的最后一个报文,因只有一个数据包,所以 No.5 看起来也就是 No.4 的重传,但实际生效机制是 TLP。而在 No.5 发送时,又会通过 inet_csk_reset_xmit_timer 重置超时时间为 RTO 212ms,当然此时的事件变为 ICSK_TIME_RETRANS;
  4. 之后仍然没有 ACK 确认的情况下,发生了 RTO 超时重传,也就是 No.6 数据包,真正意义下的第一次 RTO 超时重传数据包。

场景二

第二个是写入两个数据的场景,脚本如下。

plain 复制代码
# cat test-02.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 1000,nop,nop,sackOK>
+0 > S. 0:0(0) ack 1 <...>
+0.01 < . 1:1(0) ack 1 win 10000
+0 accept(3, ..., ...) = 4

+0.1 write(4,...,100) = 100
+0 write(4,...,100) = 100

+0 `sleep 0.25`
#

执行脚本,同时通过 tcpdump 抓取数据包,现象如下。

可以看到发出数据段后,第一次重传间隔时间为33ms,重传的数据包为之前的第二个数据段,第二次重传间隔时间为216ms(距离第一次重传的数据包),重传的数据包为之前的第一个数据段。

plain 复制代码
# packetdrill test-02.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
10:00:50.425044 tun0  In  IP 192.0.2.1.49205 > 192.168.212.208.8080: Flags [S], seq 0, win 10000, options [mss 1000,nop,nop,sackOK], length 0
10:00:50.425144 tun0  Out IP 192.168.212.208.8080 > 192.0.2.1.49205: Flags [S.], seq 3993087090, ack 1, win 64240, options [mss 1460,nop,nop,sackOK], length 0
10:00:50.435521 tun0  In  IP 192.0.2.1.49205 > 192.168.212.208.8080: Flags [.], ack 1, win 10000, length 0
10:00:50.536091 tun0  Out IP 192.168.212.208.8080 > 192.0.2.1.49205: Flags [P.], seq 1:101, ack 1, win 64240, length 100: HTTP
10:00:50.536171 tun0  Out IP 192.168.212.208.8080 > 192.0.2.1.49205: Flags [P.], seq 101:201, ack 1, win 64240, length 100: HTTP
10:00:50.569475 tun0  Out IP 192.168.212.208.8080 > 192.0.2.1.49205: Flags [P.], seq 101:201, ack 1, win 64240, length 100: HTTP
10:00:50.786048 ?     Out IP 192.168.212.208.8080 > 192.0.2.1.49205: Flags [P.], seq 1:101, ack 1, win 64240, length 100: HTTP
10:00:50.788846 ?     Out IP 192.168.212.208.8080 > 192.0.2.1.49205: Flags [F.], seq 201, ack 1, win 64240, length 0
10:00:50.788891 ?     In  IP 192.0.2.1.49205 > 192.168.212.208.8080: Flags [R.], seq 1, ack 1, win 10000, length 0
#

重传超时时间的设置过程,简述如下:

  1. No.4 为发送端发送的第一个数据包,如果之前 RTO 定时器没有运行,则会重启 RTO 定时器,并设置定时时间为 RTO,也就是 212ms。
  2. 之后进入 TLP 机制,主要通过 tcp_schedule_loss_probe 函数,timeout 值先为 224ms,而在 advancing_rto 为假的情况下,rto_delta_us 使用了 tcp_rto_delta_us 212ms,之后在 timeout = min_t(u32, timeout, usecs_to_jiffies(rto_delta_us)) 取小,仍是 212ms。因 RTO 和 TLP 共享同一个定时器 icsk_retransmit_timer,先 RTO ,后 TLP ,最后是通过 tcp_reset_xmit_timer(inet_csk_reset_xmit_timer)重置了重传时间,事件为 ICSK_TIME_LOSS_PROBE。
  3. 之后 No.5 为发送端紧接着发送的第二个数据包,实际上也会先重置 RTO 定时器,但设置的定时时间仍为 212ms,事件变为 ICSK_TIME_RETRANS。
  4. 之后再次进入 TLP 机制,仍然通过 tcp_schedule_loss_probe 函数,而此时 timeout 值为 32ms,rto_delta_us 值为 212ms,之后继续取小为 32ms,通过 tcp_reset_xmit_timer(inet_csk_reset_xmit_timer)又重置了重传时间,事件为 ICSK_TIME_LOSS_PROBE。
  5. 当最终超时并触发时,回调函数根据事件 ICSK_TIME_LOSS_PROBE 最终调用 tcp_send_loss_probe 发送探测,因为没有待发新数据包的情况,所以是发送了还没有收到 ACK 确认的报文里面的最后一个报文,也就是重传了 No.5 数据包。而在 No.6 发送时,又会通过 inet_csk_reset_xmit_timer 重置超时时间为 RTO 212ms,当然此时的事件再次变为 ICSK_TIME_RETRANS;
  6. 之后仍然没有 ACK 确认的情况下,发生了 RTO 超时重传,也就是 No.7 数据包,真正意义下的第一次 RTO 超时重传数据包,重传的是 No.4。

参考

plain 复制代码
bool tcp_schedule_loss_probe(struct sock *sk, bool advancing_rto)
{
	struct inet_connection_sock *icsk = inet_csk(sk);
	struct tcp_sock *tp = tcp_sk(sk);
	u32 timeout, rto_delta_us;
	int early_retrans;
...
	early_retrans = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_early_retrans);
	/* Schedule a loss probe in 2*RTT for SACK capable connections
	 * not in loss recovery, that are either limited by cwnd or application.
	 */
	if ((early_retrans != 3 && early_retrans != 4) ||
	    !tp->packets_out || !tcp_is_sack(tp) ||
	    (icsk->icsk_ca_state != TCP_CA_Open &&
	     icsk->icsk_ca_state != TCP_CA_CWR))
		return false;

	/* Probe timeout is 2*rtt. Add minimum RTO to account
	 * for delayed ack when there's one outstanding packet. If no RTT
	 * sample is available then probe after TCP_TIMEOUT_INIT.
	 */
	if (tp->srtt_us) {
		timeout = usecs_to_jiffies(tp->srtt_us >> 2);
		if (tp->packets_out == 1)
			timeout += TCP_RTO_MIN;
		else
			timeout += TCP_TIMEOUT_MIN;
	} else {
		timeout = TCP_TIMEOUT_INIT;
	}

	/* If the RTO formula yields an earlier time, then use that time. */
	rto_delta_us = advancing_rto ?
			jiffies_to_usecs(inet_csk(sk)->icsk_rto) :
			tcp_rto_delta_us(sk);  /* How far in future is RTO? */
	if (rto_delta_us > 0)
		timeout = min_t(u32, timeout, usecs_to_jiffies(rto_delta_us));

	tcp_reset_xmit_timer(sk, ICSK_TIME_LOSS_PROBE, timeout, TCP_RTO_MAX);
	return true;
}
相关推荐
其实防守也摸鱼2 小时前
CTF密码学综合教学指南--第三章
开发语言·网络·python·安全·网络安全·密码学
其实防守也摸鱼2 小时前
CTF密码学综合教学指南--第四章
网络·笔记·安全·网络安全·密码学·ctf
草履虫君3 小时前
VMware 虚拟机网络性能优化指南:从 11 秒到 4 秒的完整调优实践
服务器·网络·经验分享·性能优化
@insist1233 小时前
信息安全-防火墙技术演进全景:从代理NAT 到下一代及专项防火墙
网络·安全·web安全·软考·信息安全工程师·软件水平考试
优化Henry3 小时前
TDD-LTE站点Rilink=3链路故障处理案例---BBU侧C口“有发光、无收光”的排查与恢复
运维·网络·信息与通信·tdd
浪客灿心3 小时前
Linux网络传输层协议
linux·运维·网络
段一凡-华北理工大学5 小时前
【高炉炼铁领域炉温监测、预警、调控智能体设计与应用】~系列文章06:智能决策:从经验驱动到数据驱动
网络·人工智能·数据挖掘·高炉炼铁·工业智能体·高炉炉温
时空系6 小时前
第7篇:功能——打造你的工具箱 Rust中文编程
开发语言·网络·rust
BizViewStudio7 小时前
甄选方法:2026 企业新媒体代运营的短视频精细化运营与流量转化技巧
大数据·网络·人工智能·媒体