[2023][Protocol]TCP Path MTU Discovery for IPv4

本节的内容主要基于RFC 1191: Path MTU Discovery RFC 9293 只是进行了简短的描述,并没有展开细节讨论。另外RFC 8201: Path MTU Discovery for IPV6 是PMTUD的ipv6版本,基本思想和实现过程与ipv4版本类似。

Note: 本节中,主机(Host)可以指代网络中的任何三层节点,比如PC,路由器,三层交换机等。

Note: 本节中,如果没有显式标出数值单位,则默认所有对报文大小的数值单位均为 Byte

TCP Segment 也即TCP报文
Segmentation 指的是活动的TCP发送或接收的 被打包的Bytes。

每个Segmentation不总是一 一对应某个socket 的write或者send操作。

应用程序可能以message粒度(Granularity)在上层协议中进行写操作,TCP保证 TCP Segmentation 边界用户数据的read或write buffer边界的无关性。

在某些协议中,比如 Remote Direct Memory Access (RDMA) 使用的 Direct Data Placement (DDP) 和 Marker PDU Aligned Framing (MPA) ; 如果TCP 报文与用户报文的关系可以控制,那么是存在优化空间的。MPA包括一些机制可以检测和验证TCP报文应用信息数据结构(Application message data structures) 的关系,但是这仅仅是针对RDMA而言的。

总体而言,存在很多目的都可能会影响 TCP报文的大小(size)。

发送较大数据帧的目的包括:

  • 为了减少网络中数据包的数量;所以每个TCP报文可能相对大一点
  • 减少层与层之间的交互次数 和 中断(interrupts)次数,以提高处理效率潜在性能
  • 限制 TCP 报头的开销

发送尽可能大的报文有益于性能表现,但是随着报文大小的增加,性能会逐渐降低。

有时也会恰恰相反,发送小报文可能对性能有益。在一些架构上,一些实现发送1025B

的报文可能会导致系统性能 远远差于 发送1024B时的系统性能,这纯粹是由于复制操作时的数据对齐问题

发送较小的数据帧的目的包括:

  • 避免发送的TCP报文大于IP路径上,最小MTU (路径MTU),这直接会导致丢包或者报文被分片;如果对于单个数据,发送该数据的报文越多,则数据损坏的可能性越大,因为一些防火墙或者中间的路由设备肯能丢弃碎片数据包 或者与碎片相关的ICMP信息。

    如果报文太小则可能被认为是碎片数据包

  • 减少应用数据流的延迟,特别是当TCP等待应用生成更多数据时,或者是应用等待其对端的某些事件或者输入 以产生更多数据 时。

  • 在 TCP报文 与 更底层的数据单元 之间实现 Fate Sharing (同生共死),比如在IP层以下,帧大小小于IP MTU。

    TCP 某个数据对应的 Segment MTU 小于 IP MTU,则就是 TCP Segment : IP Message 一对一,即:Fate Sharing

为了实现这些竞态目标(Competing Goals) ,TCP 包含多种机制,包括 最大分段选项(Maximum Segment Size Option),路径MTU发现(Path MTU Discovery),Nagle 算法以及对IPV6 Jumbograms的支持。

1. Maximum Segment Size Option

TCP 必须实现发送和接收时的MSS Option 选项。

如果接收的MSS 与默认的 MSS 不同时,TCP应该在每个SYN中发送MSS选项。

IPV4 默认为 536,IPV6默认为 1220。这个信息放在TCP报文的Options中

如果在连接建立时收到的报文没有包含MSS的Option,则TCP实现必须默认设置为536(for ipv4)

/ 1220(for ipv6)。

TCP真正发送报文的最大size,称为 有效发送MSS(Effective send MSS),必须小于MSS,而且必须小于IP层的最大传输大小。

​ $ EMSS_S < min(IP_MTU,MSS)$

有效发送MSS (Effective MSS_send) 小于 MSS

有效接收MSS 即对端真实发送的MSS大小(EMSS_R),反应了对端主机的重组缓冲区大小

E M S S _ S = m i n ( M S S R e c e i v e + 20 , M a x M S S _ S ) − S i z e T C P h e a d e r − I P O p t i o n S i z e EMSS\_S = min(MSS_{Receive}+20,MaxMSS\_S) - Size_{TCPheader} - IP_{OptionSize} EMSS_S=min(MSSReceive+20MaxMSS_S)SizeTCPheaderIPOptionSize

M S S R e c e i v e MSS_{Receive} MSSReceive 指从对端主机收到的MSS 值,该信息在Option字段中,如果没有,则为默认536或1220,它指的是TCP报文中除报头外的大小。这里要计算TCP报文的真实大小,所以需要加上报头的20B。

M a x M S S _ S MaxMSS\_S MaxMSS_S 指的是TCP可以发送的传输层报文的最大大小,也即IP层的最大载荷量。这个值受到 I P o p t i o n IP_{option} IPoption 的影响。如果IP协议使用了其他扩展Options,则IP层载荷量会被压缩

