linux内核协议栈 TCP选项之SACK选项的接收 Ⅲ

这篇博客详细介绍了Linux内核中TCP协议栈处理SACK选项,特别是DSACK的检测过程。内容涵盖如何判断首个SACK块是否为DSACK以及检查SACK块的有效性,确保其合法性。
摘要由CSDN通过智能技术生成

Table of Contents

1 检测是否存在DSACK

2 检测SACK块是否有效


这篇笔记记录了发送方在收到SACK信息后的处理过程中,对SACK信息块的检查,具体包括:

  1. 判断第一个SACK块是否是DSACK;
  2. 检查SACK信息块(包括DSACK)是否合法。

1 检测是否存在DSACK

tcp_check_dsack()用于判断收到的第一个SACK块是否是DSACK,参数sp指向输入段携带的SACK选项信息,num_sacks表示输入段携带了几个SACK块。

关于如何判定收到的SACK块是DSACK,详细的介绍可以参考RFC 2883.实际上思路很简单,就是接收端只有在收到重复段的情况下才会发送DSACK,而重复段有两种情况:

  1. 该段已经被确认过了;
  2. 该段是个乱序段,但是之前也已经接收过该乱序段了;

所以,对应的DSACK块有两种情况:

  1. DSACK块的起始序号小于ACK序号;
  2. DSACK块的序号范围一定在后一个SACK块的序号范围之内。
static int tcp_check_dsack(struct tcp_sock *tp, struct sk_buff *ack_skb,
			   struct tcp_sack_block_wire *sp, int num_sacks,
			   u32 prior_snd_una)
{
	//DSACK只能出现在sp[0],这里提取sp[0]的起始序号
	u32 start_seq_0 = ntohl(get_unaligned(&sp[0].start_seq));
	u32 end_seq_0 = ntohl(get_unaligned(&sp[0].end_seq));
	int dup_sack = 0;

	//如果SACK块的起始序号小于输入段携带的确认号,那么说明对端一定是收到了
	//一个已经确认过的重复段,才会触发这样的SACK,所以认为这是一个DSACK
	if (before(start_seq_0, TCP_SKB_CB(ack_skb)->ack_seq)) {
		dup_sack = 1;
		tcp_dsack_seen(tp);
		NET_INC_STATS_BH(LINUX_MIB_TCPDSACKRECV);
	} else if (num_sacks > 1) {
		//如果有多个SACK块,那么提取第二个SACK块的起始序号
		u32 end_seq_1 = ntohl(get_unaligned(&sp[1].end_seq));
		u32 start_seq_1 = ntohl(get_unaligned(&sp[1].start_seq));
		//如果第二个SACK块将第一个SACK块完全包含,那么说明对端一定是收到了
		//一个乱序的重复段,所以也认为这是一个DSACK
		if (!after(end_seq_0, end_seq_1) && !before(start_seq_0, start_seq_1)) {
			dup_sack = 1;
			tcp_dsack_seen(tp);
			NET_INC_STATS_BH(LINUX_MIB_TCPDSACKOFORECV);
		}
	}

	/* D-SACK for already forgotten data... Do dumb counting. */
	//拥塞控制算法需要
	if (dup_sack && !after(end_seq_0, prior_snd_una) && after(end_seq_0, tp->undo_marker))
		tp->undo_retrans--;

	return dup_sack;
}

static void tcp_dsack_seen(struct tcp_sock *tp)
{
	//设置sack_ok的bit3,表示检测到了DSACK
	tp->rx_opt.sack_ok |= 4;
}

2 检测SACK块是否有效

@is_dsack: 要检测的SACK块是否是一个DSACK块
static int tcp_is_sackblock_valid(struct tcp_sock *tp, int is_dsack, u32 start_seq, u32 end_seq)
{
	/* Too far in future, or reversed (interpretation is ambiguous) */
	//cond1:SACK的确认范围包含了还没有发送的数据;
	//cond2: SACK块的第一个序号大于等于第二个序号,序号范围非法
	if (after(end_seq, tp->snd_nxt) || !before(start_seq, end_seq))
		return 0;

	/* Nasty start_seq wrap-around check (see comments above) */
	//和上面的cond1类似,确认范围不合理
	if (!before(start_seq, tp->snd_nxt))
		return 0;

	/* In outstanding window? ...This is valid exit for D-SACKs too.
	 * start_seq == snd_una is non-sensical (see comments above)
	 */
	//满足该条件,说明SACK确认范围确实是在[snd_una, snd_nxt)之间,合法SACK
	if (after(start_seq, tp->snd_una))
		return 1;
	//到了这里,说明SACK确认范围和[snd_una, snd_nxt)是这样的:
	//snd_una=200, snd_nxt=1000,start_seq=100, end_seq = 500
	//即SACK的前半部分是已经被ACK过的,后半部分是没有ACK的(虚假SACK)

	if (!is_dsack || !tp->undo_marker)
		return 0;

	/* ...Then it's D-SACK, and must reside below snd_una completely */
	if (!after(end_seq, tp->snd_una))
		return 0;

	if (!before(start_seq, tp->undo_marker))
		return 1;

	/* Too old */
	if (!after(end_seq, tp->undo_marker))
		return 0;

	/* Undo_marker boundary crossing (overestimates a lot). Known already:
	 *   start_seq < undo_marker and end_seq >= undo_marker.
	 */
	return !before(start_seq, end_seq - tp->max_window);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值