研究笔记 - 关于DHCPv6的研究
IPv6的地址分为两段:前缀(64bit)和后缀(64bit),以此拼接为一个完整的地址,前缀会在拨号时由运营商分配,一般是2408或2409等开头的前缀,在子网中拿到前缀后其实就能自由分配后缀部分了,无论自行配置的都是啥都能正确从全球路由到该网络,只不过一般的IPv6终端采用的都是无状态配置(SLAAC)的方式,无需DHCP服务器,为了避免冲突一般都会生成一个尽量唯一的标识,最终填充了整个128位的IPv6地址
本文编写的主要目的在于,利用DHCPv6手动分配后缀的方式,以此规划和统一网络,同时还能让IPv6地址看起来简短美观,不会过多叙述关于IPv6的基础知识,本文的目的是为了规划公网(即全球地址)IPv6配置过程中的要点,对于内网分配的私有IPv6地址不做讨论
本篇的编写形式为技术笔记,因此不会详细描述为达成特定目标而进行的详细流程,而是以技术备忘为主
例子
比如获得的前缀均为2408:8221:40a:9df0/60
,前缀60bit不满64bit,剩下4bit可以自由分配 即可用子网为2408:8221:40a:9df0::/64
至2408:8221:40a:9dff::/64
下的所有网段,一般的路由只用0子网,即9df0
,组成最终的64
位前缀
- 一个通过SLAAC自行配置的IPv6地址,根据设备唯一标识计算标识号得到
e73:148f:f7b0:b937
,并作为后缀组拼接成完整的地址,在Windows中,也被描述为临时IPv6地址
2408:8221:40a:9df0:e73:148f:f7b0:b937
- 一个通过DHCPv6分配的IPv6地址,分配的后缀为
::2
,即完整地址为
2408:8221:40a:9df0::2
附:国内各大运营商前缀地址范围
中国电信:
240e::/18
中国联通:
2408:8000::/20
中国移动/铁通:
2409:8000::/20
底层传输
DHCPv6的工作细节与以往的DHCPv4有一些不同,DHCPv4的服务端和客户端分别工作在udp/67
和udp/68
,DHCPv6其服务端和客户端分别在udp/547
和udp/546
,因此如果遇到通过防火墙的部分是需要额外配置的。其次不再直接使用设备的MAC地址来标识客户端,转而采用客户端提供的设备标识(DUID)来确定唯一的客户端并分配地址
以下例子采用tcpdump
来抓取在服务端switch0
端口的DHCPv6数据包,并存储到ducp_dump.cap
以便采用诸如Wireshark这类工具进行分析
tcpdump -i switch0 -w dhcp_dump.cap udp port 547
前缀长度
这是第一个需要关注的配置项,不过对于各大运营商而言较为固定,有以下前缀长度可选
- /48
- /56
- /60
- /64
一般国内运营商大多前缀长度为/60
,一般的路由系统基本上都会提供,但保持默认一般不需要调整,错误的前缀配置会直接导致无法访问,对于没有占满64bit的前缀,剩余的部分就可以自行自由分配,作为多个子网使用,自由分配的这部分也被称为前缀ID(Prefix ID)
内网的前缀指派方式
前缀仅仅是在PPPoE拨号时由拨号的端口获得,对于下属的子网设备由于并没有直接接入网络,因此为了获得前缀必须通过委派的方式告诉子网设备,以辅助这些设备配置正确的IPv6地址,目前常用的委派方式有
- 前缀代理PD(Prefix Delegation),路由器充当一个代理服务,由内网设备向路由器发起请求,由路由器代为访问上级DHCPv6服务器,并将获得的前缀返回客户端,这是最常用的前缀指派方式

