Auto Draft

IPv6 Deployment on Cisco IOS-XE: A Practical Engineer’s Guide

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-routing globally.
  • 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 ospf interface 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.

Enjoying this post?

Get more guides like this delivered straight to your inbox. No spam, just tech and trails.