LWN:还以为自己很懂IP分段吗?

关注了就能看到更多这么棒的文章哦~

So you think you understand IP fragmentation?

February 7, 2024
This article was contributed by Valerie Aurora
Gemini translation
https://lwn.net/Articles/960913/

So you think you understand IP fragmentation? [LWN.net]   website

[2024-02-22 Thu 12:57]

IP 的分片(fragmentation)是什么,它为什么重要,人们是否理解它?关于最后一个问题的答案是“远不如他们认为的那么好”。本文还将回答其余那些问题,并介绍fragquiz,这是作者本人编写的一个游戏,让玩家猜想 IP 数据包在它们对于网络来说过大时的行为。作为 IP 分片没有被很好理解的证据,一屋子网络专家参与了 fragquiz,但成绩完全谈不上完美。此外,我将描述一种我和一些同事开发的新型的避免分片算法,开发该算法的需求激励了开发 fragquiz 的工作。

为什么要关心?

IP 分片是指将 IP(因特网协议)数据包拆分为多个更小的数据包,然后发送到另一台计算机。TCP 和 UDP 以及许多其他网络协议都实现于 IP 之上。许多网络专家都以为他们很清楚 IP 分片何时会发生,我也以为我知道 — 直到我必须为 VPN 实现算法时。那时我了解到,和我一样,很多其他网络专家在预测数据包何时会被拆分成多个数据包时表现非常糟糕。为了解释原因,我们从 IP 分片是什么开始。

IP 数据包是因特网的基本组成部分:一个包含应用程序数据的小块,它的标头(header)描述了它包含什么、将其发送到哪里以及中间路由器允许对其做什么,等等。源主机和目标主机之间的路径上的每个路由器都会读取 IP 标头,略微更改它,查询路由表,然后(希望)将数据包发送到路径中的下一个路由器。

每个网络链路都有其可发送的最大 IP 数据包大小 size,名为:最大传输单元 (MTU)。路径 MTU (PMTU) 是两个主机之间路径上所有 MTU 中的最小值。但是,随着拥塞、断电和其他网络变化,路径会随着时间的推移改变。

IP 分片发生在将 IP 数据包拆分为多个更小的 IP 数据包时,每个数据包都有其自己的标头,以便它们能够装入网络路径的 MTU 中。在 IPv4 和 IPv6 中,分片可以在数据包来自的源计算机上发生。在 IPv4 中,数据包还可以在源和目标之间的路径上的任何路由器上被分片。

一般来说,IP 分片从几乎所有如下角度来说都会损害性能:吞吐量、延迟、CPU 使用率、内存使用率和网络拥塞。为了理解原因,想象一个典型的 IPv4 数据包包含 20 字节的 IP 元数据和 1480 字节的数据,已经被分段成每个数据包仅包含八个字节或更少的数据,总计 1480/8 = 185 个数据包。(这种情况在现实中可能发生,但不太可能;通常数据包仅被分成两个部分。)

要以八个字节为片段(fragment)发送 1480 字节的数据,源必须发送 185*20 = 3700 字节的元数据,而不是在未分段的情况下仅发送 20 字节。处理数据包标头会花费一定量的 CPU 时间,这将在路径中的每个主机上发生 185 次。目标无法将数据传递给网络堆栈,直到它接收到所有片段,因此延迟是 185 个数据包的最坏情况。目标还必须为组装分段的数据包的动作来预留内存,如果在等待合理时间后即使没有收到一个片段,它也会将内存丢弃。

更糟的是,片段更有可能丢失。许多路由器和防火墙将片段视为安全风险,因为它们不包含来自更高层协议(例如 TCP 或 UDP)的信息,并且无法基于端口进行过滤,因此它们会丢弃所有 IP 片段。此外,负载平衡系统可能会将片段路由到不同的主机,在那里它们将永远无法被重新组装。

即使 IP 数据包仅分成两部分,也会因每个数据包的开销增加一倍而通常会导致连接性能显着下降。如果将路由器配置为丢弃数据包,有时 IP 分段会导致网络“黑洞”。发起连接的小数据包会通过,但包含数据的较大数据包会被分段,所以它们都会被丢弃。这就是网络程序员非常非常希望防止 IP 分段的原因。

预防

仅发送小于或等于两个主机之间路径 MTU 的数据包就可以防止 IP 分段。但我们如何找到路径 MTU?这称为路径 MTU 发现 (PMTUD),根据网络协议和网络特性,有多种方法可以执行此操作。查找路径 MTU 的一种可靠方法是发送不允许被分段的已知大小的 IP 数据包。如果源能确认数据包到达了目标,则路径 MTU 至少与该数据包一样大。

