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.