wireshark 中带有 SLE 和 SRE 的 SACK 包详解

1 名词解释

SLE: Sequence Left Edge of already acknowledged data when Selective Acknowledgments are used. 即已收到tcp数据的左边界。
SRE: Sequence Right Edge of already acknowledged data when Selective Acknowledgments are used. 即已收到tcp数据的右边界。

2 使用场景

2.1 丢包

SACK在数据丢包需要重传时起作用。比如,服务器已发送的数据为1~34454个包,但是,客户端只收到了“1~22774,28614~34454”这些序列的包,也就是说“22775~28613”这些包已经丢了。这个时候,客户端会向服务器请求发送回馈包,说我收到了seq为22774的包,同时也乱序收到了"SLE为28614,SRE为34454"的包。那么,服务器就知道,接着从seq=22775的包开始发送,发送到seq=28613的包的时候,就不用在发送seq=28614的包了,因为客户端已经收到了。
如果ACK中不带SLE和SRE会怎样呢?那服务器就会重发从"22775"开始之后的所有的包,包括其实客户端已经收到的"28614~34454"序号的包,那就浪费网络带宽了,不是么。

2.2 乱序

当出现 SLE、SRE 的 SACK 报文并不能判别一定发生了丢包,如果从2、3层诊断确实没有发生丢包,此时可以判断:

1、没有配置rps,多个收报软中断运行各个 cpu 核上,即同一 tcp 链路报文分别在不同的核上处理,各个核上处理报文速率有快慢,导致乱序。

2、配置了rps,但是配置可能出错,需要核对一下。

本质:如果相同五元组的TCP报文被随机分配到不同的 core上处理,此时就会出现乱序的结果。

2.2.1 硬件中断处理代码

static int netif_rx_internal(struct sk_buff *skb)
{
	int ret;

	net_timestamp_check(netdev_tstamp_prequeue, skb);

	trace_netif_rx(skb);

	if (static_key_false(&generic_xdp_needed)) {
		int ret;

		preempt_disable();
		rcu_read_lock();
		ret = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), skb);
		rcu_read_unlock();
		preempt_enable();

		/* Consider XDP consuming the packet a success from
		 * the netdev point of view we do not want to count
		 * this as an error.
		 */
		if (ret != XDP_PASS)
			return NET_RX_SUCCESS;
	}

#ifdef CONFIG_RPS
	if (static_key_false(&rps_needed)) {
		struct rps_dev_flow voidflow, *rflow = &voidflow;
		int cpu;

		preempt_disable();
		rcu_read_lock();

		cpu = get_rps_cpu(skb->dev, skb, &rflow);
		if (cpu < 0)
			cpu = smp_processor_id();

		ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);

		rcu_read_unlock();
		preempt_enable();
	} else
#endif
    //没有rps
	{
		unsigned int qtail;

		ret = enqueue_to_backlog(skb, get_cpu(), &qtail);
		put_cpu();
	}
	return ret;
}

=====================================================================

/*
 * enqueue_to_backlog is called to queue an skb to a per CPU backlog
 * queue (may be a remote CPU queue).
 */
static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
			      unsigned int *qtail)
{
	struct softnet_data *sd;
	unsigned long flags;
	unsigned int qlen;

	sd = &per_cpu(softnet_data, cpu);

	local_irq_save(flags);

	rps_lock(sd);
	if (!netif_running(skb->dev))
		goto drop;
	qlen = skb_queue_len(&sd->input_pkt_queue);
	
	//skb_flow_limit:查看rx队列中是否还有空间
	if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) {
		if (qlen) {
enqueue:
			/*队列还有空间将数据包添加到input_pkt_queue队列中*/
			__skb_queue_tail(&sd->input_pkt_queue, skb);
			input_queue_tail_incr_save(sd, qtail);
			rps_unlock(sd);
			local_irq_restore(flags);
			return NET_RX_SUCCESS;
		}

		/* Schedule NAPI for backlog device
		 * We can use non atomic operation since we own the queue lock
		 */
		if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) {
			if (!rps_ipi_queued(sd))
				____napi_schedule(sd, &sd->backlog);//唤醒sotf_irq处理报文
		}
		goto enqueue;
	}

drop:
	sd->dropped++;
	rps_unlock(sd);

	local_irq_restore(flags);

	atomic_long_inc(&skb->dev->rx_dropped);
	kfree_skb(skb);
	return NET_RX_DROP;
}

说明:网卡队列大小可以通过 /proc/sys/net/core/netdev_max_backlog 修改

sack报文如下:

这里还有一点关于 wireshark 的疑问:图中的SACK报文的ack=16590226,sack_left = 16616506,sack_right=16617966。很显然sack的片段并没有被覆盖,为啥会被标记成D-SACK ???



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值