从BBR的一个bug看Linux内核before/after

上周,同事甩过来一个链接,说的是一个陈年的bug:
https://www.spinics.net/lists/stable-commits/msg211905.html

问题出在在以下的代码:

if (!before(rs->prior_delivered, bbr->next_rtt_delivered)) {
    
    
	bbr->next_rtt_delivered = tp->delivered;
	bbr->rtt_cnt++;
	bbr->round_start = 1;
	bbr->packet_conservation = 0;
}

大致意思是,如果在一个TCP连接正在传输数据时,切换拥塞控制算法到BBR,其next_rtt_delivered被初始化成了0,可能会导致BBR认为一个round可以容纳多到 2 31 2^{31} 231的报文,当这么多报文被传输后,才能进入下一个round,显然这是不现实的,地球上最大的长肥管道一个round也容不下这么多报文。

问题出在before/after的判定方式上。看下面的等价代码:

if (!before(rs->prior_delivered, 0)) {
    
    
}

什么条件下会进入if?这要搞清楚before,after的含义。

我们知道,计算机计数不是在一个无限的域上的,而是在一个圆环域上的,类似钟表。直接看图:
在这里插入图片描述

请问,相对于a,b和b‘分别和a的关系是什么,before,还是after?

简单说,拿一个半圆的弧,一个端点放在a位置:

  • 如果b在以a为起点顺时针的半圆弧内,那么before(a, b)为真。
  • 如果b在以a为起点逆时针的半圆弧内,那么after(a,b)为真。

涉及到时间的before/after判断,依然有这个问题,注意,计算机计数在半圆内生效!

所以,对于这个BBR的bug,当rs->prior_delivered为 2 31 2^{31} 231多一点时,before(rs->prior_delivered, 0)一直到rs->prior_delivered越过0,将一直为真,因此在deliver大致 2 31 2^{31} 231个报文前,if语句将永远无法进入,这会误导BBR认为一个round容纳了 2 31 2^{31} 231个报文,进而影响其rate采样计算。

以一个报文1KB为例, 2 31 2^{31} 231大致2TB,好家伙!

触发这个bug不容易,需要你恰好在一个TCP连接已经传输了 2 31 2^{31} 231多个报文之后切换到BBR算法,大概就是3TB的数据量,哪个经理直播会展示这个数量级的数据呢?领带,西装,皮鞋?

这个bug映射到现实中,大概在环400米体育场跑马拉松时会频繁触发吧,所以马拉松必须拉到城市干道中跑。

如今网络带宽动辄25GBps+了,万一有了更猛的网络呢?32位序列号显然会显得不够用,如何来做呢?嗯,tcp结构体加一个cnt就够了吗?事情变得复杂起来了…

事实上,扩展协议不是更好么?协议头加一个bit和内卷SDN哪个更简单??

如果你说你不能自定义TCP协议,或者扯到运营商,我只能说,你对网络一无所知!


浙江温州皮鞋湿,下雨进水不会胖。

Guess you like

Origin blog.csdn.net/dog250/article/details/120094377