RFC 6691: TCP Options and Maximum Segment Size (MSS)
当计算 TCP MSS 选项(Option)的值时,就是 IP MTU 减去IP和TCP报头的固定大小,TCP 或者 IP Options 不计算在内,即Options会和用户数据放在一起。发送端必须减少TCP数据载荷量用来发送额外的Options。

MSS 不是一种协商, 而是一种自己的上限, 自己不会再接受比MSS更大的报文。

V a l u e M S S = M T U − 2 0 t c p H e a d e r − 2 0 i p v 4 H e a d e r Value_{MSS}=MTU-20_{tcpHeader}-20_{ipv4Header} ValueMSS=MTU20tcpHeader20ipv4Header

S i z e T C P h e a d e r Size_{TCPheader} SizeTCPheader 指的是TCP报头的真实大小,在没有Option字段情况下为20B,如果存在Option字段则可能更大。注意一些Option可能并不总是需要在每个报文中发送的,发送端可能需要根据收到的报文实时调整数据长度大小(EMSS_S)。

I P o p t i o n S i z e IP_{optionSize} IPoptionSize 是与 TCP 连接相关的 IPv4 选项或 IPv6 扩展报头的大小,请注意,某些选项或扩展标头可能不会包含在所有数据包中。

从上述公式可以看出, E M S S _ S EMSS\_S EMSS_S ( TCP 不包括Option的真实用户数据的大小)需要考虑对端的MSS,以及下层协议的载荷。 E M S S _ S EMSS\_S EMSS_S 可以直接暴露给用户接口。

The size of the fixed TCP header is 20 bytes [RFC793], the size of
the fixed IPv4 header is 20 bytes [RFC791], and the size of the fixed
IPv6 header is 40 bytes [RFC2460].

The MSS value to be sent in an MSS Option should be equal to the effective MTU minus the fixed IP and TCP headers. By ignoring both IP and TCP Options when calculating the value for the MSS Option, if there are any IP or TCP Options to be sent in a packet, then the sender must decrease the size of the TCP data accordingly.

(Same as RFC6691 had descripted)

MSS Option 中的具体值必须小于等于 M a x M S S R e c e i v e − 2 0 t c p H e a d e r MaxMSS_{Receive}-20_{tcpHeader} MaxMSSReceive20tcpHeader M a x M S S R e c e i v e MaxMSS_{Receive} MaxMSSReceive这个值可以从IP层中获取

其实MTU是两端共同协商的,无论是哪一端,MSS Option 的值总是符合

V a l u e M S S = M T U − 2 0 t c p H e a d e r − 2 0 i p v 4 H e a d e r Value_{MSS}=MTU-20_{tcpHeader}-20_{ipv4Header} ValueMSS=MTU20tcpHeader20ipv4Header

RFC1122 3.3.2 节定义了 M a x M S S R e c e i v e = E M T U r e c e i v e − 2 0 i p H e a d e r MaxMSS_{Receive}=EMTU_{receive}-20_{ipHeader} MaxMSSReceive=EMTUreceive20ipHeader

2. Path MTU Discovery

TCP实现可以直接知道直连链路上的MTU,但是却没有办法知道整条报文路径上的MTU信息。对于IPv4,RFC 1122 建议对于非直连目的地,IP层的默认有效MTU(Effective MTU,EMTU)应该小于等于 576B,对于IPv6,MTU应该为1280B。这些固定的值往往会限制TCP连接的性能和效率。因此强烈建议TCP实现时使用路径发现(Path MTU Discovery PMTUD)和 打包层路径MTU发现(Packetization Layer Path MTU Discovery, PLPMTUD) 以优化TCP分片决策。从而避免TCP报文在中途(on-path)被分片 (即再传输途中被分片,Ipv4),以及在发送时被分片(Source Fragmentation,Ipv6)

有效MTU可以直接理解为MTU的等效量,只不过说法不一样。

2.1基本介绍

当一个IP主机要和另一个IP主机发送大量数据时,这个数据会被切成若干个IP报文发送。

但是在报文传输途径中有些路由设备可能会限制MTU大小,从而使得某些较大报文不能被成功转发到目标主机上。

因为报文太大而被丢弃

所以尽量不对数据进行分片,如 C.A.Kent Jeffrey C.Mogul : Fragmentation Considered Harmful 中所述,分片会导致传输性能下降或者直接导致通讯失败。

而为了避免不必要的分片,则必须保证报文大小 小于等于路径上最小的MTU

在这里插入图片描述

M T U < = M T U P a t h = m i n ( M T U 1 , M T U 2 , M T U 3 , M T U 4 , M T U 5 ) MTU<=MTU_{Path}=min(MTU_1,MTU_2,MTU_3,MTU_4,MTU_5) MTU<=MTUPath=min(MTU1,MTU2,MTU3,MTU4,MTU5)
现网(1990年及之前)协议的一个缺陷就是没有一个标准化的机制在任意网络路径上寻找Path MTU。

Path MTU 在 RFC 1122 中的说法是 “Effective MTU for sending (EMTU_S)”

2.2 协议总览

如何使用 IP 标头中的Don't Fragment(DF)字段来动态发现路径中的 Path MTU

