IPv6 has been “coming soon” for two decades, and yet most enterprise networks still run dual-stack at best — and many haven’t touched IPv6 at all. In 2026, that’s no longer acceptable. ISPs are handing out IPv6 prefixes by default, cloud providers route native IPv6, and a growing slice of mobile traffic is IPv6-only. If you’re managing Cisco IOS-XE gear and your IPv6 config is still ipv6 unicast-routing with nothing else, this guide is for you.
We’ll cover the full deployment stack: addressing, OSPFv3, DHCPv6, ACLs, and the verification commands that actually tell you what’s broken. Real CLI, real output, real gotchas.
Why IOS-XE IPv6 Trips People Up
Most engineers who’ve worked IPv4 for years run into a handful of consistent surprises with IPv6 on IOS-XE:
- Routing isn’t enabled by default. Even if you assign an IPv6 address to an interface, packets won’t be routed until you enable
ipv6 unicast-routingglobally. - Link-local addresses are automatic — and mandatory. Every IPv6-enabled interface generates a link-local (fe80::/10) address automatically. You can’t disable it without disabling IPv6 entirely on that interface.
- ICMPv6 is not optional. IPv6 Neighbor Discovery (ND) uses ICMPv6 in the same way ARP uses broadcast in IPv4. ACLs that block ICMPv6 will break things silently.
- OSPFv3 vs. OSPF IPv6 address family. IOS-XE supports two different ways to run OSPF over IPv6. The modern way is the address-family model under OSPFv3 — not the legacy
ipv6 ospfinterface command.
Let’s build from the ground up.
Step 1: Enable IPv6 Unicast Routing
Before anything else, IPv6 forwarding must be globally enabled. Without this, IOS-XE will accept IPv6 configuration on interfaces but won’t actually route packets.
Router(config)# ipv6 unicast-routing
Verify it’s active:
Router# show ipv6 cef summary
IPv6 CEF is enabled and running
Router# show ipv6 route summary
IPv6 routing table name is default (0x0)
IPv6 routing table maximum-paths is 16
...
If you’re on an older IOS-XE version (<16.x), also check whether ipv6 cef needs to be explicitly enabled — on modern Catalyst 9K and ISR/ASR platforms running 17.x, CEF for IPv6 is on by default when unicast-routing is enabled.
Step 2: Configuring IPv6 Addresses on Interfaces
You have three main options for assigning IPv6 addresses to an interface: static, EUI-64, and letting SLAAC handle client addressing (router-side configuration).
Static Address
Router(config)# interface GigabitEthernet0/0/0
Router(config-if)# ipv6 address 2001:db8:cafe:1::1/64
Router(config-if)# no shutdown
EUI-64 (MAC-derived)
Router(config-if)# ipv6 address 2001:db8:cafe:1::/64 eui-64
EUI-64 is convenient for router interfaces, but avoid it on servers or devices with privacy requirements — the MAC address becomes embedded in the IPv6 address.
Verify Interface Addresses
Router# show ipv6 interface GigabitEthernet0/0/0
GigabitEthernet0/0/0 is up, line protocol is up
IPv6 is enabled, link-local address is FE80::1
No Virtual link-local address(es):
Global unicast address(es):
2001:DB8:CAFE:1::1, subnet is 2001:DB8:CAFE:1::/64
Joined group address(es):
FF02::1
FF02::2
FF02::1:FF00:1
MTU is 1500 bytes
ICMP error messages limited to one every 100 milliseconds
ICMP redirects are enabled
ICMP unreachables are sent
ND DAD is enabled, number of DAD attempts: 1
ND reachable time is 30000 milliseconds (using 30000)
ND advertised reachable time is 0 (unspecified)
ND advertised retransmit interval is 0 (unspecified)
ND router advertisements are sent every 200 seconds
ND router advertisements live for 1800 seconds
ND advertised default router preference is Medium
Hosts use stateless autoconfig for addresses.
Notice the multicast groups — FF02::1 (all nodes), FF02::2 (all routers), and the solicited-node multicast for the interface address. These are required for ND to work. If you see an interface without these groups, something is broken at the IPv6 enablement level.
Also notice: Hosts use stateless autoconfig for addresses. This means Router Advertisement (RA) is being sent, and hosts on this segment can auto-configure via SLAAC. If you want hosts to use DHCPv6 instead, you’ll need to adjust the RA flags — covered in Step 5.
Step 3: Setting a Readable Link-Local Address
Auto-generated link-locals (fe80::1a2b:3c4d:5e6f:7a8b) are a pain to type in ACLs, routing configs, and troubleshooting sessions. Set a simple one manually on router interfaces:
Router(config)# interface GigabitEthernet0/0/0
Router(config-if)# ipv6 address FE80::1 link-local
This is purely cosmetic for the router — link-locals are link-scoped and never routed — but it makes your logs and routing tables vastly easier to read. On point-to-point links, using FE80::1 and FE80::2 is a common convention that pays off immediately when reading OSPFv3 neighbor output.
Step 4: OSPFv3 with Address-Family Model
The modern way to run OSPF over IPv6 on IOS-XE is OSPFv3 with the address-family ipv6 subcommand. Avoid the legacy per-interface ipv6 ospf <pid> area <x> command — it still works, but mixing the two approaches in the same network will cost you a debugging session eventually.
Enable OSPFv3
Router(config)# router ospfv3 1
Router(config-router)# router-id 10.0.0.1
Router(config-router)# address-family ipv6 unicast
Router(config-router-af)# exit-address-family
Important: OSPFv3 still requires a 32-bit router ID in IPv4 dotted-decimal format, even when running IPv6-only. The router-id is set at the process level, not under the address family. On routers without any IPv4 addresses, you must configure this manually — otherwise OSPFv3 won’t start and the log will show no obvious error.
Enable on Interfaces
Router(config)# interface GigabitEthernet0/0/0
Router(config-if)# ospfv3 1 ipv6 area 0
Verify Neighbors
Router# show ospfv3 neighbor
OSPFv3 1 address-family ipv6 (router-id 10.0.0.1)
Neighbor ID Pri State Dead Time Interface ID Interface
10.0.0.2 1 FULL/DR 00:00:37 4 GigabitEthernet0/0/0
10.0.0.3 1 FULL/BDR 00:00:35 3 GigabitEthernet0/0/0
If neighbors are stuck in INIT or 2WAY, the usual suspects are mismatched hello/dead timers or mismatched area types. One that catches people off guard: OSPFv3 uses IPv6 multicast addresses (FF02::5 and FF02::6), so an IPv6 ACL on the interface that doesn’t explicitly permit those multicast destinations will silently kill OSPFv3 while leaving IPv4 OSPF untouched. Check your IPv6 ACLs first.
For deeper OSPF troubleshooting methodology, our OSPF troubleshooting guide for IOS-XE covers adjacency state machine logic that applies directly to OSPFv3 as well.
Step 5: DHCPv6 — Stateful vs. Stateless
This is where most engineers get confused. IPv6 has two flavors of DHCPv6, controlled by flags in the Router Advertisement:
- Stateless DHCPv6 (SLAAC + DNS via DHCP): Hosts self-configure their address via SLAAC, but query a DHCPv6 server only for DNS and domain info. RA flags: M=0, O=1.
- Stateful DHCPv6: Hosts get their full address assigned by the DHCPv6 server, just like IPv4 DHCP. RA flags: M=1, O=1.
Configuring a Stateful DHCPv6 Pool
Router(config)# ipv6 dhcp pool CLIENTS-V6
Router(config-dhcpv6)# address prefix 2001:db8:cafe:10::/64 lifetime 86400 14400
Router(config-dhcpv6)# dns-server 2001:4860:4860::8888
Router(config-dhcpv6)# dns-server 2001:4860:4860::8844
Router(config-dhcpv6)# domain-name example.com
Router(config-dhcpv6)# exit
Router(config)# interface GigabitEthernet0/0/1
Router(config-if)# ipv6 address 2001:db8:cafe:10::1/64
Router(config-if)# ipv6 dhcp server CLIENTS-V6
Router(config-if)# ipv6 nd managed-config-flag
Router(config-if)# ipv6 nd other-config-flag
The managed-config-flag sets M=1 in RAs; other-config-flag sets O=1. Both are needed for fully stateful DHCPv6.
Verify DHCPv6 Bindings
Router# show ipv6 dhcp binding
Client: FE80::A00:27FF:FE4E:66A1
DUID: 00030001080027CE66A1
Username : unassigned
VRF : default
Interface : GigabitEthernet0/0/1
IA NA: IA ID 0x00000001, T1 43200, T2 69120
Address: 2001:DB8:CAFE:10::A
preferred lifetime 86400, valid lifetime 86400
expires at May 04 2026 09:22:15 (86391 seconds)
Note IA NA — Identity Association for Non-temporary Addresses — which is what regular hosts receive. If you’re seeing IA PD (Prefix Delegation) here, you’ve configured prefix delegation for a CPE device, not standard host addressing.
DHCPv6 Relay
If your DHCP server lives on a different subnet (it should), configure a relay:
Router(config)# interface GigabitEthernet0/0/1
Router(config-if)# ipv6 dhcp relay destination 2001:db8:ffff::10
Unlike IPv4 DHCP relay (ip helper-address), DHCPv6 relay is configured per-interface and points to the actual IPv6 address of your DHCP server.
Step 6: IPv6 ACLs
IPv6 ACLs on IOS-XE are named-only — no numbered ACLs for IPv6. The syntax mirrors extended IPv4 ACLs, but there’s a critical difference that will break your network if you miss it.
Router(config)# ipv6 access-list BLOCK-EXTERNAL-V6
Router(config-ipv6-acl)# permit icmp any any nd-na
Router(config-ipv6-acl)# permit icmp any any nd-ns
Router(config-ipv6-acl)# permit icmp any any router-advertisement
Router(config-ipv6-acl)# permit icmp any any router-solicitation
Router(config-ipv6-acl)# permit icmp any any packet-too-big
Router(config-ipv6-acl)# permit tcp 2001:db8:cafe::/48 any established
Router(config-ipv6-acl)# permit tcp 2001:db8:cafe::/48 any eq 443
Router(config-ipv6-acl)# permit tcp 2001:db8:cafe::/48 any eq 80
Router(config-ipv6-acl)# deny ipv6 any any log
Apply to an interface:
Router(config)# interface GigabitEthernet0/0/0
Router(config-if)# ipv6 traffic-filter BLOCK-EXTERNAL-V6 in
Always explicitly permit ICMPv6 ND messages before any deny statements. IOS-XE does NOT have an implicit permit for ND in IPv6 ACLs, unlike some other platforms. Also permit ICMPv6 type 2 (Packet Too Big) — filtering it breaks Path MTU Discovery, which causes large-packet failures that are notoriously hard to trace. See our network security guide for broader firewall and ACL design principles that apply in dual-stack environments.
Verify ACL hits:
Router# show ipv6 access-list BLOCK-EXTERNAL-V6
IPv6 access list BLOCK-EXTERNAL-V6
permit icmp any any nd-na (42 matches)
permit icmp any any nd-ns (38 matches)
permit icmp any any router-advertisement (12 matches)
permit icmp any any router-solicitation (8 matches)
permit icmp any any packet-too-big (3 matches)
permit tcp 2001:DB8:CAFE::/48 any established (1284 matches)
permit tcp 2001:DB8:CAFE::/48 any eq 443 (3812 matches)
deny ipv6 any any log (0 matches)
Step 7: IPv6 on VLANs (Layer 3 Switching)
On Catalyst 9K switches, IPv6 on SVIs works exactly like on routed interfaces — apply the address to the SVI. If you’re not familiar with the VLAN architecture itself, our VLAN segmentation guide covers the foundational concepts before you add IPv6 into the mix.
Switch(config)# interface Vlan10
Switch(config-if)# description CLIENTS-VLAN10
Switch(config-if)# ip address 192.168.10.1 255.255.255.0
Switch(config-if)# ipv6 address 2001:db8:cafe:10::1/64
Switch(config-if)# ipv6 nd managed-config-flag
Switch(config-if)# no shutdown
One common Cat9K gotcha: IPv6 CEF entries require the right SDM template. Verify it:
Switch# show sdm prefer
The current template is "advanced" template.
The selected template optimizes the resources in
the switch to support this level of features for
8 routed interfaces and 1024 VLANs.
...
number of IPv6 unicast routes: 2K
If IPv6 unicast routes shows 0 or a suspiciously low number, change the SDM template and reload:
Switch(config)# sdm prefer dual-ipv4-and-ipv6 default
Switch(config)# end
Switch# reload
Step 8: BGP for IPv6
For peering with upstream providers or running iBGP across a dual-stack core, the IPv6 address family sits alongside IPv4 under the same BGP process:
Router(config)# router bgp 65001
Router(config-router)# bgp router-id 10.0.0.1
Router(config-router)# neighbor 2001:db8:ffff::2 remote-as 65002
Router(config-router)# neighbor 2001:db8:ffff::2 description UPSTREAM-ISP
Router(config-router)# !
Router(config-router)# address-family ipv6 unicast
Router(config-router-af)# neighbor 2001:db8:ffff::2 activate
Router(config-router-af)# neighbor 2001:db8:ffff::2 prefix-list IPV6-IN in
Router(config-router-af)# neighbor 2001:db8:ffff::2 prefix-list IPV6-OUT out
Router(config-router-af)# network 2001:db8:cafe::/48
Router(config-router-af)# exit-address-family
Apply prefix filters before you bring the session up — accepting full DFZ IPv6 table (~250K+ routes) on a device that wasn’t sized for it will cause issues:
Router(config)# ipv6 prefix-list IPV6-IN seq 5 permit ::/0 le 48
Router(config)# ipv6 prefix-list IPV6-IN seq 10 deny ::/0 le 128
Router(config)# ipv6 prefix-list IPV6-OUT seq 5 permit 2001:db8:cafe::/48
For BGP path selection and attribute manipulation concepts, our BGP protocol guide covers the fundamentals — the logic applies equally to IPv4 and IPv6 address families.
Essential Verification Commands
Here’s the verification workflow to run in order when troubleshooting IPv6 connectivity on IOS-XE:
# 1. Is unicast routing enabled?
show ipv6 cef summary
# 2. What addresses does this interface have?
show ipv6 interface brief
# 3. Is the neighbor reachable at L2?
show ipv6 neighbors
# 4. What's in the routing table?
show ipv6 route
show ipv6 route 2001:db8:cafe:20::/64
# 5. CEF lookup for a specific destination
show ipv6 cef 2001:db8:cafe:20::10/128
# 6. OSPFv3 state
show ospfv3 neighbor
show ospfv3 database
# 7. DHCPv6 state
show ipv6 dhcp binding
show ipv6 dhcp pool
# 8. ACL hit counts
show ipv6 access-list
# 9. Packet-level tracing (careful in production)
debug ipv6 packet detail
The neighbor table is your ARP equivalent — if a neighbor’s state is STALE and not resolving, you have an ND problem, not a routing problem.
Router# show ipv6 neighbors
IPv6 Address Age Link-layer Addr State Interface
FE80::A00:27FF:FE4E:66A1 0 0800.27ce.66a1 REACH Gi0/0/1
2001:DB8:CAFE:10::A 2 0800.27ce.66a1 STALE Gi0/0/1
STALE means the entry hasn’t been refreshed within the reachable time window (default 30s). It transitions back to REACH after successful communication. An entry stuck in INCOMPLETE means ND is completely broken — check your ICMPv6 ACL entries and verify the link is actually passing multicast traffic.
Common Production Gotchas
1. PMTUD Breakage
IPv6 routers don’t fragment packets — only the source host does, via Path MTU Discovery. If ICMPv6 “Packet Too Big” messages are filtered anywhere in the path, large packets silently drop. This is the classic symptom: ping works, SSH works for small commands, but file transfers or HTTPS page loads hang or fail. Explicitly permit ICMPv6 type 2 in all ACLs and set interface MTU consistently:
Router(config)# interface GigabitEthernet0/0/0
Router(config-if)# ipv6 mtu 1500
2. RA Guard on Access Ports
On Catalyst switches, any device on an access port can send unsolicited Router Advertisements and redirect host traffic or hijack default gateway assignments. Enable RA Guard on untrusted ports:
Switch(config)# ipv6 nd raguard policy BLOCK-RA
Switch(config-nd-raguard)# device-role host
Switch(config-nd-raguard)# exit
Switch(config)# interface range GigabitEthernet1/0/1 - 48
Switch(config-if-range)# ipv6 nd raguard attach-policy BLOCK-RA
3. Duplicate Address Detection (DAD) Failures
If you see %IPV6-4-DUPLICATE: IPv6 address 2001:DB8::1 on GigabitEthernet0/0/0 conflicts, there’s an address conflict on the segment. Verify:
Router# show ipv6 interface GigabitEthernet0/0/0 | include duplicate|tentative
2001:DB8:CAFE:1::1 is in use, no duplicates
DAD failures are particularly common during migrations where static addresses from decommissioned devices weren’t removed, or when a misconfigured VM spins up with the same address as a router interface.
Putting It All Together
A minimal but production-ready dual-stack config for a transit interface looks like this:
interface GigabitEthernet0/0/0
description UPSTREAM-LINK
ip address 203.0.113.1 255.255.255.252
ipv6 address FE80::1 link-local
ipv6 address 2001:db8:ffff::1/127
ospfv3 1 ipv6 area 0
no shutdown
!
Note the /127 prefix on the point-to-point link — RFC 6164 recommends this specifically for router-to-router links to avoid the subnet-router anycast address ambiguity that /126 introduces. It’s the current IOS-XE best practice for P2P links.
IPv6 isn’t harder than IPv4 — it’s different in specific, learnable ways. The address format takes getting used to, ND replaces ARP in ways that matter for troubleshooting, and ICMPv6 needs to be treated as infrastructure rather than nuisance traffic. If you want to experiment without touching production gear, spin up a lab using IOS-XE in GNS3 or Cisco Modeling Labs, run through these configs end-to-end, and IPv6 will start feeling familiar fast.
Got a specific IPv6 scenario giving you trouble? Drop it in the comments — whether it’s OSPFv3 adjacency issues, DHCPv6 relay weirdness, or BGP IPv6 prefix filtering, chances are I’ve debugged it.