4. IP分片与重组
IP协议工作在链路层之上,而每种链路层技术对单帧可承载的数据量都有上限,这个上限称为最大传输单元(MTU)。以太网默认MTU为1500字节,PPP链路常见为1492或1500字节,某些隧道接口(如GRE、IPsec)因额外封装开销会更小。当IP数据报长度超过出接口MTU时,路由器或主机必须将其切分为多个较小的分片分别发送,接收端再依据IP首部中的标识、标志和片偏移字段完成重组。理解分片机制,是排查“能ping通小包却传不了大包”“网页能打开但文件下载卡死”这类问题的关键。
一、MTU与分片的触发条件
MTU是链路层帧中数据字段的最大长度,不含帧头帧尾。IPv4数据报总长度字段为16位,理论上最大65535字节,但实际发送时受出接口MTU约束。判断是否分片的依据是:IP数据报总长度 > 出接口MTU。例如主机A通过以太网(MTU 1500)发送一个总长3000字节的UDP数据报,则必须分片。
IPv4首部中与分片直接相关的字段有三个:
- 标识(Identification,16位):同一原始数据报的所有分片共享同一个标识值,接收端据此归组。
- 标志(Flags,3位):DF(Don't Fragment)置1表示禁止分片,若超MTU则丢弃并回ICMP差错;MF(More Fragments)置1表示后面还有分片,最后一个分片MF=0。
- 片偏移(Fragment Offset,13位):以8字节为单位,表示本分片数据在原始数据报数据区中的起始位置。
由于片偏移以8字节为单位,除最后一片外,每个分片的数据长度必须是8的整数倍。这是分片计算中最容易出错的地方。
二、分片计算示例
假设主机发送一个总长3000字节的IP数据报,其中IP首部20字节,数据部分2980字节,出接口MTU为1500字节。每个分片都带20字节IP首部,因此每片可承载数据最多为1500−20=1480字节,且需为8的倍数,1480÷8=185,正好整除,故每片数据1480字节。
原始数据报:总长3000,首部20,数据2980
分片1:数据1480,偏移0, MF=1,总长1500
分片2:数据1480,偏移185, MF=1,总长1500
分片3:数据 20,偏移370, MF=0,总长 40
校验:1480+1480+20 = 2980 ✓
偏移:0 / 185 / 370(单位8字节)
若MTU为1500但数据长度不是8的倍数,例如数据部分为1479字节,则每片数据应取1472字节(1472÷8=184),剩余7字节放入最后一片。可见分片长度需向下取整到8的倍数。
三、分片重组过程
重组只发生在最终目的主机,中间路由器只负责分片转发,不进行重组。目的主机收到分片后,按“源IP + 目的IP + 协议号 + 标识”四元组归入同一重组队列,再依据片偏移排序,拼装出原始数据区。重组需要满足:
- 首片偏移为0,且MF=1;最后一片MF=0;
- 各片数据区首尾相接,无重叠、无空洞;
- 所有分片在超时时间内到达(IPv4常见默认30秒或60秒)。
若任一分片丢失,整个数据报无法重组,接收端丢弃所有已到分片,上层协议(如TCP)只能依靠超时重传恢复。这也是分片会显著降低传输效率的原因。
四、路径MTU发现(PMTUD)
由于分片降低效率且易因丢片导致重传,现代IP网络普遍采用路径MTU发现机制,让发送方主动探测从源到目的整条路径上的最小MTU,从而以合适大小发送数据报,避免分片。
IPv4中PMTUD的工作过程如下:发送方将IP首部DF位置1,先按本地MTU发送。若路径中某台路由器发现数据报超过下一跳MTU且DF=1,则丢弃该包,并向源地址回送ICMP差错报文“需要分片但DF置位”(Type 3, Code 4),其中携带该路由器的下一跳MTU值。发送方收到后,将发送尺寸调小到该MTU,重新发送。如此反复,直到数据报顺利到达目的端,此时使用的尺寸即为路径MTU。
IPv6中不再由路由器分片,PMTUD是强制机制,路由器回送ICMPv6“Packet Too Big”报文,携带MTU值。若ICMP被防火墙过滤,PMTUD会失效,形成“黑洞”,表现为小包正常、大包超时。
五、用ping和traceroute观察分片行为
Linux下ping支持设置数据报大小和DF位,是观察分片最直接的工具。以下命令发送一个总长2000字节的ICMP请求(-s指定ICMP数据部分大小,实际IP总长还要加8字节ICMP首部与20字节IP首部):
# 发送1472字节ICMP数据,加28字节首部=1500,正好等于以太网MTU
ping -c 3 -s 1472 192.168.1.1
# 发送1473字节,超过MTU,若链路不允许分片则失败
ping -c 3 -s 1473 192.168.1.1
# 设置DF位,禁止分片,用于探测路径MTU
ping -c 3 -M do -s 1472 8.8.8.8
# 逐步增大尺寸,找到不分片可通过的最大值
ping -c 1 -M do -s 1400 8.8.8.8
ping -c 1 -M do -s 1450 8.8.8.8
ping -c 1 -M do -s 1472 8.8.8.8
当使用-M do且尺寸超过路径MTU时,典型输出为:
PING 8.8.8.8 (8.8.8.8) 1473(1501) bytes of data.
ping: local error: Message too long, mtu=1500
若中间路由器回送ICMP,则可能显示Frag needed and DF set (mtu = 1400),提示路径上存在更小MTU的链路。通过二分法调整-s值,可快速定位路径MTU。
traceroute则用于逐跳观察路径。Linux下使用-F设置DF位,-s指定探测包大小:
# 逐跳探测,禁止分片,观察哪一跳开始出现异常
traceroute -F -s 1472 8.8.8.8
# 若某跳返回 !F 或超时,说明该跳MTU较小或ICMP被过滤
traceroute -n -F -s 1472 8.8.8.8
输出中若某跳显示!F,表示需要分片但DF置位;若持续* * *,可能是ICMP被过滤导致PMTUD黑洞。此时可结合ping -M do逐段测试,或临时关闭DF位验证。
ping -M do二分法定位路径MTU。
六、关键参数速查
| 字段/参数 | 长度 | 作用 | 典型值 |
|---|---|---|---|
| 标识 Identification | 16位 | 同一数据报分片归组 | 递增或随机 |
| DF | 1位 | 禁止分片 | PMTUD时置1 |
| MF | 1位 | 还有后续分片 | 末片为0 |
| 片偏移 | 13位 | 数据起始位置(8字节单位) | 0,185,370… |
| 以太网MTU | — | 链路层最大数据字段 | 1500字节 |
| 重组超时 | — | 等待所有分片的时间 | 30~60秒 |
掌握分片字段的含义与计算、理解PMTUD的交互过程、并能用ping和traceroute验证路径MTU,是网络排障中定位“大包不通”类问题的基本功。实际工作中,应优先通过调整应用报文大小或启用PMTUD来避免分片,而非依赖中间设备重组。