基本的思想是 源主机开始时假设PMTU就是它已知首次下一跳的MTU。并在这条已知路径上发送报文的DF字段均置位,并以已知路径的PMTU发送报文。如果在发现新路径时,有
M T U n e w < P M T U k n o w n MTU_{new} < PMTU_{known} MTUnew<PMTUknown
则最新路径上的路由器会丢失该报文,并向源回复ICMP报文,描述为Fragmentation needed and DF Set,或者简称为"Datagram Too Big "信息。因为报文太大,新发现的路由器需要将其分片,但是报文的DF置位。此时源主机将 P M T U PMTU PMTU 更新为 M T U n e w MTU_{new} MTUnew 。这个过程一直持续到源主机认为PMTU已经足够小使得报文不会再途中被分片 或者 源主机选择不再将DF置位。

当然,如果源主机乐于在其环境中将报文分片,则可以不再将DF置位。但是通常情况下,源主机会将所有其发送的报文的DF置位,用于发现并更新PMTU。

PMTU会随时间进行更新,因为路由拓扑在不断变化。

但是PMTU也一定会有增长的时候,那么如何检测PMTU的增长呢?

为了检测PMTU的增长,主机会周期性地增加 PMTU 。这将会经常性地导致报文被丢弃并产生 "Datagram Too Big "信息,因为在大多数情况下,一条路径的PMTU往往是固定的,所以这个行为频率不应该太高。

2.3 主机行为规范

当主机收到"Datagram Too Big"信息时,它必须基于Next-Hop 的MTU字段的值来缩小它估计的 相关路径的 PMTU(具体见3.0章节)。这里并不指定主机的具体行为,因为不同的应用可能有不同的需求,从而有不同的策略。

这里只要求,如果主机收到"Datagram Too Big" 信息,它必须可以采取一定行为避免未来再次产生类似的信息。

比如:调整MTU为当前最新的PMTU,或者不再将之后报文的DF置位。但是具体行为则取决于有什么需求。实际上是二选一。

但是很显然,之前的发送报文的策略使得在一段时间内产生了"Datagram Too Big"信息,以及一些用于回复但被丢弃的报文 消耗了网络资源,所以主机默认必须强制 使用 Path MTU Discovery,不然就不要将DF置位。

主机追踪PMTU时应该越快越好,主机可能检测到PMTU的增加,但是因为这样做需要发送比当前估计的PMTU更大的数据包,考虑到PMTU增加与类似的情况可能并不经常发生,所以对PMTU增加的检测也不应频繁。

检测PMTU增加的具体方式就是发一个比当前已知PMTU大一点的报文。

在收到"Datagram Too Big"后的5分钟内或者在成功检测到PMTU增加的1分钟内,不能(MUST NOT)再试图去检测PMTU增加。

这里(RFC 1191)建议将这些设置为相对上次最小时间的两倍。

比如,收到"Datagram Too Big"后,规定5分钟内不能再检测,那么建议设置为 5 × 2 = 10 m i n 5\times2=10min 5×2=10min

在成功检测到PMTU increase后,规定1分钟内不能再检测,那么建议设置为

1 × 2 = 2 m i n 1\times2=2min 1×2=2min

主机必须可以处理不带Next-hop MTU 字段的 "Datagram Too Big"信息,因为将现网(1990年及之前)内所有的路由器在短时间内全部升级支持PMTU并不可行。如果"Datagram Too Big"报文中不存在Next-hop MTU字段,则使用默认值0代替。

RFC 792: Internet Control Message Protocol 中描述为:“unused” 字段必须设置为0

之后讨论主机回复 Old-Style "Datagram Too Big"信息的可能策略

Note:主机估计的PMTU最小不能低于68 Bytes,即68B是发送报文大小的下边界

主机不能(MUST NOT)因"Datagram Too Big"信息的内容而增加其估计的PMTU;因为意图增加PMTU的"Datagram Too Big"报文可能是陈旧的数据报,亦或是攻击者伪造的。或者是因为到达目的地有多条路径。

2.3.1 TCP MSS Option

如果主机正在使用PMTU Discovery,则它必须遵循规则:若没有得到接受者允许,它不能发送超过 576Byte 的数据包。对于TCP连接来说,这意味着:
M S S < = 576 − 2 0 i p v 4 H e a d e r − 2 0 t c p H e a d e r MSS <= 576-20_{ipv4 Header}-20_{tcpHeader} MSS<=57620ipv4Header20tcpHeader
实际上,很多TCP实现总会发送MSS Option,并将值设置在 536(对于非直连节点)。如果网络中大多数主机都遵循这个约定,那这个行为总是正确的

在没有得到对端允许的条件下,不能向非直连端点直接发送大于576B的IP数据包。上边说过了。

在现有规格下,IP 报文最大为 65535B,所以TCP的MSS最大值可能为 65595B,但是通常来说,不建议使用这个最大值,因为一些IP实现有一些 符号位错误问题(Sign-bit Bugs) that would be tickled by unnecessary use of such a large MSS。主机也需要尽可能不使用过于庞大的数据报。

To my best knowledge : 为什么是 576 的原因是:Section 3.1.2 C.A.Kent Jeffrey C.Mogul : Fragmentation Considered Harmful

省流:Author’s favorite arbitrary value

2.4 路由规范

