NDP 协议详解
IPv6 不再需要 ARP
ARP 是 IPv4 的专利。IPv6 地址 128 位,以太网帧的 ARP 字段放不下这么大的地址。更重要的是,IPv6 的设计者借机会把 ARP 的功能重新设计得更完善——这就是 NDP(Neighbor Discovery Protocol,邻居发现协议,RFC 4861)。
NDP 不是独立的协议,它运行在 ICMPv6 之上,用 5 种 ICMPv6 报文完成 6 个功能。
NDP 的五大报文类型
| ICMPv6 类型 | 名称 | 作用 | 类比 |
|---|---|---|---|
| 135 | 邻居请求(Neighbor Solicitation, NS) | "你的 MAC 是多少?" | ARP 请求 |
| 136 | 邻居通告(Neighbor Advertisement, NA) | "我的 MAC 是 xx:xx" | ARP 响应 |
| 133 | 路由器请求(Router Solicitation, RS) | "有路由器吗?" | DHCP Discover |
| 134 | 路由器通告(Router Advertisement, RA) | "我是路由器,前缀是 2001:db8::/64" | DHCP Offer |
| 137 | 重定向(Redirect) | "你走错路了,下次找他" | ICMP Redirect |
地址解析:NS/NA 替代 ARP
场景:主机 A 想 ping 主机 B(IPv6)
第一步:查邻居缓存表
# Linux
$ ip neigh
2001:db8::20 dev eth0 lladdr 00:1b:2c:3d:4e:5f REACHABLE
fe80::21a:2bff:fe3c:4d5e dev eth0 lladdr 00:1a:2b:3c:4d:5e STALE
第二步:发送邻居请求(NS)
主机 A 不知道 B 的 MAC,发送 NS:
源 IP: 2001:db8::10(主机 A)
目的 IP: ff02::1:ff00:20(请求节点组播地址)
目标地址: 2001:db8::20(被查询的 IP)
源 MAC: 00:aa:bb:cc:dd:ee
请求节点组播地址怎么来的?取目标 IPv6 地址的后 24 位,拼在 ff02::1:ff 后面:
- 目标
2001:db8::20→ 后 24 位是00:0020→ 组播地址ff02::1:ff00:20
对应的以太网组播 MAC 是 33:33:ff:00:00:20。只有 MAC 后 32 位匹配 ff:00:00:20 的网卡才会接收,避免了 IPv4 ARP 广播打扰整个子网的所有设备。
第三步:邻居通告(NA)
主机 B 收到 NS 后,单播回复 NA:
源 IP: 2001:db8::20
目的 IP: 2001:db8::10
目标地址: 2001:db8::20
MAC: 00:1b:2c:3d:4e:5f
标志 R=0, S=1, O=1
- S(Solicited)=1:表示这是对 NS 的响应,不是主动通告
- O(Override)=1:表示覆盖缓存中旧的映射
无状态自动配置(SLAAC)
IPv6 主机可以不依赖 DHCP,自己生成全球单播地址。流程如下:
第一步:生成 Link-Local 地址
主机开机自动生成 FE80::/64 地址(基于 EUI-64 或随机接口 ID)。
第二步:发送路由器请求(RS)
源 IP: ::(还没全球地址,用未指定地址)
目的 IP: ff02::2(所有路由器组播)
第三步:接收路由器通告(RA)
路由器回复 RA,包含关键信息:
- 前缀(Prefix):如
2001:db8:1:2::/64 - 前缀长度:/64
- M 位(Managed):=1 表示用 DHCPv6 有状态分配
- O 位(Other):=1 表示用 DHCPv6 无状态分配 DNS 等选项
- 默认路由器:发送 RA 的路由器 Link-Local 地址
- Lifetime:前缀有效期
第四步:组合生成全球地址
主机把 RA 中的前缀 2001:db8:1:2::/64 + 自己的接口标识符(后 64 位),拼成完整地址:
2001:db8:1:2::/64 + ::21a:2bff:fe3c:4d5e
= 2001:db8:1:2:21a:2bff:fe3c:4d5e
生活例子:路由器是"小区物业",广播"本小区门牌号前缀是幸福里 3 号楼";主机是住户,自己生成"3 号楼 2 单元 301",组合成完整地址。
SLAAC 的核心优势是零配置:主机开机后自动生成 Link-Local,通过 RS/RA 获取前缀,组合成全球地址,全程不需要 DHCP 服务器。
重复地址检测(DAD)
主机生成地址后,不敢直接用,先发 NS 问自己:
源 IP: ::(未指定地址,表示"我还没用这个地址")
目的 IP: ff02::1:ffxx:xxxx(请求节点组播,基于待检测地址)
目标地址: 2001:db8:1:2:21a:2bff:fe3c:4d5e
如果没人回复 NA,说明地址唯一,可以放心使用。如果有人回复,说明地址冲突,主机停止配置该地址。
DAD 只检测 Link-Local 和自动配置的地址,DHCPv6 分配的地址由服务器保证唯一性。
路由器发现与默认网关
IPv4 中,默认网关要么手工配置,要么通过 DHCP 获取。IPv6 中,主机通过 RA 自动发现默认路由器:
- 路由器周期性发送 RA(默认 200 秒一次)
- 主机开机时主动发 RS 催促路由器回复 RA
- 主机可以同时收到多个路由器的 RA,根据优先级和度量选择默认网关
RA 中的重定向:如果主机 A 把包发给路由器 R1,但 R1 发现 R2 更近,R1 发 Redirect 告诉 A:"下次直接发给 R2"。
NDP 与 ARP 的对比
| 特性 | ARP(IPv4) | NDP(IPv6) |
|---|---|---|
| 协议层次 | 独立协议 | 基于 ICMPv6 |
| 地址解析 | 广播(打扰所有设备) | 组播(只打扰目标设备) |
| 自动配置 | 依赖 DHCP | SLAAC 不依赖 DHCP |
| 重复检测 | 无(靠 DHCP 或人工) | DAD 内置 |
| 路由器发现 | 依赖 DHCP 或手工 | RA 自动发现 |
| 链路层独立性 | 与以太网强耦合 | 独立于链路层 |
上图对比了 NDP 的两大核心功能:地址解析(NS/NA) 和 路由器发现(RS/RA)。与 ARP 的广播不同,NDP 使用请求节点组播,大幅减少了无关设备的打扰。
本篇小结
- NDP 替代 ARP,运行在 ICMPv6 之上
- NS/NA 完成地址解析,使用请求节点组播避免广播风暴
- RS/RA 实现无状态自动配置(SLAAC)和路由器发现
- DAD 在地址使用前检测冲突
- NDP 比 ARP 更高效、更安全、功能更完善
动手实践
查看 IPv6 邻居缓存:
$ ip -6 neighWireshark 过滤
icmpv6.type == 135 || icmpv6.type == 136,观察 NS/NA 交互在 Linux 上触发 RS:
$ sudo rdisc6 eth0观察 RA 内容:前缀、M 位、O 位、默认路由器 Lifetime
对比 IPv4 ARP 广播和 IPv6 NDP 组播的 Wireshark 抓包,观察目的 MAC 和目的 IP 差异