[How-To] NPTv6 with dynamic prefix delegation (and some thoughts)

Started by OPNenthu, May 02, 2026, 11:31:11 PM

Previous topic - Next topic
Just some notes on the configuration because I didn't find a tutorial here.  Let me know if it exists and I'll link to it.

I'll give some thoughts about this in post #2.

If you're looking to set up IPv6 the right way, this is not it.  Please go here instead.

---

(Tested on OPNsense 26.1.7_1-amd64)

The objective is to use static ULA /64 prefixes on internal interfaces (e.g. LAN) which are controlled by you and independent of the ISP, but which get translated on egress to a delegated prefix.  The first 64 bits of the ULA get overwritten but the lower 64 (host) bits remain intact.  This means if you use privacy extensions on a web client those temporary address host bits will be preserved.

This is how it might look in firewall logs:

You cannot view this attachment.

You need at least one internal interface tracking the delegated prefix from WAN.  This is a required input to the NPTv6 rule so that it knows which external prefix to translate to.  I think it's required that your ISP delegates at least a /60 so that one of the prefix IDs can be used for the tracking interface, but don't quote me on this.  I don't know how you would track a /64 delegation and you unfortunately cannot have the NPTv6 rule just use whatever is assigned on WAN already (see Note 3 below).

You can reuse the same tracking interface in multiple NPTv6 rules, however.

You can also use a dedicated loopback as the tracking interface in case you don't already have one.  In my testing the new 'Identity Association' (idassoc) mode works for this purpose though I guess you can also use 'Track Interface (legacy)' (track6).  I haven't tried it.  Take care to disable any automatic radvd configs that get created in that case.

1. Go to Interfaces->Devices->Loopback and add new device.
2. Go to Interfaces->Assignments and assign this device with a name.
3. Go to Interfaces->[name] and enable it.
- Leave the IPv4 config set to None.
- Change the IPv6 config to "Identity Association" and choose your WAN as the parent interface.
- Choose an available prefix ID and save.

Note 1: don't add any rules on this interface as it won't be passing traffic.  It's just there to hold the prefix.

You cannot view this attachment.

Finally the NPTv6 rule itself:

1. Go to Firewall->NAT->NPTv6 and click the "+" button to add a new rule.
- Set "Interface" to WAN (this is the interface that the rule applies to for outbound translation)
- Set "Internal IPv6 prefix" to one of your ULA prefixes for an internal network.
- Leave "External IPv6 prefix" blank.
- Set "Track Interface" to the internal interface tracking the prefix, or the loopback we set up earlier.
- Save.

Repeat for additional ULA prefixes you need to translate.

Note 2: you don't need to specify the internal interface anywhere.  NPTv6 cares only about prefixes.

Note 3: if you don't specify either a tracking interface or an external prefix for translation (which we don't use for dynamic prefixes), there is currently a validation bug that will not stop you from doing this and it will cause IPv6 services that you have listening on WAN (e.g. WireGuard tunnels) to break.

And that should be it. This is how it might look for several subnets getting translated to the same tracking interface's prefix:

You cannot view this attachment.
N5105 | 8/250GB | 4xi226-V | Community

https://www.youtube.com/watch?v=XI9NG068TwI

Some thoughts:

For me this is an experiment to see if it helps with some specific issues (and to learn something new) but I don't plan to keep this configuration long term.  The main problem is that if IPv4 is enabled then many web clients and browsers will prefer that over a ULA for egress, so IPv6 will almost never be used except as a fallback.  I think there are ways to configure that on a per-client basis but I'm not going to try.

(UPDATE: On a subnet with Android mobile clients I see much higher IPv6 usage, so those may already be de-prioritizing IPv4.)

I think this method becomes more attractive in an IPv6 only network where the ULA route is the only one, because then you can have true independence from the ISP's delegation for internal networks and there is nothing competing with the ULA for priority.  Still, it flies against the IPv6 design principle where everything is globally addressable and any kind of NAT (even just prefix translation) should not be needed.  As already pointed out, addressability != reachability.

There are however a few annoyances that this attempts to work around and they mainly have to do with challenges associated with dynamic prefixes.  Put bluntly, many ISPs are not following RIPE recommendations[1] for persistent /48 prefix assignments.  Most of the below reasons stem from that fateful decision.

I think the most obvious reason is if you don't have enough /64 prefixes for your subnetting.  If your ISP only delegates a /60 and you need more than 16 subnets (or 15+WAN, because in DHCPv6-PD the WAN interface consumes one) then you might consider using ULAs for additional subnets and translating them to one of the existing prefixes.

A second reason is because of firewalling challenges[2] related to dynamic GUAs.  In some cases it's difficult to enforce boundaries using only the network aliases that OPNsense maintains.  For example, you might be tempted to create an alias named "IPV6_INTERNET" and define it as 2000::/3, then use that in filters or policy routing.  The problem is that your GUAs are included in that and (at least as of 26.1) you have no way to exclude your dynamic prefixes.

A third reason is because some particular quirks may affect you.  My ISP gives me a long-lived prefix which persists even after modem reboots.  The ISP also reboots the modem weekly for updates and network maintenance.  This causes the prefix to become deprecated and clients should not use the same prefix again, by some unfortunate language[3] in section 3.5 of RFC 8981.  The effect is that until/unless clients reset their network interfaces, SLAAC fails to generate new addresses which is particularly insidious if you rely on temporary addresses with IPv6 privacy extensions.  If there's one thing I cannot abide, it's silent failure[4] of privacy related functions and in my recent testing NPTv6 does fix this.

All said however, I think NPTv6 for these use cases is still not the second, third, or even fourth best alternative.  Before this is considered at all I would look at something like ndp-proxy-go[5] or the trick of borrowing an unused prefix from somewhere[6], if you can.  Just beware that some ISPs (like mine) do Stupid ThingsTM with RAs (like advertising ULA routes) so the ndp-proxy is not recommended then.

I'll mention also this article[7] linked by @JavierĀ® in case you are interested in a true NAT option, although the described solution is centric to the OpenBSD flavor of pf and would need some adaptation for OPNsense.  This one still has the same problem of ULA priority as NPTv6, though.

NAT impressions:

Regarding outbound NAT, in my testing I see that NPTv6 does not seem to interfere with it.  I have a VPN interface with an attached gateway and traffic leaving that interface is NATed to the wireguard tunnel address.  I think because the Outbound NAT / SNAT rules have higher precedence than the NTPv6 / binat rules in the pf ruleset those are overriding the NPTv6 rules:

You cannot view this attachment.

root@firewall:~ # cat /tmp/rules.debug | grep nat
...
nat log on wg1 inet from any to any -> (wg1:0) port 1024:65535 # SNAT on WAN_VPN0
nat log on wg1 inet6 from any to any -> (wg1:0) port 1024:65535 # SNAT on WAN_VPN0 (IPv6)
...
binat log on igc1 inet6 from fd5a:xxxx:xxxx:1002::/64 -> (lo1:0)/64 # NPTv6 WAN<->VPN (/64)

So interactions with outbound NAT seem safe.

Finally, what about ingress?  Well, for hosts behind the firewall I would guess that NAT port forwards / DNAT should still work.  I don't presently host anything and haven't tested it.


[1]: https://www.ripe.net/publications/docs/ripe-690/
[2]: https://github.com/opnsense/core/issues/7000
[3]: https://www.rfc-editor.org/rfc/rfc8981.pdf
[4]: https://forum.opnsense.org/index.php?topic=44435.0
[5]: https://forum.opnsense.org/index.php?topic=44071.0
[6]: https://forum.opnsense.org/index.php?topic=31374.msg151647#msg151647
[7]: https://eradman.com/posts/ipv6-strategy.html
N5105 | 8/250GB | 4xi226-V | Community

https://www.youtube.com/watch?v=XI9NG068TwI