当路由器因为DF数据包超过了下一跳的MTU从而不能转发该数据包是,路由器需要向源发送ICMP Destination Unreachable message报文。

即:DF置位的数据包

在这里插入图片描述

为了支持Path MTU Discovery技术,路由器必须在ICMP报文头部的 low-order 16bits 中指定下一跳的MTU。因此ICMP信息应为以下格式:

在这里插入图片描述

其中 Next-Hop MTU 字段为路由器建议的通过下一跳的最大DF报文。

包括IP报文头部和载荷。这个字段的值永远不小于 68, 规定每个路由器"必须有能力转发68B的报文,且不分片"

2.5. 主机如何处理 Old-Style 报文

这里主要介绍几种主要的 处理主机处理Next-Hop MTU 字段为0 "Datagram Too Big"报文的策略。

最简单的处理策略是:假设PMTU是 当前的 PMTU 与 576 之间的最小值
a s s u m e    P M T U = m i n ( P M T U c u r r e n t , 576 ) assume\space\space PMTU = min(PMTU_{current},576) assume  PMTU=min(PMTUcurrent,576)
之后,在该路径上发送的所有报文DF都不置位。

RFC 1122: Requirements for Internet Protocols 中描述到:假如说路径中的Minimum MTU 信息无法获得,则IP层应该使用的EMTU_S 应保证 E M T U _ S < = 576 EMTU\_S<=576 EMTU_S<=576

这个策略优势在于 可以快速终止(关闭连接)。

This strategy has the advantage that it terminates quickly, and does no worse than existing practice.

为了避免在某些情况下的报文分片,并充分利用 inter-network 的资源

inter-network,指的是 src-host 到 dst-host 之间的整个网络。相对的,intra-network 就是这整个网络中局部的网络。

更加精巧,复杂(Sophisticated)的策略出现了,它设计到搜索 Path MTU 的估计值,通过不断发送DF置位的报文 同时不断调整这些报文的大小。优秀的搜索策略应该在使用有限的报文同时,对PMTU的搜索值应该尽量精准。

更少的代价,更精准的数值

比如:使用之前估计值的 0.75 0.75 0.75 倍,即:
P M T U n e w = 0.75 × P M T U o l d PMTU_{new} = 0.75\times PMTU_{old} PMTUnew=0.75×PMTUold
这不失为一种方法,但是不推荐,因为这个算法往往收敛非常慢,并且计算的值往往小于真实的PMTU。

此时,一个更加(More Sophisticated)精密,复杂的方法出现了!可以在数据包大小之间进行二分搜索。这种方法往往收敛迅速。但是存在比较严重的缺陷:实现复杂,这个方法需要识别 完整的数据将在何时到达另一端

Note: 到这里为止,文档没有做任何算法上的描述。这里认为:这个算法可以用于估计你的报文是慢了还是快了,以此来评估估计的PMTU是大了还是小了,之所以这么理解,是基于一个前提:MTU越小发送数据速度越慢,因为中间设备要处理重复的IP header相对越多。

这个实现受多种不可预测的内容影响,所以我认为这个过程不是确定性的(not deterministic)

所以也不推荐(RFC 1191:We also do not recommend this strategy) 。


最后这一个方法是推荐的:

上述两种方式均将整个网络的所有MTU考虑在内,即盲目地搜索PMTU,我们可以考虑若干有关的MTU(比如:IP报文走过的路径上的MTU集合)。

可以将所有类型网络中类似类似的MTU值放在一个集合内,并从中选取最小值作为 “plateau” 。

这种方式仅仅会造成几个百分点的误差,但显然比差出八位数要好多了。

通过这个表,其收敛速度就类似二分查找最坏情况时的速度,比其他方法要好很多。

这个表所罗列的MTU项均在 f ( x ) = 2 x f(x)=2^x f(x)=2x 附近,所以如果表中未提供某个MTU值,那么估计的误差也会在 2 倍以内。

参见2.7 PMTU可能的值

任何搜索策略必须存储之前的估计信息,并应该作用于下一个估计值。一个方法是使用当前缓存的PATH MTU 估计值,但实际上存在更有价值的信息——"Datagram Too Big"信息。所有的ICMP Destination Unreachable 报文,包括了源的IP报文头部,这其中就有 在不进行分片情况下造成"Datagram Too Big"的报文总长度。因为这个值可能小于当前估计的PMTU值,但是仍然大于实际的PMTU,这可以作为一个非常有价值的参考信息用于选择下一个PMTU估计值。

关于 Plateau 的细节将在 2.7 节 讨论

2.6. 主机实现(Host Implr)

这里主要讨论主机软件如何实现 PMTU Discovery 。本节讨论主要目的是为了提供一个参考,而非标准。

首先列出我们实现时所面临的问题:

  • 具体由哪个(哪些)层实现 PMTU Discovery
  • PMTU 的信息应该缓存在哪
  • 如何移除 过时的(Stale) PMTU信息
  • 传输层和更上层的协议必须做些什么
2.6.1 Layering