因此,为了防止 IP 分段,你必须充分理解 IP 分段,以便预测两件事:源主机发送的 IP 数据包的大小,以及是否允许任何中间路由器将数据包分段成较小的数据块。这取决于以下内容(当然还有其他因素):

  • 本地接口的 MTU

  • IP 版本 (IPv4 或 IPv6)

  • IP 数据包标头中的选项设置(options)

  • 协议 (TCP/UDP/ICMP/等)

  • 套接字选项

  • 系统范围的 PMTUD 相关的任何设置

  • 相关的 PMTU 缓存条目

如果发送方尝试发送一个 IP 数据包,该数据包大于通往接收主机的任何路径部分上的 MTU,则有三种可能性: send() 系统调用返回 EMSGSIZE ,数据包被分段,或数据包被丢弃。(后两个可能会在源主机或中间路由器上发生,具体取决于数据包类型和选项。)当我表示某人“理解 IP 分段”时,我的意思是他们可以预测对于一个特定的数据包来说,可能会发生这些事情中的哪一件。

清楚理解了吗?

如果你一年前问我,大多数网络专家能否预测一个 IP 数据包的大小和分段状态,我会很肯定地说“能”。后来,我必须为一个 VPN 实现 DPLPMTUD。(是的,这是一个真实的缩写词,用于真实的软件,来自一个真实的 RFC。它代表数据报分组层路径最大传输单元发现。)

乍一看,这似乎很容易。我的同事都是网络专家,在应用程序上工作了很多年,这是一个基于 WireGuard 的 VPN,使用 IPv4 和 IPv6。我们一起想出了一个快速、简单的路径 MTU 发现算法。他们确信该软件只发送无法分段的数据包,所以我们要做的就是使用一个内置的 ping 功能发送正确大小的探测数据包,并记录响应。试想我们惊讶的表情,因为数据包抓取到大量分段过的数据包。

当我在寻找禁用 IP 分段的方法时,我发现了很多误导性的且无益于解答问题的答案。有时候最佳答案反而会被踩。官方文档要么不存在(macOS),要么很难理解(Linux)。我们都认为探测数据包应该在一个套接字上发送 IP_PMTUDISC_DO 在 Linux 上,但花了几个星期才意识到我们真正想要的是 IP_PMTUDISC_PROBE 。最终我弄清楚了 Linux 和 macOS 的所有正确设置,但比预想的时间要长得多。

我想和其他人分享我的经验,但现在我面临一个更困难的问题:你如何教人们学习一些他们自以为已经知道的东西?在每一个角落,包括我对镜自照时,人们都错误地认为对 IP 分段的理解有着过分的自信。另外,我们得实事求是,IP 分段有点无聊。

介绍 fragquiz

我决定编写一个游戏来帮助人们学习 IP 分段。这个程序将发送大于本地网络连接的 MTU(网关接口)的数据包,同时更改 IP 版本(IPv6 或 IPv4)、传输层协议(TCP 或 UDP)和套接字分段选项(在 macOS 上禁止/允许分段,对于 Linux,根据 ip(7) 手册页,使用四个不同的 PMTUD 选项)。然后,它将报告数据包是否已发送、数据包的分段设置以及它是否在路由过程中被分段——但首先它会让用户猜测将会发生什么。最后,它将告诉他们分数,并鼓励他们像 Wordle 一样将他们的分数和程序的链接发送给其他人。

有一些要求:

  • 可以用在 Mac 和 Linux 上

  • 容易运行(无需超级用户,无需单独的服务器,无需配置)

  • 无需虚拟化、隧道或环回接口,因为它们经常会出现与 MTU 相关的错误

  • 无需主机数据包跟踪,因为分段/重新组装通常发生在网络接口上

我决定使用 traceroute 式解决方案。traceroute 的默认模式以较小的生存时间(TTL)或 IPv6 的跳数限制发送数据包。当路由器接收数据包时,它将 TTL 减 1;如果现在 TTL 为零,并且数据包不是发给路由器本身的,它将丢弃数据包,并将 ICMP 超时消息发送回源地址。然后,Traceroute 会从超时消息中读取发送路由器的 IP 地址并进行打印。它会继续发送具有递增 TTL 的数据包,以找到距离目标越来越近的路由器的 IP 地址。

Fragquiz 使用相同的 TTL 技术,向每个数据包发送一个小 TTL,并读取路由器发送的 ICMP 超时数据包。超时消息包括触发消息的数据包的标头,其中包含数据包大小和分段状态。在 macOS 和 Linux 上,非特权用户可以使用非特权 ICMP 套接字类型读取(和发送)受限的 ICMP 消息子集。

它奏效了,但与此同时出了一些意外。

最初,我假设路由器不会重新组装 TTL 为一的数据包,因为它们会立即减小 TTL 并在处理完毕后丢弃它。但我测试的第一个路由器,我的家庭 WiFi 接入点,确确实实这么做了。我添加了代码,使用具有递增 TTL 值且大于 MTU 的数据包自动探测网络,直到接收到一个针对片段而非整个数据包的超时消息,表示数据包到达在发送超时消息之前还没有重新组装数据包的路由器。之后我将该值设置成 TTL,放在用来测试 IP 分段的数据包里。通常,必要的 TTL 为 1 或 2;我实际见过的最大的 TTL 是 6,这意味着前五台路由器都在发送超时回应之前重新组装了片段。

