IPv6 on Cisco IOS-XE: Complete Configuration Guide for Dual-Stack Networks

IPv6 on Cisco IOS-XE: Complete Configuration Guide for Dual-Stack Networks

IPv4 addresses ran out years ago. If you’re still running a pure IPv4 network and haven’t at least thought about IPv6, your window for a leisurely migration is closing fast. Whether you’re managing a service provider edge, a campus network, or a data center fabric, understanding how to configure, verify, and troubleshoot IPv6 on Cisco IOS-XE is no longer optional — it’s survival gear.

This guide walks through everything you need to deploy IPv6 on modern Cisco IOS-XE platforms: addressing schemes, static and dynamic addressing, OSPFv3 and BGP for IPv6, dual-stack operation, prefix delegation, and the CLI tricks that will save you hours of head-scratching. All examples use real IOS-XE syntax, tested on Catalyst 9300/9400 and ISR 4000 series platforms.


Why IPv6 Still Feels Optional (But Isn’t)

Most enterprise networks get by on RFC 1918 space with NAT, so the urgency hasn’t felt real. But a few things are changing that calculus:

  • ISPs are dropping or surcharging public IPv4 — cloud providers and mobile carriers increasingly hand you a /128 or nothing at all.
  • IoT scale — a modern factory floor or smart-building deployment can’t address everything under 10.0.0.0/8.
  • Dual-stack is the bridge — running both protocols simultaneously is the safest migration path, and IOS-XE handles it cleanly.
  • Security requirements — some compliance frameworks now explicitly require IPv6 capability awareness and hardening.

If you’ve already waded through the differences between IOS, IOS-XE, and IOS-XR, you know that IOS-XE ships with a full IPv6 stack that’s been production-hardened for over a decade. Let’s use it.


IPv6 Addressing Fundamentals (The Parts That Actually Matter)

Before you touch the CLI, internalize these three address types:

  • Global Unicast (GUA) — publicly routable, 2000::/3. Equivalent to a public IPv4 address.
  • Link-Local (LLA) — automatically assigned to every IPv6-enabled interface, fe80::/10. Never routed beyond a single link. Used for neighbor discovery and routing protocol peering.
  • Unique Local Address (ULA) — fc00::/7, the IPv6 equivalent of RFC 1918 private space.

The thing that trips up engineers new to IPv6: you always have both a GUA and a link-local on any IPv6 interface. When pinging or telneting to a link-local, you must specify the egress interface with a % modifier (more on this later).

Subnet sizing: the standard allocation for a single LAN segment is a /64. That gives you 18 quintillion host addresses per subnet. Stop trying to carve it smaller — it breaks Stateless Address Autoconfiguration (SLAAC) and causes operational headaches.


Enabling IPv6 on IOS-XE

IPv6 routing is off by default. Enable it globally first:

Router# configure terminal
Router(config)# ipv6 unicast-routing
Router(config)# ipv6 cef

ipv6 cef enables Cisco Express Forwarding for IPv6 — you want this for anything beyond lab work. On most IOS-XE platforms, CEF is on by default, but it’s worth verifying:

Router# show ipv6 cef
IPv6 CEF is enabled and running

Assigning an IPv6 Address to an Interface

Router(config)# interface GigabitEthernet0/0/0
Router(config-if)# ipv6 address 2001:db8:acad:1::1/64
Router(config-if)# ipv6 enable
Router(config-if)# no shutdown

ipv6 enable (without an address) generates only a link-local. Including a GUA implicitly enables IPv6 on the interface, but being explicit doesn’t hurt.

Verify:

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:ACAD:1::1, subnet is 2001:DB8:ACAD: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 RAs are suppressed (all)

Setting a Static Link-Local

Auto-generated link-locals based on EUI-64 are ugly and hard to remember. Set them manually in a loopback-compatible way:

Router(config-if)# ipv6 address fe80::1 link-local

This makes routing protocol configs and troubleshooting far easier — your neighbor’s next-hop shows up as FE80::1 instead of FE80::2EC2:60FF:FE12:3456.


SLAAC and DHCPv6

IPv6 offers multiple ways for hosts to get addresses. Understanding them matters for how you configure downstream interfaces.