在IP协议栈中,具体发送多大的报文取决于IP上层的协议。称这些上层协议为"打包层协议"(Packetization Protocol) ,数据打包的行为往往由传输层协议提供(如TCP),但是打包也可以由更上层的协议来操作(比如基于UDP的上层协议)。

不管怎样,IP层提供不可靠的数据传输,总归要有协议来保证数据在传输前后的一致性和完整性。此时 打包层协议的功能和作用就可以总结为:为数据传输提供校验,确保传输前后的一致性和完整性,为更上层应用提供可靠的数据传输。

在"打包层"实现 PMTU Discovery 可以简化很多 层之间(Inter-layer)的问题,但是有很多缺陷:

  • 需要重做"打包层"协议,比如需要重新设计TCP协议。
  • 不同的"打包层"协议之间需要考虑PMTU信息共享,这实现起来非常困难
  • 一些面向连接的"打包层"协议难以扩展长时间维护PMTU信息的功能

因此,我们认为 IP层才是那个需要保存PMTU信息的层,同时使用ICMP层(即:ICMP协议)来处理收到的"Datagram Too Big"报文。此时"打包层"仍然需要对PMTU的改变做出响应(进行一些调整),比如调整发送的DF报文的大小

Don’t Fragment (DF) 是IP层的控制字段,上面说到,IP层报文的大小是取决于上层协议填充多少数据的。

Note: 这里说的是存储在IP层,而不是存储在IP报文。

但是我们并不期望IP层简单地将所有报文DF置位,因为应用可能使用的是UDP,UDP协议本身是无法控制其报文大小的,上层交给它多少数据,它就尽可能一次性都发出去。但其实也是有上限的 理论值上限是:
D a t a U D P = 2 16 − H e a d e r U D P = 15536 − 8   B y t e Data_{UDP}=2^{16}-Header_{UDP}=15536-8\space Byte DataUDP=216HeaderUDP=155368 Byte
这里的数值来源于RFC 768: User Datagram Protocol 中描述的UDP报文头部的Length字段最大为16位。即 Length 字段的最大值为 2 16 − 1 2^{16}-1 2161,最小为 8 B 8B 8B (UDP Header Size)。

虽然我们要尽可能避免报文被分片,但是仍然存在某些协议需要保持分片(比如NFS,RFC 1094 - NFS: Network File System Protocol specification),虽然不太优雅,但是不能破坏这些协议。

Protocols involving intentional fragmentation, while inelegant, are sometimes successful (NFS being the primary example),and we do not want to break such protocols.

为了实现上述特性,Packetization Layers 需要对IP 层进行一些扩展:需要一种功能可以跟踪Maxmimum Send Transport SizeMaxMSS_S 的变化,具体是:
M a x M S S _ S = E M T U _ S − 2 0 i p H e a d e r MaxMSS\_S=EMTU\_S-20_{ipHeader} MaxMSS_S=EMTU_S20ipHeader

MaxMSS_S 即 MMS_S 参见Section 3.3.3 RFC 1122: Requirements for Internet Hosts - Communication Layers 即 收到的MTU。原始文档中的定义为MMS,这里该为了MaxMSS便于理解,其实两者是一个东西。

这里给出简短的解释:MaxMSS 即整个IP报文减去固定头部的大小(即载荷+IP Options),_S 意为 Send,相对的有 Receive (_R)。

在这里插入图片描述

图:Path MTU -华为 (huawei.com)

2.6.2 Storing PMTU information

IP层应该关联它记录到的某个Path的每个PMTU值。区分Path的时可以通过比较 (Src-addr, Dst-addr, ToS) 来区分不同的Path。有些实现可能是(Src-addr,ToS) ,但是对于单宿主主机Path来说,无妨。

Single-Homed Hosts:即主机只有一个addr

当然有些实现可能更复杂,可能考虑了更多因素来区分不同的Path。

最合适的存储方式就是作为路由表 中的一个字段,路由设备需要为到达 网络中的每一个主机 的可能的路径均设计一个字段用于维护该路径上的PMTU。
S e t ( P a t h A ) = { ( P a t h A , D s t 1 , P M T U 1 ) , ( P a t h A , D s t 2 , P M T U 2 ) , ( P a t h A , D s t 3 , P M T U 3 ) , . . . } Set(Path_A)=\{(Path_A,Dst_1,PMTU_1),\\(Path_A,Dst_2,PMTU_2),\\(Path_A,Dst_3,PMTU_3),\\...\} Set(PathA)={(PathA,Dst1,PMTU1),(PathA,Dst2,PMTU2),(PathA,Dst3,PMTU3),...}
当第一个包被转发到路由器时,如果路由器没有到网络中每个地址的路由,则使用默认路由

S e t p m t u ( D e f a u l t _ R o u t e _ P a t h ) Set_{pmtu}(Default\_Route\_Path) Setpmtu(Default_Route_Path) ,PMTU应该初始化为该路径上直连链路的MTU,即首跳MTU: P M T U i n i t PMTU_{init} PMTUinit

如果多条Path共享同一个直连链路,则它们之间的 P M T U i n i t PMTU_{init} PMTUinit 相等。

如果多条Path之间的第一跳不是同一个链路,则它们之间的 P M T U i n i t PMTU_{init} PMTUinit 不相等

