踢走绊脚石,MTU解析与常见问题汇总-上篇
金秋十月,在这国庆和中秋双节之际,首先祝福大家双节快乐。
今天让我们来聊聊MTU的话题。
相信无论网工还是程序员,都会多多少少的碰到过由MTU引发的问题,也往往对MTU值的选择和计算头疼不已。
通常情况下,我们可以通过调整TCP-MSS值的大小,或者使用Path-MTUDiscovery 等技术来解决处理。
可在某些环境较为复杂的情况下,这些工具可能就不那么好使了。
所以如果不彻底了解MTU的定义以及相关工具的工作原理。出现问题后排错就显得尤为困难了。
为了彻底搞懂MTU,我将用两篇文章给大家介绍什么是MTU以及相关工具的工作原理。
1. 第一篇文章主要内容为MTU的定义以及数据包分片技术细节。
2. 第二篇文章将要介绍如何自动发现最佳MTU值以及相关工具,以及在特定场景下MTU所产生的问题以及解决方法,例如IPSec***,或者不同网络环境OSPF,BGP对接等。
上篇:MTU的工作原理以及数据包分片细节
既然要讨论MTU工作原理,那首先需要明确MTU的定义。
那什么是MTU?
相信大家都见过日常道路上的限高杆,其作用是限制道路上的车辆高度。在计算机网络环境里面,MTU就是链路上的限高杆,与现实世界的道路相似,在网络世界里面不同介质的链路也存在不同的MTU值。
严格来说,虽说单个IP数据包最大长度可达65535字节。而在传输过程中,传输链路规定了单个数据包传输长度。当IP数据包长度超过此长度后,数据包将被切割为小于或等于链路规定长度的小IP包。
此规定长度就是MTU值,全名为:Maximum Transmission Unit 最大传输单元。
让我们来看看一般情况下,各个介质下的MTU值。(单位:字节)
上图可以看出,不同链路存在不同的MTU值,以常见的以太网MTU值为例,其MTU值一般为1500字节。那这1500字节都包含了些什么?
如下,以Juniper的 SRX接口输出内容为例:
lab@SRX01> show interfaces ge-0/0/2
Physical interface: ge-0/0/2, Enabled, Physical link is Up
Interface index:136, SNMP ifIndex: 519
Link-level type:Ethernet, MTU:1514, Link-mode: Full-duplex, Speed: 1000mbps
<此处省略多余输出>
Logical interfacege-0/0/2.0 (Index 72) (SNMP ifIndex 528)
<此处省略多余输出>
Protocol inet, MTU:1500
Flags:Sendbcast-pkt-to-re
Addresses,Flags: Is-Preferred Is-Primary
Destination:1.1.1/24, Local: 1.1.1.1, Broadcast: 1.1.1.255
<此处省略多余输出>
大家可以看到,上面提到了两个MTU值,分别是1514 和1500。
先说说1514,从“Link-level type:Ethernet”可以看出,它是ISO模型第二层链路层的MTU值。1514字节包含了如下内容:
<二层以太网帧头14字节> --<数据包负载1500字节:包含三层IP头,四层或以上内容>
而1500为ISO模型第三层网络层的MTU,从“Protocol inet”可以看出,1500字节包含了所有网络层的数据,从IP包头到传输层UDP/TCP包头,甚至应用层的用户数据。
<3层IP头20字节>--<数据包负载1480字节:包含四层或者以上内容>
小结,MTU值取决于两个因素:
1. 不同链路介质存在不同MTU值
2. 相同链路下,不同ISO层级也存在不同MTU值,原则上低层级的MTU值是大于高层级的MTU值,为包含关系。
在理解MTU的定义以后,可以看出其实MTU就是一个普普通通的链路数据包大小阈值,就其本身而言,它没有任何定义上的错误,那为什么江湖上还有这么多关于MTU引起的纷争呢?
原因在于互联网世界中,不可能单存使用某一种介质的链路。
而就算在广泛使用的Ethernet以太网中,也存在了使用不同技术从而导致MTU值不相同的例子,例如大家熟知的GRE隧道,IPsec隧道,PPPOE接口等虚拟逻辑接口等。
当数据包穿越不同MTU值的链路时,往往就会出现各种问题。就好比高速路换普通公路必然会引起拥堵一样。MTU不匹配也会导致诸多问题。
MTU导致的问题
以下为常见的MTU不匹配导致的网络问题:
1. 因为链路MTU不匹配问题导致网络产生大量分片数据包,从而影响路由器包转发效能。
2. 在IPsec ***环境下,同样因为分片包原因,导致***吞吐量下降50%以上。
3. 二层链路MTU值低于三层链路MTU值从而导致数据包被丢弃。
4. 某些路由协议因为MTU值不匹配原因导致协议建立不成功。
总的来说,绝大部分还是和数据包分片有关,因此这里有必要花点篇幅来讨论下数据包分片的细节。也为后续章节做准备。
MTU分片包示例
为了便于理解,先来看一个因为MTU值原因导致数据包传输过程中被分片的案例:
lab@SRX01>ping 2.2.2.2 size 1500 source 1.1.1.1 count 2
一台名为SRX01的路由器ping了另外一台路由器SRX02 两次,每个ping数据包长度为1500字节。
中间链路抓包后,截图如下:
如上图所示,IP为1.1.1.1的主机ping了IP为2.2.2.2的主机两次。但由于数据包本身长度1500字节,额外还要加上IP和ICMP包头的长度,很明显总长度超过了网络层的MTU值1500字节,导致数据包被分片。
继续深入分析:
大家请注意,在数据包长度部分(Length),第一个数据包总长为1514字节,第二个数据包总长为62字节。那这长度是怎么计算的?
在开始计算之前,再次强调ping 数据包长度为1500字节,不包含IP头长度以及ICMP头长度。
先看看第一个数据包的1514怎么计算:
首先1514字节包含了二层链路帧头,三层IP包头,以及ICMP包头,最后是ping 的数据等。
用一个等式可以轻松理解1514的计算方法:
1514字节=14字节以太网二层帧 + 20字节三层IP包头 +8字节ICMP包头+1472字节数据包。
那接下来剩下的62字节分片数据包怎么计算?答案如下:
62字节= 14字节以太网二层帧 + 20字节三层IP包头 + 剩余28字节数据包
(注:28字节=总长1500字节数据包– 已经传输的1472字节数据包)
数据包分片技术细节
乍看之下,IP数据包分片不就是数据包大于MTU就被切割传输了?
但是仔细想想,很多问题还没有解释清楚?
例如:
1. 谁决定了到底是否分片,中间链路的路由器还是收发端?
2. 数据包接收方怎么知道这是不是分片包?
3. 因为时延的问题导致数据包到达顺序不一致,接收方怎么正确重组数据包?
4. 当有成千上万个不同流量的分片数据包同时到达接收方,因为分片数据包只存在IP包头+剩余数据,而缺少传输层包头,那接收方怎么甄别数据包属于某个上层协议?
为了回答以上问题,我们需要详细理解IP分片包的工作机制。
还是以Ping测试为例,本案中主机A向主机B Ping 4000字节数据包。网络中间链路MTU为标准的1500字节。
借助一个形象化的比喻:我们可以把一个数据包比喻为一辆货车,如下图。主机A点发出了一个×××大卡车,携带货物为4000字节长度,大卡准备驶向主机B点。同时为了让主机B点知晓货物内容以及收货人具体信息等,在这4000字节长度的货物上又封装了一个装箱单。
数据包生产过程:
主机A在产生数据包的时候会一次性把所有数据塞进一个完整的数据包(×××大卡车),此数据包包含了二层帧头,三层IP头(×××大卡车头),ICMP头(装箱单),同时也包含了有效载荷4000字节(货物)。
数据包传输过程:
当数据包通过途中某个链路时,三层网络层链路MTU(道路限高)为1500字节。因为包大小4000字节大于1500字节,这个时候数据包面临两个选择:分还是不分!
不分片,数据包就被无情被丢弃。
而分片就需要卸下负载(货物),换成小数据包在传输(大卡车换小面包车)。
那你说到底谁决定是否允许分片?
问题1:谁决定了数据包是否分片,中间链路的路由器还是收发端?
答案:决定数据包是否需要分片取决于始发地的主机A,主机A会往IP数据包里面写入一个叫做DF(don't-fragment)的字段告诉其他路由器是否允许对此数据包进行分片操作。
如果值设置为1,表示主机A不想让数据包被其他路由器分片。反之如何值为0,则允许分片。
例如下图,此数据包就被始发地A标记:请不要分片!
但是如设置了不想分片,那遇到链路MTU值比数据包小的情况下,数据包就会被中途的路由器丢弃。
在这里,我们假设始发地A允许分片。(DF=0)
分片本质上就是把数据包的负载(大卡车上的货物+装箱单)卸下后,根据链路限高来分割负载(货物+装箱单)并换成更小的数据包(面包车)来运输。但随之而来的问题是:分割后的每一个数据包都需要一个新包IP包头来运输数据包(每一辆面包车的车头),所以在计算整体数据包大小是否满足链路MTU值的时候,新IP包头大小也得计算在内,毕竟过限高杆的时候车头大小也是需要考虑的。
在本例中,以网络层1500字节MTU为标准,这4000字节加上包头的数据包被分为了三个小数据包,大小如下:
第一个包:1514字节(包含14字节二层帧)
包内容:14字节以太网帧头+20字节IP头+8字节ICMP头+1472字节负载
第二个包:1514字节(包含14字节二层帧)
包内容:14字节以太网帧头+20字节IP头+1480字节
第三个包:1082字节(包含14字节二层帧)
包内容:14字节以太网帧头+20字节IP头+1048字节
大家有没有发现,只有第一个包存在ICMP包头,后续的数据包不再封装ICMP包头。其实很简单,借用我们的比喻,这ICMP包头就是装箱单,在货物被切割的是,装箱单就跟着第一个数据包走了,我们总不至于把装箱单撕成碎片分别放到不同的小面包车上吧。
另外一个问题,因为分片是在途中发生,接收方的主机B完全不知情。本来主机B希望进门的是一个×××大卡,结果来了几辆小面包车,如果不说明清楚主机B肯定不接收货物。那么我们很有必要需要让其知道,中间大卡因为超载被强制换成小面包车了。
问题2:数据包接收方怎么知道这是不是分片包?
答案:当路由器在分片数据包的时候,会在IP包头处放置另外一个标记:more fragment(更多分片)。
除了分片最末尾的数据包外,其他所有分片数据包都存在此标记。
而末尾数据包之所以不存在此标记,则是因为它是最后一个,后续再也没有分片数据包了。
同时,在分片过程中,除了标记“更多分片”让主机B了解数据包被分片之外,我们还需要告知主机B数据包的分片顺序。从而引出如下问题:
问题3:因为时延的问题导致数据包到达顺序不一致,接收方怎么正确重组数据包?
答案:当数据包被分片时,路由器会根据其IP包载荷计算每一个包的起始位置,称作分片偏移量。接收端主机B会根据其偏移量来重组数据包。假如因为网络延迟原因,分片最后一个包先于其他两个数据包到达。通过偏移量,主机B会等待所有数据包到达以后,重组数据包。
本例中,第一个包的起始位置为0,所以其offset偏移量为0。而第二个包起始位置为第一个包的末端。本例为1480字节。以此类推,第三个数据包起始位置为第二个数据包的末端,为2960字节。(第一个1480+第二个1480=2960字节)
数据包对比如下:
以上为第二个数据包的1480字节偏移量示例。
上图为三个分片数据包offset偏移量汇总,其结果与我们所分析的完全一致。
问题4:当有成千上万个不同流量的分片数据包同时到达接收方,因为分片数据包只存在IP包头+剩余数据,而缺少传输层包头,那接收方怎么知晓此数据包是否属于某个上层协议?
答案:有了上面介绍的offset偏移量以后,当主机B接收到分片数据包,它会在根据另外一个参数最终确认所有的分片是否源自同一个源数据包,并完成重组。此参数为:Identification(数据包ID)。
如下图:
顾名思义,数据包ID类似于大家的×××一样,用来唯一标识某一个数据包。当数据包被分片后,同属于某一个源数据包的所有分片包将会使用同一个ID号。当数据包到达目的地主机B以后,主机B可以通过识别ID的方式在成千上万个不同数据包流中识别出相同源的数据包流,完成数据包重组。
在我们的示例中,当一个个的小面包车最后全部到达主机B以后。B站根据上面的信息把所有负载重组,并根据第一个小面包车的装箱单(第一个数据包的icmp 包头)把负载递交给上层协议ICMP,从而完成整个传输过程。
小结:
本篇我们一起分析了MTU的定义,以及数据包在网络链路中出现的分片原因以及计算方法等。
总的来说,由于MTU不匹配的原因,数据包会出现如下两种情况,
1. 数据包不能被分片,从而被丢弃。
因为数据包被丢弃,某些协议无法工作正常。同时也会影响例如对丢包非常敏感的TCP协议性能。
2. 数据包被分片并传输到目的地。
虽然数据包最终被完整的传输到目的地。但在分片过程中,路由器不得不花费更多的硬件资源来处理并转发数据包。一方面引入了数据包的延迟,同时也增加了路由器的数据包转发量。
借用卡车的例子,在卡车卸货并装车过程中,大家很容易想到这会需要花费更多的人工处理时间,同时把原来一辆车能处理的负载变为多辆车同时处理,无形中增加了后续传输道路的汽车拥堵程度。
上篇就此结束,下篇中我将要带领大家一起分析什么是TCP-MSS,Path-MTU Discovery。通过自动发现MTU值,从而尽可能避免数据包分片的发生。同时简要分析三种MTU导致的常见问题,敬请期待。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
排序算法(三)冒泡、选择排序的Python实现及算法优化详解
说在前面 最近一年太忙,博客长草了。近日用Python实现了常用排序算法,供大家参考。 Java版本排序算法及优化,请看以前的文章。 《排序算法之简单排序(冒泡、选择、插入)》 《排序算法(二)堆排序》 1、排序概念 这里不再赘述,请参看前面2篇文章 2、简单排序之冒泡法Python实现及优化 原理图 2.1、基本实现 num_list=[ [1,9,8,5,6,7,4,3,2], [1,2,3,4,5,6,7,8,9] ] nums=num_list[1] print(nums) length=len(nums) count_swap=0 count=0 #bubble_sort foriinrange(length): forjinrange(length-i-1): count+=1 ifnums[j]>nums[j+1]: tmp=nums[j] nums[j]=nums[j+1] nums[j+1]=tmp count_swap+=1 print(nums,count_swap,count) 2.2、优化实现 思路:如果本轮有交互,就说明顺序不对;如果本轮无交换,说明是...
- 下一篇
Shell脚本实现ssh免密登录及批量配置管理
本节索引 场景分析 ssh免密登录 pssh工具批量管理 SHELL自动化脚本 本篇总结 场景分析 作为一个运维工程师,不是每个人工作的环境都想阿里、腾讯那样,动不动就上亿的PV量,上万台服务器。我们通常还是工作在,几十台上百台服务器这样的环境,而使用ansible或者puppet这样的自动化运维工具则显得大材小用,并且最终的效果可能还不如几个小工具达到的效果好。像ssh免密登录在配合pssh这样的推送工具,在配合自动化配置脚本,可以说是即方便也使用。这一节将详细带大家以shell脚本的形式实现ssh免密登录进行百台机器的配置和管理。 ssh服务 随着明文通信协议telnet渐渐退出历史舞台,ssh这个作为安全的远程登录工具,更加受广大用户的青睐。SSH 为 Secure Shell 的缩写,由 IETF 的网络小组(Network Working Group)所制定;SSH 为建立在应用层基础上的安全协议。SSH 是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。SSH最初是UNIX系统上的一个程序,后来又迅速扩...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2全家桶,快速入门学习开发网站教程
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS8编译安装MySQL8.0.19
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2配置默认Tomcat设置,开启更多高级功能