SLAAC (Stateless Address Autoconfiguration)

Hosts hear Router Advertisements (RAs) and build their own address from the advertised prefix + their interface ID. No DHCP server needed. Enable it by sending RAs:

Router(config-if)# ipv6 nd ra-interval 200
Router(config-if)# no ipv6 nd ra suppress

Check that RAs are being sent:

Router# show ipv6 interface GigabitEthernet0/0/1 | include ND RA
  ND RAs are sent every 200 seconds

Stateless DHCPv6 (SLAAC + DNS)

SLAAC doesn’t hand out DNS servers. If you need DNS but want to keep address assignment stateless:

Router(config)# ipv6 dhcp pool STATELESS-POOL
Router(config-dhcpv6)# dns-server 2001:db8:acad::53
Router(config-dhcpv6)# domain-name ignaonline.local
Router(config-dhcpv6)# exit

Router(config)# interface GigabitEthernet0/0/1
Router(config-if)# ipv6 dhcp server STATELESS-POOL
Router(config-if)# ipv6 nd other-config-flag

The other-config-flag (O-flag) in RAs tells hosts: “Get your address via SLAAC, but query DHCPv6 for other options like DNS.”

Stateful DHCPv6

When you need full address assignment control:

Router(config)# ipv6 dhcp pool STATEFUL-POOL
Router(config-dhcpv6)# address prefix 2001:db8:acad:2::/64 lifetime 86400 3600
Router(config-dhcpv6)# dns-server 2001:db8:acad::53
Router(config-dhcpv6)# exit

Router(config)# interface GigabitEthernet0/0/2
Router(config-if)# ipv6 dhcp server STATEFUL-POOL
Router(config-if)# ipv6 nd managed-config-flag
Router(config-if)# ipv6 nd ra suppress

The M-flag tells hosts to use DHCPv6 for address assignment, not SLAAC.


Static IPv6 Routing

Static routes work just like IPv4 with one gotcha: when specifying a next-hop link-local, you must include the egress interface:

! Works:
Router(config)# ipv6 route 2001:db8:acad:10::/64 GigabitEthernet0/0/0 fe80::2

! Fails silently (link-local needs interface context):
Router(config)# ipv6 route 2001:db8:acad:10::/64 fe80::2

Default route:

Router(config)# ipv6 route ::/0 GigabitEthernet0/0/0 fe80::1

Verify:

Router# show ipv6 route static
IPv6 Routing Table - default - 5 entries
Codes: C - Connected, L - Local, S - Static, ...

S   2001:DB8:ACAD:10::/64 [1/0]
     via FE80::2, GigabitEthernet0/0/0
S   ::/0 [1/0]
     via FE80::1, GigabitEthernet0/0/0

OSPFv3 for IPv6

OSPFv3 is the evolution of OSPFv2 specifically for IPv6. While OSPFv2 embeds addresses in LSAs, OSPFv3 decouples the protocol from the address family — it runs over link-local addresses and supports both IPv4 and IPv6 via Address Family (AF) mode.

This is the approach you want on modern IOS-XE:

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
Router(config-router)# exit

Router(config)# interface GigabitEthernet0/0/0
Router(config-if)# ospfv3 1 ipv6 area 0
Router(config-if)# ospfv3 1 ipv6 network point-to-point

Verify neighbors (they’ll show as link-local addresses):

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/BDR        00:00:36    4               GigabitEthernet0/0/0
Router# show ospfv3 database
          OSPFv3 1 address-family ipv6 (router-id 10.0.0.1)

                Router Link States (Area 0)

ADV Router       Age         Seq#        Fragment ID  Link count  Bits
 10.0.0.1        42          0x80000003  0            1           B
 10.0.0.2        56          0x80000002  0            1           None

Redistribute connected networks and set a passive interface on LAN-facing ports:

Router(config)# router ospfv3 1
Router(config-router)# address-family ipv6 unicast
Router(config-router-af)# redistribute connected
Router(config-router-af)# passive-interface GigabitEthernet0/0/1

BGP for IPv6 (MP-BGP)