并且需要保证,执行 PMTU Discovery 的过程不应该影响 PMTU,当且仅当收到"Datagram Too Big"报文时,才进行PMTU更新。

PMTU Discovery 只用于创建或者修改路由条目

当收到"Datagram Too Big"信息时,由ICMP协议的Next-Hop MTU字段来提供最新的PMTU

"Datagram Too Big"信息就是一条ICMP

如果收到了"Datagram Too Big"包含的指定的目标主机(或者说路由)不在记录中,则创建一条 S e t ( P a t h A ) . a d d D s t ( D s t n e w   , P M T U i n i t ) Set(Path_A).addDst(Dst_{new}\space,PMTU_{init}) Set(PathA).addDst(Dstnew ,PMTUinit)

  • 如果PMTU小于新的PMTU的估计,则更新
    i f   S e t ( P a t h A ) . g e t P M T U ( D s t ) < M T U n e w t h e n    S e t ( P a t h A ) . u p d a t e ( M T U n e w ) if \space Set(Path_A).getPMTU(Dst)<MTU_{new}\\then\space \space Set(Path_A).update(MTU_{new}) if Set(PathA).getPMTU(Dst)<MTUnewthen  Set(PathA).update(MTUnew)

  • 必须在PMTU缩小时通知"打包层",即 任何正在使用当前Path的"打包层"实例(如TCP Connection),都必须被在PMTU减小时被通知。

  • 如果连接实例上发的报文导致产生了"Datagram Too Big"消息,即使没有PMTU更新,则该实例也应该被通知(notify):报文被丢弃了。

即使"Datagram Too Big"使用的是UDP,那么也必须通知相关的TCP连接

这里原文中确实没有提到任何其他情况下,Host 应该怎么做,或者提供更详尽的说法。

通知机制(notification mechanism)可以类似于ICMP 中原点抑制报文(Source Quench message)使用的机制,在一些实现中,现存的通知机制无法分辨涉及的连接,所以就需要其他的机制辅助。

此时,可以避免使用异步通知机制(asynchronous notification mechanism)用来减少 PMTU

这里的异步机制为:如果Packetization Layer 实例第一次发送了MTU大于当前PMTU的报文,则是允许的,如果再次发送MTU>PMTU的报文,则第二次发送的成功与否取决于第一次发送报文的结果。如果第一次发送的报文引发了"Datagram Too Big"信息,则第二次发送将被阻止,如果第一次发送的报文发送成功,则更新PMTU(上边说过了),那么第二次发送的报文也会成功。

即:下一次发送的报文是否会被阻止,取决于上一次发送的报文的结果

在这种方式下,如果尝试发送的DF数据包的MTU大于PMTU,则同步返回异常并给予特定的错误提示。这种方式适用于无连接的Packetization Layer(如UDP),在这种情况下,将使用正常的超时重传机制来恢复丢失的数据报。

一个十分重要的内容是:

对"打包层"实例的PMTU变化的通知(notification) 与 对"打包层"实例的报文丢弃的通知是有区别的,后者应该尽快完成,(即 从"打包层"实例的角度看,这个通知是异步的),前者应该被推迟到 实例创建新报文时。

应该只针对那些因为"Datagram Too Big"信息而丢弃的报文进行重传(Retransmission)。

这里的重传的语义只限制在Path MTU 这一节,不代表整个TCP语义空间中的重传,

前者是一个行为,后者是一个机制。

2.6.3 清理Stale PMTU信息

Stale 意为过时的,老旧的,陈旧的,因为实在找不到一个满意的中文词来形容Stale PMTU,所以直接使用 Stale PMTU,而不进行翻译。

网络拓扑是动态变化的;路由会随着时间变化而变化。如果对于某个Dst,其路由产生了变化,则使用Stale PMTU信息是非常不合适的。因此主机上缓存的PMTU信息可能会过时。

路由的变化往往也会使得Path产生变化

因为主机往往使用DF报文进行PMTU Discovery,如果过时的PMTU值非常大,那么在发送报文到某个地址的瞬间会发现Stale PMTU不合适。但是却不存在一种机制可以用于检测Stale PMTU是不是太小了,所以实现时可以对Stale PMTU使用"Age"机制。

就是经常被提到的老化机制,只有当PMTU被认为是Stale PMTU时,才会开始使用老化机制。

当PMTU的值在一段时间内(10分钟左右)没有减少,则PMTU的值会被置为 P M T U i n i t PMTU_{init} PMTUinit

即首跳MTU,见2.6.2

并且同时应该通知 Packetization Layer ,PMTU变化了;这个行为意在重新对Path进行一次完整的PMTU检测。

实现时应该提供一个修改超时时间的方法,不一定是建议的10分钟;并且,超时时间可以设置为无限。因为对于不同类型的网络来说,10分钟不一定合适。

即永不超时

上层协议不能(MUST NOT)以为PMTU的增长而将报文重传,因为增长往往不会预示着报文被丢弃。

实现老化机制的一个方法是在路由表中添加一个时间戳(Timestamp)字段,且该字段在初始时应该设置为一个保留值,这个保留值表示PMTU从未被修改过。

