闲说一个关于TSO/IP分片的NAT模块bug

声明,这个Bug无论是Kernel社区还是别的什么地方,早就已经解决了,但是 这种引入Bug的思路 至今仍在。

这是我在2011年时候的一起排查案例,当时我在Linux 2.6.8内核上工作,嗯,超级老的内核。问题的描述可以看下面的文章:
从Linux 2.6.8内核的一个TSO/NAT bug引出的网络问题排查观点: https://blog.csdn.net/dog250/article/details/46798461

哈哈,我又要旧事重提了。

简单点说,Bug的触发来自于下面的逻辑:

ip_fragment(skb)
{
	if skb is DF {
		icmp_send(need Fragment);
		return;
	}
	...
}
...
	if (skb->len > dst_pmtu(skb->dst) && !skb_shinfo(skb)->tso_size) {
    	return ip_fragment(skb, ip_finish_output);
	}
...

要点在于:

  1. skb的IP头设置了DF;
  2. 本地使能了TSO

照理说,逻辑本不该进入ip_fragment,而是径直向下进入网卡,然后由网卡做TSO。但是却没有。可以肯定的就是skb的tso_size被错误地置0了。

如果是内核发包逻辑的错误置0,那么这个Bug不会藏得这么深: 只要有nat规则,发大包就会触发! 所以说,这个问题几乎可以肯定就是NAT模块的问题,与主发包逻辑无关。

最终确认,NAT模块只要被加载,就会接管skb,从而无条件实施copy on write操作,问题的根源在于在它copy的时候, 遗漏了tso_size字段的copy。 进而触发了Bug。


这个问题和前天我描述的那个Overlayfs问题的要点完全一致:

  • 两个对象或者模块必须相互关联,然而它们之间却是独立的,很难让它们完美配合。

NAT和TSO以及IP Fragment有什么关系?但是它们真的就有关系了…

我最讨厌的一句话就是 “这个问题和XXX没关系…” 这是典型的信任模型,不能证明有问题那就是没问题,十分便于甩锅,而如果想要快速准确定位问题解决问题,就要采取不信任模型,不能证明没问题那就是有问题,换句话说就是 任何蛛丝马迹都要查查。


后来,我发现,值得庆幸的是,直到5.1版本的内核,代码逐渐变得clean and clear了,很多杂糅在一起的关系被解除,比如inet_peer,dst_entry,neigh。这便是拆毁了很多Bug滋生的温床。非常不错。

我曾经仔细梳理过这些问题是如何引入和如何被发现和解决的。几乎无一例外,问题几乎都是纯coder引入的,为了满足某种设计模式,为了让代码变得更好看…但是运维工程师,网络工程师发现并排除了这些错误。

这说明一个纯程序员懂点特定领域的东西,一个特定领域的工程师懂点编程,显得多么重要。


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

你可能感兴趣的:(闲说一个关于TSO/IP分片的NAT模块bug)