📚 第四章 IP协议
📄 一句话概括

IPv6 只有源端能分片,中间路由器遇到太大的包只能回「包太大」。 所以每条链路的 MTU 必须至少 1280,否则 IPv6 根本跑不起来。

📝 本节和前面几节的关系

4.5.2 讲了 IPv4 的分片,这一节讲 IPv6 改成了什么样。 4.5.3 的 PMTUD 在 IPv6 里是「必需」而不是「优化」,原因就在这里。

一、只有源端能分

IPv6 把「中间路由器分片」这件事彻底删了。 要分片,只有发送方自己分;中间设备过不去,只能回一个 ICMPv6「包太大」。

项 IPv4 IPv6
源端分片 可以 可以
路由器分片 可以 禁止
过不去时 切了继续发 回 ICMPv6 报错

第三行是这次改动的全部意义:从「偷偷切」变成「明确报错」。 报错意味着源端一定会知道,而「偷偷切」意味着源端永远不知道自己的包被切成了几片。 这个改动让路由器的工作变简单了: 不用维护重组缓冲区,不用等片到齐,转发路径上少了一整类状态。 代价是「源端必须自己搞清楚路径 MTU」——所以 PMTUD 从「优化」变成了「必需」。

二、分片扩展首部

IPv6 的分片信息放在「分片扩展首部」里,长度固定 8 字节。 它只在需要分片时出现,不分片就没有。

字段 长度 作用
下一个首部 1 字节 指向下一层
保留 1 字节 填 0
片偏移 13 位 单位 8 字节
M 标志 1 位 后面还有没有
标识 32 位 同一组的编号

第五行那个 32 位标识比 IPv4 宽: IPv4 只有 16 位,包一多就容易重号;IPv6 给到 32 位,撞号概率大大降低。 这是「顺手改好」的一个细节。 「只在需要时出现」是扩展首部机制的好处:不分片的包完全不带这 8 字节, 而 IPv4 的「标识、标志、片偏移」是每个包都占着位置。

三、1280 这条底线

因为路由器不能分片,链路的 MTU 就必须「自己扛得住」。 规范把底线定在 1280 字节:任何一条能跑 IPv6 的链路,都必须能传 1280 字节的包。

链路 MTU 能不能跑 IPv6
以太网 1500 能
PPPoE 1492 能
某些隧道 可能低于 1280 要特殊处理

第三行是实际会遇到的问题:隧道封装会吃掉几十字节,套两层就可能低于 1280。 这时只能「在隧道入口就把包切到 1280 以下」, 或者干脆在隧道里再分片——后者很别扭,但有时没别的办法。 所以「1280」不是一个随便定的数:它要「比以太网小得多(容得下封装开销)」, 又要「比 576(IPv4 的老底线)大得多(效率不至于太差)」。

四、和 IPv4 的取舍对比

两种设计各有代价,值得摆在一起看:

维度 IPv4 的做法 IPv6 的做法
路由器负担 要能分片、要缓存 不碰分片
源端知情权 常常不知情 一定知情
实现复杂度 分布在所有设备 集中在两端
老设备兼容 天然兼容 要求链路达标

第二行是这次改动的核心价值:「谁发包谁负责」比「路上谁都能改」更符合工程直觉。 IPv4 的分片像是「邮局替你拆包裹」——拆了你还不知道,丢了也算你的。 第四行是 IPv6 的「隐性门槛」:它要求「链路必须达标」, 所以老网络升级 IPv6 时,第一件事往往是「量一遍 MTU」。 这也是 IPv6 部署比想象中慢的原因之一。

五、动手 Demo:IPv6 只在源端分片

先算「分片时每片能装多少」,再把 4000 字节切一遍。 注意 1448 和 1460 的差:分片要多带 8 字节的扩展首部。

mtu, data = 1500, 4000
per = (mtu - 40 - 8) // 8 * 8
off = 0
while data > 0:
    take = min(per, data)
    data -= take
    print(take, off, 1 if data else 0)
    off += take // 8

实跑输出:

--------------------------------------------------------------------
IPv6 只在源端分片
--------------------------------------------------------------------
**IPv6 把「中间路由器分片」这件事取消了**:
**要分,只有发送方自己分;中间设备遇到太大的包,只能回一个「包太大」。**
**这一刀砍掉了路由器最讨厌的一类工作。**

**先看一片能装多少数据**:
  链路 MTU     分片时每片数据     不分片时每片数据
  -------------------------------------------------
  1500         1448               1460
  1280         1232               1240
  9000         8952               8960

**第二列比第三列少 8**——**因为分片时要多带一个 8 字节的「分片扩展首部」。**

**拿 4000 字节数据在 MTU 1500 上切**:
  片号     数据长度     偏移(8 字节)     M 标志
  ------------------------------------------------
  1        1448         0                  1
  2        1448         181                1
  3        1104         362                0

**3 片拼出 4000 字节**,**每片都不超过 1500**(数据加 40 加 8)。

**「路由器不分片」带来一个硬约束**:**链路的 MTU 必须至少 1280**,
**否则 IPv6 根本跑不起来**——**这也是为什么所有 IPv6 的规范都把 1280 写成「最低要求」。**

**对比一下 IPv4**:
**IPv4 允许中间路由器分片,看着「更宽容」,实际更糟**——
**因为分片在中间发生,源端不知道,重传的代价全由接收方承担。**
**IPv6 把这件事推回源端,是「让该负责的人负责」。**

1448 这个数要会算:1500 减 40(IPv6 头)再减 8(分片首部)得 1452, 向下取到 8 的倍数就是 1448。 而 IPv4 那边是 1480——同样 1500 的链路,IPv6 每片少装 32 字节。 这个差不是小事:分片场景下 IPv6 要切更多片,掉一片全丢的风险更高。 所以「IPv6 更快」这句话要加限定:首部处理更快,但分片场景反而更费。 最后那张对比表里「实现复杂度」这一行最值得想:IPv4 把复杂度摊给了所有路由器, 所以「任何一台设备都要能分片」;IPv6 把它收回到两端,路由器可以做得更简单更快。 这是「端到端原则」的一次回归。

30 秒实验

  1. 算一下:MTU 1280 时,IPv6 分片每片最多装多少数据?
  2. 想一下:为什么 IPv6 的最低 MTU 定在 1280 而不是 1500?
  3. 想一下:隧道场景为什么容易出 MTU 问题?

常见误区

  1. 以为「IPv6 完全不能分片」。 源端可以分,只是路由器不许分。
  2. 以为「IPv6 的 MTU 底线和 IPv4 一样」。 不同,IPv4 是 68,IPv6 是 1280。
  3. 以为「IPv6 头短所以一定更快」。 分片场景反而更费,因为每片要多带 8 字节。
自测三问(点开对答案) 1. IPv6 谁可以分片? —— **只有源端可以**,中间路由器不许分片。 2. 路由器遇到过大的包怎么办? —— **回一个 ICMPv6「包太大」消息**,让源端自己调整。 3. IPv6 要求链路的最小 MTU 是多少? —— **1280 字节**,达不到就跑不了 IPv6。

下一节逐字段看 IPv4 首部:那 20 个字节里到底装了什么。

相关笔记