当PMTU因为"Datagram Too Big"信息而减少时,时间戳必须设置为当前时间。

每过一分钟,检查一遍路由表的时间戳字段,若时间戳为保留值,则不进行操作,而如果

通过比较当前时间戳与字段上的时间戳,发现时间差大于 超时时间,则

  • 将PMTU置为 P M T U i n i t PMTU_{init} PMTUinit

  • 通知正在使用该路由路径的Packetization Layer,PMTU变化了

    原文直译为:正在使用该路由的 Packetization Layer 被通知PMTU增加(increase)了

如果路由条目被移出,那么相关的PMTU信息也会消失,这往往是因为收到了ICMP重定向消息;或者是因为路由表的守护进程清理了陈旧的路由条目。

多源主机(Multi-homed host)可能因为拓扑的变化而使用不同的网络接口;当这种情况发生时,如果Packetization Layer 没有被通知,那么该主机将仍然使用的缓存的PMTU信息,尽管此时的PMTU相对于当前Path的MTU显得可能非常小。
( P a t h A , D s t 1 , P M T U 1 ) − > ( P a t h B , D s t 1 , P M T U 2 ) w h e r e    P M T U 2 > P M U T 1 (Path_A,Dst_1,PMTU_1) ->(Path_B,Dst_1,PMTU_2) \\ where \space \space PMTU_2 > PMUT_1 (PathA,Dst1,PMTU1)>(PathB,Dst1,PMTU2)where  PMTU2>PMUT1

因为Packetization Layer 实例(如TCB Transmission Control Block)只缓存了IP地址,端口等应用有关信息,且只在被通知时才会间接修改MTU大小;在上述情景下,显然对于新Path来说,Packetization Layer 发送的报文全部在 P M T U 2 PMTU_2 PMTU2大小之内,所以不会产生"Datagram Too Big"。

此时可以进行通知的机制只有超时机制PMTU变化

假如说 P M T U 2 PMTU_2 PMTU2已经稳定,则只能等待其超时。但此时如果定义的超时时间为"永不超时",则将没有任何一种机制再被触发从而通知 Packetization Layer PMTU变化了。

该实例将在其生命周期内总是以远小于 P M T U 2 PMTU_2 PMTU2 的MTU发送报文

在这种情况下

解决方案就是不管是因为什么原因造成的,通知 Packetization Layer 接下来PMTU可能发生变更。

如何6.0节介绍了更加精密的检测PMTU增加的方法

2.6.4 TCP 层的行为

TCP必须跟踪连接实例到某个地址的PMTU,且不应该发送比PMTU大的报文。一个简单地实现是在创建新报文时,调用IP层提供的接口如 GET_MAXSIZES来获取该信息,但这可能会影响效率。

使用"Slow-start"拥塞避免算法的TCP实现需要同时计算并缓存与PMTU相关的若干变量。

在PMTU发生变化时,接收异步通知消息会更加简单,所以这些变量可能被更新。

Congestion Avoidance and Control - Lawrence Berkeley National Laboratory

TCP必须存储从对端收到的MSS(默认为536B),并且发送的数据包(Segment)不能大于该值。

详细见 TCP MSS Option

当收到"Datagram Too Big"信息时,表明数据包被路由器丢掉了。之后就按照报文丢失的情况处理即可——等待重传计时器超时,然后重传该报文。如果PMTU Discovery比较耗时,可能会增加传输的延迟。

此外,还可以在被通知PMTU变化后,立即对报文进行重传,但是只针对那些收到"Datagram Too Big" 连接实例。当然,重传的报文不能大于最新的PMTU。

对于每条"Datagram Too Big"消息,并不能理所应当地认为应该对所有相应的数据进行重传。因为对于TCP来说,有可能是单个较大数据块被拆分成若干TCP报文,如果这些报文都引发了"Datagram Too Big"消息,则会导致TCP重复传输该单个数据块若干次。此时如果新的PMTU仍然不正确,那么这个过程将持续发生,短时间内,网络中将存在大量冗余的报文。

所以,TCP必须可以分辨出"Datagram Too Big"是不是真正减少了PMTU。

因为PMTU increase 不会产生上述情况。

现代TCP实现均使用了拥塞避免慢启动来提升性能,因为"Datagram Too Big"信息而导致的重传不应该影响拥塞窗口,但是应该触发慢启动机制(即:在没有收到ACK之前,只重传一次)

TCP的性能可能会因为发送者的最大窗口大小不是报文大小的整数倍而下降

这里并不是拥塞窗口,拥塞窗口的大小总是报文大小的整数倍

在很多系统中(比如 4.2BSD以及其衍生系统),报文的大小总是被设置为1024B,并且发送窗口大小为1024的整数倍,这是非常合理的。

但是如果使用了PMTU Discovery,这个关系往往是不成立的,因此,当PMTU变化时,TCP的发送窗口大小也必须跟着变。发送窗口的理论最大值应该为 P M T U − 40 PMTU-40 PMTU40 的倍数;实际上需要保证小于等于发送者缓存区大小。

P M T U − 2 0 i p v 4 H e a d e r − 2 0 t c p H e a d e r PMTU-20_{ipv4 Header}-20_{tcpHeader} PMTU20ipv4Header20tcpHeader