BGP supports IPv6 through the IPv6 unicast address family. You configure the neighbor relationship using IPv4 (or IPv6) peer addresses and activate them under the address-family ipv6:

Router(config)# router bgp 65001
Router(config-router)# bgp router-id 10.0.0.1
Router(config-router)# no bgp default ipv4-unicast
Router(config-router)# neighbor 2001:db8:acad:1::2 remote-as 65002
Router(config-router)# address-family ipv6 unicast
Router(config-router-af)# neighbor 2001:db8:acad:1::2 activate
Router(config-router-af)# network 2001:db8:100::/48
Router(config-router-af)# exit

Verify:

Router# show bgp ipv6 unicast summary
BGP router identifier 10.0.0.1, local AS number 65001
BGP table version is 5, main routing table version 5
4 network entries using 688 bytes of memory
4 path entries using 352 bytes of memory

Neighbor              V    AS MsgRcvd MsgSent  TblVer  InQ OutQ Up/Down  State/PfxRcd
2001:DB8:ACAD:1::2    4 65002      23      22       5    0    0 00:15:34         3
Router# show bgp ipv6 unicast
BGP table version is 5, local router ID is 10.0.0.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*> 2001:DB8:100::/48
                    ::                       0         32768 i
*> 2001:DB8:200::/48
                    2001:DB8:ACAD:1::2       0             0 65002 i

For more on BGP filtering and route manipulation, check out the BGP communities and route filtering deep dive — the same community and prefix-list mechanics apply to the IPv6 address family.


Dual-Stack Configuration

Running dual-stack (IPv4 + IPv6 simultaneously) is the safe migration strategy. Here’s a full interface example:

Router(config)# interface GigabitEthernet0/0/0
Router(config-if)# description WAN to ISP
Router(config-if)# ip address 203.0.113.1 255.255.255.252
Router(config-if)# ipv6 address 2001:db8:acad:1::1/64
Router(config-if)# ipv6 address fe80::1 link-local
Router(config-if)# no shutdown

Check both protocol stacks:

Router# show interfaces GigabitEthernet0/0/0 | include Internet|IPv6
  Internet address is 203.0.113.1/30

Router# show ipv6 interface brief GigabitEthernet0/0/0
GigabitEthernet0/0/0       [up/up]
    FE80::1
    2001:DB8:ACAD:1::1

ICMPv6 and Neighbor Discovery — Don’t Block It

ICMPv6 is not optional. Unlike IPv4 where ICMP is used mainly for diagnostics, ICMPv6 is fundamental to IPv6 operation. Neighbor Discovery Protocol (NDP) uses ICMPv6 to do everything ARP does in IPv4, plus router discovery and prefix advertisement.

If you have IPv6 ACLs, make sure these ICMPv6 types are permitted inbound:

Router(config)# ipv6 access-list INBOUND-IPV6
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 echo-request
Router(config-ipv6-acl)# permit icmp any any echo-reply
Router(config-ipv6-acl)# permit icmp any any packet-too-big
Router(config-ipv6-acl)# permit icmp any any time-exceeded

packet-too-big and time-exceeded are required for Path MTU Discovery. Block those and large TCP transfers silently stall — a classic “works in the lab, broken in production” failure mode.


IPv6 ACLs — Key Differences from IPv4

IPv6 access lists use named ACLs only (no numbered). Apply them with ipv6 traffic-filter:

Router(config)# ipv6 access-list BLOCK-EXTERNAL
Router(config-ipv6-acl)# deny ipv6 any 2001:db8:internal::/48
Router(config-ipv6-acl)# permit ipv6 any any

Router(config)# interface GigabitEthernet0/0/0
Router(config-if)# ipv6 traffic-filter BLOCK-EXTERNAL in

There is an implicit deny ipv6 any any at the end, same as IPv4. Unlike IOS-XE IPv4 ACLs, IPv6 ACLs automatically add implicit permits for NDP (neighbor solicitation and neighbor advertisement to/from link-local ff02:: addresses) to prevent you from accidentally breaking connectivity. Don’t rely on this — be explicit about NDP permits in any restrictive ACL.

Verify hits:

