pmtu.docx
《pmtu.docx》由会员分享,可在线阅读,更多相关《pmtu.docx(20页珍藏版)》请在冰豆网上搜索。
pmtu
PMTU
MTU问题多多,那么尽可能避免由MTU引发的网络问题将显得尤为重要。
通常而言,我们可以使用以下方法来检测和避免MTU问题:
1. 手工测试发现MTU值
2. TCP-MSS
3. PathMTUDiscovery
今天我们就来聊聊以上工具的具体细节以及如何通过他们发现链路MTU的最佳值,同时奉上两个典型的由MTU引发的问题案例。
什么是最佳MTU值?
最佳MTU值,是指保证数据包不被分片的情况下,以字节为基本单位的链路单个数据包尺寸的最大临界值。
如果数据包再多一个字节长度,数据包就会被分片。
此值称为最佳MTU值。
MTU下篇:
TCP-MSS,PathMTUDiscovery详解以及常见问题分析
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站点Server7, IP:
3.3.3.3
B站点Server8, IP:
4.4.4.4
两者可以通过ISP的BGP路由互联互通。
场景介绍:
此场景存在的问题为:
运营商内核心路由器之间的MTU值未知。
但由于网络通信是一个端到端的整体行为,若不通过某种方法获悉运营商之间的链路MTU值。
那A和B之间的通信可能存在数据包分片甚至丢包的行为。
在开始研究如何发现端到端的链路最优MTU值之前,让我们先做一个简单的Ping联通性测试以确保SRX01和SRX02能够正常通信。
Ping包成功,两端链路通信正常。
通过Traceroute可以看出穿越的运营商路由器IP地址。
注:
上图中,默认情况下,Juniper的Ping包负载尺寸为56字节。
服务器连通性测试:
环境确认测试完成,让我们来逐一介绍MTU的发现方式:
1.冷兵器时代:
手工测试
最简单也是最直观的MTU检测方式自然是手工测试。
手工测试提供了其他工具无法比拟的优势:
可视性,直观性。
手工测试示例
让我们来看看如何通过手工测试法发现链路的MTU值。
1
2
3
4
5
6
7
8
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. Size8000是告知SRX01请产生一个8000字节的载荷并封装到ICMP中。
为什么要用8000,是因为SRX01的本地链路MTU最大值为9000字节(包含IP包头+ICMP包头),所以我需要随机选择一个比9000稍小的数。
在上面测试中,当我用8k字节ping一个数据包的时候,ping测试失败。
从而证明中间链路的MTU值是小于8000字节的。
同时,在Ping2.2.2.2 的过程中,SRX01收到一个错误信息。
36bytesfrom1.1.1.100:
fragneededandDFset(MTU1500)
有一条从1.1.1.100发过来的消息,内容大致如下:
你的数据包太大。
要想往前走,数据包需要分片,可是DF=1,我没法给你分。
(顺便悄悄告诉你,前方MTU值为1500。
)
难道ISP运营商内部还有咱的人,还给透露小道消息,在这里先不管这高人是谁,后续聊到PathMTUDiscovery时,将通过抓包揭晓谜底。
我们就只需要知道有高人指点,前方ISO网络层数据包MTU大小最大为1500字节。
抱着半信半疑的态度,我们再Ping测试一次。
但是在开始测试之前,先打一个小算盘:
网络层1500字节=20字节IP头+8字节ICMP头+1472字节的载荷。
按照上面的推理,我猜如果Ping数据包载荷size=1472字节的话,就会成功。
而如果稍稍多一点,例如1473字节Ping就会失败。
通过实验证明:
PingSize=1472字节的包:
1
2
3
4
5
6
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
PingSize=1473字节的包:
1
2
3
4
5
6
7
8
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值呢?
2.小米加步枪:
TCP-MSS协商
TCP-MSS,全称TCPmaximumsegmentsize。
翻译过来是TCP最大报文尺寸。
它的值代表TCP传输层期望对端发送给自己单个TCP报文的最大尺寸。
通俗来说,当TCP协议两端在初始协商进行TCP三次握手协议的时候,主机两端会把自己当前所在链路的MSS值告知对方。
当一端主机收到另外一端的MSS值候,它会评估其MSS值并与自己的MSS值做对比,取最小的值来决定TCP发送的最大报文尺寸。
如何计算本地MSS值?
本地MSS=MTU-20字节的标准IP头-20字节的标准TCP头(换个角度看其实就是TCP负载)
那TCP-MSS如何在避免数据包分片中,发挥它的作用呢?
让我们来看一个实际示例:
还记得在测试环境中介绍的两台服务器Server7 和Server8吗?
现在两台服务器要搞事情了。
如上图,服务器Server7 要往Server8 用SCP的方式传送一个40M的文件。
Server7 的MTU 为9000字节,Server8 的MTU2000 字节。
TCP行为理论分析:
在TCP协商期间,Server7和Server8之间会把自己当前的MSS值告知对方,通过对比本地MSS与对方的MSS值后,取两者最小值,如下:
Server7MSS值为9000-40=8960字节。
Server8MSS值为2000-40=1960字节。
很明显,Server7和Server8 会达成协议,两者中取Server8的MSS值,即1960字节。
这个值将是他们的TCP数据传输过程中单个TCP数据包载荷的最大值。
让我们通过实验抓包来验证理论分析:
如上图,正如理论分析。
在Server7(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 的确使用了Server8汇报的1960字节的TCP-MSS为最大报文尺寸,而不是自身的8960字节。
从而证明我们的理论分析完全正确。
但是,让我们换个视角看看这些数据包在运营商网络里面是什么样:
上图为运营商路由器SRX03 的ge-0/0/0接口抓包,大家是否已经看见了每个数据包都带了一个分片包的尾巴,以及大量刺眼的TCPAck数据包。
一个完整的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作如下配置:
setsecurityflowtcp-mssall-tcpmss1460
完成配置以后,在站点A的SRX01面向运营商接口的ge-0/0/1抓包如下:
如上图,当Server7和Server8 在协商TCP-MSS时,无论是Server7 或Server8发送的TCP-MSS均为1460字节。
同时,通过查看TCP数据流,我们可以很明显的看出修改前后的区别,在上图中TCP完成窗口初始化后,直接以1514字节的整包传输,而不是先尝试2014字节。
TCP-MSS实验小结:
· 通过修改TCP-MSS的确能够有效的防止TCP流量触及天花板,进而优化TCP的传输性能。
· 但是TCP-MSS有一个弊端,那就是需要人工先提前检测出整个链路的MTU值,并在出口路由器上全局干预站点的TCP-MSS协商。
你说要是这MTU能够自动发现,完全不需要人为干预多好啊!
方法是有的,请接着看。
3.鸟枪换炮:
PathMTUDiscovery(链路MTU自动发现协议)
PathMTUDiscovery,MTU自动发现!
这工具一听就觉得高大上,但是作为资深网工,我们不光要知道 PathMTUDiscovery(以下简称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 的DestinationUnreachable,FragmentNeeded(目标不可达,需要分片)的消息给源主机。
对源主机而言,相比知晓目标不可达而言。
它更需要另外一个消息:
这个ICMP包中另有玄机,它同时也包含了此链路路由器的下一跳MTU值!
回到本文章第一步手工测试MTU那一段,我曾提到了一个运营商内部人士,他悄悄告知了我们ISP运营商正确的MTU值是多少。
而这个高人正是丢包路由器的ICMP 目标不可达消息。
我们可以把这个ICMP数据包理解为一个信使,通过告知源主机正确MTU值后,源主机可以调节字节的发送数据包尺寸,从而避免数据过大被丢弃。
演示案例:
还是以TCP-MSS中的Server7和Server8通信为例,服务器Server7 要往Server8 用SCP的方式传送一个40M的文件。
Server7MTU9000字节,Server8MTU2000 字节。
不过与TCP-MSS情况不同,在此案例中我们在Server7和Server8上开启了PMTU。
同时关闭站点A与B出口路由器SRX01 和SRX02的TCP-MSS拦截:
1
2
3
4
5
6
7
8
9
10
11
12
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消息。
常见MTU典型故障总结以及案例分析
1.防火墙做的好事儿,ICMP被干掉了。
在PathMTUDiscovery中,我们发现PMTU工作的核心机制在于主机能够收到中间某个节点发过来的ICMP不可达数据包,并包含了下一跳MTU值。
那假设我们在上述拓扑中,把站点A的SRX01入站口放置ACL阻止ICMP从运营商进入站点A。
虽然理论上给站点A带来了额外的安全保障,但是副作用就是PMTU被干掉了。
因为缺失ICMP消息,PMTU不再生效。
解决方法:
日常工作中,大家可以考虑在边界防火墙上放行ICMP协议,或者至少放行ICMPunreachable(ICMPType3Code4)消息。
2.夹心饼干,三层设备之间夹杂二层设备。
在一般网络拓扑环境中,往往在三层路由器设备之间会通过二层交换机汇聚互联。
但是在某些情况下,因为设计原因或者人为失误。
导致二层交换机的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通告的路由?
SRX03BGP邻居信息:
1
2
3
4
5
6
7
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/
#上输出中,SRX05的35.0.0.5在SRX03的邻居列表中,并且SRX03收到了SRX05宣告的6条路由。
如下:
1
2
3
4
5
6
7
8
9
10
11
12
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的路由条目:
1
2
3
4
5
6
7
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