IPv6 FAQ
本来是想写一篇 IPv6 的文档,可以帮助不熟悉 IPv6 的同事快速了解 IPv6。但是后来发现越写越庞大,太过细节,重复了很多 IPv6 书籍中的内容,从而偏离了原本的目的。所以最终还是决定以 FAQ 的形式提供速查。
FAQs
Q: IPv6 的地址如何表示?
IPv6 的地址使用 128 bits 表示,每 16 bits 为一组,用冒号分隔,分成 8 组,如
|
|
为了便于输入和记忆,我们可以将之简化
- 去除每组前面的 0,上面的地址变为:
2001:db8:0:0:1:2:3:4
- 如果是连续的多组 0,那么可以直接简化为
::
,地址变为:2001:db8::1:2:3:4
Q: 为什么显示的 IPv6 地址后面有 /64
?
一个 IPv6 的地址,可以分为网络地址和主机地址 2 个部分。通过网络前缀可以指定网络地址所占用的位数。如果要表示一个 IPv6 的网络前缀(表示一个网段),可以用 ipv6地址/前缀长度
的格式。如:
|
|
表示地址的前 64 bits( 2001:db8::
)为网络地址,其余的( 1:2:3:4
)为主机地址。
Q: 为什么我的网卡上会显示多个 IPv6 地址?
一个接口可以拥有多个 IPv6 的地址,在 RFC4291 中定义了不同 IPv6 地址的用途和地址范围。
从类型上来说,IPv6 地址主要分为单播(unicast),多播(multicast), anycast(RFC1546)等几种。anycast 地址很少涉及,所以这里不予讨论。
单播(unicast)
单播地址可以细分为以下几种(这里用 IPv4 做对比,便于理解其用途)
Scope | RFC | Prefix | IPv4 equivalent |
---|---|---|---|
Link Local | 4291 | fe80::/10 | 169.254.0.0/16 |
Unique Local | 4193 | fc00::/8 fd00::/8 | 10.0.0.0/8, 172.16.0.0/12, |
192.168.0.0/16 | |||
Global | 4291 | 2000:/3 | class A,B,C except private |
Loopback | 4291 | ::1/128 | 127.0.0.1 |
Unspecified | 4291 | ::/128 | 0.0.0.0 |
-
链路本地地址(LLA - Link Local Address)
由前缀
fe80::/10
指定- 环回地址(
::1
)作为一个特殊的链路本地地址,相当于 IPv4 的127.0.0.1
- 链路本地地址只在接口所连接的本地网络链路上有效
- 环回地址(
-
唯一本地地址(ULA - Unique Local Address)
这就是我们通常所说的私网地址。只在本地局域网内有效,其跟 LLA 的区别在于 ULA 可以跨路由器,而 LLA 不可以。
唯一本地地址(ULA)由前缀
fc00::/7
指定。可以分为fc00::/8
和fd00::/8
两个 block 。RFC 4193 定义了 ULA 的地址格式如下
1 2 3 4 5 6
| 7 bits |1| 40 bits | 16 bits | 64 bits | +--------+-+------------+-----------+----------------------------+ | Prefix |L| Global ID | Subnet ID | Interface ID | +--------+-+------------+-----------+----------------------------+
其中
L
0 作为预留不适用,所以该值总是为 1。也就是说有效的前缀值目前只有fd00::/8
Global ID
这是一个 40 bits 的伪随机值,保证了每次生成的都是一个唯一值。之所以添加这个字段,是为了让 ULA 具备 GUA 的唯一性属性,从而在不同网络进行合并的时候可以避免地址前缀冲突的问题。
更多的信息可以参考以下几份资料
-
全球单播地址(GUA - Global Unicast Address)
这就是我们通常所说的公网地址。它的有效范围是全球网络。
多播(multicast)
在 IPv6 中没有广播地址,所以对于需要发送给多个接收端的封包,使用了多播地址(有的文献里也会翻译成组播)。
多播地址的地址范围定义则如下所示:
Type | RFC | Prefix | IPv4 equivalent |
---|---|---|---|
Multicast | 4291 | ff00::/8 | 224.0.0.0/4 |
常用的多播地址如下表所示
Address | Description |
---|---|
ff01::1 | All nodes in the interface-local |
ff02::1 | All nodes in the link-local |
ff01::2 | All routers in the interface-local |
ff02::2 | All routers in the link-local |
地址 ff01::1
的说明可能比较难理解,关键在于 interface-local
,指的是接口本地范围。
RFC4297 中对此的说明如下
|
|
简单来讲,ff01::1 只用于本地还回地址的多播。当 lo interface 起来的时候会加入该多播组。
Q: Linux 使用 ip 命令查看的时候可以看到每个 IPv6 地址后面都有 lifetime,这是什么意思 ?
对于每一个 IPv6 地址来说,有两个 Lifetime。这两个时间在接口获得 ip 的时候就开始倒计时。
-
preferred-lifetime 首选生存期。表示前缀可用的时间范围。在此范围内,前缀地址可以作为源地址或者目的地址自由使用。当生存期倒计时归零,则地址变的不可用(除了之前开始尚未结束的相关事务)
-
valid-lifetime 前缀在不可用之前保持可用的总时间。首选生存期到期后,任何自动配置的地址都将被弃用,并且仅用于在首选生存期到期之前开始的事务。如果有效生存期也过期,则地址将变得完全不可用。
因此,随着这两个 lifetime 的变化,IPv6 地址也会表现为不同的状态,如下图所示
|
|
这些地址状态在 RFC4862 中定义,简单描述则如下所示
- preferred 新连接使用的地址。当 preferred lifetime 过期则地址变成 deprecated
- deprecated 旧连接使用的地址。当 valid lifetime 过期则地址变成 invalid
- valid 有效地址。包括 preferred address 和 deprecated address
- invalid 无效地址
Q: 一个接口上的 IPv6 地址是如何产生的?
简单的说,链路本地地址(LLA)和环回地址由系统自身生成;而唯一本地地址(ULA)和全球单播地址(GUA)则是由服务器分配而来。这里说的服务器,提供的可以是路由器的 radvd 服务,也可以是 dhcpv6 服务。
Q: 链路本地地址(LLA)是如何生成的?
链路本地地址由本机独立产生,不依赖于外部环境。产生的过程大致如下:
- 获取网卡的 MAC 地址,如
00:e0:81:2e:b6:d1
- 反转第一字节的第二比特:
02:e0:81:2e:b6:d1
- 在中间插入 2 字节
FFFE
,扩展到 MAC 地址到 64 位 :02:e0:81:FF:FE:2e:b6:d1
- 加上 LLA 的前缀
fe80::/64
:fe80::2e0:81FF:FE2e:b6d1
前面三步从 48 位的 MAC 转换成 64 位主机地址的过程称为 EUI64。
需要注意的是,该地址产生之后,还需要使用 NDP(Network Discovery Protocol)协议,向本地链路发送 NS 包,执行 DAD 检测,检查是否该地址已经被占用。如果有人回复 NA,那就说明有冲突,需要人工校正。
什么是网络发现协议( NDP - Network Discovery Protocol)?
网络发现协议(NDP)定义在 RFC4861 中,主要包含了 2 部分的内容。
- NS, NA
- RS, RA
邻居发现协议
邻居发现协议主要用来查询 IPv6 地址对应的主机的 MAC 地址。使用到了两种类型的封包,即 NS 和 NA。
一个正常的 NS, NA 交互如图所示
完整的封包可以 在线查看 ,也可以从以下链接下载
要查看 NDP 的解析结果,Linux 下可以通过以下命令查看
|
|
输出为
|
|
该命令类似 ipv4 的 arp -a
路由发现协议
路由发现协议则是主机用来查找网络中的路由器(网关)。主要使用的封包类型为 RS 和 RA。
当路由启用了 radvd 服务,那么每次启动后就会向本地链路网络周期性的发送 RA 封包。主机也可以通过主动发送 RS 封包查询 RA。
通过路由发现协议,主机可以从 RA 包中获取到主机的 IPv6 地址前缀,默认路由的网关地址,以及 DNS 地址(RFC6106)。这样,正常上网所需要的要素就齐全了。
当主机接收到 RA 报文的时候,应该根据报文中的内容添加或者删除路由。具体的规则参考 RFC4191 。
如何添加路由主要看两个地方
- 一个是在 RA 报文的 Flags 中的
Default Router Preference
字段和Router lifetime
。当 Lifetime 不为 0 的时候,添加默认路由;为 0 时表示删除该路由。观察 ipv6-router-advertisement-leaving.pcapng 的 packet 1 和 packet 2。主机接收到第一个 RA 的时候就会添加路由,而接受到第二个的时候则会删除默认路由。 - 另一个是看 Route Information Option,这个里面也有 Preference 和 Lifetime。用法同上,但是优先级要高于前者。两者的区别在于前者只用来管理默认路由,而 Option 大多用来添加某特定前缀的路由
Q: ULA 和 GUA 是如何获取的?
IPv6 地址的获取方式可以分为 2 种。一个是 SLAAC,还有一个是 DHCPv6。
SLAAC 协议的具体描述在 RFC4862 中。利用 NDP 中定义的 RA 报文,给主机提供了前缀,路由,默认网关等信息,从而保证了在网络中没有其他服务器的情况,主机可以获取到 IPv6 地址并正常的访问外部网络。
需要注意的是 RFC4862 只考虑了 IP 地址的分配问题,没有考虑到 DNS 的部分。所以按照 RFC4862 实现的 SLAAC,主机是不能正常解析域名的。基于此,又出了一份 RFC6106,给 SLAAC 添加了 DNS 的支持。
DHCPv6 由 RFC8415 定义。实现类似 DHCPv4,就不展开说明了。
Q: SLAAC 是怎么工作的?
SLAAC 的过程大致分为两个阶段:
- 生成链路本地地址(LLA)
- 使用 Network Discovery 协议生成全球单播地址(GUA)
链路本地地址的生成过程在前面已经讲过,这里直接看主机怎么通过 NDP 获取 GUA 的。
对于支持 SLAAC 的网络,网关会定时发送 RA,提供 Prefix 给网络中的主机使用。而每台主机在适当的时机(比如网卡 UP)也会发送 RS 去主动查询。当主机获取到 RA 中包含的 Prefix 后,就可以通过 EUI64 的方式生成 GUA 了。
一个普通的 RA 包的内容如图所示
完整的封包可以 在线查看 ,也可以从以下链接下载
可以看到这个 RA 包发送给 link local 上的所有主机,提供了前缀
2401:4900:d00:ffff::/64
,默认网关自己(Lifetime > 0),DNS Server 两个
2401:4900::10
和 2401:4900::11
通过这两个阶段,主机就能自动的获取到 IPv6 的 GUA 地址和 Gateway 地址。但是要正常跟外界沟通解析 DNS,还需要 DNS Server 等信息。因此 RFC6106 又给 RA 包中增加了 RDNSS 选项。这样,只需要 SLAAC 就能提供主机和外界交流所需要的全部功能了。
Q: 什么是 DHCPv6 Stateless 和 DHCPv6 Stateful?
所谓的 Stateless 和 Stateful 是针对 IPv6 地址而言。有状态(Stateful)表示 IPv6 地址的管理(租赁和回收等)由特定的服务完成,这里的服务就是 DHCPv6 Server;而无状态(Stateless)顾名思义就是 IPv6 地址由主机自己生成,DHCPv6 不参与管理。
那么我们都知道 DHCPv6 服务就是用来分配 IP 地址的,搞这么个 Stateless 模式出来又不分配 IP 有什么用呢?这就要回到前面提到过的 SLAAC 模式了。SLAAC 在最初设计的时候并没有 DNS 相关的 option,所以也就无法给主机分配 DNS Server。为了弥补这个缺失,就使用了 DHCPv6 Stateless 来提供相关信息。此时 SLAAC 负责分配 IP,而 DHCPv6 负责提供其他的信息(通过 Information-Request)。
知道了工作机制,再来看一下对应的封包。
DHCPv6 Stateful 的封包如下
完整的封包可以 在线查看 ,也可以从以下链接下载
DHCPv6 Stateless 的封包如下:
完整的封包可以 在线查看 ,也可以从以下链接下载
ipv6_faq.file.dhcpv6_stateless_no_pd.pcap
这里需要注意的是,如果 DHCPv6 Stateless 需要获取 PD 的话,那么就不是发送 Information-Request 而是要发送 Solicit 的了。此时的封包应该如下所示:
完整的封包可以 在线查看 ,也可以从以下链接下载
ipv6_faq.file.dhcpv6_stateless_pd.pcap
Q: SLAAC 和 DHCPv6 是如何协同工作的?
IPv6 允许 SLAAC 和 DHCPv6 同时存在。两者可以并列运行也可以互相配合。
那么主机怎么知道应该通过哪种方式进行呢?这就要说到 RA 包中的 3 个关键 flag 了。
- Autonomous flag(简称 A flag):表示是否配置无状态 IP。在一个 RA 报文中,可存在多个 prefix,比如 2401::/64、2402::/64、2403::/64,每个 prefix 都可以独立配置 A flag
- 为 on 时(对应 bit 位为 1):表示客户端应当在该 prefix 范围内自动生成 IPv6 地址(客户端通过 DAD 自行保证地址可用),并配置子网路由条目、网关
- 为 off 时(对应 bit 位为 0):表示客户端不应当在该 prefix 范围内自动生成 IPv6 地址,但是可以配置子网路由条目、网关
- Managed flag(简称 M flag):表示是否配置有状态 IP。M flag 是 RA 报文的全局参数,一个 RA 报文只有一个 M flag
- 为 on 时(对应 bit 位为 1):表示在 SLAAC 流程结束后开始 DHCPv6 stateful 流程,也就是告诉客户端可以通过 DHCPv6 来获得 IPv6 地址和其他参数(如 DNS 列表)
- 为 off 时(对应 bit 位为 0):表示不通过 DHCPv6 来获得 IPv6 地址
- Other flag(简称 O flag):表示是否通过 DHCPv6 获得除 IP 以外的其他参数(如 DNS 列表)。
O flag 也是 RA 报文中的全局参数,一个 RA 报文只有一个 O flag。 注意:仅当 M flag 为
off 时,该参数才会被读取。
- 为 on 时(对应 bit 位为 1):当 M flag 为 on,或者 M flag 为 off 且至少有一个 A flag 为 on 时,将通过 DHCPv6 获得其他参数
- 为 off 时(对应 bit 位为 0):当 M flag 为 on 时,依然将通过 DHCPv6 获得其他参数;当 M flag 也为 off 时,将不通过 DHCPv6 获得其他参数
各 flag 的组合功能可以用下表说明
Flag | RA IP | DHCPv6 IP | DHCPv6 DNS |
---|---|---|---|
M=1, O=1, A=1 | Y | Y | Y |
M=1, O=1, A=0 | N | Y | Y |
M=1, O=0, A=1 | Y | Y | Y |
M=1, O=0, A=0 | N | Y | Y |
M=0, O=1, A=1 | Y | N | Y |
M=0, O=1, A=0 | N | N | Y |
M=0, O=0, A=1 | Y | N | N |
M=0, O=0, A=0 | N | N | N |
无状态和有状态并不是相互对立的,他们可以同时存在,也就是一张网卡上可以同时出现通过 RA 生成的 IP 以及通过 DHCPv6 获得的 IP。
主机获取 IP 的过程可以用以下流程图来帮助理解
|
|
Q: OpenWRT 中是如何通过配置项实现 IPv6 各种获取 IP 的方式的?
OpenWRT 跟获取 IPv6 地址相关的参数最主要的有 2 个
- reqaddress 是否通过 DHCPv6 获取 IP
- reqprefix DHCPv6 是否请求 IA_PD
network.wan6.reqaddress | GUI/Request IPv6-address |
---|---|
none | disabled |
try | try |
force | force |
network.wan6.reqprefix | GUI/Request IPv6-prefix of length |
---|---|
auto | Automatic |
no | disabled |
int value(64, 60…) | int value(64, 60…) |
reqaddress | reqprefix | Method |
---|---|---|
none | disabled | SLAAC + DHCPv6 stateless (information-request) |
none | auto/int | SLAAC + DHCPv6 stateless (PD in solicit) |
try | disabled | SLAAC + DHCPv6 stateful (no PD in solicit) |
try | auto/int | SLAAC + DHCPv6 stateful (PD in solicit) |
force | disabled | SLAAC + DHCPv6 stateful (no PD in solicit) |
force | auto/int | SLAAC + DHCPv6 stateful (PD in solicit) |
force 和 try 的区别在于,如果 DHCPv6 server 不支持 IA_NA,那么 force 会保持 DHCPv6 stateful 模式,然后失败重发 solicit;而 try 会自动切换成 DHCPv6 stateless 模式
Q: 如何使用 wireshark 过滤 slaac 和 dhcpv6 的封包?
|
|
Appendix
IPv6 RFCs
本文中提及的 RFC 列表如下
RFC No. | RFC Title |
---|---|
4191 | Default Router Preferences and More-Specific Routes |
4861 | Neighbor Discovery for IP version 6 (IPv6) |
4862 | IPv6 Stateless Address Autoconfiguration |
6106 | IPv6 Router Advertisement Options for DNS Configuration |
8415 | Dynamic Host Configuration Protocol for IPv6 (DHCPv6) |
更全的 IPv6 相关 RFC 可以参考一下链接