计算机网络 08:IPv6 改了什么,地址、NDP、SLAAC、链路本地与双栈
目的地址变成 IPv6 后,主机还会广播 ARP 查询网关吗?一个接口已经有 fe80:: 开头的地址,是否说明它拿到了可跨网络通信的地址?收到路由器通告,又是否等于同时获得地址、直连前缀和默认路由?
这些问题不能仅用“地址从三十二位增加到一百二十八位”回答。第 07 篇建立了前缀与下一跳的关系,本篇保持这一关系,观察 IPv6 如何表达地址范围、发现邻居和取得配置。真实实验使用静态地址,SLAAC 与路由器通告另以规范和条件表解释。
地址文本与作用范围
IPv6 地址通常写成八组十六位十六进制数。连续的零可以按规则压缩,但压缩只改变文本表示,不改变地址本身。例如 2001:db8:8:1::2 与展开后的八组写法是同一个一百二十八位值;前缀长度仍表示固定多少位。
本篇的 2001:db8:8:1::/64 与 2001:db8:8:2::/64 是两个不同的文档示例地址块,来自 RFC 3849 保留的范围,仅用于隔离实验。不会把它们配置到公网,也不因名字中出现 IPv6 就声称它们可被全球路由。
RFC 4291 §2.5.6定义链路本地地址范围 fe80::/10。这类地址用于所在链路的通信,路由器不得将源或目的为链路本地地址的报文转发到另一条链路。它可以作为相邻路由器的下一跳地址,却不是跨越多个网络的最终服务地址。
同一台主机有多个接口时,不同链路上都可能存在 fe80::1。fe80::1%eth0 中的 %eth0 为本地操作提供作用域选择,不属于在线 IPv6 地址的一百二十八位。RFC 4007 §11讨论这种 zone 标识;把带百分号的文本直接当成报文字段会混淆两种表示。
邻居发现不是 ARP 换一种地址
IPv6 通过邻居发现协议 NDP 完成地址解析等工作。Neighbor Solicitation(NS)和 Neighbor Advertisement(NA)是 ICMPv6 消息,类型分别为 135、136;NDP 还包括路由器发现和邻居可达性检测,范围比单纯取得 MAC 更广。
在冷邻居解析的典型以太网场景中,发送者将 NS 发往由目标地址导出的 solicited-node multicast 地址。这个目标是当前下一跳,而不一定是数据报的最终目的地。第 06 篇中“先选路由,再解析下一跳”的关系仍然成立,变化的是链路上的协议交换。
RFC 4861 §7.2.2说明解析时的请求过程。后续可达性探测可以采用单播,因此不能把每个 NS 都描述成多播,也不能因为看到 NS 就认定它一定来自空邻居缓存。
假设默认下一跳为 fe80::1,其低二十四位是 00:00:01,相应 solicited-node multicast 为 ff02::1:ff00:1。在以太网上,RFC 2464 §7规定 IPv6 多播映射到 33:33 加目的 IPv6 地址的低三十二位;这个例子对应 33:33:ff:00:00:01。
1 | |
以上先给出字段推导。它不能替代抓包,尤其不能证明某个目标已回应;真实观察还需对照 NA、邻居表和后续应用回包。
通告中的三个独立判断
Router Advertisement(RA)可以携带默认路由器信息与 Prefix Information Option(PIO)。收到同一份消息,并不意味着里面的所有信息只有一个开关。
RA 的 Router Lifetime 表示发送者作为默认路由器的有效期。PIO 的 L 位影响 on-link 前缀信息,A 位影响无状态地址自动配置 SLAAC。前缀自身还有相关有效期;不能把 Router Lifetime 当成整份通告里所有状态的共同到期时间。
RFC 4861 §4.2、§4.6.2分别定义这些字段。特别是 L=0 并不宣布该前缀一定在链路之外,它只是没有通过这一位作出 on-link 声明。其他已有信息仍可能影响判断。
设接口没有此前的相关状态,收到语法与来源检查均合格的 RA,PIO 前缀及寿命也满足处理条件,可以按下表分别推演:
| 输入条件 | 从这些字段能推导的变化 | 不能直接推出的结论 |
|---|---|---|
| Router Lifetime 为正 | 发送者可进入默认路由器选择相关状态 | PIO 必然用于生成地址 |
| Router Lifetime 为零 | 不将发送者作为有效默认路由器保留 | PIO 的信息也全部失效 |
| L=1、A=0 | 可以提供 on-link 信息,不依此生成 SLAAC 地址 | 接口已取得该前缀下的自动地址 |
| L=0、A=1 | 可参与 SLAAC 条件检查,没有据此声明 on-link | 该前缀必然 off-link |
| L=1、A=1 | 分别处理 on-link 与地址配置 | 地址立刻可用、外部网络已经可达 |
这是一张协议条件表,不是本次运行的 RA 实验结果。它刻意省略已有状态更新、寿命防护及多路由器选择,使用前提不能从表中删除。
SLAAC 与重复地址检测
SLAAC 不能简化为“收到任意前缀就拼上 MAC”。RFC 4862 §5.5.3要求检查 A 位、前缀与接口标识长度、寿命等条件。地址形成方式与接口标识策略也有关,不应把所有 IPv6 地址都写成由 MAC 直接推导。
一个地址被加入接口配置后,还可能处于 tentative 状态。重复地址检测 DAD 用于检查拟使用地址是否出现冲突;它与正常通信时解析下一跳并非同一个目的。看到 NS 时,应结合源地址、Target 和地址状态判断用途,不能把 DAD 请求统计成应用流量触发的解析。
RFC 4862 §5.4的 DAD 也涉及手动配置的单播地址,并存在禁用等例外。因此“只有自动配置才需要 DAD”和“任意系统永远会执行 DAD”都过于绝对。检测没有发现冲突,也不保证此后永远不会发生地址冲突。
本篇脚本等待实验地址不再 tentative,再开始应用发送。它不通过关闭 DAD 来回避等待,但也没有注入重复地址;等待通过只说明这次启动条件满足,不能宣称已测试冲突处理分支。
隔离的 IPv6 转发路径
沿用第 06 篇的三节点形状,为两条链路配置不同的 IPv6 前缀。A、B 的默认路由使用各自链路上 R 的链路本地地址。即使两个出口旁都写 fe80::1,它们也位于不同链路。
1 | |
附件 lab.py 在专用 Linux 虚拟机内创建自己的网络命名空间和 veth,转发设置只作用于 R 的命名空间。没有修改宿主默认路由,也不接入物理接口或生产网络。复跑需要该环境已有 Python 与 iproute2,以及相应网络管理权限。
1 | |
程序保存路由、邻居表和原始帧,以自建 UDP 服务回显作为本次应用往返检查。抓包解码只面向脚本产生的已知报文格式,不是能处理任意扩展首部的通用 IPv6 分析器。
本次捕获与邻居表
完整记录见 run.jsonl。本次运行环境为 Linux 6.18.10-200.fc43.aarch64、Python 3.14.3、iproute2 6.14.0。脚本手动设置链路本地地址,并用 addrgenmode none 避免再生成额外地址;这与关闭 DAD 不同。
初始表并不是整个空数组:其中已有 ff02::16、ff02::2、ff02::1:ff00:2 三个多播映射,状态为 NOARP。查询路由后的表相同。这些条目没有证明网关已经解析;本实验应检查的条件是没有单播邻居,尤其没有 fe80::1 的已解析映射。
NOARP 是 Linux 邻居状态名称,不能因名字中有 ARP 就说这次 IPv6 使用了 ARP 协议。过滤多播只是为了验证冷单播解析的前提,原始日志仍保留全部条目,避免隐藏观察范围。
| 本次观察 | 结果 |
|---|---|
| 路由查询 | 下一跳 fe80::1,出口 eth0 |
| NS | Target 为 fe80::1,IPv6 目的为 ff02::1:ff00:1 |
| NS 的 Ethernet 目的 | 33:33:ff:00:00:01 |
| 发出的 UDP 数据帧 | IPv6 目的为 2001:db8:8:2::2,MAC 目的为 R 的 02:00:00:00:08:01 |
| 应用与最终单播邻居 | 20 字节回显相等;唯一单播邻居为 fe80::1,状态 REACHABLE |
原始记录共四帧:NS、NA、UDP 请求与回显。NA 的 Target 同样为 fe80::1。包字段、邻居变化与回显载荷相互对应,说明的是这一次隔离转发过程;单独一个 NA 不能证明远端应用完成了处理。
脚本结束时停止自己创建的子进程,删除本次三个命名空间并核对清理结果。没有执行全局清理,也没有改写其他命名空间。
双栈与分片职责
双栈表示环境可以分别运行 IPv4 与 IPv6,不表示一个族成功就能证明另一个族成功。地址来源、路由、邻居发现及返回路径应分开检查。DNS 记录和应用选择哪个地址,又是后续章节的问题;本篇命令直接指定实验地址。
第 06 篇的 IPv4 实验使用 ARP 解析网关,本篇 IPv6 实验使用 NDP。两者都区分最终目的地与当前下一跳。IPv4 广播 ARP 与 NS 使用不同报文格式,一个地址族的邻居项也不能说明另一个地址族的状态。
IPv6 还有明确的分片职责区别:RFC 8200 §4.5、§5规定分片由源节点完成,中途路由器不执行分片。这不等于路径永远不会遇到 MTU 问题。本篇只发送小载荷,没有触发 Packet Too Big,也没有验证 PMTUD;相关故障实验留到第 09 篇。
验证边界
实验地址与默认路由均为静态设置,没有发送或处理 RA,没有运行 SLAAC 地址生成实验。NDP 解析成功不能补足这些缺口,协议条件表也不是抓包输出。
应用回显仅证明该隔离拓扑的一次 IPv6 往返。它不能代表公网、物理设备、生产服务或吞吐性能;也没有覆盖真实双栈地址选择、扩展首部组合、重复地址冲突和过大报文处理。内核与工具版本随原始记录保存,不能把本次行为不加条件地推广到其他实现。
练习
第一题:下一跳变成 fe80::1234:5678,计算 solicited-node multicast 和对应以太网多播 MAC。NS 的 Target 是否也要变成这个多播地址?
校验:低二十四位为 34:56:78,对应 ff02::1:ff34:5678 与 33:33:ff:34:56:78;Target 仍是待解析的单播地址 fe80::1234:5678。请求发给谁和请求询问哪个地址是不同字段。
第二题:收到 Router Lifetime=0、PIO L=1、A=0、前缀寿命为正的有效 RA,能否概括成“这份 RA 没有任何作用”?若接口此前没有该前缀的自动地址,这份 PIO 是否要求创建一个?
校验:不能忽略全部信息;默认路由器与 on-link 前缀分别处理。A=0 不据此生成 SLAAC 地址。答案依赖题设中的有效性与初始状态,不包括其他配置来源已经安装地址的情况。
参考资料
- RFC 4291:地址与链路本地范围;RFC 4007 §11:作用域标识。
- RFC 4861:NS、NA、RA、PIO 和邻居发现。
- RFC 4862:DAD 与 SLAAC 条件;RFC 2464 §7:以太网多播映射。
- RFC 8200:IPv6 首部与分片职责。
- Linux IP sysctl 文档:转发与 RA 接收配置;实际实验只改自建命名空间。





