IPv6 邻居发现的目的是在连接的子网上获取一个已知的 IPv6 地址并获得相应的 MAC 地址。这使您能够将 L3 IPv6 数据包封装在 L2 帧中并在 L2 介质上发送。
这与 IPv4 ARP 非常相似,您可以在连接的子网上获取已知的 IPv4 地址,并获取相应的 MAC 地址。
诀窍是,您需要通过发送 L2 帧来获取您的信息。您需要具有该帧的有效 L2 目标地址。但是您还没有该帧的 L2 单播目的地。
让我们看一下 IPv4 ARP 示例:
这是来自 192.168.0.10 的 192.168.0.19 的 IPv4 ARP:
For reference:
192.168.0.10 = c0a8 000a
192.168.0.19 = c0a8 0013
My ethernet = 685b 3589 0a04
[iMac:~] droot% sudo tcpdump arp -x
tcpdump: data link type PKTAP
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on pktap, link-type PKTAP (Apple DLT_PKTAP), capture size 262144 bytes
18:58:01.528290 ARP, Request who-has 192.168.0.19 tell 192.168.0.10, length 28
0x0000: ffff ffff ffff 685b 3589 0a04 0806 0001
0x0010: 0800 0604 0001 685b 3589 0a04 c0a8 000a
0x0020: 0000 0000 0000 c0a8 0013
在上面的例子中,为了保证帧会被目的地解码,目的地有一个未知的 L2 MAC 地址,ARP 将 L2 以太网帧发送到目的地 ff.ff.ff.ff.ff.ff,以太网广播地址。
这种 1982 年设计的 ARP 协议 (RFC 826) 效率低下。每个 ARP 请求都会导致子网上每个主机的中断。
IPv6 的设计者希望做得更好。 他们只希望预期目的地“侦听”邻居发现数据包。 他们很乐意将以太网帧的目的地设为 IPv6 目的地。
问题一:目的MAC地址为48位。IPv6 地址是 128 位。
这是来自 fe80::c0e:acdb:30a3:482c 的链路本地地址 fe80::aaaa:aaaa:aaaa:aaaa 的 IPv6 邻居发现/请求请求
[iMac:~] droot% sudo tcpdump -x -v icmp6 && ip6 == 135
Password:
tcpdump: data link type PKTAP
tcpdump: listening on pktap, link-type PKTAP (Apple DLT_PKTAP), capture size 262144 bytes
19:33:58.748554 IP6 (hlim 255, next-header ICMPv6 (58) payload length: 32) imac.local > ff02::1:ffaa:aaaa: [icmp6 sum ok] ICMP6, neighbor solicitation, length 32, who has fe80::aaaa:aaaa:aaaa:aaaa
source link-address option (1), length 8 (1): 68:5b:35:89:0a:04
0x0000: 3333 ffaa aaaa 685b 3589 0a04 86dd 6000
0x0010: 0000 0020 3aff fe80 0000 0000 0000 0c0e
0x0020: acdb 30a3 482c ff02 0000 0000 0000 0000
0x0030: 0001 ffaa aaaa 8700 4cfc 0000 0000 fe80
0x0040: 0000 0000 0000 aaaa aaaa aaaa aaaa 0101
0x0050: 685b 3589 0a04
第一个改进是,这实际上是一个发往 IPv6 多播地址 ff02::1:ffaa:aaaa 的 ICMPv6 数据包。我们不需要为每个第 2 层技术定义一个单独的类似 ARP 的协议。我们使用 L3-multicast 并定义一个 L3-multicast to L2-multicast 协议。IPv6 邻居发现使用 ICMPv6 消息类型 135。
我们不能使用 IPv6 单播目的地作为邻居发现目的地,因为我们不知道如何将 128 位 IPv6 单播地址编码为 48 位以太网单播地址。
但是我们可以定义一个规则来从我们的 L3 单播目的地选择一个特定的 L3 多播目的地。来自 RFC4291 https://www.rfc-editor.org/rfc/rfc4291#section-2.7.1
Solicited-Node multicast address are computed as a function of a
node's unicast and anycast addresses. A Solicited-Node multicast
address is formed by taking the low-order 24 bits of an address
(unicast or anycast) and appending those bits to the prefix
FF02:0:0:0:0:1:FF00::/104 resulting in a multicast address in the
range
FF02:0:0:0:0:1:FF00:0000
to
FF02:0:0:0:0:1:FFFF:FFFF
所以我们对 fe80::aaaa:aaaa:aaaa:aaaa 的 IPv6 单播请求变成了对 ff02::1:ffaa:aaaa 的 ICMPv6 邻居请求请求,ff02::1:ffaa:aaaa 是一个 IPv6 多播地址。
对于 IPv6 L3 多播地址,我们有一个规则:它们可以在 L2 目的地为 33:33:xx:xx:xx:xx 的线路上传输,其中 L3 地址的最后 32 位被转换为 L3 地址的最后 32 位。 L2地址!
这导致此目标 mac 地址:
fe80::aaaa:aaaa:aaaa:aaaa -> ff02::1:ffaa:aaaa -> 3333.ffaa.aaaa
什么是关于这个凉爽的是,这并不会导致对在网络上的每台主机中断。仅侦听 3333.ffaa.aaaa 的 L2 多播的主机。假设最后 24 位是“随机”(不是真的),那将是 1600 万 (2^24) 个主机中的 1 个。
有一些复杂性,但这只是因为我们有一些规则:
- 将 IPv6 L3 单播地址转换为用于邻居请求的 IPv6 L3 组播地址的规则
- 将 IPv6 L3 组播地址转换为 L2 组播地址进行传输的规则。
我们不能这样做:
fe80::aaaa:aaaa:aaaa:aaaa (IPv6 unicast)
-> fe80::aaaa:aaaa:aaaa:aaaa (ICMPv6 multicast?)
-> ??
我们只是不知道如何将 IPv6 单播地址转换为 L2 地址。由于我们要从 128 位变为 48 位,因此在某些时候我们必须使用多->一映射。在 IPv4 ARP 中,我们使用以太网广播(all->one)。在 IPv6 中,我们使用 L3 多播/L2 多播(多->一)。