Router# show ipv6 access-list BLOCK-EXTERNAL
IPv6 access list BLOCK-EXTERNAL
    deny ipv6 any 2001:DB8:INTERNAL::/48 (42 matches)
    permit ipv6 any any (18730 matches)

IPv6 Prefix Delegation (DHCPv6-PD)

Prefix delegation lets an upstream router hand a downstream router an entire prefix to manage locally — useful in CPE deployments or when your ISP hands you a /48 to subdivide:

! Upstream router (delegating server)
Router(config)# ipv6 local pool PREFIX-POOL 2001:db8:acad::/48 56
Router(config)# ipv6 dhcp pool PD-POOL
Router(config-dhcpv6)# prefix-delegation pool PREFIX-POOL lifetime 86400 3600
Router(config-dhcpv6)# exit

Router(config)# interface GigabitEthernet0/0/0
Router(config-if)# ipv6 dhcp server PD-POOL
! Downstream router (requesting client)
Router2(config)# interface GigabitEthernet0/0/0
Router2(config-if)# ipv6 dhcp client pd PREFIX hint ::/56

Router2(config)# interface GigabitEthernet0/0/1
Router2(config-if)# ipv6 address PREFIX ::1/64

This technique slots naturally into tiered network designs — the same principle that ISPs use to delegate space to enterprise customers.


Troubleshooting IPv6 Connectivity

Quick diagnostic workflow:

Step 1: Check interface status and addressing

Router# show ipv6 interface brief
GigabitEthernet0/0/0   [up/up]
    FE80::1
    2001:DB8:ACAD:1::1
GigabitEthernet0/0/1   [up/up]
    FE80::1
    2001:DB8:ACAD:2::1

Step 2: Ping using link-local (specifying interface)

Router# ping fe80::2 source fe80::1%GigabitEthernet0/0/0 repeat 5
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to FE80::2, timeout is 2 seconds:
Packet sent with a source address of FE80::1
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/2 ms

Step 3: Verify neighbor cache (replaces ARP table)

Router# show ipv6 neighbors
IPv6 Address                              Age Link-layer Addr State Interface
FE80::2                                     0 c800.3120.0001 REACH Gi0/0/0
2001:DB8:ACAD:1::2                          3 c800.3120.0001 STALE Gi0/0/0

Step 4: Traceroute

Router# traceroute ipv6 2001:db8:acad:10::1 source GigabitEthernet0/0/0
Type escape sequence to abort.
Tracing the route to 2001:DB8:ACAD:10::1

  1 2001:DB8:ACAD:1::2 4 msec 4 msec 4 msec
  2 2001:DB8:ACAD:10::1 8 msec 8 msec 8 msec

Step 5: Check routing table

Router# show ipv6 route
IPv6 Routing Table - default - 8 entries
Codes: C - Connected, L - Local, S - Static, O3 - OSPFv3, B - BGP

O3  2001:DB8:ACAD:10::/64 [110/2]
     via FE80::2, GigabitEthernet0/0/0
C   2001:DB8:ACAD:1::/64 [0/0]
     via GigabitEthernet0/0/0, directly connected
L   2001:DB8:ACAD:1::1/128 [0/0]
     via GigabitEthernet0/0/0, receive
S   ::/0 [1/0]
     via FE80::1, GigabitEthernet0/0/0

Common Issues

  • NDP not resolving: Check that ICMPv6 ND types aren’t blocked by an ACL on the interface.
  • OSPFv3 neighbors stuck in INIT: Verify both sides have ipv6 unicast-routing enabled and check for ACLs blocking ICMPv6.
  • Large packets failing: Check that ICMPv6 packet-too-big messages aren’t being dropped — this breaks PMTUD and causes TCP hangs.
  • Addresses not showing up: Confirm ipv6 unicast-routing is enabled globally.

IPv6 Security Hardening on IOS-XE

A few quick wins for securing your IPv6 deployment:

RA Guard — Block Rogue Router Advertisements

Router(config)# ipv6 nd raguard policy BLOCK-RA
Router(config-nd-raguard)# device-role host
Router(config-nd-raguard)# exit

Router(config)# interface GigabitEthernet0/0/5
Router(config-if)# ipv6 nd raguard attach-policy BLOCK-RA

This prevents hosts on access ports from sending RAs that could redirect traffic or confuse SLAAC on other devices.

DHCPv6 Guard

Router(config)# ipv6 dhcp guard policy DHCP-SERVER-GUARD
Router(config-dhcp-guard)# device-role server
Router(config-dhcp-guard)# exit

Router(config)# interface GigabitEthernet0/0/6
Router(config-if)# ipv6 dhcp guard attach-policy DHCP-SERVER-GUARD

IPv6 Source Guard

On access switches, bind IPv6 addresses to ports to prevent spoofing:

Switch(config)# ipv6 source-guard policy SG-POLICY
Switch(config-sisf-policy)# deny global-autoconf
Switch(config-sisf-policy)# exit

Switch(config)# interface GigabitEthernet1/0/12
Switch(config-if)# ipv6 source-guard attach-policy SG-POLICY

For a comprehensive look at securing your network infrastructure, pair these controls with the management plane hardening guide — the control-plane and management-plane risks compound each other.


Quick Reference: Most Used IPv6 Show Commands

show ipv6 interface brief                  ! All interfaces, address, status
show ipv6 interface GigabitEthernet0/0/0   ! Detailed interface info
show ipv6 neighbors                        ! NDP cache (like 'show arp')
show ipv6 route                            ! Full IPv6 routing table
show ipv6 route ospf                       ! OSPFv3-learned routes only
show ipv6 route bgp                        ! BGP-learned IPv6 routes
show ipv6 protocols                        ! Running IPv6 routing protocols
show ospfv3 neighbor                       ! OSPFv3 adjacency state
show ospfv3 database                       ! LSA database
show bgp ipv6 unicast summary              ! BGP IPv6 neighbor summary
show bgp ipv6 unicast                      ! BGP IPv6 table
show ipv6 access-list                      ! IPv6 ACL hit counts
show ipv6 dhcp pool                        ! DHCPv6 pool status
show ipv6 dhcp binding                     ! Stateful DHCPv6 leases
debug ipv6 routing                         ! Route table changes (use with care)
debug ipv6 nd                              ! Neighbor discovery events

Migration Strategy: From IPv4 to Dual-Stack

Here’s the realistic path for an enterprise migration:

  1. Phase 1 — Infrastructure: Enable dual-stack on routers and distribution switches. Run OSPFv3 alongside OSPFv2. Assign IPv6 addresses to all infrastructure interfaces.
  2. Phase 2 — Services: Enable IPv6 on DNS (AAAA records), NTP, syslog, and SNMP. Update ACLs to include IPv6 permit rules.
  3. Phase 3 — Access Layer: Enable IPv6 on access switches. Deploy RA guard and DHCPv6 guard. Configure SLAAC or DHCPv6 for end hosts.
  4. Phase 4 — Validation: Run both protocols in parallel for at least 30 days. Monitor logs for ICMPv6 drops. Validate PMTUD by testing large file transfers end-to-end.

For the automation layer, Python with Netmiko handles IPv6 configuration beautifully — you’re just passing different strings to the same CLI functions. The network automation with Netmiko and NAPALM guide covers the tooling if you want to script your dual-stack rollout.


Final Thoughts

IPv6 on IOS-XE is mature, well-documented, and operationally predictable once you internalize a handful of differences from IPv4: link-local addressing on every interface, NDP replacing ARP, OSPFv3 peering over link-locals, and the absolute requirement to not block ICMPv6. The CLI is familiar — if you’ve configured OSPFv2 or BGP in IPv4, the IPv6 address-family equivalents will click within an hour.

The hard part is organizational, not technical: getting the addressing plan approved, updating documentation, and training tier-1 support staff on the new show commands. Start with a lab, build muscle memory on the verification commands, and the production rollout becomes straightforward.

If IPv6 is new territory, the best way to get comfortable fast is to spin up a few CSR1000V instances in GNS3 or EVE-NG and run through these examples until the commands are automatic. The network engineers who’ve done that work now won’t be scrambling when the next project requires native IPv6 from day one.

Enjoying this post?

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