2.7. PMTU 的可能的值

在 4.主机如何处理 Old-Style 报文一节中最后提到了一个对PMTU的搜索算法,该算法基于一个" Plateau"表,该表提供了网络中主要数据链路技术的常用的MTU值。表中Comments列代表可能的链路技术,MTU表示该链路技术常用的MTU。之后将MTU相近的链路技术划为一组,每组的Plateau(暂时翻译为"参考值"),该参考值为该组中最小的MTU值。

如果组中的MTU不太一样,则在参考值后的括号中标出组最小MTU与组中最大MTU的比值的近似数,用来描述组中MTU数值的差异性。

每个组之间使用 ‘=’ 划分。

   Plateau    MTU    Comments                      Reference
   ------     ---    --------                      ---------
              65535  Official maximum MTU          RFC 791
              65535  Hyperchannel                  RFC 1044
   65535
   32000             Just in case(以防万一,因为使用最大值是不被建议的)
   =========================================================
   
              17914  16Mb IBM Token Ring    Drew Daniel Perkins.                        											Private Communication
   17914
   ======================================================
   
              8166   IEEE 802.4                    RFC 1042
   8166
   ======================================================
   
              4464   IEEE 802.5 (4Mb max)          RFC 1042
              4352   FDDI (Revised)                RFC 1188
   4352 (1%)
   ======================================================
   
              2048   Wideband Network              RFC 907
              2002   IEEE 802.5 (4Mb recommended)  RFC 1042
   2002 (2%)
   ======================================================
   
              1536   Exp. Ethernet Nets            RFC 895
              1500   Ethernet Networks             RFC 894
              1500   Point-to-Point (default)      RFC 1134
              1492   IEEE 802.3                    RFC 1042
   1492 (3%)
   ======================================================
   
   
              1006   SLIP                          RFC 1055
              1006   ARPANET                       BBN 1822
   1006
   
   ======================================================
   
   
              576    X.25 Networks                 RFC 877
              544    DEC IP Portal      Michael Reilly.  Private 												Communication.
              512    NETBIOS                       RFC 1088
              508    IEEE 802/Source-Rt Bridge     RFC 1042
              508    ARCNET                        RFC 1051
   508 (13%)
   
   ======================================================
   
   
              296    Point-to-Point (low delay)    RFC 1144
   296
   ======================================================
   
   
   68                Official minimum MTU          RFC 791
   ======================================================

Table 7-1: Common MTUs in the Internet

我们不希望表中的值,尤其是较高 MTU 级别的值,永远有效。这些数值仅仅作为建议,而非标准。TCP实现时总应该参考最新建议的参考值。并且,TCP实现时类似改表的数据结构不应该太长,因为太多表项往往会影响PMTU的搜索速度,从而增加TCP传输延迟。

而且,实现这应该提供适当的用户接口用于修改表中的条目,比如BSD衍生的Unix系统可以使用ioctl命令进行表修改。

如果表中没有近似的值,则可以加入若干个 2 x + 40 2^x+40 2x+40 的值。

在 [5.3 清理Stale PMTU信息] 中提到,我们需要一种机制用于检测 PMTU increase,方法是简单地重新进行 Discovery 操作,代价就是会使用几个包。

另一种方式是周期性地将PMTU增加到表中的一个参考值,比如这里使用的PMTU为 994,那么在过一定时间后,将这个值增加到1006,(表中最接近且大于PMTU的值)。再过一段时间,增加到 1492(如果1006没有触发"Datagram Too Big")。

因为参考值之间可能距离比较接近,而PMTU一次增加的比较多; 假如说5min增加一次PMTU到一个新值。

所以如果在1006时没有触发Datagram Too Big,则增加PMTU的周期应该缩短(比如缩减到2min),相反,如果触发了Data Too Big,则应该加长周期(比如加长到10min)。

在任何情况下,这个周期都必须大于报文 的一次往返时间(Round-trip Time)

2.8. 安全性考虑

路径MTU发现机制可能造成两种服务拒绝攻击,两者都基于有恶意的一方发送伪造的"Datagram Too Big"报文给一个主机。

在第一种攻击中,伪造的报文指定比实际PMTU 小的多的PMTU。这种攻击其实不会阻止数据流的传输,因为主机不会将PMTU设置得非常小,只会使得数据传输速度变慢(因为报文变小了)。

在另一种攻击中,伪造的报文指定的 PMTU 大于实际值。 如果接收,受害者发送的数据报就会被某个路由器丢弃,从而造成暂时阻塞。 在一次往返时间内,主机会察觉该错误(因为它会收到"Datagram Too Big"),但频繁重复这种攻击会导致大量数据报被丢弃。

但是主机只会因为"datagram Too Big"而减少PMTU,而永远不会因为该信息而增加PMTU,所以这种攻击其实是无效的。

如果恶意方能阻止受害者接收合法的"Datagram Too Big",也会造成问题,但在这种情况下,只有简单的拒绝服务攻击可以奏效。

last

Path MTU 增强版:RFC 4821: Packetization Layer Path MTU Discovery

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天晴丶SnowCrystal

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值