目录

IPv6 FAQ

本来是想写一篇 IPv6 的文档,可以帮助不熟悉 IPv6 的同事快速了解 IPv6。但是后来发现越写越庞大,太过细节,重复了很多 IPv6 书籍中的内容,从而偏离了原本的目的。所以最终还是决定以 FAQ 的形式提供速查。

FAQs

Q: IPv6 的地址如何表示?

IPv6 的地址使用 128 bits 表示,每 16 bits 为一组,用冒号分隔,分成 8 组,如

1
2001:0db8:0000:0000:0001:0002:0003:0004

为了便于输入和记忆,我们可以将之简化

  • 去除每组前面的 0,上面的地址变为: 2001:db8:0:0:1:2:3:4
  • 如果是连续的多组 0,那么可以直接简化为 :: ,地址变为: 2001:db8::1:2:3:4

Q: 为什么显示的 IPv6 地址后面有 /64 ?

一个 IPv6 的地址,可以分为网络地址和主机地址 2 个部分。通过网络前缀可以指定网络地址所占用的位数。如果要表示一个 IPv6 的网络前缀(表示一个网段),可以用 ipv6地址/前缀长度 的格式。如:

1
2001:db8::1:2:3:4/64

表示地址的前 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::/8fd00::/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 中对此的说明如下

1
2
Interface-Local scope spans only a single interface on a node and is useful only
for loopback transmission of multicast.

简单来讲,ff01::1 只用于本地还回地址的多播。当 lo interface 起来的时候会加入该多播组。

Q: Linux 使用 ip 命令查看的时候可以看到每个 IPv6 地址后面都有 lifetime,这是什么意思 ?

对于每一个 IPv6 地址来说,有两个 Lifetime。这两个时间在接口获得 ip 的时候就开始倒计时。

  • preferred-lifetime 首选生存期。表示前缀可用的时间范围。在此范围内,前缀地址可以作为源地址或者目的地址自由使用。当生存期倒计时归零,则地址变的不可用(除了之前开始尚未结束的相关事务)

  • valid-lifetime 前缀在不可用之前保持可用的总时间。首选生存期到期后,任何自动配置的地址都将被弃用,并且仅用于在首选生存期到期之前开始的事务。如果有效生存期也过期,则地址将变得完全不可用。

因此,随着这两个 lifetime 的变化,IPv6 地址也会表现为不同的状态,如下图所示

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17

     ┌────────────┬────────────────────┬────────────────────┬───────────────────┐
     │            │                    │                    │                   │
     │    DAD     │     preferred      │    deprecated      │     invalid       │
     │            │                    │                    │                   │
 ────┴────────────┴────────────────────┴────────────────────┴───────────────────┴──►

     ┼  invalid   ┼                  valid                  ┼     invalid       ┼

                  ┼   new connection   ┼    old connection  ┼



                  │  preferred lft out │
 ─────────────────┼────────────────────┴────────────────────┬──────────────────────►
                  │                      deprecated lft out │

这些地址状态在 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 交互如图所示

/ox-hugo/ipv6_faq.img.66ad38d7.png
ns_na

完整的封包可以 在线查看 ,也可以从以下链接下载

要查看 NDP 的解析结果,Linux 下可以通过以下命令查看

1
ip -6 neighbor show

输出为

1
fe80::200:ff:feaa:2 dev eth0 lladdr 00:00:00:aa:00:02 router STALE

该命令类似 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 包的内容如图所示

/ox-hugo/ipv6_faq.img.547d0cb3.png
ra

完整的封包可以 在线查看 ,也可以从以下链接下载

可以看到这个 RA 包发送给 link local 上的所有主机,提供了前缀 2401:4900:d00:ffff::/64 ,默认网关自己(Lifetime > 0),DNS Server 两个 2401:4900::102401: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 的封包如下

/ox-hugo/ipv6_faq.img.475e84a0.png
dhcpv6_pd

完整的封包可以 在线查看 ,也可以从以下链接下载

