分片、MTU 与路径发现
MTU:网络层的"限高杆"
MTU(Maximum Transmission Unit,最大传输单元)是链路层能承载的最大数据帧长度。常见值:
| 链路类型 | MTU | 备注 |
|---|---|---|
| 以太网 | 1500 字节 | 最常用 |
| PPPoE(宽带拨号) | 1492 字节 | 1500 - 8 字节 PPPoE 头 |
| IEEE 802.11(Wi-Fi) | 2304 字节 | 但通常限制在 1500 |
| Jumbo Frame | 9000 字节 | 数据中心内部 |
| 令牌环(已淘汰) | 4464 字节 | 历史遗留 |
注意:MTU 是链路层概念,但直接影响 IP 层。如果 IP 数据报超过 MTU,必须分片或丢弃。
MSS:TCP 的"自我约束"
MSS(Maximum Segment Size)是 TCP 选项,在三次握手时协商:
MSS = MTU - IP 首部(20) - TCP 首部(20) = 1500 - 40 = 1460 字节
如果一方声明 MSS=1460,另一方声明 MSS=1400(因为中间有 PPPoE),双方取较小值 1400。
关键意义:TCP 尽量让每个段刚好装在一个 IP 包里,避免 IP 层分片。因为分片有开销、有丢包风险、有重组延迟。
生活例子:快递公司规定箱子最大 1500mm。你发货时把货物切成 1460mm 的块,留 40mm 给包装纸(IP+TCP 头)。如果知道对方只能收 1400mm 的箱子,你就切 1400mm。
IPv4 分片机制
当 IP 包超过出接口 MTU 时,路由器可以分片:
分片三件套
- Identification:所有分片相同,表示"我们是同一个包"
- Flags:DF(Don't Fragment)、MF(More Fragments)
- Fragment Offset:该分片在原始包中的位置,单位 8 字节
分片计算示例
原始包:Total Length = 3000 字节,首部 20 字节,数据 2980 字节 MTU = 1500,每个分片最多 1480 字节数据(1500 - 20 首部)
| 分片 | MF | DF | Offset | 数据范围 | 长度 |
|---|---|---|---|---|---|
| 第 1 片 | 1 | 0 | 0 | 0~1479 | 1500 |
| 第 2 片 | 1 | 0 | 185 | 1480~2959 | 1500 |
| 第 3 片 | 0 | 0 | 370 | 2960~2979 | 40 |
Offset 计算:
- 第 1 片:0 / 8 = 0
- 第 2 片:1480 / 8 = 185
- 第 3 片:2960 / 8 = 370
目的主机重组:收到所有分片后,按 Offset 排序,拼接数据,交给上层。
分片的关键是 Identification 相同(表示同属一个包)、MF 标志(More Fragments,1 表示后面还有)、Fragment Offset(排序依据)。任何一个分片丢失,目的主机都无法重组,TCP 必须重传整个原始包。
分片的风险
- 任何一片丢失,整个包报废:TCP 必须重传整个原始包
- 重组开销:目的主机需要缓存、排序、拼接
- 防火墙绕过:攻击者利用分片重叠或极小分片绕过 ACL/IDS 检测
- 不同路径分片:如果路径 MTU 变化,后续分片可能在不同路由器分片,导致重组复杂
IPv6 分片机制
IPv6 的设计哲学:中间路由器绝对不分片。
- 源主机在发送前就知道路径 MTU,按 MTU 大小发包
- 如果包超过 MTU,中间路由器丢弃,返回 ICMPv6 "Packet Too Big"
- 源主机收到后,自己分片(通过 Fragment 扩展首部)
为什么这样设计?
- 路由器转发速度是核心指标,分片消耗 CPU
- IPv6 要求所有链路至少支持 1280 字节,源主机至少能发 1280 字节的包
Path MTU Discovery(路径 MTU 发现)
经典 PMTUD(RFC 1191 / 1981)
原理:发送方故意设 DF=1,探测路径最小 MTU。
主机 A 发送 1500 字节包,DF=1
↓
路由器 R 发现下一跳 MTU=1400,但 DF=1 不能分片
↓
R 丢弃包,返回 ICMP "需要分片但 DF 置位"(Type 3, Code 4)
↓
A 收到 ICMP,得知路径 MTU < 1500
↓
A 尝试 1400 字节,DF=1
↓
如果还收到 ICMP,继续降;如果成功,1400 就是路径 MTU
PMTUD 的黑洞问题
很多防火墙为了安全,丢弃所有 ICMP(包括"需要分片")。结果是:
- 发送方发大包,被中间路由器丢弃
- 发送方永远收不到 ICMP,不知道要降 MTU
- TCP 连接建立后,大包一直丢,小 ACK 能通,连接"半死不活"
生活例子:你寄一个大箱子,快递员说"太大了,换小箱子",但这条消息被门卫截获了。你一直没收到通知,以为箱子在路上,其实早被扔了。
PLPMTUD(Packetization Layer PMTUD,RFC 4821)
不依赖 ICMP,由 TCP 层主动探测:
- TCP 发送不同大小的探测包(带 DF=1)
- 如果某个大小丢包,就降一级再试
- 通过"是否收到 ACK"推断 MTU,而不是等 ICMP
现代操作系统(Windows Vista+、Linux 2.6.30+)默认启用 PLPMTUD。
经典 PMTUD 的致命弱点是依赖 ICMP 返回。一旦中间防火墙丢弃 ICMP(很多企业网/云环境默认如此),发送方永远不知道要降 MTU,TCP 连接表现为"能建立但传大数据就卡住"。PLPMTUD 通过"是否收到 ACK"推断 MTU,彻底摆脱对 ICMP 的依赖。
本篇小结
- MTU 是链路层最大帧长,以太网 1500 字节
- MSS = MTU - 40(IP+TCP 头),TCP 握手协商,避免 IP 分片
- IPv4 路由器可分片,目的主机重组;IPv6 仅源主机分片
- PMTUD 通过 DF=1 + ICMP 探测路径最小 MTU
- ICMP 黑洞导致 PMTUD 失效,PLPMTUD 通过丢包推断解决
- 分片有风险:丢一片全报废、防火墙绕过、重组开销
动手实践
观察本机 MTU:
# Linux ip link show eth0 # Windows netsh interface ipv4 show subinterfaces强制分片测试:
# Windows:ping -f -l 3000 8.8.8.8(DF=1,包大小 3000) # Linux:ping -M do -s 3000 8.8.8.8观察是否收到 "Fragmentation needed" 或请求超时
Wireshark 抓分片包,观察 Identification、MF、Fragment Offset 字段
思考:为什么 IPv6 要求所有链路至少支持 1280 字节?如果某链路只支持 576 字节(如早期拨号),IPv6 怎么办?