文章目录
- 前言
- 一、Why care?
- 二、Prevention
- 三、Well-understood?
- 四、Introducing fragquiz
- 五、A novel (?) algorithm
- 六、Reader challenge
- 七、traceroute
- 八、ICMP
- 参考资料
前言
本文来自:https://lwn.net/Articles/960913/
February 7, 2024This article was contributed by Valerie Aurora
IP 在 TCP/IP 参考模型中处于第三层,也就是网络层。
网络层的主要作用是:实现主机与主机之间的通信,也叫点对点(end to end)通信。
如下图所示:
网络层最常使用的是 IP 协议(Internet Protocol),IP 协议会将传输层的报文作为数据部分,再加上 IP 包头组装成 IP 报文,如果 IP 报文大小超过 MTU(以太网中一般为 1500 字节)就会再次进行分片,得到一个即将发送到网络的 IP 报文。
图片来自于:图解网络介绍
一、Why care?
IP分片是在将IP(Internet Protocol)数据包发送到另一台计算机之前将其分割成较小片段的过程。TCP和UDP以及许多其他网络协议都是在IP之上实现的。许多网络专家认为他们知道何时会发生IP分片,而我也曾这样认为,直到我不得不为一个VPN实现一个算法。那时我才意识到,像我一样,许多其他网络专家在预测数据包何时会被分割成片段方面并不擅长。为了解释这个问题,我们首先来看一下IP分片是什么。
IP数据包是互联网的构建单元,它是一个包含应用数据的小块,并包含描述其内容、传输目的地以及中间路由器允许对其进行的操作等信息的头部。源主机和目标主机之间的每个路由器都会读取IP头部,并稍作修改,然后参考路由表,将数据包发送到路径上的下一个路由器。
每个网络链路都有一个最大的IP数据包大小限制,称为最大传输单元(MTU – Maximum Transmission Unit)。路径最大传输单元(PMTU)是两个主机之间路径上所有MTU中的最小值。然而,路径可能会随时间变化,这取决于拥塞、故障和其他网络变化。
IP分片是指当IP数据包被分割成较小的IP数据包时发生的过程,每个小数据包都有自己的头部,以便适应网络路径的最大传输单元(MTU)。在IPv4和IPv6中,分片可以发生在数据包的源端,即数据包的发送计算机。在IPv4中,数据包还可以被源端和源端与目的地之间路径上的任何路由器分片。
一般而言,IP分片对性能在吞吐量、延迟、CPU使用率、内存使用率和网络拥塞等方面都会产生负面影响。让我们来看看为什么会这样。假设我们有一个典型的IPv4数据包,其中包含20字节的IP元数据和1480字节的数据,并将其分割成每个数据包只包含8字节或更少数据的片段,总共分割成了1480/8 = 185个数据包(这在现实中可能不太可能发生,通常数据包只会被分割成两个片段)。
要将1480字节的数据以每个8字节的片段发送,源端需要发送185 * 20 = 3700字节的元数据,而不是在未分片的情况下只发送20字节。处理数据包头部需要一定的CPU时间,这将在路径上的每个主机上发生185次。直到接收到所有的片段,目的地才能将数据传递给网络堆栈,因此延迟是185个数据包的最坏情况。目的地还必须为组装分片数据包保留内存,如果在合理的时间内没有接收到任何一个片段,它将会丢弃这些内存。
更糟糕的是,分片更容易丢失。许多路由器和防火墙将分片视为安全风险,因为它们不包含来自TCP或UDP等更高级别协议的信息,无法根据端口进行过滤,所以它们会丢弃所有的IP分片。此外,负载均衡系统可能会将分片路由到不同的主机上,导致它们无法重新组装。
即使将一个IP数据包仅分割成两个片段,由于每个数据包的开销翻倍,通常也会导致连接性能的明显下降。有时,如果路由器配置为丢弃分片,IP分片可能导致网络出现"黑洞"。发起连接的小数据包可以通过,但包含数据的较大数据包被分割成片段,因此它们都会被丢弃。这就是为什么网络程序员非常希望避免IP分片的原因。
二、Prevention
IP分片可以通过只发送小于或等于两个主机之间路径MTU的数据包来防止。但是我们如何找到路径MTU呢?这被称为路径MTU发现(PMTUD),根据网络协议和网络特性,有多种方法可以实现这一点。一种可靠的找到路径MTU的方法是发送已知大小的不允许分片的IP数据包。如果源端收到确认报文表明数据包已经到达目的地,那么路径MTU至少与该数据包的大小相同。
要防止IP分片,您必须对IP分片有足够的了解,以预测两个关键点:源主机发送的IP数据包的大小,以及中间路由器是否允许将数据包分片为较小的片段。这取决于以下几个方面:
the MTU of the local interface
the IP version (IPv4 or IPv6)
the options in the IP packet header
the protocol (TCP/UDP/ICMP/etc.)
the socket options
any system-wide PMTUD-related settings
any relevant PMTU-cache entries
(1)本地接口的最大传输单元(MTU):MTU确定了可以在网络接口上传输的IP数据包的最大大小,而无需进行分片。不同的接口和网络技术可能具有不同的MTU值。
(2)IP版本(IPv4或IPv6):IP分片的过程在IPv4和IPv6之间可能有所不同。了解每个IP版本特定的分片机制至关重要。
(3)IP数据包头部选项:IP数据包头部的特定选项的存在可能会影响分片行为。例如,IP头部中的“不分片”(DF)标志表示该数据包不应进行分片。
(4)协议:传输协议的选择(如TCP、UDP、ICMP等)可以影响分片。某些协议可能对分片具有特定的要求或限制。
(5)套接字选项:套接字级别的选项或配置可能会影响是否允许或偏好IP分片。这些选项可以由应用程序或操作系统设置。
(6)PMTUD相关设置:与路径MTU发现(PMTUD)相关的系统级和应用程序级设置可以影响分片行为。这些设置确定系统如何处理最优路径MTU的发现。
(7)PMTU缓存条目:路径MTU发现依赖于缓存和更新特定路径的MTU信息。缓存条目可以影响分片决策过程。
如果发送方尝试发送一个大于路径上任何部分的MTU的IP数据包,有三种可能性:send()系统调用返回EMSGSIZE错误、数据包被分片,以及/或者数据包被丢弃。(后两种情况可能发生在源主机或中间路由器,具体取决于数据包类型和选项。)当我说某人"理解IP分片"时,我指的是他们能够预测对于给定的数据包可能发生的情况。
三、Well-understood?
如果在一年前,你问我大多数网络专家是否能够预测IP数据包的大小和分片状态,我会自信地回答"是的"。然而,后来我不得不为VPN实施 DPLPMTUD(Datagram Packetization Layer Path Maximum Transmission Unit Discovery)(是一个真实的首字母缩写词,代表了一个真实的RFC所指的软件)。
最初,这似乎是一件容易的事情。我的同事们是经验丰富的网络专家,有着丰富的应用程序开发经验,而该VPN是基于WireGuard的,同时支持IPv4和IPv6。我们一起设计了一种快速简单的路径MTU发现算法。他们相信该软件已经只发送不可分片的数据包,所以我们只需要使用内置的ping功能发送合适大小的探测数据包,并记录响应即可。然而,当我们检查数据包捕获结果时,却发现数据包竟然被分片了。
在寻找禁用IP分片的方法时,我在Stack Overflow上找到了很多误导性和无用的答案。有时候,最佳答案竟然被点踩。官方文档要么根本不存在(macOS),要么很难理解(Linux)。我们都认为应该在Linux上使用带有IP_PMTUDISC_DO标志的套接字发送探测数据包,但花了几周的时间才意识到我们实际上需要使用IP_PMTUDISC_PROBE。最终,我找到了Linux和macOS的正确设置,但花费的时间比预期要长得多。
我希望与其他人分享我所学到的知识,但现在我面临着更难的问题:如何教给人们他们认为自己已经知道的东西?我发现无论我看到哪里,包括镜子中,人们对IP分片都充满了自信的错误。而且,让我们面对现实,IP分片确实有点无趣。
四、Introducing fragquiz
我决定编写一个游戏来帮助人们学习IP分片。这个程序将发送大于本地网络连接的MTU(网关接口)的数据包,同时更改IP版本(IPv6或IPv4)、传输层协议(TCP或UDP)和套接字分片选项(在macOS上是是否分片,而在Linux上则有ip(7)手册中的四个不同的PMTUD选项)。然后,它会报告数据包是否被发送、数据包的分片设置如何以及是否在传输过程中被分片。但首先,它会让用户猜测会发生什么。最后,它会告诉他们他们的得分,并鼓励他们将得分和程序的链接发送给其他人,类似于Wordle游戏的方式。
我有几个要求:
适用于macOS和Linux
易于运行(无需超级用户权限、无需单独的服务器、无需配置)
无需虚拟化、隧道或回环接口,因为它们经常与MTU相关的错误
无需主机数据包追踪,因为分片/重组通常发生在网络接口上
我决定使用类似于 traceroute 的解决方案。traceroute 的默认模式发送具有较小生存时间(TTL)或 IPv6 的跳数限制的数据包。当路由器接收到数据包时,它会将 TTL 减一;如果 TTL 现在为零并且数据包不是发送给路由器自身的话,路由器将丢弃该数据包并向源地址发送一个 ICMP 时间超过消息。然后,traceroute 从时间超过消息中读取发送路由器的 IP 地址并将其打印出来。它继续发送具有递增 TTL 的数据包,以找到越来越接近目标的路由器的 IP 地址。
Fragquiz 使用相同的 TTL 技术,将每个数据包都以较小的 TTL 发送,并读取路由器发送的 ICMP 时间超过消息。时间超过消息包含触发该消息的数据包的头部信息,其中包括数据包的大小和分片状态。在 macOS 和 Linux 上,非特权用户可以使用非特权 ICMP 套接字类型读取(和发送)一组受限制的 ICMP 消息。
它奏效了,但一路上也有一些惊喜。
最初,我以为路由器不会重新组装 TTL 为 1 的数据包,因为它们只会在完成后将 TTL 减少并丢弃它。但是,我测试的第一个路由器,我的家用 WiFi 接入点,确实这样做了。我添加了代码,自动使用递增的 TTL 值对网络进行探测,直到它接收到一个针对片段而不是整个数据包的时间超过消息,表示数据包到达了一个在发送时间超过消息之前不重新组装数据包的路由器。然后,我将该值用于测试 IP 分片的数据包的 TTL。通常情况下,所需的 TTL 为 1 或 2;我在实践中见过的最大 TTL 是 6,这意味着路由器 1 到 5 在发送时间超过响应之前都重新组装了片段。
有些网络根本不发送时间超过消息。我通过艰难的方式发现了这一点,当 fragquiz 突然停止工作时,我花了几分钟疯狂地尝试弄清楚自己是如何破坏代码的。然后我意识到关闭 VPN 后,fragquiz 又开始工作了。根据我的经验,一个网络能够正确生成 IPv4 和 IPv6 的时间超过消息是很罕见的。
虽然非特权 ICMP 监听器允许非超级用户在 macOS 上读取时间超过消息,但该代码仅在 Linux 的超级用户上工作。根据 ICMP 套接字的初始提交消息,只有使用发送套接字上的 IP_RECVERR,非特权用户才能读取 ICMP 时间超过消息。我没有实现这个功能,所以目前 Linux 版本只能在 root 用户下工作。
macOS 和 Linux 都会在操作系统中保留一个已发现的路径 MTU 缓存。缓存的路径 MTU 在某些情况下会影响 IP 分片行为,这使得测试变得麻烦,因为我必须等待缓存的路径 MTU 过期。我希望将来能添加一个清除路径 MTU 缓存的选项。此外,请注意原型版本目前具有一种类似占位符的许可证,但我计划在将来发布一个带有开放许可证的版本。
五、A novel (?) algorithm
最后,我承诺解释一下我与 Salman Aljammaz 和 James Tucker 共同创建的新算法。
大多数路径 MTU 发现算法一次只测试一个路径 MTU。它们发送一个特定大小的数据包,看它是否通过,然后决定下一步该怎么做:发送一个更大的数据包、发送一个更小的数据包,或者决定当前路径 MTU 的估计已经足够好,终止搜索算法。这可能需要多次往返来找到最佳的路径 MTU。
我们的第一个想法是,正如 Custura 等人在实际世界中所展示的那样,可能的数据包大小非常有限,少于十个。我们并不是第一个意识到这一点的人;事实上,RFC 8899 中就提到:“实现可以通过从常见 PMTU 大小的表中选择步长来优化搜索过程。”
我们所做的不同之处在于:我们同时发送所有可能的数据包大小。因此,如果本地 MTU 是 9000 字节,我们同时发送大小为 1280、1400、1500、8000 和 9000 字节的数据包。另一端对每个收到的数据包发送确认。然后,我们将路径 MTU 设置为被确认的最大数据包大小。如果相差几个字节也没关系;大多数 PMTU 搜索算法在达到“足够接近”的时候停止探测。
每隔十分钟,我们通过发送一个更大的 MTU 大小的数据包来重新探测路径 MTU。如果我们收到了更大 MTU 大小的确认,那么就知道路径 MTU 发生了变化,我们会重新探测所有比它大且小于本地 MTU 的数据包大小。否则,我们将使用当前的路径 MTU 再延长十分钟。如果由于任何原因,包括路径 MTU 的缩小,我们开始丢包,我们将从头重新协商连接。
这个算法的延迟为一个往返时间(RTT),非常简单:一个计时器,一个静态表格和一个变量来保存当前的路径 MTU。不足之处是,如果其他路径 MTU 搜索算法能够用较少的数据包找到路径 MTU,那么这个算法可能会使用更多的带宽。
六、Reader challenge
我希望你现在可以自豪地说,你也不理解IP碎片化。如果你仍然不确定,这里有一个有趣的结束挑战:下载fragquiz,并在标准配置的Linux或macOS上运行以下内容。(如果你在过去10分钟内通过TCP连接到bing.com,请将其替换为最近未连接的域。如果你在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)手册页。
七、traceroute
在计算机领域中,traceroute是用于显示可能路径和测量数据包在Internet协议(IP)网络中传输延迟的网络诊断命令。它们记录了路由的历史,即从路径中的每个连续主机(远程节点)接收到的数据包的往返时间;每个跳跃中平均时间的总和是建立连接所花费的总时间的度量。Traceroute会继续执行,除非发送的数据包有超过两次丢失(通常为三个);在这种情况下,连接将中断,无法评估路由。另一方面,Ping仅计算从目标点返回的最终往返时间。
Traceroute命令通过发送一系列的数据包(通常为ICMP Echo Request)并观察每个数据包的传输路径和往返时间来工作。在每个跳跃中,数据包经过一个中间节点(路由器或主机),然后转发到下一个节点,直到到达目标节点。每个跳跃中的节点会将其往返时间和其他相关信息返回给源节点,以便源节点可以构建整个路径的图像。
NAMEtraceroute — trace the route to a hostSYNOPSIStraceroute [option ...] hostDESCRIPTIONPrint the route packets trace to network host.
在类Unix操作系统(如Linux、FreeBSD、NetBSD、OpenBSD、DragonFly BSD和macOS)中,traceroute命令默认发送一系列的User Datagram Protocol(UDP)数据包。这些UDP数据包的目标端口号范围通常是33434到33534。
这些操作系统上的traceroute实现提供了选项来选择不同的协议来发送数据包。例如:
(1)ICMP Echo Request数据包(-I):您可以选择使用ICMP Echo Request数据包来进行路由跟踪,而不是使用UDP。这个选项允许您使用ICMP数据包执行类似于ping命令的路由跟踪操作。
(2)任意协议(-P):-P选项允许您指定发送数据包所使用的任意协议。您可以选择使用UDP、TCP、ICMP等协议。通过指定协议,您可以自定义在路由跟踪中发送的数据包类型。
time-to-live(TTL)值,也称为跳数限制(hop limit),用于确定通过中间路由器到达目标的路径。Traceroute发送的数据包具有逐渐增加的TTL值,从TTL值为1的数据包开始。路由器在路由过程中将数据包的TTL值减1,当TTL值达到零时丢弃数据包,并返回ICMP错误消息ICMP Time Exceeded(超时)。对于第一组数据包,第一个路由器接收到数据包,将TTL值减一,并丢弃该数据包,因为此时TTL值为零。路由器向源地址发送一个ICMP Time Exceeded消息。下一组数据包被赋予TTL值为2,因此第一个路由器转发数据包,但第二个路由器丢弃它们,并回复ICMP Time Exceeded消息。以此方式继续,traceroute使用返回的ICMP Time Exceeded消息构建数据包经过的路由器列表,直到到达目标,并返回ICMP Destination Unreachable消息(如果使用UDP数据包)或ICMP Echo Reply消息(如果使用ICMP Echo消息)。
traceroute 的作用:
(1)路径追踪(Trace the Path):traceroute使用逐渐增加的TTL值来发送数据包,以确定数据包所经过的中间路由器或节点。每当数据包经过一个路由器时,该路由器会将TTL值减1,并在将TTL值减至零时丢弃数据包并返回一个ICMP Time Exceeded报文。traceroute利用这些报文来构建数据包的路径,显示经过的路由器的IP地址。通过路径追踪,可以确定数据包在网络中的流动路径,这样的过程,traceroute 就可以拿到了所有的路由器 IP。
(2)确定路径的最大传输单元(MTU)(Determine Path MTU):MTU是指在网络通信中,能够传输的最大数据包大小。为了确定路径上的最大传输单元,traceroute在数据包中设置不分片(Don’t Fragment)标志位,并逐渐增加数据包的大小。当数据包超过某个路由器的MTU限制时,该路由器会丢弃数据包并返回一个ICMP Destination Unreachable报文,指示数据包太大无法传输。通过观察这些报文,traceroute可以确定路径上的最小MTU值,帮助确定网络中可能存在的MTU限制和数据包分片问题。
八、ICMP
Internet Control Message Protocol(ICMP)是互联网协议套件中的一个支持协议。它被网络设备(包括路由器)用于发送错误消息和操作信息,以指示与另一个IP地址通信的成功或失败情况。例如,当请求的服务不可用或无法到达主机或路由器时,会发出错误指示。ICMP与传输协议(如TCP和UDP)不同,通常不用于系统之间的数据交换,也不经常被终端用户网络应用程序使用(除了一些诊断工具,如ping和traceroute)。
ICMP 主要的功能包括:确认 IP 包是否成功送达目标地址、报告发送过程中 IP 包被废弃的原因和改善网络设置等。
在 IP 通信中如果某个 IP 包因为某种原因未能达到目标地址,那么这个具体的原因将由 ICMP 负责通知。
ICMP是互联网协议套件的一部分,其定义在RFC 792中。ICMP消息通常用于诊断或控制目的,或者作为对IP操作中的错误所产生的响应(如RFC 1122中所指定)。ICMP错误消息被发送到发起数据包的源IP地址。
例如,每个设备(如中间路由器)在转发IP数据报时,会将IP头中的生存时间(TTL)字段减一。如果结果TTL为0,数据包将被丢弃,并向数据报的源地址发送一个ICMP超时消息。
许多常用的网络工具都基于ICMP消息。通过使用特殊设置的IP TTL头字段发送IP数据报,并查找在传输过程中产生的ICMP超时和目的地不可达消息,可以实现traceroute命令。相关的ping工具使用ICMP回显请求和回显回复消息来实现。
ICMP在使用上像是IP的高级协议,但实际上ICMP是IP的一个组成部分。尽管ICMP消息包含在标准IP数据包中,但ICMP消息通常作为一种特殊情况进行处理,与正常的IP处理有所区别。在许多情况下,有必要检查ICMP消息的内容,并将适当的错误消息传递给负责发送导致ICMP消息被发送的IP数据包的应用程序。
ICMP是一个网络层协议,在七层OSI模型中属于第3层协议。基于四层TCP/IP模型,ICMP是一个网络层协议,在互联网标准RFC 1122的TCP/IP四层模型中属于第2层协议,或者在现代五层TCP/IP协议定义中(由Kozierok、Comer、Tanenbaum、Forouzan、Kurose、Stallings等人提出)属于第3层协议。
以四层TCP/IP模型(应用层,传输层,网络层和网络接口层)为基准,ICMP是一个网络层协议。ICMP 报文是封装在 IP 包里面,它工作在网络层,是 IP 协议的助手。
图片来自于:图解网络介绍
ICMP数据包 Header的类型字段,大致可以分为两大类:
(1)一类是用于诊断的查询消息,也就是「查询报文类型」
(2)另一类是通知出错原因的错误消息,也就是「差错报文类型」
ICMP数据包没有与之关联的TCP或UDP端口号,因为这些端口号与上层的传输层相关联。
参考资料
图解网络介绍
https://en.wikipedia.org/wiki/Traceroute
https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol