⚠️ Modernization Notice These notes were written in 2018–2019. IPv6 CPE and OS support, DHCPv6 implementations, and ISP best practices have evolved significantly since then. Treat this as a record of real testing and the architectural decisions behind it, and validate against current RFCs, CPE datasheets, and OS release notes before deploying in production.

Overview

This post brings together two pieces of work I did on IPv6 for PPP broadband. The first is a set of notes and lab tests on running a single dual-stack PPP session per subscriber and assigning IPv6 prefixes to the CPE from a Cisco IOS XE BNG. The second is a design sketch for assigning static IPv6 prefixes per customer from a RADIUS/AAA backend. They belong together: the lab work shows how Cisco ISG/IOS XE actually behaves when prefixes arrive over RADIUS, and the design sketch is the natural next step once you decide that static, per-customer prefixes are what you want.

The whole topic splits naturally into two halves: the ISP↔CPE link, and the CPE↔host LAN. How far you can push depends heavily on CPE capabilities and end-user operating systems. These notes focus mostly on the ISP↔CPE side, where IPv6 is enabled per customer. In our setup IPv6 options live under the virtual-template, and IPv6 is switched on per subscriber with a RADIUS Cisco AVpair:

1
cisco-avpair="lcp:interface-config#1=ipv6 enable"

Worth reading first:

MTU Considerations

Some intermediate nodes on the Internet block ICMPv6, which breaks Path MTU Discovery. If you hit reachability problems, lower the MTU and adjust TCP MSS — this works for IPv6 just as it does for IPv4:

1
2
mtu 1492
ip tcp adjust-mss 1452

ISP–CPE IPv6 PPP Model

Single PPP Dual-Stack Session

Using a single PPP session for both IPv4 and IPv6 is far more efficient than running separate sessions, and it is widely supported on both CPE and BNG. Separate sessions double the PPP session count on the BNG and complicate every subscriber resource that is keyed on the session (AAA, RADIUS, accounting). Most CPE today default to dual-stack on a single session — the main thing is to make sure IPv6 is actually enabled, because on many CPE it ships disabled.

Static /64 Prefixes vs. Dynamic Assignment

The Broadband Forum suggests a /60 per broadband customer, but a /64 is enough for typical consumers. Either way, plan your address blocks with plenty of headroom toward /60. I prefer static prefix assignment per customer because it removes most of the operational pain:

AspectStaticDynamic (non-persistent)
AuditingSimple: prefix → customer mapping lives in your databaseHarder: needs separate DHCPv6 logging, since the delegated prefix is not in session accounting
Prefix changes (CPE/DSLAM reboot)NoneCPE must tell LAN hosts to re-lease DHCPv6 prefixes — many CPE handle this badly
AccountingPrefix is in the session recordsDHCPv6 runs after session establishment, so accounting must be updated afterward
NATNot used and not neededNot used and not needed
SecurityCPE firewall / customer access device featuresCPE firewall / customer access device features

If you do go dynamic, remember that the Delegated-IPv6-Prefix is not included in the accounting records (it is meant for DHCPv6), so you have to enable DHCPv6 logging or periodic accounting updates to capture it:

1
aaa accounting update newinfo periodic 60

(Periodic updates may not be necessary in your environment.)

Static assignment sidesteps most of this. There is no NAT for IPv6 and none is needed — security is left to the CPE firewall and the customer’s own access devices. Assign the prefix when you provision the customer in your back office. Avoid mapping username or port directly to a prefix (that leaks information); use an offset or similar scheme instead, and remember to return unused prefixes to the pool.

SLAAC and Stateful DHCPv6

There are several ways to get a prefix from the ISP to the CPE:

  • SLAAC relies on ND Router Advertisement (RA) messages. The CPE needs RA in any case to install its default route, and the RA carries the prefix used for the WAN link.
  • DHCPv6 is generally how the CPE obtains the LAN prefix (prefix delegation). Some CPE can also derive a LAN prefix from the RA-received prefix instead.
  • DNS can come from IPv4 in a dual-stack environment. RA can announce DNS (RFC 8106 / RFC 6106), but support is uneven across CPE; DHCPv6 is the more reliable channel.

You can use different prefixes for the WAN side and the LAN side of the CPE, and in a real deployment you usually should.


IPv6 Prefix Delegation for PPP Customers

RADIUS Attributes

AttributePurpose
Framed-IPv6-PrefixPrefix advertised by SLAAC/ND on the WAN link
Delegated-IPv6-PrefixPrefix handed to the CPE LAN via DHCPv6-PD
Framed-IPv6-PoolPool name for dynamic SLAAC prefix selection
Delegated-IPv6-Prefix-PoolPool name for dynamic DHCPv6 delegation (RFC 6911)
Framed-Interface-IdInterface identifier the CPE may use as the lower 64 bits of its WAN address

Delegated-IPv6-Prefix is not carried in accounting messages, because it is expected to be used with DHCPv6. If you rely on it, enable DHCPv6 accounting so the delegated prefix lands in the session’s accounting records.

Cisco ISG/IOS XE DHCPv6 Fallback Logic

Cisco ISG/IOS XE has a useful fallback when you point DHCPv6 at AAA for prefix delegation:

  1. If you configure AAA for DHCPv6 and a Delegated-IPv6-Prefix comes back in the Access-Accept, that prefix is used as the DHCPv6 prefix.

  2. If AAA is configured for DHCPv6 but no prefix is returned, the BNG makes a second AAA request just to fetch the prefix.

  3. You can avoid that second request by feeding the Framed-IPv6-Prefix to the DHCPv6 server. To do that, stop ND from advertising it as an on-link prefix:

    1
    
    no ipv6 nd prefix framed-ipv6-prefix
    

    Now a single attribute can drive both the WAN (ND/SLAAC) and LAN (DHCPv6) prefix.

  4. If both Framed-IPv6-Prefix and Delegated-IPv6-Prefix are present, the delegated prefix wins for the DHCPv6 server.

You can also send a pool name instead of a fixed prefix via Framed-IPv6-Pool (SLAAC) or Delegated-IPv6-Prefix-Pool (DHCPv6, RFC 6911).

Framed-Interface-Id

From Broadband Forum TR-187:

In contrast with IPCP, which provides other configuration information, IPV6CP only negotiates an interface identifier. Other configuration information is provided via Neighbor Discovery and DHCPv6. The 64-bit interface identifier is used to derive the Link-local address on the WAN interface.

If Framed-Interface-Id is returned in the Access-Accept, the BNG offers it to the CPE during IPV6CP. A CPE that honors it will use that identifier as the lower 64 bits of its WAN IPv6 address — effectively a predefined /128. How you construct the interface-id is up to the ISP; a hash over customer information works, as long as you watch for collisions.


Configuration: Cisco IOS XE ASR1000

Our IPv4 model is built on RADIUS. Dynamic customers draw from IPv4 pools defined on the BNG; static customers have their address returned via Framed-IP-Address in the Access-Accept. We deliberately avoid RADIUS-based pools for IPv4 because tracking and releasing resources for large numbers of customers is hard — the BNG does this far better, releasing the resource when the PPP session drops.

For IPv6 we wanted the same property: static prefix assignment per customer, with the dynamic (pool) option still available. For the tests below I used the same prefix on the WAN and LAN sides to keep things simple, but in production they would normally differ.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
ipv6 dhcp binding track ppp
! Even if you do not use a local pool, this keeps the same prefix
! across customer reconnects and flaps.

aaa authorization configuration IPv6_PD group TEST_RADIUS

ipv6 dhcp pool AAA
 prefix-delegation aaa method-list IPv6_PD
 dns-server 2001:4860:4860::8888
 dns-server 2001:4860:4860::4444

interface Virtual-Template3
 mtu 1492
 ip unnumbered LoopbackX
 ip verify unicast reverse-path
 no ip split-horizon
 ip tcp adjust-mss 1452
 no logging event link-status
 ipv6 unnumbered LoopbackX
 ipv6 nd managed-config-flag
 ipv6 nd ra dns server 2001:4860:4860::4444
 no ipv6 redirects
 no ipv6 unreachables
 ipv6 dhcp server AAA
 ipv6 verify unicast reverse-path
 no peer default ip address
 keepalive 60 3
 ppp authentication pap pppoe
 ppp authorization pppoe
 ppp accounting pppoe
 service-policy type control PPPOE
 end

We run Internet access inside a VRF, so the per-customer AVpair also re-asserts the unnumbered source interface (the VRF-forwarding service policy otherwise strips the source address):

1
cisco-avpair="lcp:interface-config#1=ipv6 enable\nipv6 unnumbered loopbackX"

Per-Customer RADIUS Attributes

AttributeExamplePurpose
Framed-IPv6-Prefix2a02:ff0:00f0:0003::/64Static WAN prefix (SLAAC)
Delegated-IPv6-Prefix2a02:ff0:00f0:0002::/64Static LAN prefix (DHCPv6)
Framed-IPv6-Pooldynamic-pool-1Dynamic WAN prefix pool name
Delegated-IPv6-Prefix-Pooldynamic-pd-pool-1Dynamic LAN prefix pool name
Framed-Interface-IdDEDE:DEDE:DEDE:DEDECPE WAN interface ID

Customer Profiles

ProfileWANLANNotes
Static WAN + Static LANFramed-IPv6-PrefixDelegated-IPv6-PrefixIf you insist on the same prefix for both (strongly discouraged), add no ipv6 nd prefix framed-ipv6-prefix
Dynamic WAN + Static LANFramed-IPv6-PoolDelegated-IPv6-PrefixMost usable option for broadband
Static WAN + Dynamic LANFramed-IPv6-PrefixDelegated-IPv6-Prefix-PoolEasier CPE management if you compute the CPE address from Framed-Interface-Id; assumes no TR-069
Dynamic WAN + Dynamic LANFramed-IPv6-PoolDelegated-IPv6-Prefix-PoolFully dynamic

Tests on Cisco IOS XE (ASR1000)

A few things about the lab are worth stating up front, because they shaped the results. I used a ZTE CPE and macOS as the host. The ZTE CPE in auto mode cannot get a GUA from a DHCPv6-delegated prefix — it wants SLAAC; manual configuration changes that. macOS has no DHCPv6 client at all and configures itself purely from RA. So in this lab, the host’s GUA always comes from SLAAC, and DHCPv6-PD is about what the CPE receives for its LAN.

Each test below keeps the key debug lines inline; the complete per-session debug is linked beneath it.

Test 1 — Framed-IPv6-Prefix alone (why you need no ipv6 nd prefix framed-ipv6-prefix)

In this first run the customer profile returns only Framed-IPv6-Prefix (plus Framed-Interface-Id), and the virtual-template does not carry no ipv6 nd prefix framed-ipv6-prefix. The Access-Accept looks like this:

1
2
3
4
5
6
RADIUS: Received from id 1645/109 X.X.X.X:1812, Access-Accept, len 473
RADIUS:  Framed-IPv6-Prefix  [97]  20
RADIUS:   00 40 2A 02 0F F0 00 F0 00 03 00 00 00 00 00 00 00 00   [ @*]
RADIUS:  Framed-Interface-Id [96]  21
RADIUS:   44 45 44 45 3A 44 45 44 45 3A 44 45 44 45 3A 44  [DEDE:DEDE:DEDE:D]
RADIUS:   45 44 45               [ EDE]

The Framed-Interface-Id is honored during IPV6CP — the BNG NAKs the CPE’s proposed identifier and the CPE accepts the one from AAA:

1
2
3
Vi2.1 IPV6CP AUTHOR: Says use id 4445:4445:3A44:4544 ! Framed-Interface-Id from access-accept
Vi2.1 IPV6CP: O CONFNAK [REQsent] id 1 len 14
Vi2.1 IPV6CP:    Interface-Id 4445:4445:3A44:4544 (0x010A444544453A444544)

The WAN prefix is advertised in RA, so SLAAC works. But because we did not redirect the framed prefix to DHCPv6, the CPE’s DHCPv6 SOLICIT for an IA-PD has nothing to bind to, and the router discards it:

1
2
3
IPv6 DHCP: Received SOLICIT from FE80::4445:4445:3A44:4544 on Virtual-Access2.1
IPv6 DHCP_AAA: Can't initialise authorization request
IPv6 DHCP: Discard message due to AAA failure

So Framed-IPv6-Prefix on its own gives you a WAN prefix via SLAAC but no LAN delegation. That is exactly the gap no ipv6 nd prefix framed-ipv6-prefix closes. Full session debug: test-1-framed-ipv6-prefix-without-dhcpv6.md.

Test 2 — no ipv6 nd prefix framed-ipv6-prefix (the configuration we use)

Here the virtual-template adds the one missing line:

1
2
3
4
5
6
7
8
interface Virtual-Template3
 ...
 ipv6 unnumbered loopbackX
 no ipv6 nd prefix framed-ipv6-prefix
 ipv6 nd ra dns server 2001:4860:4860::4444
 ...
 ipv6 dhcp server AAA
 ...

Now the single Framed-IPv6-Prefix from RADIUS feeds both sides: ND still advertises the prefix for SLAAC on the WAN, and the same prefix is available to the DHCPv6 server for the LAN delegation. This time the SOLICIT is answered from the AAA pool:

1
2
3
4
5
6
7
8
IPv6 DHCP: Received SOLICIT from FE80::4445:4445:3A44:4544 on Virtual-Access2.1
IPv6 DHCP: Using interface pool AAA
IPv6 DHCP_AAA: Got prefix 2A02:FF0:F0:3::/64
...
  option IA-PD(25), len 41
    option IAPREFIX(26), len 25
      preferred 604800, valid 2592000, prefix 2A02:FF0:F0:3::/64
IPv6 DHCP: Sending ADVERTISE to FE80::4445:4445:3A44:4544 on Virtual-Access2.1

The subsequent REQUEST creates the binding and installs the delegated route:

1
2
IPv6 DHCP: Allocating prefix 2A02:FF0:F0:3::/64 in binding for FE80::4445:4445:3A44:4544, IAID 000003E8
IPv6 DHCP: Route added: 2A02:FF0:F0:3::/64 via FE80::4445:4445:3A44:4544 dist 1 iaid 000003E8 vrf INTERNET

This is the case we run in practice. Because the prefix arrives as Framed-IPv6-Prefix, it shows up in the session’s accounting records, which aligns IPv6 auditing with our existing IPv4 method — no separate DHCPv6 logging required for this first phase. Full session debug: test-2-dynamic-wan-static-lan-recommended.md.

Test 3 — Both Framed and Delegated prefixes

In the last run the profile returns both Framed-IPv6-Prefix (…:0003::/64) and Delegated-IPv6-Prefix (…:0002::/64). Because the delegated prefix is present, no ipv6 nd prefix framed-ipv6-prefix is no longer needed:

1
2
3
4
5
RADIUS: Received from id 1645/112 X.X.X.X:1812, Access-Accept, len 493
RADIUS:  Delegated-IPv6-Prefi[123] 20
RADIUS:   00 40 2A 02 0F F0 00 F0 00 02 00 00 00 00 00 00 00 00   [ @*]
RADIUS:  Framed-IPv6-Prefix  [97]  20
RADIUS:   00 40 2A 02 0F F0 00 F0 00 03 00 00 00 00 00 00 00 00   [ @*]

The two prefixes now do separate jobs. ND advertises the framed prefix (…:0003) for SLAAC on the WAN, while DHCPv6 hands out the delegated prefix (…:0002) for the LAN:

1
2
3
4
5
IPv6 DHCP_AAA: Got prefix 2A02:FF0:F0:2::/64
IPv6 DHCP: Allocating prefix 2A02:FF0:F0:2::/64 in binding for FE80::4445:4445:3A44:4544, IAID 000003E8
IPv6 DHCP: Route added: 2A02:FF0:F0:2::/64 via FE80::4445:4445:3A44:4544 dist 1 iaid 000003E8 vrf INTERNET
...
ICMPv6-ND:   prefix 2A02:FF0:F0:3::/64 [LA] 2592000/604800

This confirms the precedence: when both are present, the delegated prefix drives DHCPv6 and the framed prefix drives ND. Using both with the same value is pointless; the value of this mode is precisely that WAN and LAN can differ. Full session debug: test-3-separate-framed-and-delegated-prefixes.md.


IPv6 Static Prefix Assignment via RADIUS (a design sketch)

This second part is a design I worked through but did not lab-test. Treat it as a starting point, not a validated recipe.

As the tests above show, dynamic prefix assignment causes trouble: depending on lifetimes, customer devices can end up holding several prefixes at once, and the audit story is messy. So the obvious question is how to assign a static prefix per customer from AAA.

There are a few approaches. The prefix can be assigned at customer provisioning time. If you have more than one PE, you should manage prefix pools per PE and assign from the customer’s PE — otherwise you lose route summarization on the PE. That means keeping a mapping between customers and their PE.

For AAA-based services the assignment happens in the AAA server. Doing it dynamically (allocate on session start, release on session end) drags in all the pool-management drawbacks we were trying to avoid. A mapping-based approach is cleaner: keep a table that maps customer (and optionally NAS) to a prefix. If you run DSLAMs, you might even map a prefix to each port, so a customer inherits the prefix of whatever port they connect to.

The flow behaves a little like a dynamic pool, but the binding is sticky:

When a prefix request comes in, the AAA server looks up the customer in the mapping. If found, it returns the existing prefix. If not, it picks a free prefix, assigns it, and returns it. Including the NAS in the mapping lets a customer move between PEs (an infrequent move — adding a new NAS and spreading users across it, or a customer relocating — not Wi-Fi-style roaming).

