通常,每当主机认为数据包由于太大而被丢弃时,就会发生路径最大传输单位发现(PMTUD)。
这可能是对所需的ICMP分段响应(类型3,代码4)的响应,该响应明确指示数据包已丢弃。在典型的实践中,所有IPv4数据包都设置有“不要分段”(DF)标志,因此,任何超出MTU的数据包都会引发这种响应。IPv6根本不支持分段。
某些路由器或主机防火墙经常丢弃所有ICMP,因为天真的管理员认为ICMP存在安全风险。或者,某些链路聚合方案可能会中断ICMP传递。在RFC4821中,提出了一种不依赖于ICMP的发现MTU的替代机制。
tracepath
是我最喜欢的用于探测MTU的Linux工具。这是来自局域网上具有9001 MTU的主机的示例,但该主机必须遍历IPsec VPN才能达到10.33.32.157:
$ tracepath -n 10.33.32.157
1?: [LOCALHOST] pmtu 9001
1: 10.1.22.1 0.122ms pmtu 1500
1: 169.254.3.1 1.343ms pmtu 1422
1: 10.255.254.61 23.790ms
2: no reply
^C [this host won't return an ICMP port unreachable, so tracepath won't terminate]
使用以下命令可以观察到ICMP错误tcpdump
:
$ sudo tcpdump -p -ni eth0 'icmp and icmp[0] == 3 and icmp[1] == 4'
14:46:57.313690 IP 10.1.22.1 > 10.1.22.194: ICMP 10.33.32.157 unreachable - need to frag (mtu 1500), length 36
14:46:57.315080 IP 169.254.3.1 > 10.1.22.194: ICMP 10.33.32.157 unreachable - need to frag (mtu 1422), length 556
MTU发现被缓存。在Linux中,可以观察到并清除它ip
(注意Linux 3.6之后的更改):
$ ip route get 10.33.32.157
10.33.32.157 via 10.1.22.1 dev eth0 src 10.1.22.194
cache expires 591sec mtu 1422
$ sudo ip route flush cache
$ ip route get 10.33.32.157
10.33.32.157 via 10.1.22.1 dev eth0 src 10.1.22.194
cache
对于TCP,作为连接设置的一部分,可以避免超过MTU。每一端发送的SYN中都包含最大段大小(MSS)。TCP报头(20字节,不包括options)和IP报头(20字节)意味着MSS和MTU之间相差40字节。
这是使用以下文件传输大文件时这两个主机之间建立连接的示例scp
:
$ sudo tcpdump -p -ni eth0 'host 10.33.32.157 and tcp[13]&2 == 2'
IP 10.1.22.194.45853 > 10.33.32.157.22: Flags [S], seq 634040018, win 26883, options [mss 8961,sackOK,TS val 10952240 ecr 0,nop,wscale 7], length 0
IP 10.33.32.157.22 > 10.1.22.194.45853: Flags [S.], seq 1371736848, ack 634040019, win 26847, options [mss 1379,sackOK,TS val 10824267 ecr 10952240,nop,wscale 7], length 0
在第一个数据包中,本地主机提议的MSS为8961。这是配置的9001 MTU,少40个字节。返回的SYN / ACK的MSS为1379,表示MTU为1419。我碰巧知道在该网络中,远程主机也发送了8961,但是该值已由路由器修改,因为它知道该路径包括Internet路径( MTU 1500)来自IPsec隧道的开销。该路由器还修改了我们发送的8961的MSS,使其在另一台主机上显示为1419。这称为MSS夹紧。
因此从某种意义上讲,PMTUD一直在发生。实际上,如果MSS钳制到位并且所有流量都通过TCP进行,或者没有一个路由器的MTU小于端点上配置的MTU,则它可能永远不会发生。即使没有MSS限制,当缓存过期时,它也很少发生。