据我了解,IPv6 分段不是由路由器执行的,只有端到端节点执行。当路径上的任何路由器收到大于其下一跳链路的 MTU 的数据包时,它会丢弃该数据包,并使用 ICMPv6“数据包太大”回复源 IP。
以下是我在设置中观察到的情况:
在我的本地以太网链路启动后,我访问了一个 HTTP 页面,其中的请求导致发送了一个大数据包(1965 字节)。我的路由器用 ICMPv6 回复说数据包太大,我的 MTU 是 1492(ADSL ATM 链路的 MTU)。然后我的机器将 TCP 数据包分成两个较小的数据包(1492 和 545 字节),然后重试(而不是向 IPv6 添加扩展标头进行分段,这是我预期应该发生的)。
到目前为止一切顺利。让我感到困惑的是,从那时起,尽管有些传出的数据包大于 2K(例如 2399 字节),路由器不再发送“数据包太大”响应,并且一切似乎都很好(即,使用较小的数据包不会重新传输)。
知道这里发生什么事吗?
我使用的是 Linux 3.14.23,我的路由器基于 Tomato。目前我的路由器上没有数据包监控。
答案1
标准允许在 IPv6 和物理层之间设置一层进行逐跳分段。事实上,如果物理层的 MTU 小于 1280 字节,这种逐跳分段甚至是强制性的。IPv6 层以下的这种分段的具体工作原理超出了 IPv6 标准的范围。RFC 2460 中的确切措辞如下:
在任何无法完整传送 1280 八位字节数据包的链路上,必须在 IPv6 以下的层提供特定于链路的分段和重组。
您考虑的碎片化是 IPv6 中的端到端碎片化。这种碎片化只能由最初发出数据包的节点执行。任何中间路由器都不允许对其转发的数据包执行这种碎片化。
据我从你的问题中了解,在你的情况下,这两种碎片都没有发生。
如果您能够从 HTTP 客户端向路由器发送一个 2KB 的数据包,则意味着您的 LAN 已配置为使用巨型帧。另一种可能性是 HTTP 客户端运行在支持 TCP 分段卸载的主机上。如果是这种情况,则在发送主机上使用 tcpdump 观察时,第一个数据包可能显示为 2KB,但在线路上,它实际上可能是一个包含前 1500 个字节的数据包和另一个包含其余字节的数据包。
1500 字节对于 ADSL 链路上的 MTU 来说仍然太大。通过使用适当的工具(例如 Wireshark)检查错误消息,可以在客户端计算机上看到触发太大错误消息的数据包的实际大小。
一旦客户端的 TCP 堆栈收到“太大”错误,会发生什么情况取决于正在使用哪个 TCP 堆栈。有些会使用 IPv6 分段重新传输相同的 TCP 段,其他会将 TCP 段拆分为两个较小的 TCP 段。IPv6 标准规定如下:
为了发送大于路径 MTU 的数据包,节点可以使用 IPv6 Fragment 标头在源处对数据包进行分段,然后在目的地重新组装。但是,任何能够调整数据包以适应测量的路径 MTU(即低至 1280 个八位字节)的应用程序都不鼓励使用此类分段。
我认为这是建议将 TCP 段作为两个较小的段重新传输,而不是使用 IPv6 分段。TCP 分段优于分段的原因有很多。
路由器可能会对过大错误消息进行速率限制。因此,如果您发送多个 TCP 段,每个段都大于 2KB,您可能只会收到第一个错误消息。一旦 TCP 堆栈重新传输第一次超过 MTU 的数据包,它应该能够通过使用较小的 MTU 来处理此问题。
您看到的可能只是速率限制低于您的预期。您可以尝试衡量实际使用的速率限制,然后仅在发现速率限制过低时才采取进一步措施。
答案2
一旦确定了初始碎片大小,它应该会坚持到链接。初始数据包最初会失败。其他数据包将在 IPv6 堆栈中被碎片化,然后再发送。在客户端转储流量并检查数据包大小。您应该看到较大的数据包(响应)在传输之前被碎片化。
您指出第一次分片后一切正常。这表明数据包被适当地分片了,否则它们很可能会因后续路由的分片而失败。