2018-11-09

Linux内核bug引起Mesos、Kubernetes、Docker的TCP/IP数据包失效


最近发现Linux内核bug,会造成使用veth设备进行路由的容器(例如Docker on IPv6、Kubernetes、Google Container Engine和Mesos)不检查TCP校验码(checksum),这会造成应用在某些场合下,例如坏的网络设备,接收错误数据。这个bug可以在三年前任何一个测试过的内核版本中发现。补丁已经被整合进核心代码,正在回迁入3.14之前的多个发行版中(例如SuSE,Canonical)。如果在自己环境中使用容器,强烈建议打上此补丁,或者等发布后,部署已经打上补丁的核心版本。

注:Docker默认NAT网络并不受影响,而实际上,Google Container Engine也通过自己的虚拟网络防止硬件错误。

编者:Jake Bower指出这个bug跟一段时间前发现的另外一个bug很相似。有趣!

起因

十一月的某个周末,一组Twitter负责服务的工程师收到值班通知,每个受影响的应用都报出“impossible”错误,看起来像奇怪的字符出现在字符串中,或者丢失了必须的字段。这些错误之间的联系并不很明确指向Twitter分布式架构。问题加剧表现为:任何分布式系统,数据,一旦出问题,将会引起更长的字段出问题(他们都存在缓存中,写入日志磁盘中,等等…)。经过一整天应用层的排错,团队可以将问题定位到某几个机柜内的设备。团队继续深入调查,发现在第一次影响开始前,扇入的TCP纠错码错误大幅度升高;这个调查结果将应用从问题中摘了出来,因为应用只可能引起网络拥塞而不会引起底层包问题。

编者:用“团队”这个词可能费解,是不是很多人来解决这个问题。公司内部很多工程师参与排错,很难列出每个人名字,但是主要贡献者包括:Brian Martin、David Robinson、Ken Kawamoto、Mahak Patidar、Manuel Cabalquinto、Sandy Strong、Zach Kiehl、Will Campbell、Ramin Khatibi、Yao Yue、Berk Demir、David Barr、Gopal Rajpurohit、Joseph Smith、Rohith Menon、Alex Lambert and Ian Downes、Cong Wang。

一旦机柜被移走,应用失效问题就解决了。当然,很多因素可以造成网络层失效,例如各种奇怪的硬件故障,线缆问题,电源问题,等….;TCP/IP纠错码就是为保护这些错误而设计的,而且实际上,从这些设备的统计证据表明错误都可以检测到—那么为什么这些应用也开始失效呢?

隔离特定交换机后,尝试减少这些错误(大部分复杂的工作是由SRE Brain Martin完成的)。通过发送大量数据到这些机柜可以很容易复现失效数据被接收。在某些交换机,大约~10%的包失效。然而,失效总是由于核心的TCP纠错码造成的(通过netstat -a返回的TcpInCsumError参数报告),而并不发送给应用。(在Linux中,IPv4 UDP包可以通过设置隐含参数SO_NO_CHECK,以禁止纠错码方式发送;用这种方式,我们可以发现数据失效)。

Evan Jones(@epcjones)有一个理论,说的是假如两个bit刚好各自翻转(例如0->1和1->0)失效数据有可能有有效的纠错码,对于16位序字节,TCP纠错码会刚好抵消各自的错误(TCP纠错码是逐位求和)。当失效数据一直在消息体固定的位置(对32位求模),事实是附着码(0->1)消除了这种可能性。因为纠错码在存储之前就无效了,一个也纠错码bit翻转外加一个数据bit翻转,也会抵消各自的错误。然而,我们发现出问题的bit并不在TCP纠错码内,因此这种解释不可能发生。

很快,团队意识到测试是在正常linux系统上进行的,许多Twitter服务是运行在Mesos上,使用Linux容器隔离不同应用。特别的,Twitter的配置创建了veth(virtual ethernet)设备,然后将应用的包转发到设备中。可以很确定,当把测试应用跑在Mesos容器内后,立即发现不管TCP纠错码是否有效(通过TcpInCsumErrors增多来确认),TCP链接都会有很多失效数据。有人建议激活veth以太设备上的“checksum offloading” 配置,通过这种方法解决了问题,失效数据被正确的丢弃了。

到这儿,有了一个临时解决办法,Twitter Mesos团队很快就将解决办法作为fix推给了Mesos项目组,将新配置部署到所有Twiter的生产容器中。

排错

当Evan和我讨论这个bug时,我们决定由于TCP/IP是在OS层出现问题,不可能是Mesos不正确配置造成的,一定应该是核心内部网络栈未被发现bug的问题。

为了继续查找bug,我们设计了最简单的测试流程:

单客户端打开一个socket,每秒发送一个简单且长的报文。

单服务端(使用处于侦听模式的nc)在socket端侦听,打印输出收到的消息。

网络工具,tc,可以用于发送前,任意修改包内容。

