Windows Server for beginners. Part 9: DHCPv6
All parts of the series “Windows Server for beginners”In IPv4 a client without DHCP knows nothing: no address, no gateway, no DNS. In IPv6 it's the other way round: a machine gets its address and gateway from the router via Neighbor Discovery without any server at all, as described in parts 14 and 15 of the networking series. The DHCPv6 protocol itself — the Solicit, Advertise, Request and Reply messages, DUID identifiers, the ff02::1:2 multicast group — is covered in part 16. This part is about what place is left for DHCPv6 next to SLAAC, how a Windows server fills that place, and how an IPv6 scope differs from the IPv4 scope of the previous part.
The short version, for those deciding whether they need this part: in a domain of Windows machines with a single router you can skip DHCPv6 entirely. The long version is why, and when you do need it after all.
Who hands out what
An IPv6 host has three sources of settings, and they aren't interchangeable:
Address Gateway DNS servers Suffix Other options
(NTP, PXE, …)
RA from router SLAAC yes RDNSS DNSSL no
DHCPv6 stateless no no yes yes yes
DHCPv6 stateful yes no yes yes yes
The gateway always comes from the Router Advertisement — DHCPv6 has no router option, and that's deliberate: a router announcing itself is the best source of information about itself. So an IPv6 host won't work without an RA even if DHCPv6 gives it an address and everything else, which means DHCPv6 is always an addition to RA, not a replacement.
The address comes either from SLAAC (the A flag on the prefix in the RA: the host builds the address itself from the prefix and its interface identifier), or from DHCPv6 (stateful), or both: the host then has two addresses from the same prefix. Which mode to use is what the router tells the host with two flags in the RA header:
- M (Managed address configuration) — "get your address via DHCPv6";
- O (Other configuration) — "get the other settings via DHCPv6".
The combinations: M=0, O=0 — RA only, DNS comes from RDNSS; M=0, O=1 — the address from SLAAC, DNS and the rest from DHCPv6 (stateless); M=1 — the address from DHCPv6, with O implied. The flags are set by the router, and the DHCPv6 server has no influence on them at all: the most common reason why "DHCPv6 is configured but clients don't come to it" is that the flags in the RA aren't raised. On Cisco these are ipv6 nd managed-config-flag and ipv6 nd other-config-flag on the interface, in radvd on Linux AdvManagedFlag on and AdvOtherConfigFlag on, on MikroTik the managed-address-configuration and other-configuration ND parameters. If the router is a Windows Server with the routing role, the flags are set on the interface:
Set-NetIPInterface -InterfaceAlias Ethernet -AddressFamily IPv6 -Advertising Enabled -AdvertiseDefaultRoute Enabled -ManagedAddressConfiguration Enabled -OtherStatefulConfiguration Enabled
DNS servers in pure SLAAC come in the RDNSS option of the same RA, and search suffixes in the DNSSL option. Windows understands RDNSS since Windows 10 1703 and Windows Server 2019, Linux and macOS have for a long time, and Android understands only this: it doesn't ask for an address or DNS via DHCPv6 (a DHCPv6 client appeared in it in 2025, but only for prefix delegation), so a network that hands out DNS exclusively via DHCPv6 leaves phones without an IPv6 resolver.
DUID and IAID
In IPv4 a client is identified by its MAC address. In DHCPv6 it's the DUID (DHCP Unique Identifier), one per machine rather than per interface, and the IAID (Identity Association Identifier), the number of an interface within the machine. A reservation is bound to the DUID + IAID pair.
There are four DUID types; Windows uses DUID-LLT: type 1, the link type, the creation time and the MAC address of the first adapter at the time of creation. It's generated once at first boot, stored in the registry and never changes afterwards, even if the adapter is replaced:
PS C:\> ipconfig /all | Select-String 'DUID|IAID'
DHCPv6 IAID . . . . . . . . . . . : 101193053
DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-2E-5A-7B-1C-00-15-5D-01-02-10
PS C:\> [BitConverter]::ToString((Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters').Dhcpv6DUID)
00-01-00-01-2E-5A-7B-1C-00-15-5D-01-02-10
00-01 is the DUID-LLT type, 00-01 is Ethernet, 2E-5A-7B-1C is seconds since January 1, 2000, followed by the MAC. The consequence for reservations: a reinstalled machine gets a new DUID (a new time), and a reservation by the old DUID stops working even though the MAC is the same. That's why printers and similar equipment in IPv6 more often get static addresses or rely on SLAAC with a MAC-based identifier (EUI-64), which stays constant for the device.
The IPv6 scope
The same DHCP Server role as in the previous part, the same set of cmdlets with v6 in the name. A scope is defined by a prefix, and the prefix is always a /64:
PS C:\> Add-DhcpServerv6Scope -Prefix 2001:db8:1:: -Name 'Office v6' -Preference 0 -State Active
PS C:\> Get-DhcpServerv6Scope
Prefix Name State Preference PrefLifetime ValidLifetime T1 T2
------ ---- ----- ---------- ------------ ------------- -- --
2001:db8:1:: Office v6 Active 0 8.00:00:00 12.00:00:00 4.00:00:00 6.09:36:00
Four times instead of a single lease duration: the preferred lifetime is how long the address may be used for new connections (8 days), the valid lifetime is how long existing connections from it still work (12 days), and T1 and T2 are when the client goes to renew with the same server (Renew) and with any server (Rebind). These are the same concepts as for addresses from an RA, and the client manages an address from DHCPv6 the same way as an address from SLAAC: a deprecated address doesn't disappear after the preferred lifetime, it just stops being chosen for new connections.
There is no range inside a scope. A /64 holds eighteen quintillion addresses, and the Windows server hands them out at random from the whole prefix, not in order. Addresses that mustn't be handed out are defined as exclusions: Add-DhcpServerv6ExclusionRange -Prefix 2001:db8:1:: -StartRange 2001:db8:1:: -EndRange 2001:db8:1::ffff sets aside the first 65,536 addresses for servers and equipment, although the chance that a random address falls into that range is negligible anyway.
Preference is option 7 of the protocol: a client that receives Advertise messages from several servers picks the one with the higher value, and at 255 it takes that one at once without waiting for the rest. This is the stand-in for failover we'll come back to.
Options are set at the server and scope level; there's no gateway among them, and that's not a mistake:
PS C:\> Set-DhcpServerv6OptionValue -DnsServer 2001:db8:1::10 -DomainSearchList corp.example.com
PS C:\> Get-DhcpServerv6OptionValue -Prefix 2001:db8:1:: -All | Format-Table OptionId, Name, Value -AutoSize
OptionId Name Value
-------- ---- -----
23 DNS Recursive Name Server {2001:db8:1::10}
24 Domain Search List {corp.example.com}
| Option | Name | Purpose |
|---|---|---|
| 23 | DNS Recursive Name Server | DNS servers; the counterpart of IPv4 option 6 and of RDNSS |
| 24 | Domain Search List | search suffixes; the counterpart of 15 and 119 |
| 31 | SNTP Servers | time over SNTP |
| 56 | NTP Server | time over NTP (separate options for the two protocols) |
| 39 | Client FQDN | the client's name for DNS registration; the counterpart of 81 |
| 59 | Boot File URL | network boot: the boot loader URL instead of the 66/67 pair |
| 16 | Vendor Class | vendor class; Windows sends MSFT 5.0 |
The DNS server address in option 23 must be an IPv6 address of the controller: dc1 needs a static IPv6 address for this (New-NetIPAddress -IPAddress 2001:db8:1::10 -PrefixLength 64), and it must be registered in the zone as AAAA. A Windows client that has both an IPv4 and an IPv6 DNS server address asks over IPv6 first, so a mistake in this option slows down name resolution for everyone: a query first goes to an address where there's no DNS server and waits for a timeout.
A reservation is made by the DUID and IAID from the client's ipconfig /all:
PS C:\> Add-DhcpServerv6Reservation -Prefix 2001:db8:1:: -IPAddress 2001:db8:1::31 -ClientDuid '00-01-00-01-2A-11-22-33-00-11-22-33-44-55' -Iaid 50331682 -Name 'printer-3'
Stateless
If the router raised only the O flag, clients don't ask for an address: they send an Information-Request with the options they need and get a Reply without an address. The Windows server needs nothing extra enabled for this: it hands out server-level options to everyone on an Information-Request, and a scope is needed only to pick the options of a particular segment by the relay agent or interface address. Such an exchange doesn't count as a lease and doesn't show up in Get-DhcpServerv6Lease: the server keeps no state, hence the name. If you do need to keep track of stateless clients, the server can maintain a separate list of them: Set-DhcpServerv6StatelessStore -Prefix 2001:db8:1:: -Enabled $true -PurgeInterval 1.00:00:00 turns it on for the scope, and Get-DhcpServerv6StatelessStatistics shows how many such clients each prefix has.
Stateless is a compromise for networks where SLAAC takes care of addresses, but DNS has to be handed out to clients that don't understand RDNSS (Windows before 1703, some network equipment), or where options that RA never carries are needed: NTP, network boot. If there are no such clients, you can leave the O flag down, and clients will make do with the RA.
DNS registration
A Windows client registers its AAAA records itself, with the same secure update as its A records: for the address from SLAAC, for the address from DHCPv6, for a static one. It doesn't register temporary addresses (privacy extensions — a random identifier that changes once a day, enabled by default on Windows clients), and that's right: otherwise the zone would fill up with junk. But the temporary address is the one the client uses for outgoing connections, and that's how it will appear in server logs. On servers temporary addresses are turned off with Set-NetIPv6Protocol -UseTemporaryAddresses Disabled from part 2: a server should answer and go out to the network from the address that's in DNS.
For clients that don't register themselves, the server does it — through option 39, with the same settings as in IPv4 but a separate cmdlet:
PS C:\> Set-DhcpServerv6DnsSetting -DynamicUpdates OnClientRequest -DeleteDnsRROnLeaseExpiry $true -NameProtection $false
The update account (Set-DhcpServerDnsCredential) is shared by IPv4 and IPv6. The reverse zone 0.0.0.0.1.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa from the DNS part accepts PTR records from the server and from clients exactly like 1.168.192.in-addr.arpa.
Two servers without failover
Failover in Windows DHCP works only for IPv4. It wasn't built for IPv6, and the reason lies in how addressing works: two servers handing out random addresses from the same /64 won't give out the same address twice over the whole life of the network, with a probability small enough to ignore, and if they do, the client will detect the duplicate with a DAD check over Neighbor Discovery and ask for another one. There's no need to synchronize leases.
So fault tolerance for IPv6 is simply two servers with the same scope. Once dc2 appears in part 10 with the role installed, the same scope is created on it, and the second controller is added to the DNS server list on both servers:
PS C:\> Add-DhcpServerv6Scope -ComputerName dc2.corp.example.com -Prefix 2001:db8:1:: -Name 'Office v6' -Preference 0 -State Active
PS C:\> Set-DhcpServerv6OptionValue -ComputerName dc2.corp.example.com -DnsServer 2001:db8:1::10, 2001:db8:1::11 -DomainSearchList corp.example.com
PS C:\> Set-DhcpServerv6OptionValue -ComputerName dc1.corp.example.com -DnsServer 2001:db8:1::10, 2001:db8:1::11 -DomainSearchList corp.example.com
The client sends a Solicit to ff02::1:2, receives an Advertise from both, picks one by preference (if equal, the first to answer) and renews the lease with the server that issued it: the Renew goes to the same ff02::1:2, but carries the identifier of the chosen server, and only that server answers. If it stays silent until T2, the client sends a Rebind without a server identifier, any server answers it, and the second server will give it a new address. For a while the client lives with two addresses, then the first one becomes deprecated. To the client this is indistinguishable from changing addresses when moving to another network, and IPv6 is ready for that by design. Reservations and exclusions have to be kept identical on the two servers by hand: Export-DhcpServer with Import-DhcpServer carry over the IPv6 configuration too.
Relaying from other subnets works as in IPv4: a relay agent on the router (ipv6 dhcp relay destination, dhcrelay -6) wraps the Solicit into a Relay-Forward message with the client's link address, by which the server picks the scope.
Do you need DHCPv6
In a domain of modern Windows, Linux and macOS machines, with one or two routers announcing a prefix with RDNSS and DNSSL, DHCPv6 isn't needed: everyone gets an address, a gateway and DNS from the RA (domain members already have a suffix — the domain name), registers AAAA records themselves, and the only setting left is to turn off temporary addresses on servers. A controller is found through AAAA and SRV records just as through A.
Stateful DHCPv6 is needed when:
- you need accounting: who had which address and when, in a single log rather than in the neighbor tables of every router;
- you need predictable addresses on clients without static configuration — reservations;
- there are clients without RDNSS support, or you need options RA doesn't have (NTP, PXE);
- security policy requires addresses to be assigned by a controlled server rather than made up by the host.
What the Windows DHCPv6 server can't do: prefix delegation (DHCPv6-PD, when a branch router is given a /56 to hand out across its networks) — for that you need ISC Kea or the provider's equipment.
A hands-on look
Where each of the client's addresses came from:
PS C:\> Get-NetIPAddress -AddressFamily IPv6 -InterfaceAlias Ethernet | Format-Table IPAddress, PrefixOrigin, SuffixOrigin, AddressState, ValidLifetime -AutoSize
IPAddress PrefixOrigin SuffixOrigin AddressState ValidLifetime
--------- ------------ ------------ ------------ -------------
2001:db8:1:0:a1b2:c3d4:e5f6:1234 RouterAdvertisement Link Preferred 29.23:59:50
2001:db8:1:0:7c1e:9a2b:3d4f:5e60 RouterAdvertisement Random Preferred 6.23:59:50
2001:db8:1:0:f8a3:2c17:9e04:6b5d Dhcp Dhcp Preferred 11.23:58:10
fe80::a1b2:c3d4:e5f6:1234%4 WellKnown Link Preferred Infinite
Four addresses on one interface: a permanent one from SLAAC (Link; by default Windows picks the interface identifier at random once rather than building it from the MAC — part 2 shows how to change that), a temporary one (Random: it's used for new connections for a day and lives for a week), one from DHCPv6 (Dhcp) and the link-local one. This is what a network looks like where the router raised both the A flag on the prefix and M: the client got an address both ways. If you want DHCPv6 only, the A flag is cleared on the prefix in the RA.
What the router says — the RA itself in a pktmon capture (the ICMPv6 filter is pktmon filter add -t ICMPv6):
Internet Control Message Protocol v6
Type: Router Advertisement (134)
Cur hop limit: 64
Flags: 0xc0, Managed address configuration, Other configuration
Router lifetime (s): 1800
ICMPv6 Option (Prefix information : 2001:db8:1::/64)
Flag: 0xc0, On-link flag(L), Autonomous address-configuration flag(A)
Valid Lifetime: 2592000
Preferred Lifetime: 604800
ICMPv6 Option (Recursive DNS Server 2001:db8:1::10)
ICMPv6 Option (DNS Search List Option corp.example.com)
ICMPv6 Option (Source link-layer address : 00:15:5d:01:02:01)
The M and O flags are raised, the prefix has A and L, RDNSS and DNSSL are present: this network hands out DNS both ways. The gateway is the sender of this packet, its link-local address, and that's exactly how it will appear in the client's routing table: Get-NetRoute -AddressFamily IPv6 -DestinationPrefix ::/0 shows fe80::… in NextHop.
The DHCPv6 exchange itself, with the filter pktmon filter add -p 546 -p 547 and ipconfig /renew6 on the client:
No. Source Destination Protocol Info
1 fe80::a1b2:c3d4:e5f6:1234 ff02::1:2 DHCPv6 Solicit XID: 0x8a1f3c CID: 000100012e5a7b1c00155d010210
2 fe80::215:5dff:fe01:20a fe80::a1b2… DHCPv6 Advertise XID: 0x8a1f3c CID: 000100012e5a7b1c00155d010210 IAA: 2001:db8:1:0:f8a3:2c17:9e04:6b5d
3 fe80::a1b2:c3d4:e5f6:1234 ff02::1:2 DHCPv6 Request XID: 0x8a1f3c CID: 000100012e5a7b1c00155d010210 IAA: 2001:db8:1:0:f8a3:2c17:9e04:6b5d
4 fe80::215:5dff:fe01:20a fe80::a1b2… DHCPv6 Reply XID: 0x8a1f3c CID: 000100012e5a7b1c00155d010210 IAA: 2001:db8:1:0:f8a3:2c17:9e04:6b5d
The whole exchange runs between link-local addresses: the client has nothing else to talk with yet, and for the server the same link is enough. The link-local address of dc1 is built from the MAC address (EUI-64, ff:fe in the middle), because random identifiers are turned off on the server, as part 2 advised. CID is the client's DUID from the registry. In the Solicit the Windows client includes option 3 (IA_NA with the IAID), 8 (Elapsed time), 6 (Option Request: 23, 24, 39), 16 (Vendor class MSFT 5.0) and 39 (FQDN ws1.corp.example.com). The Reply carries the address with its preferred and valid lifetimes, options 23 and 24, and option 39 confirming that the server will register the PTR.
Leases and statistics on the server:
PS C:\> Get-DhcpServerv6Lease -Prefix 2001:db8:1:: | Format-Table IPAddress, ClientDuid, Iaid, HostName, LeaseExpiryTime -AutoSize
IPAddress ClientDuid Iaid HostName LeaseExpiryTime
--------- ---------- ---- -------- ---------------
2001:db8:1:0:f8a3:2c17:9e04:6b5d 00-01-00-01-2E-5A-7B-1C-00-15-5D-01-02-10 101193053 ws1.corp.example.com 27.10.2026 9:03:12
PS C:\> Get-DhcpServerv6ScopeStatistics -Prefix 2001:db8:1::
Prefix AddressesFree AddressesInUse PendingAdvertises ReservedAddress PercentageInUse
------ ------------- -------------- ----------------- --------------- ---------------
2001:db8:1:: 18446744073709551614 1 0 1 0
AddressesFree is eighteen quintillion minus two (the leased one and the reserved one): the very reason failover isn't needed. PendingAdvertises are addresses offered in an Advertise for which the client hasn't come back with a Request yet; if there are many of them, clients are choosing another server or not finishing the exchange.
The IPv6 audit log is in separate files, %SystemRoot%\System32\dhcp\DhcpV6SrvLog-Thu.log, with codes from 11000: 11000 Solicit, 11001 Advertise, 11002 Request, 11003 Confirm, 11004 Renew, 11005 Rebind, 11006 Decline, 11007 Release, 11008 Information Request, 11009 scope full, 11022–11024 a DNS update request, failure and success. There's no separate code for Reply: an assigned address shows up in the Request or Renew line. Stateless clients appear under codes 11008 and 11032 (Information Request from a stateless client).
The domain now has a full set of addressing services, and they all live on one server. It's time to fix that — not by adding yet another role, but with a second controller, and along the way to look at how two controllers agree on whose copy of the directory is the right one.
Next — part 10: the second controller and replication. Install-ADDSDomainController, the global catalog, how the KCC builds the topology, sites, subnets and site links, repadmin and reading its output, SYSVOL replication through DFS-R, and why a controller's DNS client should look at its partner first and only then at itself.
Only registered users can leave a comment.
Sign in or register to leave a comment.