PMTUD 究竟何时执行?(路径 MTU 发现)

网络工程 ipv4
2021-07-02 14:03:54

本网站其他问题引发的讨论中,我意识到我对何时执行路径 MTU 发现 (PMTUD)没有深入的了解

我知道它是做什么的——发现从客户端到服务器的路径上最低的 MTU)。
我知道它是如何做到的——发送越来越大的数据包,并设置“不分段”位,看看你可以通过多大的数据包而不会出现“ICMP 需要分段”错误。

那么我的问题是,主机何时执行 PMTUD?

我正在寻找特定的案例。不仅仅是“当主机想要发现路径 MTU 时”这样的通用内容。如果您可以提供执行此操作的主机的数据包捕获,或提供生成此类数据包捕获的说明,则奖励积分。

另外,我特别指的是 IPv4。我知道在 IPv6 临时路由器不负责分段,并且可以想象 PMTUD 发生得更常见。但是现在,我正在寻找 IPv4 中 PMTUD 的具体示例。 (虽然如果您可以将 PMTUD 放在一起的唯一数据包捕获是在 IPv6 中,我仍然很乐意看到它)

3个回答

答案很简单:只要主人喜欢。真的。就这么简单。

下面的解释假设只使用 IPv4 环境,因为 IPv6 消除了路由器中的分段(强制主机始终处理分段和 MTU 发现)。

没有严格的规则来管理主机何时(或什至是否)执行路径 MTU 发现。PMTUD 浮出水面的原因是,由于各种原因碎片化被认为是有害的。为避免数据包碎片,PMTUD 的概念作为一种变通方法被赋予了生命。当然,一个好的操作系统应该使用 PMTUD 来最小化碎片。

因此,自然而然地,何时使用 PMTUD 的确切语义取决于发送方的操作系统 - 特别是套接字实现。我只能说 Linux 的具体情况,但其他 UNIX 变体可能差别不大。

在 Linux 中,PMTUD 由IP_MTU_DISCOVER套接字选项控制您可以getsockopt(2)通过指定级别IPPROTO_IPIP_MTU_DISCOVER选项来检索其当前状态此选项仅对SOCK_STREAM套接字有效SOCK_STREAM套接字是双向的、面向连接的、可靠的套接字;实际上它是 TCP 套接字,尽管其他协议也是可能的),并且设置后,Linux 将完全按照 RFC 中的定义执行 PMTUD 1191.

注意,在实践中,PMTUD 是一个连续的过程;发送数据包时设置了 DF 位 - 包括 3 次握手数据包 - 您可以将其视为连接属性(尽管实现可能愿意在某些时候接受一定程度的碎片并停止使用 DF 发送数据包位设置)。因此,PMTUD 只是该连接上的所有内容都使用 DF 发送这一事实的结果。

不设置IP_MTU_DISCOVER怎么

有一个默认值。默认情况下,IP_MTU_DISCOVERSOCK_STREAM套接字上启用这可以通过阅读来阅读或改变/proc/sys/net/ipv4/ip_no_pmtu_disc零值表示IP_MTU_DISCOVER在新套接字中默认启用;非零意味着相反。

无连接套接字呢?

这很棘手,因为无连接、不可靠的套接字不会重新传输丢失的段。将数据打包成 MTU 大小的块成为用户的责任。此外,如果消息太大错误,用户需要进行必要的重传因此,本质上用户代码必须重新实现 PMTUD。不过,如果您准备好迎接挑战,您可以通过将IP_PMTUDISC_DO标志传递setsockopt(2).

底线

  • 主机决定何时(以及是否)使用 PMTUD
  • 当它使用 PMTUD 时,它就像一个连接属性,它不断发生(但在任何时候实现都可以自由地停止这样做)
  • 不同的操作系统使用不同的方法,但通常,可靠的、面向连接的套接字默认执行 PMTUD,而不可靠的、无连接的套接字则不执行

通常,只要主机认为数据包由于太大而被丢弃,就会发生路径最大传输单元发现 (PMTUD)。

这可能是为了响应需要 ICMP 分段(类型 3,代码 4)的响应,明确指示数据包已被丢弃。在典型的实践中,所有 IPv4 数据包都设置了“不分段”(DF) 标志,因此任何超过 MTU 的数据包都将引发此类响应。IPv6 根本不支持分片。

一些路由器或主机防火墙经常丢弃所有 ICMP,因为幼稚的管理员认为 ICMP 是一种安全风险或者,某些链路聚合方案可能会中断 ICMP 交付RFC4821 中提出了一种不依赖于 ICMP 的发现 MTU 的替代机制

tracepath是我最喜欢的用于探测 MTU 的 Linux 工具。以下是 LAN 上具有 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个字节不包括选项)和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,但该值已被路由器修改,因为它知道该路径包括互联网路径( MTU 1500) 来自 IPsec 隧道的开销。该路由器还将我们发送的 8961 的 MSS 修改为在另一台主机上显示为 1419。这称为MSS 钳位

所以从某种意义上说,PMTUD 一直在发生。在实践中,如果 MSS 限制到位并且所有流量都通过 TCP 发生,或者如果没有任何路由器的 MTU 小于端点上配置的 MTU,它实际上可能永远不会发生。即使没有 MSS 限制,它也可能很少发生,当缓存过期时。

PMTUD 用于计算 TCP 会话的最佳 MSS。一个例子是 Cisco 或 Juniper 路由器上的 BGP 实施。

http://www.juniper.net/techpubs/en_US/junos12.1/topics/usage-guidelines/routing-configuring-mtu-discovery-for-bgp-sessions.html

谢谢。