ipv6_faq.file.dhcpv6_pd.pcap

DHCPv6 Stateless 的封包如下:

/ox-hugo/ipv6_faq.img.c351bb2b.png
dhcpv6_stateless_no_pd

完整的封包可以 在线查看 ,也可以从以下链接下载

ipv6_faq.file.dhcpv6_stateless_no_pd.pcap

这里需要注意的是,如果 DHCPv6 Stateless 需要获取 PD 的话,那么就不是发送 Information-Request 而是要发送 Solicit 的了。此时的封包应该如下所示:

/ox-hugo/ipv6_faq.img.e5693f01.png
dhcpv6_stateless_pd

完整的封包可以 在线查看 ,也可以从以下链接下载

ipv6_faq.file.dhcpv6_stateless_pd.pcap

Q: SLAAC 和 DHCPv6 是如何协同工作的?

IPv6 允许 SLAAC 和 DHCPv6 同时存在。两者可以并列运行也可以互相配合。

那么主机怎么知道应该通过哪种方式进行呢?这就要说到 RA 包中的 3 个关键 flag 了。

/ox-hugo/ipv6_faq.img.b21184c5.png
ra_flags

  • 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 的过程可以用以下流程图来帮助理解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

       ─┬─           ┌─────────────────────┐
        │            │ generate link local │
        │            │ address             │
        │            └──────────┬──────────┘
        │                       │
        │                       │
        │            ┌──────────▼──────────┐
        │            │ send RS, wait for RA│
        │            │ from a gateway      │
        │            └──────────┬──────────┘
        │                       │
        │                       │
        │            ┌──────────▼──────────┐
        │            │ RA is received?     ├────────────────────────────────┐
        │            │                     │ N                              │
        │            └──────────┬──────────┘                                │
        │                       │ Y                                         │
        │                       │                                           │
        │            ┌──────────▼──────────┐                                │
                     │ get prefix in RA    │                                │
      SLAAC          │                     │                                │
                     └──────────┬──────────┘                                │
        │                       │                                           │
        │                       │                                           │
        │            ┌──────────▼──────────┐                                │
        │            │ A flag is set?      ├─────────────────┐              │
        │            │                     │ N               │              │
        │            └──────────┬──────────┘                 │              │
        │                       │ Y                          │              │
        │                       │                            │              │
        │            ┌──────────▼──────────┐      ┌──────────▼──────────┐   │
        │            │ generate IP, route, │      │ generate only route │   │
        │            │ gateway             │      │ and gateway         │   │
        │            └──────────┬──────────┘      └──────────┬──────────┘   │
        │                       │                            │              │
        │                       │                            │              │
        │                       ◄────────────────────────────┘              │
        │                       │                                           │
        │                       │                                           │
        │            ┌──────────▼──────────┐      ┌─────────────────────┐   │
       ─┼─           │ M flag is set?      ├──────► O flag is set?      ├───┤
        │            │                     │ N    │                     │ N │
        │            └──────────┬──────────┘      └──────────┬──────────┘   │
        │                       │ Y                          │ Y            │
        │                       │                            │              │
                     ┌──────────▼──────────┐      ┌──────────▼──────────┐   │
      DHCPv6         │ use DHCPv6 get IP   │      │ use DHCP get other  │   │
                     │ and other options   │      │ options             │   │
        │            └──────────┬──────────┘      └──────────┬──────────┘   │
        │                       │                            │              │
        │                       │                            │              │
       ─┴─                      ◄────────────────────────────┴──────────────┘
                                │
                                │
                                ▼

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 的封包?

1
2
3
4
5
# 过滤 NS NA
icmpv6.type==135 || icmpv6.type == 136

# 过滤指定设备间的 RS RA, dhcpv6
((icmpv6.type == 133 || icmpv6.type == 134 || dhcpv6) && (eth.addr == <client_mac> || eth.addr == <server_mac>))

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 可以参考一下链接

References