Table format

1
NasIPAddress | prefix | username | timestamp

Constraints:

  • NasIPAddress + prefix must be unique.
  • prefix + username must be unique.
  • To seed it, insert every prefix for a given NAS with an empty username.

AAA flow

Look up the customer’s existing prefix:

1
2
3
SELECT prefix FROM prefix_table
WHERE username = '{User-Name}'
  AND NasIPAddress = '{Nas-Ip-Address}';

If nothing is found, claim a free prefix for this user. In a multithreaded AAA server two threads can race on the same row, so randomize which free row you take to spread the contention:

1
2
3
4
5
UPDATE prefix_table
SET username = '{User-Name}', timestamp = '{current_timestamp}'
WHERE username = ''
  AND NasIPAddress = '{Nas-Ip-Address}'
LIMIT 1;

Then read it back. The earlier UPDATE may have lost the race to another thread, so the re-SELECT is what tells you whether you actually own a prefix:

1
2
3
SELECT prefix FROM prefix_table
WHERE username = '{User-Name}'
  AND NasIPAddress = '{Nas-Ip-Address}';

If this still returns nothing, the allocation failed — reject the session and let the CPE retry. If a NAS runs out of free prefixes, that reject is also your signal to raise a warning for that NAS.

This gives a subscriber the ability to move between PEs while keeping a stable prefix. You do have to clean the table up for such users: find duplicates and keep the latest record.


Reference: IPv6 Address Assignment Methods

A quick recap of the mechanisms referenced throughout, since the right combination depends on your CPE and host OS.

Static — customer and ISP agree on the prefix in advance; assigned at account creation.

SLAAC — based on RA, and effectively required on every link for proper IPv6 operation. RA carries prefixes and lifetimes, some flags, and the default router and its lifetime. For DNS, RFC 6106 (later RFC 8106) adds a DNS option to RA — check CPE and host support.

DHCPv6 stateless — still uses SLAAC for the prefix and router; DHCPv6 only supplies DNS. The host sends RS, the router replies with an RA carrying the other-config flag, and the host then queries DHCPv6 for DNS.

DHCPv6 stateful — SLAAC only kicks off the process. The host sends RS, the router replies with the managed-config flag, and the host configures prefix and DNS over DHCPv6 (using its link-local address). If no router is found, the host may start stateful DHCPv6 on its own.

DHCPv6 prefix delegation — the ISP↔CPE case. An extension to DHCPv6 that delegates a set of subnets rather than a single address.

OS behavior also depends on the combination of the A bit and the M/O flags; the IETF draft “DHCPv6/SLAAC Interaction Problems on Address and DNS Configuration” covers the failure modes.

FlagMeaningEffect on the host
M (Managed)Get the address/prefix from DHCPv6Host uses DHCPv6, not SLAAC, for addressing
O (Other)Get other config from DHCPv6Host uses SLAAC for the prefix, DHCPv6 for DNS
A (Address/autonomous)Use the RA prefix for SLAACHost derives its address from the RA prefix

A note on logging

The ISP logs the customer’s assigned prefix (a /64 or shorter), either via a RADIUS AVP or from the static mapping. Logging individual host addresses is much harder for enterprise customers, and IPv6 Privacy Extensions — on by default in most operating systems — make it worse: the OS generates a fresh temporary address per service, so a single user produces a great many addresses, and the hashed generation complicates troubleshooting. It can be turned off at the OS level; check current guidance for the OS in question. This is another argument for logging the prefix, not the address.


Summary

The practical conclusions from this work:

  1. Run a single dual-stack PPP session per subscriber — more efficient than separate IPv4/IPv6 sessions.
  2. Prefer static /64 (or /60) prefixes — simpler auditing and customer management, and no NAT to worry about.
  3. On Cisco IOS XE, no ipv6 nd prefix framed-ipv6-prefix lets one attribute drive both WAN and LAN (Test 2) — and keeps the prefix in session accounting.
  4. Use separate Framed- and Delegated- prefixes when WAN and LAN must differ (Test 3); the delegated prefix wins for DHCPv6.
  5. For static per-customer assignment, a mapping table in AAA beats dynamic pools — at the cost of writing careful, race-safe lookup logic.
  6. Log the prefix, not the address — Privacy Extensions make per-address logging impractical.

References


The content of this post was created by the author; AI was used for editing and restructuring.