大家好python
在《MTU工具解析与常见问题汇总-上篇》中,咱们一块儿讨论了MTU的基本定义,以及数据包分片的具体细节,同时也简单列举了MTU不匹配致使的问题和对网络数据传输的危害。api
既然MTU问题多多,那么尽量避免由MTU引起的网络问题将显得尤其重要。一般而言,咱们可使用如下方法来检测和避免MTU问题:安全
1. 手工测试发现MTU值服务器
2. TCP-MSS网络
3. PathMTU Discoverytcp
今天咱们就来聊聊以上工具的具体细节以及如何经过他们发现链路MTU的最佳值,同时奉上两个典型的由MTU引起的问题案例。ide
什么是最佳MTU值?工具
最佳MTU值,是指保证数据包不被分片的状况下,以字节为基本单位的链路单个数据包尺寸的最大临界值。若是数据包再多一个字节长度,数据包就会被分片。此值称为最佳MTU值。性能
为了演示常见MTU检测方法,特搭建网络测试环境以下:测试
上图网络拓扑中,存在两个网络站点A和B。A站点和B站点能够经过ISP运营商互联互通。
A站点出口路由器为SRX01,出口IP为1.1.1.1
B站点出口路由器为SRX02,出口IP为2.2.2.2
站点A和站点B出口路由器的MTU值以下:
A站点的出口MTU值为巨型MTU值9000字节。
B站点的出口MTU 值为2000字节。
在A,B站点各有公网服务器一台:
A站点Server 7, IP:3.3.3.3
B站点Server 8, IP:4.4.4.4
二者能够经过ISP的BGP路由互联互通。
此场景存在的问题为:运营商内核心路由器之间的MTU值未知。但因为网络通讯是一个端到端的总体行为,若不经过某种方法获悉运营商之间的链路MTU值。那A和B之间的通讯可能存在数据包分片甚至丢包的行为。
在开始研究如何发现端到端的链路最优MTU值以前,让咱们先作一个简单的Ping联通性测试以确保SRX01和SRX02可以正常通讯。
lab@SRX01>ping 2.2.2.2 source 1.1.1.1 rapid count 5 PING2.2.2.2 (2.2.2.2): 56 data bytes !!!!! --- 2.2.2.2ping statistics --- 5 packets transmitted, 5 packets received, 0% packet loss round-tripmin/avg/max/stddev = 21.491/32.152/37.884/6.149 ms lab@SRX01> lab@SRX01>traceroute 2.2.2.2 source 1.1.1.1 tracerouteto 2.2.2.2 (2.2.2.2) from 1.1.1.1, 30 hops max, 40 byte packets 1 1.1.1.100 (1.1.1.100) 12.050ms 8.905 ms 14.453 ms 2 35.0.0.5 (35.0.0.5) 41.774ms 21.306 ms 22.849 ms 3 2.2.2.2 (2.2.2.2) 42.211 ms 29.103 ms 35.168 ms
Ping包成功,两端链路通讯正常。经过Traceroute能够看出穿越的运营商路由器IP地址。
注:上图中,默认状况下,Juniper的Ping包负载尺寸为56字节。
服务器连通性测试:
环境确认测试完成,让咱们来逐一介绍MTU的发现方式:
最简单也是最直观的MTU检测方式天然是手工测试。
手工测试提供了其余工具没法比拟的优点:可视性,直观性。
让咱们来看看如何经过手工测试法发现链路的MTU值。
lab@SRX01> ping 2.2.2.2 source 1.1.1.1 rapid count 1 do-not-fragment size 8000 PING 2.2.2.2 (2.2.2.2): 8000 data bytes 36 bytes from 1.1.1.100: frag needed and DF set (MTU 1500) Vr HL TOS Len ID Flg off TTL Pro cks Src Dst 4 5 001f5c c999 2 0000 40 014c02 1.1.1.1 2.2.2.2 . --- 2.2.2.2 ping statistics --- 1 packets transmitted, 0 packets received, 100% packet loss
针对上面的Ping测试,有几点须要说明:
1. Do-not-fragment关键字很重要,在《MTU共计解析与常见问题汇总-上篇》中曾经提到过,发送方能够自主肯定数据包是否须要被分片。而决定的方法为设置DF=1,告知下游路由器请不要对数据包进行分片。在此测试案例中,我指定了do-not-fragment。顾名思义,SRX01会根据个人要求把ICMP包的DF设置为1。
2. Size 8000是告知SRX01请产生一个8000字节的载荷并封装到ICMP中。为何要用8000,是由于SRX01的本地链路MTU最大值为9000字节(包含IP包头+ICMP包头),因此我须要随机选择一个比9000稍小的数。
在上面测试中,当我用8k字节ping一个数据包的时候,ping测试失败。从而证实中间链路的MTU值是小于8000字节的。
同时,在Ping 2.2.2.2 的过程当中,SRX01收到一个错误信息。
36 bytes from 1.1.1.100: frag neededand DF set (MTU 1500)
有一条从1.1.1.100发过来的消息,内容大体以下:
你的数据包太大。要想往前走,数据包须要分片,但是DF=1,我无法给你分。(顺便悄悄告诉你,前方MTU值为1500。)
难道ISP运营商内部还有咱的人,还给透露小道消息,在这里先无论这高人是谁,后续聊到Path MTU Discovery时,将经过抓包揭晓谜底。
咱们就只须要知道有高人指点,前方ISO网络层数据包MTU大小最大为1500字节。
抱着半信半疑的态度,咱们再Ping测试一次。
可是在开始测试以前,先打一个小算盘:
网络层1500字节=20字节IP头+8字节ICMP头+1472字节的载荷。
按照上面的推理,我猜若是Ping数据包载荷size=1472字节的话,就会成功。而若是稍稍多一点,例如1473字节Ping就会失败。
经过实验证实:
Ping Size=1472字节的包:
lab@SRX01>ping 2.2.2.2 source 1.1.1.1 rapid count 1 do-not-fragment size 1472 PING2.2.2.2 (2.2.2.2): 1472 data bytes ! --- 2.2.2.2ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-tripmin/avg/max/stddev = 28.471/28.471/28.471/0.000 ms
Ping Size=1473字节的包:
lab@SRX01>ping 2.2.2.2 source 1.1.1.1 rapid count 1 do-not-fragment size 1473 PING2.2.2.2 (2.2.2.2): 1473 data bytes 36 bytesfrom 1.1.1.100: frag needed and DF set (MTU 1500) Vr HLTOS Len ID Flg off TTL Pro cks Src Dst 4 5 00 05dd d60b 2 0000 40 01 590f 1.1.1.1 2.2.2.2 . --- 2.2.2.2ping statistics --- 1 packets transmitted, 0 packets received, 100% packet loss
验证成功。咱们如今能够100%肯定,此神秘人物透露的1500字节的MTU值是绝对可信的。同时也获悉端到端链路ISO第三层的最佳MTU值为1500字节。
以上就是手工测试法,简单明了。
可是有一个问题?
做为管理员,咱们经过测试明确了端到端MTU的实际值。但是咱们如何也让网络中的应用程序也知晓这1500字节的MTU值呢?
让咱们继续往下走。
TCP-MSS,全称TCP maximum segment size。翻译过来是TCP最大报文尺寸。它的值表明TCP传输层指望对端发送给本身单个TCP报文的最大尺寸。
通俗来讲,当TCP协议两端在初始协商进行TCP三次握手协议的时候,主机两端会把本身当前所在链路的MSS值告知对方。当一端主机收到另一端的MSS值候,它会评估其MSS值并与本身的MSS值作对比,取最小的值来决定TCP发送的最大报文尺寸。
如何计算本地MSS值?
本地MSS=MTU-20字节的标准IP头-20字节的标准TCP头(换个角度看其实就是TCP负载)
那TCP-MSS如何在避免数据包分片中,发挥它的做用呢?
让咱们来看一个实际示例:
还记得在测试环境中介绍的两台服务器Server 7 和Server 8吗?
如今两台服务器要搞事情了。
如上图,服务器Server 7 要往Server 8 用SCP的方式传送一个40M的文件。Server7 的MTU 为9000字节,Server8 的MTU 2000 字节。
TCP行为理论分析:
在TCP协商期间,Server 7和Server 8之间会把本身当前的MSS值告知对方,经过对比本地MSS与对方的MSS值后,取二者最小值,以下:
Server 7MSS值为9000-40=8960字节。
Server 8 MSS值为2000-40=1960字节。
很明显,Server 7和Server8 会达成协议,二者中取Server 8的MSS值,即1960字节。这个值将是他们的TCP数据传输过程当中单个TCP数据包载荷的最大值。
让咱们经过实验抓包来验证理论分析:
如上图,正如理论分析。在Server 7(3.3.3.3)与Server8(4.4.4.4)TCP三次握手期间,Server7发送了8960字节的MSS,而Server8发送了1960字节的MSS。
为了证实Server7 选择了较低的1960字节的MSS,让咱们看看数据包传输过程当中的TCP数据包大小。
由于TCP存在窗口流控机制,TCP速率不会从一开始就达到最大值,而是一个按部就班增加的过程,固然最后仍是如上图红框所示达到了TCP的单包最大值2014字节,即TCP-MSS值。
这里的2014为整个数据包大小,分拆后以下:
2014字节=14字节二层帧头+20字节IP头+20字节标准TCP头+12字节TCP可选项+1948的负载。
此处的TCP-MSS仍然为1960字节,只是被拆分红为了12字节的TCP可选项+1948的负载。
(记住MSS计算是以标准的20 字节TCP头为参考,在计算时拖挂的TCP选项被放在负载以内考虑)
经过抓包证实,Server7 的确使用了Server 8汇报的1960字节的TCP-MSS为最大报文尺寸,而不是自身的8960字节。从而证实咱们的理论分析彻底正确。
可是,让咱们换个视角看看这些数据包在运营商网络里面是什么样:
上图为运营商路由器SRX03 的ge-0/0/0接口抓包,你们是否已经看见了每一个数据包都带了一个分片包的尾巴,以及大量刺眼的TCP Ack数据包。
一个完整的2014字节数据包就这样被一分为二:1514字节数据包+534字节数据包
那为何数据包在运营商网络会被分片?
你们是否还记得。在第一步中,咱们经过人工方式知晓了运营商网络的ISO第三层网络层MTU值为1500字节。因此很明显,2014字节的数据包超过了1514(1500网络层+14字节链路层二层帧头)的大小,从而数据包被切割分片。
解决方案:拦截TCP-MSS值
因为管理员经过手工测试获悉了整条链路的TCP-MSS值。若是咱们可以配置站点A或B的SRX路由器,拦截并修改Server7 和Server8之间的TCP-MSS协商为手工测试的TCP-MSS值,变相告知两台服务器正确的端到端TCP-MSS。那岂不避免了服务器上的TCP应用不至于触及到运营商网络的MTU天花板,从而避免被分片的命运?
答案是Yes!
在SRX01以及SRX02做以下配置:
set security flow tcp-mss all-tcp mss 1460
完成配置之后,在站点A的SRX01面向运营商接口的ge-0/0/1抓包以下:
如上图,当Server 7和Server8 在协商TCP-MSS时,不管是Server7 或Server8发送的TCP-MSS均为1460字节。
同时,经过查看TCP数据流,咱们能够很明显的看出修改先后的区别,在上图中TCP完成窗口初始化后,直接以1514字节的整包传输,而不是先尝试2014字节。
TCP-MSS实验小结:
· 经过修改TCP-MSS的确可以有效的防止TCP流量触及天花板,进而优化TCP的传输性能。
· 可是TCP-MSS有一个弊端,那就是须要人工先提早检测出整个链路的MTU值,并在出口路由器上全局干预站点的TCP-MSS协商。
你说要是这MTU可以自动发现,彻底不须要人为干预多好啊!
方法是有的,请接着看。
Path MTUDiscovery,MTU自动发现!
这工具一听就以为高大上,可是做为资深网工,咱们不光要知道 Path MTU Discovery(如下简称PMTU)是什么,更须要清楚PMTU是如何高大上,并自动发现MTU最优值的?
先上理论:
顾名思义,PMTU自动发现就是赋予应用程序权力让其自主发现端到端链路的MTU值,并绕开数据包分片这个坑。
目前支持PMTU的协议为TCP以及UDP。
其MTU发现机制很是巧妙和简单,用一句话形容就是:不怕犯错勇于尝试,最后总会得出正确值。
方法以下:
当主机发送TCP/UDP数据包时,主机会在每个发送的数据包上打上DF=1的标志,当这些数据包通过下游链路到达目的地以前,因为DF1的缘由数据包永远不会被中间节点分片。
天然而然,数据包的结局只可能有两种:被中间路由器丢弃或者顺利到达目的地。
同时,主机在发送数据包给下游目的地时,它并不知晓整个网络链路上的最小MTU值。介于此,主机在初期发送数据包时,数据包尺寸为其所在链路的MTU值。
把上面两个条件组合起来后,主机发送的数据包就会出现以下结局:
1. 若是主机本地链路的MTU大于端到端链路中某一点的MTU值,那么这个数据包由于有DF=1的缘由,会被丢弃。
2. 若是路由器本地链路的MTU为整个端到端链路中最小值时,数据包很幸运的被送达目的地。
对于结局2,皆大欢喜。
而咱们须要重点说说结局1。在结局1中,当链路中路由器丢弃此数据包时,此路由器会返回一个ICMP 的Destination Unreachable,Fragment Needed(目标不可达,须要分片)的消息给源主机。
对源主机而言,相比知晓目标不可达而言。它更须要另一个消息:
这个ICMP包中另有玄机,它同时也包含了此链路路由器的下一跳MTU值!
回到本文章第一步手工测试MTU那一段,我曾提到了一个运营商内部人士,他悄悄告知了咱们ISP运营商正确的MTU值是多少。而这个高人正是丢包路由器的ICMP 目标不可达消息。咱们能够把这个ICMP数据包理解为一个信使,经过告知源主机正确MTU值后,源主机能够调节字节的发送数据包尺寸,从而避免数据过大被丢弃。
演示案例:
仍是以TCP-MSS中的Server7和Server8通讯为例,服务器Server 7 要往Server 8 用SCP的方式传送一个40M的文件。Server7 MTU9000字节,Server8 MTU 2000 字节。
不过与TCP-MSS状况不一样,在此案例中咱们在Server7和Server8上开启了PMTU。
同时关闭站点A与B出口路由器SRX01 和SRX02的TCP-MSS拦截:
lab@SRX01#delete security flow tcp-mss [edit] lab@SRX01#show | compare [editsecurity] - flow { - tcp-mss { - all-tcp { - mss 1460; - } - } - }
经过上图,Server7 和Server8恢复到了8960字节和1960字节的TCP-MSS。
重点在下图:
你们能够看见,根据TCP-MSS协商原则,Server7最终选择了较低的2014字节,可是这个值仍是大于了运营商链路中的MTU=1514。
正如以前所说,IP为1.1.1.100(ISP运营商SRX03)的路由器针对每个丢弃的数据包回复了一个ICMP不可达消息。而更有意思的是,在这个ICMP包里面携带了下一跳MTU=1500的惊天大秘密。
而做为源的Server7也不简单,当它收到这个小道消息后,立刻修改了本身的数据包发送尺寸。从2014当即改成1514(底部红色字体数据包串),并使用此尺寸来发送后续的TCP重传数据包。
PMTU小结:
以上就是一个典型的PMTU行为案例。从中能够看出,PMTU不须要人为干预其具体MTU值,PMTU会经过试错的方式来发现最佳MTU值。
固然链路中可能存在各式各样的MTU值,而PMTU则以不变应万变。只要把上述过程在不一样链路中重复一次或屡次就必定会最终发现一个端到端的最优MTU值。
那PMTU有什么缺陷?
你或许已经猜到了,它太依赖ICMP消息。
在Path MTU Discovery中,咱们发现PMTU工做的核心机制在于主机可以收到中间某个节点发过来的ICMP不可达数据包,并包含了下一跳MTU值。
那假设咱们在上述拓扑中,把站点A的SRX01入站口放置ACL阻止ICMP从运营商进入站点A。
虽然理论上给站点A带来了额外的安全保障,可是反作用就是PMTU被干掉了。
由于缺失ICMP消息,PMTU再也不生效。
解决方法:
平常工做中,你们能够考虑在边界防火墙上放行ICMP协议,或者至少放行ICMP unreachable(ICMP Type 3 Code 4)消息。
在通常网络拓扑环境中,每每在三层路由器设备之间会经过二层交换机汇聚互联。
可是在某些状况下,由于设计缘由或者人为失误。致使二层交换机的MTU值小于三层路由器MTU值,从而致使网络故障。
可能有人会问,那MTU小了,分片就能够了,也不至于产生网络故障。
可是仔细考虑发现,数据包分片只存在于ISO第三层的网络设备上,而二层节点只查看二层帧,其并无重写三层IP头的能力,更不知晓三层IP层的具体网络细节。
当数据包过大之后,二层设备是不会也不能去分片数据包,由于在配置的时候,没有赋予它任何此二层VLAN的IP信息,致使其没有写IP数据包的能力去执行数据包分片。
让咱们来看一个诡异的示例:
如上图,SRX3和SRX5之间配置了9000字节的MTU,而SRX03和SRX05则经过SRX04的二层设备互联。
在二层SRX04设备上,全部ISO二层帧MTU值为1514。
因为SRX03和SRX05是ISP运营商路由器,它们之间运行了BGP路由协议。可是令网络工程师不解的是,SRX03和SRX05的BGP邻居虽然处在UP状态。可是SRX05没有收到任何SRX03通告的路由?
SRX03 BGP邻居信息:
root@SRX3> show bgp summary Groups: 2 Peers: 2 Down peers: 0 Table TotPaths Act Paths Suppressed History Damp State Pending inet.0 10 7 0 0 0 0 Peer AS InPkt OutPkt OutQ Flaps Last Up/DwnState|#Active/Received/Accepted/Damped... 1.1.1.1 101 13 19 0 3 3:53 3/4/4/0 0/0/0/0 35.0.0.5 200 5 34 6904 7 11 4/6/6/0 0/0/0/0
#上述输出中,SRX05的35.0.0.5在SRX03的邻居列表中,而且SRX03收到了SRX05宣告的6条路由。以下:
root@SRX3> show route receive-protocol bgp 35.0.0.5 inet.0: 10009 destinations, 10013 routes (10009 active, 0holddown, 0 hidden) Prefix Nexthop MED Lclpref AS path * 2.2.2.0/24 35.0.0.5 200 I * 4.4.4.0/24 35.0.0.5 200 201 I * 10.69.109.0/24 35.0.0.5 200 I 10.221.22.0/24 35.0.0.5 200 201 I 35.0.0.0/24 35.0.0.5 200 I * 172.16.1.0/24 35.0.0.5 200 201 I root@SRX3>
可是在SRX05中,却没有收到任何SRX03的路由条目:
root@SRX5> show bgp summary Groups: 2 Peers: 2 Down peers: 0 Table TotPaths Act Paths Suppressed History Damp State Pending inet.0 4 3 0 0 0 0 Peer AS InPkt OutPkt OutQ Flaps Last Up/DwnState|#Active/Received/Accepted/Damped... 2.2.2.2 201 36075 35936 0 3 1w4d6h 3/4/4/0 0/0/0/0 35.0.0.3 100 2 5 0 8 4 0/0/0/0 0/0/0/0
更诡异的是,二者的BGP邻居老是过一段时间后就闪断一次。
让咱们来经过抓包来揭示谜底:
在二层设备SRX04上抓包以下:
很明显,当SRX03(35.0.0.3)与SRX05(35.0.0.5)经过TCP创建BGP邻居时一切正常。数据包大小均不大于1500字节。
可是当BGP开始经过Update消息传输路由条目(NLRI)的时候,开始以单包8000字节大小以上数据包传输。而中间的SRX04 ISO二层MTU为1514字节(三层MTU为1500字节),这8000字节的数据包将会被无情的丢弃。而由于SRX04是二层设备,其将不会返回任何ICMP信息给SRX03。形成SRX03的TCP数据包不断超时并重传。最终在到达TCP最大重传次数后,BGP邻居被删除。
解决方法:
请务必保证路由器之间的二层设备MTU值与三层路由器设备MTU相同。
在这片文章中,咱们一块儿分析了常见MTU最优值的发现方法,并简单列举了两个常见MTU问题,知识回顾以下:
1. 手工MTU发现:经过手工ICMPPing测试发现最优MTU值。
2. TCP-MSS:TCP协议的最大传输报文尺寸,经过在中间防火墙或者路由器干预端到端的TCP-MSS协商从而达到避免链路数据包分片的目的。
3. Path MTU Discovery:用于TCP/UDP数据包,经过设置初始数据包的DF=1不断试探最佳MTU值,最终发现端到端的最佳MTU值。
固然,在平常工做中,MTU的问题会比咱们想象的更为复杂。
例如嵌套了IPsec ***和GRE隧道的端到端流量等。可是万变不离其中,只要你们掌握了MTU和相关工具的工做原理和细节。咱们就能够从容分析各类场景并使用对应的工具来解决实际问题。
谢谢!