先说结论:把 DHCPv6 拆成有状态和无状态,是 IPv6 改造里最容易让人绕晕、也最容易被低估的一个分岔口。我见过不少网络工程师在 IPv6 双栈改造时,下意识按 IPv4 的思路把 DHCPv6 配成完整的有状态模式,地址倒是能拿到,但后面接路由器的 PD 前缀没有任何着落;也有人图省事全程只做 SLAAC,结果 DNS 和域名后缀没法统一下发,排障时连抓包都不知道从哪里看。这篇文章就把 DHCPv6 有状态和无状态的区别、报文细节、部署选型、配置样例和踩坑记录一次性讲清楚,适合正在做 IPv6 改造的网工、运维人员,也适合刚入门的网络技术学习者。
1. 理解有状态和无状态:拨开IPv6地址下发的迷雾
1.1 IPv6地址下发的两条技术路线
在 IPv4 网络里,地址几乎只有一种正经下发方式,就是 DHCP。到了 IPv6,规则变了:IPv6 地址长度 128 位,地址空间充裕,于是协议设计者给了两条路线。第一条是 SLAAC,全称 Stateless Address Autoconfiguration,无状态地址自动配置。路由器周期发送 RA(Router Advertisement,路由器通告)报文,或者主机上线主动发送 RS(Router Solicitation,路由器请求)请求 RA,主机拿到 RA 里的网络前缀后,自行拼接接口标识,生成一个完整的 IPv6 地址,不需要向任何服务器登记。第二条是 DHCPv6,路由器或专用服务器通过 DHCPv6 协议给客户端分配地址或前缀。这个流程和 IPv4 DHCP 类似,服务器维护一份分配表,客户端与服务器有多次报文交互。
两条路线并不是互斥的。IPv6 在设计时故意把“地址获取”和“其他网络参数获取”拆开,而 RA 报文里有两位关键的标志位 M 和 O,用来指导客户端如何配合。正是这两个标志位,把 DHCPv6 分成了有状态和无状态:M=1 代表地址要走受管协议(也就是 DHCPv6),M=0 代表地址走 SLAAC;O=1 代表其他配置(DNS、搜索域、NTP)要从 DHCPv6 获取。所以严格来说,“无状态 DHCPv6”的意思是 DHCPv6 服务器不负责分配地址、不维护地址状态,只做其他参数下发;而“有状态 DHCPv6”则是服务器要维护每一个已分配地址的租约状态。很多人把“无状态”理解成“不用 DHCPv6”,这是完全错误的,无状态 DHCPv6 依然有 DHCPv6 的报文交互,只是交互内容里没有地址分配那一项。
1.2 有状态与无状态的协议定义
从协议角度看,有状态和无状态其实是“DHCPv6 服务器是否维护绑定状态(binding)”。有状态模式下,服务器在收到客户端的 Solicit 后,会从本地地址池里选一个地址,建立一条“客户端 DUID + IA_NA + 分配的地址”的绑定记录,然后用 Advertise 报文通知客户端。这个绑定记录有生命周期、有续租逻辑,和 IPv4 DHCP 的 lease 记录很相似。正因为有记录,网络管理员才能通过服务器日志准确知道“某个 IPv6 地址曾经分配给谁、什么时候分配、什么时候释放”。
无状态模式下,DHCPv6 服务器更像一个“参数下发器”。客户端已经用 SLAAC 从 RA 报文里拿前缀生成了自己的地址,不再需要服务器分配。服务器只负责在 Reply 报文中携带 DNS、域名搜索后缀、NTP 服务器等参数。因此它不需要维护任何地址租约,也没有地址池。这里的“无状态”精确含义是:服务器没有地址分配状态,而不是说整个网络不用 DHCPv6。很多系统把无状态模式称为“stateless DHCPv6”,把有状态模式称为“stateful DHCPv6”,翻译成中文就是无状态 DHCPv6 和有状态 DHCPv6。
2. 报文级拆解:RA标记和DHCPv6交互
2.1 RA里的M/O标记:决定客户端行为的开关
RA 报文是 IPv6 里最容易抓到的控制面报文,也是观察有状态无状态最直观的入口。ICMPv6 type 134 就是 RA,里面携带路由器生存时间、前缀选项、MTU,以及我们最关心的两个 bit:M 和 O。M 全称 Managed address configuration flag,O 全称 Other configuration flag。两者的含义分别为:
- M=1:主机应该使用 DHCPv6 获取 IPv6 地址;
- M=0:主机应该使用 RA 中的前缀自己生成 IPv6 地址,也就是 SLAAC;
- O=1:主机应该使用 DHCPv6 获取除地址之外的其他配置,比如 DNS 和搜索域;
- O=0:主机不使用 DHCPv6 获取其他配置,通常依靠 RA 中的 RDNSS 选项或手工配置获取 DNS。
把这四个值组合起来,就得到四种典型的部署行为:
| M | O | 最终客户端行为 |
|---|---|---|
| 0 | 0 | 纯 SLAAC,地址自生成,DNS 靠 RA RDNSS 或手工配置 |
| 0 | 1 | 无状态 DHCPv6,地址自生成,DNS 等参数通过 DHCPv6 获取 |
| 1 | 0 | 有状态 DHCPv6 分配地址,其他参数通过 RA 或其他方式获取 |
| 1 | 1 | 完整有状态模式,地址和参数都通过 DHCPv6 获取 |
实际部署中,M=1/O=1 最常见,因为既然开了 DHCPv6,顺手把 DNS 也一起发下去更省事。而 M=1/O=0 比较少见,除非 DNS 规划确实放在其他通道里。理解这两个 flag,是判断一台终端最终会用什么方式获得地址和参数的关键。很多排障到最后,都会回到一句话:“先看看 RA 报文里的 M/O 到底是多少。”
2.2 DHCPv6的Solicit/Advertise/Request/Reply流程
有状态模式下,客户端在收到 M=1 的 RA 后,会发起四次交互:Solicit、Advertise、Request、Reply。客户端向组播地址 ff02::1:2(所有 DHCPv6 服务器和中继代理)发送 Solicit,报文里携带一个 IA_NA(Identity Association for Non-temporary Address)选项,请求服务器为这个 identity association 分配地址。有效的服务器回 Advertise,告诉客户端自己能提供什么地址、租期多长。客户端随后从多个候选服务器里选择一个,发送 Request,正式请求分配。服务器收到后回复 Reply,包含实际分配的地址、首选生命周期和有效生命周期。后续还有 Renew、Rebind、Release,对应续租、重新绑定和释放,机制和 IPv4 DHCP 很像,但报文格式完全不同。
无状态模式的流程要简单得多。客户端收到 M=0/O=1 的 RA 后,不再为地址走四步流程,而是直接发送一个 Information-request 报文,请求服务器下发 DNS、搜索域、NTP 等参数。服务器回 Reply,报文中没有 IA_NA,只有其他配置选项。整个过程不需要租约,不需要维护任何地址状态,所以叫无状态。这也意味着,如果用tcpdump抓包,看到有状态模式会有 Solicit、Advertise、Request、Reply 四连,而无状态模式通常只有 Information-request 和 Reply 两个报文。这个特征是我平时判断配置是否生效的第一个抓手。
还有一个经常被忽略的关键点:DHCPv6 不仅能分配地址,还能分配前缀。IA_PD 选项是用来获取前缀的,它不同于 IA_NA 获取地址。常见场景是企业出口路由器向上级 ISP 请求一个 /64、/60 或 /56 的前缀,然后这个路由器再把前缀分解成多个 /64 下发给内网网段。PD 请求和地址获取使用同一套 DHCPv6 交互流程,只不过 Solicit 里带的是 IA_PD 而不是 IA_NA。这个功能只通过 DHCPv6 实现,SLAAC 完全替代不了。所以选型时,只要拓扑里存在“下级路由器要拿前缀”,就要认真评估是否启用 DHCPv6-PD;严格来说 PD 也是在分配可记录的资源,多数厂商会把它归入有状态能力来做。
3. 有状态还是无状态:部署模式与应用场景对比
3.1 典型组网模式速览
IPv6 网络里,并不存在一个“最佳模式”,只有“当前业务是否匹配的模式”。先看一张速览表,把三种最常见的部署模式放到一起对比:
| 模式 | 地址来源 | DNS等配置来源 | 服务器是否维护地址状态 | 典型场景 |
|---|---|---|---|---|
| 纯 SLAAC | RA 前缀 + 客户端自生成 | RA RDNSS / 手工 | 否 | 极简接入、IoT、实验室 |
| 无状态 DHCPv6 | RA 前缀 + 客户端自生成 | DHCPv6 | 否 | 办公终端、BYOD、移动网络 |
| 有状态 DHCPv6 | DHCPv6 IA_NA | DHCPv6 / RA | 是 | 服务器、审计场景、需要 PD 的组网 |
纯 SLAAC 最大的优势是部署最简单,路由器只需要开启 RA,地址下发的逻辑就完事了,连额外的服务器都不用。但它的劣势也很明显:DNS 下发依赖 RA 里的 RDNSS 选项,而 RDNSS 选项的缓存时间和兼容性在不同操作系统上表现并不一致;地址完全由终端自己生成,管理员没有集中分配记录,出了问题很难定位是哪个终端在用哪个地址。
无状态 DHCPv6 兼顾了 SLAAC 的地址自生成能力和 DHCPv6 的参数集中下发能力。终端继续用 RA 前缀自动算地址,但 DNS、搜索域这些参数统一从 DHCPv6 服务器获取。由于服务器不维护地址租约,它不需要关心地址池大小,也不用处理续租风暴,压力远小于有状态模式。这是目前园区办公网比较推荐的一种做法。
有状态 DHCPv6 则把“地址分配”这个动作重新收回到服务器端。每个地址从哪个池、哪个时间段、给了哪个 DUID,都有记录。适合对地址使用有强审计需求的场景,比如服务器、打印机、门禁控制器等固定设备。另外,需要给下联路由器分配前缀的 DHCPv6-PD,通常也必须依赖有状态能力来维护前缀绑定关系。
3.2 如何结合业务选择合适的方案
选型时很多人会问“哪个更好”,实际操作里应该反着问:你的业务对地址可见性、终端自治能力和配置下发能力分别是什么要求?
如果是一般企业办公网,几百上千台终端,用户设备五花八门,还可能需要访客 Wi-Fi、BYOD 接入。这类场景用无状态 DHCPv6 通常体验最好。终端自己生成临时地址,隐私保护更好;DNS 和域名后缀由 DHCPv6 统一下发,修改 DNS 时只要改一处服务器配置即可。不需要逐台终端去手动配 DNS,也不需要在 DHCPv6 服务器里维护大量租约记录。
如果是政务、金融、医疗这类对审计要求较高的网络,或者运营商城域网里的网关设备,那有状态 DHCPv6 几乎是必选。管理员需要知道某个地址什么时候被哪个账号使用,遇到安全事件要能快速定位终端。只有服务器端维护绑定记录才能做到。另一个典型场景是给下级路由器分配前缀,企业在通过专线上行接入时,上级通常只会给你一个 /56 或者 /60,然后你自己通过 DHCPv6-PD 往下分配。这种 PD 分配,无状态模式一般搞不定,老老实实上有状态。
还有一类是 IoT 和哑终端场景。传感器、摄像头、水电表这些设备数量大、状态简单、不需要每个都登记固定地址。给它们配纯 SLAAC,把 DNS 配置放进 RA 的 RDNSS 里,或者干脆让设备不依赖 DNS,只通过 IP 访问平台,这样最省事。不要为了“统一管理”硬套有状态 DHCPv6,因为 IoT 设备的 DUID 和地址更新逻辑五花八门,反而容易造成地址池碎片化。
当然,现实网络经常是几种模式混合在一起。同一个三层接口上,并不限制只能用某一种模式。只要 RA 里的 M/O bit 配置正确,不同网段、不同 VLAN 完全可以按业务需要选择不同模式。关键是先想清楚业务诉求,再去动配置。
4. 实战配置:用Linux快速搭一个双模式DHCPv6环境
4.1 环境准备:radvd和DHCPv6服务器
我习惯用 Linux 做 IPv6 协议验证,因为工具链比较直接,而且所有关键参数都能通过配置文件暴露出来。实验环境很简单:一台 Linux 主机作为路由器/DHCPv6 服务器,接一个局域网;另一台 Linux 或者 Windows 作为客户端。首先在服务器上开启 IPv6 转发,修改/etc/sysctl.conf:
net.ipv6.conf.all.forwarding = 1 net.ipv6.conf.eth0.forwarding = 1 net.ipv6.conf.eth0.accept_ra = 0接着安装两个软件:radvd负责发送 RA,isc-dhcp-server里的 DHCPv6 服务负责响应 DHCPv6 请求。Debian/Ubuntu 上安装命令是:
apt install radvd isc-dhcp-serverradvd 的作用是让客户端知道:这个链路上有一个前缀,以及 M/O flag 是多少。ISC DHCPv6 服务器则处理实际 DHCPv6 报文。如果只是做纯 SLAAC,只装 radvd 就够了,但我们要模拟无状态和有状态两种 DHCPv6,所以两个组件都装。启动前先把服务停住,避免配置文件还没改就自动跑起来污染测试环境。
4.2 无状态DHCPv6配置步骤
无状态模式的目标是:地址走 SLAAC,DNS 等参数走 DHCPv6。先配置 radvd,让 RA 报文里 M=0、O=1,前缀设为2001:db8:100::/64,并开启AdvAutonomous on,这样客户端才会用 SLAAC 自生成地址。
interface eth0 { AdvSendAdvert on; AdvManagedFlag off; AdvOtherConfigFlag on; prefix 2001:db8:100::/64 { AdvOnLink on; AdvAutonomous on; }; RDNSS 2001:db8:1::53 { }; };这里的AdvOtherConfigFlag on对应 O=1,AdvManagedFlag off对应 M=0。再配置 DHCPv6 服务器,注意子网里不能写 range6,因为不需要分配地址,只需要下发其他参数:
option dhcp6.name-servers 2001:db8:1::53; option dhcp6.domain-search "example.com"; subnet6 2001:db8:100::/64 { }在 ISC DHCPv6 里,option dhcp6.name-servers和option dhcp6.domain-search是下发 DNS 和搜索域的关键选项。不写 range6,服务器就不会分配地址;但它仍然会在收到 Information-request 时回复这些参数。配置完成后启动服务:
systemctl start radvd systemctl start isc-dhcp-server验证时,在客户端网卡上抓包看 RA,或者用radvdump查看本机发出的 RA 内容;在客户端执行ip -6 addr应该能看到基于2001:db8:100::/64自动生成的地址。再执行dhclient -6或者让系统网络管理器触发 DHCPv6 请求,抓包应该能看到 Information-request 和 Reply,Reply 里带 DNS 配置。
4.3 有状态DHCPv6配置步骤
切换到有状态模式,只需要把 RA 中的 M flag 置为 1,并让 DHCPv6 服务器分配地址。radvd 配置改成:
interface eth0 { AdvSendAdvert on; AdvManagedFlag on; AdvOtherConfigFlag on; prefix 2001:db8:100::/64 { AdvOnLink on; AdvAutonomous off; }; };注意这里我把AdvAutonomous off了。原因是,即使 M=1,很多操作系统依然会在 A bit 仍为 1 的情况下额外生成 SLAAC 地址。为了让测试环境严格表现出“纯有状态”的行为,干脆关掉 RA 里的自动配置标志。如果实际生产环境希望保留 SLAAC 作为兜底,也可以不关,但需要清楚这会产生两类地址并存。
DHCPv6 服务器配置则要加上 range6,并可以按需配置 prefix6 来下发前缀:
option dhcp6.name-servers 2001:db8:1::53; subnet6 2001:db8:100::/64 { range6 2001:db8:100::100 2001:db8:100::200; prefix6 2001:db8:200:: /56; }range6告诉服务器这个子网可以分配哪些地址,prefix6是用于 DHCPv6-PD 的前缀池。如果只是做地址分配,不涉及下联路由器,prefix6 可以不写;但如果你在模拟“上级网关 + 下级路由器”的组网,就必须给下级路由器分配一个前缀,这里就要用到 prefix6。
验证方法也很直接。客户端网卡重新获取地址后,执行ip -6 addr看到的是2001:db8:100::100到2001:db8:100::200之间的地址,而不是随机生成的后缀。抓包能看到四步完整的 Solicit、Advertise、Request、Reply 流程。如果想确认 PD,在下级路由器接口抓包,能看到携带 IA_PD 的 Solicit,以及携带分配前缀的 Reply。
如果用的是华为、思科、华三这类商业设备,思路完全一样,只是命令不同。核心路由器接口上需要通过 ND 配置打开managed-config-flag或other-config-flag,地址池交给ipv6 dhcp pool这种命令定义。配置的关键不是背命令,而是把 RA 的 M/O flag 和 DHCPv6 地址池对应清楚:M=1 就一定要有地址池,否则客户端会一直拿不到地址;O=1 则只需要下发参数,地址池可以没有。
5. 常见问题与排查技巧实录
5.1 有状态模式设备仍然生成SLAAC地址
这个问题我在生产环境里踩过不止一次。明明把 RA 的 M flag 配成了 1,DHCPv6 地址也正常分下去了,但过一段时间发现终端接口上同时存在两个 IPv6 地址:一个是 DHCPv6 分配的2001:db8:100::xxx,另一个是终端用 SLAAC 生成的2001:db8:100:0:xxxx:xxxx:xxxx:xxxx。原因就是 RA 前缀选项里的 A bit(AdvAutonomous)仍然为 1,操作系统判定“这条前缀允许 SLAAC”,于是顺手又自动生成了一个地址。
如果业务上只需要 DHCPv6 地址,最好把 RA 里的AdvAutonomous off关掉。如果由于某些原因必须保留 SLAAC 兜底,那就要在地址规划上做好预期,避免应用配置里写死单个 IPv6 地址,导致地址一多就不认。另外在 Linux 客户端,还可以通过 sysctl 关闭指定接口的 SLAAC 能力:
sysctl -w net.ipv6.conf.eth0.autoconf=0但这种方式依赖客户端配合,不能作为网络侧的唯一控制手段。
5.2 无状态DHCPv6 DNS下发失败
无状态模式下最典型的故障是:客户端能拿到 RA、能用 SLAAC 生成地址,但 DNS 一直为空。排查时我习惯按顺序查三个点。
第一点,看 RA 报文的 O flag 是不是 1。很多设备默认只发了 RA,没开other-config-flag,客户端根本不会发起 DHCPv6 的 Information-request,自然拿不到 DNS。抓到 RA 后用 Wireshark 看 ICMPv6 option 里的 Managed Address Configuration 和 Other Configuration 两个字段即可。
第二点,看 DHCPv6 服务器有没有在客户端所在链路监听,以及有没有配置option dhcp6.name-servers。曾经遇到一个案例,DHCPv6 服务器配置在多网卡机器上,但是服务只监听了其中一个接口,导致另一半网段拿不到配置。解决办法是确认启动参数和 interface 配置,或者通过抓包确认服务器是否有回复。
第三点,注意客户端差异。Windows 对无状态 DHCPv6 的触发相对积极,收到 O=1 后会主动发 Information-request;但某些 Linux 发行版在没有启用 NetworkManager 或 dhcpcd 的情况下,默认只做 SLAAC,不会主动去请求 DHCPv6 的其他配置。遇到 Linux 终端 DNS 为空,可以先在客户端手动跑一次dhclient -6看看能不能拿到,能拿到就说明网络侧没毛病,问题在终端的 DHCPv6 状态机没被正确触发。
5.3 DHCPv6-PD 的几个典型坑
DHCPv6-PD 比地址分配更容易出错,尤其是第一次做前缀下发的时候。最典型的坑是只配置了range6地址池,没配置 prefix6 前缀池,然后下级路由器就一直报“No prefix for IA_PD”。原因很简单:服务器没有可分配的前缀资源,自然回不了包。需要在 DHCPv6 服务器上位下发前缀的场景单独设置 prefix pool,并且保证池子里的前缀长度足够。比如上级只给了你一个/56,你需要在配置里把这一整段作为 prefix pool,由路由器按需申请/60或/64,而不是逐个子网手动写死。
另一个坑是“无状态 DHCPv6 能不能做 PD”。很多管理员以为无状态 DHCPv6 不做地址绑定,应该也能做前缀下发,结果客户端请求 IA_PD 后,服务器一直无响应。严格来说,PD 资源需要被记录和追踪,属于状态化分配。虽然少数实现允许把 PD 和地址分配分成两套流程,但生产环境里我建议把 PD 视为“有状态能力”,配置时统一用有状态模式来管理,这样行为最可控。
还有一个常见问题是前缀分配后没有及时续租。前缀的租期比地址租期长,但也不是永久有效的。如果下级路由器离线重拨,或者上级 DHCPv6 服务重启,可能出现前缀冲突或旧前缀没有释放的情况。排查时看服务器日志里的 IA_PD 记录,必要时手动释放再重新获取。
5.4 排查用到的命令速查表
整理一份我日常排查 DHCPv6 问题时常用的命令,方便直接抄:
| 目的 | 命令 / 工具 | 关键输出 |
|---|---|---|
| 查看接口 IPv6 地址 | ip -6 addr | 地址列表、生命周期 |
| 抓取 RA / DHCPv6 报文 | tcpdump -i eth0 -vv 'icmp6 or udp port 546 or udp port 547' | RA 的 M/O flag、DHCPv6 报文过程 |
| 查看本机发出的 RA 配置 | radvdump | 前缀、flag、RDNSS |
| 查看 DHCPv6 租约 | /var/lib/dhcp/dhcpd6.leases | DUID、地址、租期 |
| 手动发起 DHCPv6 | dhclient -6 -v eth0 | 请求过程、是否获得地址和参数 |
| 强制客户端发送信息请求 | dhcpcd -6 eth0 | 是否触发 Information-request |
抓包永远是第一步。很多时候不需要想复杂的原因,先把 RA 和 DHCPv6 交互报文抓到,M/O flag、IA_NA 字段、Reply 里的 option 一眼就能看出问题在哪。尤其是跨厂商对接时,两边对 flag 的理解不一致,抓包能直接定位是哪一侧没有按协议走。
6. 个人实操中的一点体会
做 IPv6 网络规划和排障这段时间,我最大的感受是:不要带着 IPv4 DHCP 的惯性去理解 DHCPv6。IPv4 里 DHCP 几乎是唯一标准答案,但 IPv6 给了 SLAAC、无状态 DHCPv6、有状态 DHCPv6 多条路线,而且它们还能混用。真正决定选哪种模式的,不是“哪种更高级”,而是你需要在多大程度上控制终端的地址行为。需要精细登记和审计就用有状态,希望终端自治、又想集中下发 DNS 就用无状态,IoT 这种重点不在管理链路上的场景就大胆开纯 SLAAC。
最后再分享一个小技巧:在配置链路里增加一台抓包用的 Linux 主机,不管业务上线还是排障,都先把 RA 报文抓下来看一遍 M/O flag。只要这两个 bit 没理解清楚,后续地址获取、PD 下发、DNS 配置都可能连环出问题。把 flag 和配置对应关系记牢,很多看似诡异的问题其实都能在十分钟内定位出来。