一旦客户端和服务端对接上,用网络工具失效所有发出包,发送10秒钟。

可以在一个台式机上运行客户端,服务器在另外一个台式机上。通过以太交换机连接两台设备,如果不用容器运行,结果和我们预想一致,并没有失效数据被接收到,也就是10秒内没有失效包被接收到。客户单停止修改包后,所有10条报文会立刻发出;这确认Linux端TCP栈,如果没有容器,工作是正常的,失效包会被丢弃并重新发送直到被正确接收为止。

0 (2).gif

The way it should work: corrupt data are not delivered; TCP retransmits data

Linux和容器

现在让我们快速回顾一下Linux网络栈如何在容器环境下工作会很有帮助。容器技术使得用户空间(user-space)应用可以在机器内共存,因此带来了虚拟环境下的很多益处(减少或者消除应用之间的干扰,允许多应用运行在不同环境,或者不同库)而不需要虚拟化环境下的消耗。理想地,任何资源之间竞争都应该被隔离,场景包括磁盘请求队列,缓存和网络。

Linux下,veth设备用于隔离同一设备中运行的容器。Linux网络栈很复杂,但是一个veth设备本质上应该是用户角度看来的一个标准以太设备。

为了构建一个拥有虚拟以太设备的容器,必须(1)创建一个虚机,(2)创建一个veth,(3)将veth绑定与容器端,(4)给veth指定一个IP地址,(5)设置路由,用于Linux流量控制,这样包就可以扇入扇出容器了。

为什么是虚拟造成了问题

我们重建了如上测试场景,除了服务端运行于容器中。然后,当开始运行时,我们发现了很多不同:失效数据并未被丢弃,而是被转递给应用!通过一个简单测试(两个台式机,和非常简单的程序)就重现了错误。

0 (3).gif

失效数据被转递给应用,参见左侧窗口。

我们可以在云平台重建此测试环境。k8s的默认配置触发了此问题(也就是说,跟Google Container Engine中使用的一样),Docker的默认配置(NAT)是安全的,但是Docker的IPv6配置不是。

修复问题

重新检查Linux核心网络代码,很明显bug是在veth核心模块中。在核心中,从硬件设备中接收的包有ip_summed字段,如果硬件检测纠错码,就会被设置为CHECKSUM_UNNECESSARY,如果包失效或者不能验证,者会被设置为CHECKSUM_NONE。

veth.c中的代码用CHECKSUM_UNNECESSARY代替了CHECKSUM_NONE,这造成了应该由软件验证或者拒绝的纠错码被默认忽略了。移除此代码后,包从一个栈转发到另外一个(如预期,tcpdump在两端都显示无效纠错码),然后被正确传递(或者丢弃)给应用层。我们不想测试每个不同的网络配置,但是可以尝试不少通用项,例如桥接容器,在容器和主机之间使用NAT,从硬件设备到容器见路由。我们在Twitter生产系统中部署了这些配置(通过在每个veth设备上禁止RX checksum offloading)。

还不确定为什么代码会这样设计,但是我们相信这是优化设计的一个尝试。很多时候,veth设备用于连接统一物理机器中的不同容器。

逻辑上,包在同一物理主机不同容器间传递(或者在虚机之间)不需要计算或者验证纠错码:唯一可能失效的是主机的RAM,因为包并未经过线缆传递。不幸的是,这项优化并不像预想的那样工作:本地产生的包,ip_summed字段会被预设为CHECKSUM_PARTIAL,而不是CHECKSUM_NONE。

这段代码可以回溯到驱动程序第一次提交(commit e314dbdc1c0dc6a548ecf [NET]: Virtual ethernet device driver)。 Commit 0b7967503dc97864f283a net/veth: Fix packet checksumming (in December 2010)修复了本地产生,然后发往硬件设备的包,默认不改变CHECKSUM_PARTIAL的问题。然而,此问题仍然对进入硬件设备的包生效。

核心修复补丁如下:

diff — git a/drivers/net/veth.c b/drivers/net/veth.c

index 0ef4a5a..ba21d07 100644

— — a/drivers/net/veth.c

+++ b/drivers/net/veth.c

@@ -117,12 +117,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)

kfree_skb(skb);

goto drop;

}

-

- if (skb->ip_summed == CHECKSUM_NONE &&

- rcv->features & NETIF_F_RXCSUM)

- skb->ip_summed = CHECKSUM_UNNECESSARY;

if (likely(dev_forward_skb(rcv, skb) == NET_RX_SUCCESS)) {

struct pcpu_vstats *stats = this_cpu_ptr(dev->vstats);

结论

我对Linux netdev和核心维护团队的工作很钦佩;代码确认非常迅速,不到几个星期补丁就被整合,不到一个月就被回溯到以前的(3.14+)稳定发行版本中(Canonical,SuSE)。在容器化环境占优势的今天,很惊讶这样的bug居然存在了很久而没被发现。很难想象因为这个bug引发多少应用崩溃和不可知的行为!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容