08 | 分段:MTU引发的血案
该思维导图由 AI 生成,仅供参考
案例:重传失败导致应用压测报错
- 深入了解
- 翻译
- 解释
- 总结
本文通过一个案例详细介绍了在公有云服务中,客户对软件负载均衡进行压力测试时遇到的问题。文章首先介绍了压测失败的背景和环境,然后通过抓包分析,分别展示了绕过负载均衡和经过负载均衡的压测过程中的报文情况。在经过负载均衡的压测中发现了大量的重传和重复确认,进一步分析发现是由于客户端未收到服务端的第一个数据报文导致的。最后,文章提到了Wireshark的Flow Graph工具,可以更直观地展示报文的流向,帮助读者更好地理解报文情况。通过这个案例,读者可以深入了解MTU、MSS、分片、分段等概念的联系和区别,以及在网络设计中可能出现的问题和解决方法。文章还介绍了解决MTU引发的问题的一般对策和“暗箱操作”,展示了网络领域的灵活性和可玩性。文章还介绍了TSO和IP分片的概念,以及如何使用ethtool工具查看和控制offload相关特性。通过对比成功和失败两种场景下的不同的抓包文件,能比较快地定位到问题根因。最后,文章提出了两道思考题,引发读者对于网络设计和问题排查的思考。
《网络排查案例课》,新⼈⾸单¥59
全部留言(28)
- 最新
- 精选
- 江山如画问题1: 从可用性角度分析,通过 iptables 修改 mss 只对 tcp 报文生效,对 udp 报文不生效。由于udp 报文传输时没有协商 mss 的过程,如果发现 udp 负载长度比 mtu 大,会交给网络层分片处理,分片传输途中只要有一个分片丢了,由于 udp 没有反馈给发送端具体是哪个分片丢了的能力,只能重新传整个包,传输效率会变低。 从运维角度分析,由于修改中间环节某个服务器的 iptables ,对于其它侧是透明的,可能会对定位问题带来困扰。 问题2: 遇到过 mtu 引发的问题。之前手动创建了虚拟网卡,和物理网卡之间做了流量的桥接,发现有些报文在二者之间转发时会被丢掉,分析发现虚拟网卡的 mtu 设置过大,并且报文 DF 位设置为了 1,通过 ifconfig 命令把虚拟网卡的 mtu 改小,报文就可以正常转发了。
作者回复: 回答的很详细,很明显经过了仔细的思考,不是随便写的,不光为你的答案,我更要为你的学习态度点赞! udp这部分,协议要求是udp载荷不要超过512字节,比常见MTU小很多,所以可能这个超出MTU的问题没有tcp那样严重。但是就像你说的,udp丢包的话,相应的容错和重传,协议栈本身是不做的,这就给应用程序提出了要求。 从运维角度分析的答案也很好,我也是很看重这一点:如果把配置“藏”在各个地方而其他人不知道,就等于是给其他运维和开发人员“埋雷”,我们应该尽量避免。
2022-02-07420 - Geek_955506Flow Graph 展示页的左下角有一个复选框 "limt to display filter",勾上之后就只展示过滤的内容了,不需要再单独保存展示
作者回复: 多谢补充,工具的使用方面有很多小的细节:)
2022-06-059 - 张靳开始对[为什么有重复确认(DupAck)]这个小节标志位Dup ACK的两个报文是载荷为688和57的两个报文的确认报文不是很理解,在杨老师指导下豁然开朗,做一下我的理解记录: 最开始我对wirshark的符号有误解,我选中Dup ACK报文发现是7号报文(三次握手的ack报文)的重复确认(有两个对勾),我就以为是7号报文的确认报文,这个理解本身有问题,因为ack本身没有ack了,不然就没完没了了。查阅了Dup ACK的定义,当发现丢包或者乱序的时候接收方会收到一些Seq序列号比期望值大的包,每收到这种包就会ack一次期望的Seq值。 所以我们知道可能有丢包才会有重复确认,且确认得是对端发过来得报文。那么结合整个tcp流来看,发生重传得报文是没收到的,然后确认了688和57载荷得报文。
作者回复: 很好,自己想明白了这知识就真的是你的了:)
2022-02-124 - 斯蒂芬.赵不明白,如果传入的tcp载荷超过了mtu值,不应该根据mss值自动的分段么
作者回复: 是的,不过这里的麻烦是由于很多网络限制,通信两端其实经常无法收到PMTU ICMP消息,也就导致无法及时调整MSS来适配网络状况,也是文中案例的诱因之一。
2023-01-11归属地:山东2 - piboye包的收发路径不一致的时候, MSS 协商是不是就失效了? 运营商网络, 如果出现链路调整, 之前协商的MSS 是不是也会可能失效了? 特别是长链接场景, 这种情况, 是不是要预先设置一个比较保守的值?
作者回复: Linux对MSS会随时间发生变化这一点也是有机制来保护的,比如依赖PMTU报文进行调整。PMTU是网络设备会遵循的协议,当设备收到一个超过其MTU的报文时,一方面会丢弃,一方面会返回一个Destination Unreachable的ICMP消息,具体可以参考RFC 1191: https://www.rfc-editor.org/rfc/rfc1191 由于网络的复杂性,这种ICMP消息经常会被拦截,导致发送端压根不知道自己的报文因为MTU超限而被丢弃了,就会导致各种问题。 在已经知道有隧道的情况下,尽量设置合理的相对低的MTU值,是保障网络通信的一个经验。
2022-08-03归属地:上海2 - Geek3340老师,有一点不是很理解: 由于 Tunnel 1 比 Tunnel 2 的封装更大一些,所以服务端选择了不同的传输尺寸,一个是 1388,一个是 1348。 为啥会有这种选择呢,按照理解,MSS会自动从MTU-40来计算,不太理解为啥中间的IPIP隧道会影响到MSS的分段 经过LB的,1388 + 40(TCPIP) + IP(20) + IP(20) = 1468 不经过LB的,1348 + 40(TCPIP) + IP(20) = 1408 在本次案例中,以下命令能解决问题,应该如何理解呢?我理解1400应该是调整小了MSS到1400,但是之前的1388也没有超限呀,不理解: iptables -A FORWARD -p tcp --tcp-flags SYN SYN -j TCPMSS --set-mss 1400
作者回复: 您好,图中tunnel1也是用在gz和bj服务器之间的,并不是仅仅在gz和bj lb之间。 第二个问题,mss改为1400是一个示例,并不是说当时这个案例就是用了这个数值。 有任何其他疑问也都可以提哈
2022-02-0852 - 志强老师有几个疑问请教: 问题1."我们选中 575 号报文"下边的图中Dup ACK报文是31号,"为什么是两个重复确认报文呢?我们把视线从 2 个 DupAck 报文往上挪"下边的图中dup ack 就变成了3号,是人为修改的还是怎么回事? 问题2.有两次31号报文的dup ack,是因为收到了额外两次17号报文吧,有人肯能会问那为啥看不到17号报文的重传呢,这个我也不太清楚,可能是处理重传的位置在捕获抓包之后吧,要是在客户端抓包就能看到17号报文的重传,请老师指正 问题3.无论是握手的ack 还是数据的ack,这个ack是谁给回的,知道是内核给回复,具体哪一层的什么函数处理过后给回复的ack;kcp老师了解吗,是应用层在再给回复还是也是内核给的恢复 谢谢老师,期待您的详细解答
作者回复: 关于这3个问题: 1. 你看到DupAck to从#31变成了#3,是因为截图的问题,#3那个是另外单独保存的抓包文件,所以编号不一样,但是TCP流确实是同一个。这个截图我们刚替换成统一#31的,也感谢你的细心! 2. 这两次对#31报文的DupAck,只是因为握手之后的第一个数据报文(也就是1388字节那个)没有到客户端,但后面两个报文到了,所以客户端回复的两次ack号只能停留在握手结束的时候。这不是说客户端收到了两次SYN+ACK。DupAck的意思是:“我需要你那边跟这个ack号等值的seq号的报文”,而不是“我需要你再发一次我刚刚ack过的报文”,这里正好差了一个“身位”,你再想想? 3. ack是内核协议栈回复的,你可以从github上git clone一下linux内核代码,然后在IDE里,搜一下TCPHDR_ACK在代码中的位置。大体上说,ACK标志位的设置是一类函数,比如SYN、FIN、RST(不含被动RST)、传输阶段等报文都是在各自不同的函数里面设置了ACK标志位,而它们的发送都会走到tcp_transmit_skb()来完成。 kcp没了解,也没用过:(
2022-02-0832 - 潘政宇杨老师,中间设备不是只是转发作用吗,协商mss也参与吗
作者回复: 中间设备负责转发,但因为IP和TCP报文是不加密的,所以可以在中间环节修改这些转发报文的MSS字段,这就起到了让通信两端都按照修改后的MSS进行传输的效果。 理论上说,不光MSS,几乎其他任何IP头部和TCP头部字段都可以被中间设备修改。
2022-02-0722 - DexterTCP分段的计算公司,那个IP header length和TCP header length是如何的得知的?难道都是按照默认的IP HDR =20, TCP HDR =20吗?这不是很可靠吧。 还有TCP segmentation probe功能开启是不是能够解决案例中的问题? [root@master03 ipv4]# cat tcp_mtu_probing 0 [root@master03 ipv4]# 问题2:如果启用了TSO或者GRO,为什么经常在抓包中看到TCP segment还是1460? 问题3: LRO是什么?老师能帮忙解答下吗?
作者回复: 您好~ 1. 你问的是RFC879里面的这个公式吗?SndMaxSegSiz = MIN((MTU - sizeof(TCPHDR) - sizeof(IPHDR)), MSS) 。MSS是每个TCP连接各自维护的,所以是根据这条连接的实际情况来决定大小。比如在握手阶段,已经确定要使用TCP option的timestamp,那么这里的TCPHDR就不再是20字节了,而是更大,得出的MSS就更小。 mtu_probing没有在这个案例里使用,遗憾的是现场早就没有了,要不然可以试试。 2. 启用了TSO或者GRO,还是看到1460?这我倒没注意,一般启用以后,常见的是2920、2800或者更大的值。你依然看到1460,有一个可能性是网卡并没有严格去执行offload。 3. LRO是Large Receive Offload,跟GRO类似。LRO/GRO启用后,tcpdump抓取到的接收方向的报文就是合并过的。
2022-02-261 - Dexteriptables -A FORWARD -p tcp --tcp-flags SYN SYN -j TCPMSS --set-mss 1400 --- 文中说是在nat表,不过这个command没有指定nat表,而且nat表中应该没有forward chain
作者回复: 嗯这里有误,我更正一下,是默认(没有指定-t)的filter表,谢谢:)
2022-02-261