有些网络根本不发送超时消息。我不时发现 fragquiz 突然停止工作,然后花了数分钟如梦似幻地想弄明白我是如何毁了代码的。后来我意识到关闭我的 VPN 就可以让 fragquiz 再次工作了。以我的经验,很少有网络能正确地为 IPv4 和 IPv6 生成超时消息。

虽然非特权 ICMP 侦听器允许非超级用户在 macOS 上读取超时消息,但该代码仅适用于 Linux 上的超级用户。根据 ICMP 套接字的 初始提交信息,非特权用户只能通过发送套接字上的 IP_RECVERR 来读取 ICMP 超时消息。我没有实现,所以目前的 Linux 版本只能作为 root 运行。

macOS 和 Linux 都保存着操作系统发现的路径 MTU 的缓存。缓存的路径 MTU 会在某些情况下影响 IP 分段行为,这让人头疼,因为我需要等待缓存的路径 MTU 过期。我想在未来添加一个选项来清除路径 MTU 缓存。此外,请注意,原型版本现在有一个占位符形式的许可证,但我计划在未来发布一个具有开放许可证的版本。

我 在 RIPE 87 上介绍过,这是一个网络运营商和互联网服务提供商的会议。在演讲的最后,我让听众通过举手投票来玩 fragquiz。几乎每个问题上,都有两种回答:“是”和“否”。共同算起来,他们的得分低于 80%。这意味着一个由专业的网络工程师和研究人员组成的听众一起努力都没有完成作业拿到 “B”等级。我想我们可以安全地得出结论,理解 IP 分段是很困难的。

一种新的(?)算法

最后,我说过我要解释一下这个新算法,这是我与萨尔曼·阿尔贾马斯和詹姆斯·塔克共同创建的。

大多数路径 MTU 发现算法一次测试一个路径 MTU。他们发送一定大小的数据包,观察它是否通过,然后决定下一步做什么:发送更大的数据包、发送更小的数据包,或者确定当前的路径 MTU 估计值已经足够好并终止搜索算法。这可能需要几个往返时间才能找到最佳路径 MTU。

我们的第一个见解是,as shown by Custura, et al.,在现实世界中,会有很少一部分(小于十个)可能的数据包 size。我们并不是第一个意识到这一点的人;事实上,RFC 8899 中说:“通过从常见 PMTU 大小的表中选择步长,可以优化搜索过程”。

我们做出的改变之处是:我们同时发送所有可能的数据包大小。因此,如果本地 MTU 为 9000 字节,那么我们就同时发送大小分别为 1280、1400、1500、8000 和 9000 字节的数据包。另一端发送它看到的每个数据包的确认。然后,我们将路径 MTU 设置为已确认的最大数据包大小。即使它相差几字节也无妨;大多数 PMTU 搜索算法在找到“足够近”的值后就会停止探测。

每隔十分钟,我们通过发送下一个 MTU 大小的数据包来重新探测路径 MTU。如果我们得到更大 MTU 大小的确认,则表示路径 MTU 已经改变,并且我们重新探测所有大于该值且小于本地 MTU 的数据包大小。否则,我们再使用当前的路径 MTU 十分钟。如果出于任何原因(包括路径 MTU 缩小)开始丢失数据包,我们则从头重新协商连接。

该算法的延迟为一个 RTT(往返时间),且非常简单:一个计时器、一个静态表和一个变量来保存当前路径 MTU。缺点是,如果其他路径 MTU 搜索算法可以通过较少的包找到路径 MTU,那么它可能会比其他路径 MTU 搜索算法使用更多带宽。

读者挑战

我当然希望你现在可以大胆地承认你也不理解 IP 分段了。如果你仍然不确定,这里还有一个有趣的结束挑战:在 Linux 或 macOS 上使用标准配置下载 fragquiz 并运行以下内容。(如果你在过去 10 分钟内与 bing.com 建立了 TCP 连接,请使用你最近未连接过的域名来替换它。如果你使用的是 macOS,则不需要 sudo。)

$ sudo ./fragquiz -p udp4 -f default -a bing.com:80
$ sudo ./fragquiz -p tcp4 -f default -a bing.com:80
$ sudo ./fragquiz -p udp4 -f default -a bing.com:80

第一个命令和第三个命令得到的结果是否一致?为什么?提示:参考上面链接的 Linux ip(7) 手册页。

[ 瓦莱丽·奥罗拉 是一位软件顾问,她喜欢编写可笑的 hack 脚本并解决困难的系统问题。 ]

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

dcea20152649560126352ad2530a3768.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值