- 单独配置路由通告RA(Router Advert),其实就是在上述前缀代理中剔除代理的部分,路由器自己配置了前缀无需访问服务器,在客户端请求时直接将本地已经配置的前缀公告给客户端。由于路由器大多没有全球地址,大多用于内网DHCPv6中自行划定IPv6网络时使用,在这里由于没有内网IPv6地址的定制需求故不需要配置
PD的工作流程本身就隐含了RA部分,无需再额外配置
IP分配方式
客户端想要配置一个IPv6地址,需要与服务器(或者是上级路由器)使用特定的数据包(RA报文)进行协商,确认采用的IPv6地址分配方式,分为有状态和无状态两种,具体的方式有
- 无状态地址配置(SLAAC, Stateless Address Autoconfiguration),默认客户端最常用的方式,不需要DHCPv6服务器
- 无状态DHCPv6(Stateless DHCPv6),除了IPv6地址,其余部分通过(如DNS参数)通过DHCP获取
- 有状态DHCPv6(Stateful DHCPv6),除了网关,其余均从DHCP获取,本文的目标则采用此方法
设备标识
在DHCPv6中,不再直接使用MAC作为识别客户端的依据,而是使用设备标识,即DUID(DHCP Unique Identifier),其基本格式为2字节DUID类型+不定长DUID值
根据RFC 3315的规定,DUID的取值方式有以下四种,这四种取值方式会反映在客户端发给服务器头部的DUID类型(type)中体现
- 基于MAC地址+时间生成的标识DUID-LLT,值为0x0001
- 采用厂商提供的唯一ID标识DUID-EN,值为0x0002
- 基于MAC地址生成的标识DUID-LL,值为0x0003
- RFC 6355扩展了一个根据手动配置的UUID生成的标识,值为0x0004
其中,除了DUID-LLT,其余取值方式获得的DUID是唯一的,而DUID-LLT方式的固定性依各操作系统的具体实现而定,而如果你在操作系统打开了IPv6隐私扩展,这会再每次分配时采用不同的DUID,从而获得不同的IPv6后缀。只有能稳定唯一的DUID,才能作为静态分配后缀的前提
在Windows中,DUID的取值方式为DUID-LLT,但会在第一次生成之后反复使用,因此在整体上是固定的,具体可参考Microsoft Windows官方文档:
- 第一次启动 DHCP 客户端服务时,会生成 DHCPv6 消息的 DUID。 DUID 的值存储在 Dhcpv6DUID 注册表项中。 如果删除注册表项的值,则当在客户端计算机上启用或连接接口时,会重新创建该值。 Dhcpv6DUID 注册表项位于以下注册表子项下:
\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\TCPIP6\Parameters
相关DHCPv6服务配置
要配置服务,自然首先得知道服务端DUID,但是目前还没有一个比较好的获取方式,只能通过抓包或者查看DHCP分配池来得知,后者需要客户端先与服务端交互获得一个IPv6地址,而后才能在服务端分配池看到这个已分配客户端的信息,本文采用此方法来获知DUID
以Ubnt的设备为例,在switch0分配的DHCP缓存池的位置在/var/run/dhcpv6-switch0-pd.leases
,不同操作系统和配置位置有很大不同,仅供参考
ia-na "\000\000\000\000\000\001\000\001%HD\018LV\1352\272\369" {
cltt 0 2023/01/08 10:20:17;
iaaddr 2408:8220:483:4872:532a:47e7:af0a:ae8a {
binding state expired;
preferred-life 27000;
max-life 43200;
ends 0 2023/01/08 22:20:17;
}
}
ia-na
开头的每个配置项就是一个客户端,后面紧跟的一大串二进制内容就是包含DUID的数据,其格式为IA(Identity Association)标识+DUID,IA是用于给一个客户端区分不同接口使用的,以避免多个接口同时使用同个DUID产生冲突,iaaddr
就是已经分配的这个IPv6地址。将这一串二进制转换为十六进制,去掉头部的4字节IAID,剩余部分就是DUID,以上面的数据为例,其中DUID类型为0x0001,因此是一个采用DUID-LLT取值的设备标识
IAID DUID
| |
----------- -----------------------------------------------
00:00:00:00:00:01:00:01:25:48:44:01:38:4c:56:5d:32:ba:1e:39
----- -----------------------------------------
| |
Type Payload
就目前观察到的而言,Win设备一般采用DUID-LLT(0x0001),而Linux设备常用DUID-UUID(0x0004),除非打开了IPv6隐私扩展
最后将这个得到的16进制DUID,采用:
进行分隔,作为控制面板中客户端标识一栏填入的值,并指定分配后缀,如::2
,在我这个Ubnt路由系统中,最终配置会被体现在PD服务产生的配置文件/var/run/dhcpv6-switch0-pd.conf
中,这个文件同时包含了动态前缀的信息,组成了DHCPv6服务最终采用的配置
shared-network switch0-pd {
subnet6 2408:8220:483:4872:0:0:0:0/64 {
host winserver {
host-identifier option dhcp6.client-id 00:01:25:48:44:01:38:4c:56:5d:32:ba:1e:39;
fixed-address6 2408:8220:483:4872::2;
}
range6 2408:8220:483:4872:0:0:0:0/64;
}
}
最后,在配置完成之后,要记得已经要删掉刚刚缓存池leases中对应客户端的配置,否则缓存会一直存在导致新的配置无法生效