OPNsenseXVI · IPv6IPv6 firewall
IPv6 transition pitfalls — dual-stack, NAT64, AAAA records, and what breaks
What you'll learn
- Recognise the failure modes of a partial IPv6 rollout
- Explain DNS AAAA ordering and how Happy Eyeballs changes the picture
- Plan for dual-stack pitfalls: routing, firewall rules, application behaviour
- Briefly describe NAT64/DNS64 and when it applies
- Identify the operational incidents that come from half-implemented IPv6
Prerequisites
Verified against OPNsense 25.x · FreeBSD 14.x · PF (FreeBSD packet filter) FreeBSD 14.x · Unbound 1.20+ · Kea DHCP OPNsense 25.x plugin · WireGuard in-kernel + OPNsense plugin · strongSwan (IPsec plugin) OPNsense 25.x plugin · OpenVPN 2.6.x · Suricata 7.x · 2026-08-14
Half-implemented IPv6 is worse than no IPv6 at all. A firewall with broken IPv6 produces incidents that look like routing failures, DNS failures, or application bugs — none of which are actually wrong. The operator who enables IPv6 on a host but does not enable it on the firewall, or vice versa, ends up debugging a problem that does not exist on either protocol in isolation but does exist in the gap between them.
This lesson covers the failure modes of a partial IPv6 rollout, the dual-stack gotchas the operator must plan for, the DNS AAAA ordering that determines which protocol clients use, and the brief NAT64/DNS64 pattern that some operators use to bridge IPv4-only destinations.
The half-implemented IPv6 incident pattern
The pattern: a firewall or a host has IPv6 partially enabled. Symptoms:
- “Application X cannot reach service Y” — but service Y is reachable over IPv4.
- “DNS returns an answer but the connection times out” — the AAAA record points to an address that does not work.
- “The host has IPv6 but cannot reach external IPv6” — outbound IPv6 is broken at the firewall.
- “The host reaches external IPv6 but cannot reach internal IPv6” — internal services are IPv4 only and the host prefers IPv6.
In every case, the root cause is the same: one side of the boundary is dual-stack, the other is single-stack, and the asymmetry surfaces as a connectivity failure.
DNS AAAA ordering — Happy Eyeballs
When a client resolves a hostname that has both A and AAAA records, the client has to choose which protocol to use. The two possibilities:
- AAAA first (RFC 6724 default): the client tries IPv6 first. If it fails (timeout, error), it falls back to IPv4.
- A first: the client tries IPv4 first. If it fails, it falls back to IPv6.
The RFC 6724 default is AAAA first. But a client with broken IPv6 connectivity waits for the IPv6 attempt to time out (typically 1-3 seconds) before falling back to IPv4. The user perceives a slow connection.
Happy Eyeballs (RFC 6555, RFC 8305) is the algorithm most modern browsers and HTTP libraries use to mitigate this:
- The client starts both an IPv4 connection and an IPv6 connection in parallel.
- Whichever connects first wins.
- The other connection is torn down.
With Happy Eyeballs, a broken IPv6 path does not slow the user-visible connection. Without it (older clients, some embedded systems, some custom applications), a broken IPv6 path is a 1-3 second delay per connection.
$ dig +short www.example.com A ; dig +short www.example.com AAAA93.184.216.34
2606:2800:220:1:248:1893:25c8:1946Illustrative output
Common dual-stack pitfalls
Five pitfalls appear repeatedly in production:
-
AAAA records pointing to dead addresses. A DNS server has stale AAAA records (e.g. for a server whose IPv6 address changed). Clients resolve the hostname, attempt to connect over IPv6, time out, fall back to IPv4. The fix: regular AAAA hygiene, or use CNAMEs that follow dynamic DNS.
-
Firewall rules written for IPv4 only. A firewall has excellent IPv4 rules but no IPv6 rules. IPv6 traffic hits the default-deny rule. The fix: mirror every IPv4 rule to IPv6.
-
Outbound NAT not configured for IPv6. Most deployments disable outbound NAT for IPv6 (IPv6 hosts have end-to-end connectivity). But if outbound NAT is misconfigured, IPv6 traffic from the LAN cannot reach the Internet. The fix: verify the IPv6 routing and confirm outbound NAT is disabled or correctly configured.
-
Application-level IPv6 issues. Some applications are dual-stack-aware but have bugs. A custom application might bind to
0.0.0.0only (IPv4) and refuse IPv6 connections. The fix is application-level: the operator updates the application or works around it. -
MTU mismatches. IPv6 requires Path MTU Discovery. If a path has a smaller MTU than the endpoints assume, packets are dropped silently (no fragmentation in IPv6). The fix: ensure MTU 1500 end-to-end or use MSS clamping in the firewall.
NAT64 and DNS64 — brief
For networks that want IPv6-only hosts but reach IPv4-only destinations, NAT64 + DNS64 is the bridge:
- DNS64 synthesises an AAAA record for an IPv4-only destination by embedding the IPv4 address into a well-known prefix (typically
64:ff9b::/96). The IPv6-only host receives a synthetic AAAA record and connects. - NAT64 translates the IPv6 packet to an IPv4 packet at the gateway, forwards to the IPv4-only destination, and translates the response back to IPv6.
The IPv6-only host thinks it is talking to an IPv6 destination; the IPv4-only destination thinks it is talking to an IPv4 client. NAT64 in the middle makes it work.
OPNsense has a NAT64 plugin (os-nat64) that implements the translation. The operator configures:
- The NAT64 prefix (
64:ff9b::/96by default per RFC 6052). - The DNS64 server (or DNS64 function in Unbound).
- The translation rule (typically: any IPv6 source, destination in NAT64 prefix → translate and forward).
NAT64 is not a replacement for proper dual-stack; it is a tool for the specific scenario of “IPv6-only client network that needs to reach IPv4-only destinations”. The operator who deploys NAT64 should know exactly why and what the tradeoffs are.
The IPv6 rollout checklist
A clean IPv6 rollout on OPNsense has eight steps:
- Enable IPv6 on WAN. DHCPv6 client with prefix delegation.
- Verify PD received.
ifconfig <wan>shows an IA_NA and an IA_PD. - Plan the address scheme. /64 per subnet, ULA for internal, global for external.
- Configure track-interface on LANs. Each LAN gets a /64 from the delegated prefix.
- Enable RA service on LANs. Mode “Assisted” (M=0, O=1) for SLAAC + DHCPv6 DNS.
- Mirror firewall rules. Every IPv4 rule has an IPv6 counterpart.
- Verify connectivity. Test from a host with IPv6 enabled; confirm global IPv6 reachability.
- Publish AAAA records. Internal services get ULAs; external services get global addresses. Update DNS.
If any step is skipped, the rollout is incomplete and the operator should expect incidents. The discipline is in completing the list, not in declaring IPv6 “done”.
Summary
- Half-implemented IPv6 is worse than no IPv6: it produces incidents that look like routing or DNS failures on either protocol.
- DNS AAAA ordering and Happy Eyeballs determine whether broken IPv6 is “slow” or “broken” for users.
- Five common dual-stack pitfalls: stale AAAA records, missing IPv6 rules, broken outbound NAT, application-level issues, MTU mismatches.
- NAT64 + DNS64 is a specific tool for IPv6-only networks reaching IPv4-only destinations, not a default.
- Test IPv6 separately from IPv4; the monitoring system should distinguish them.
- The IPv6 rollout checklist: enable on WAN, verify PD, plan scheme, track on LANs, RA on LANs, mirror rules, verify connectivity, publish AAAA.
Knowledge check · 4 questions
Q1. A dual-stack host connects to a service. The hostname has both A and AAAA records. The IPv6 connection times out, then the host falls back to IPv4 and succeeds. The user perceives a 2-second delay. What is the most likely cause?
Q2. NAT64 with DNS64 is a default IPv6 deployment tool that should be used on every dual-stack OPNsense firewall to bridge IPv4-only destinations.
Q3. Which of the following are common dual-stack pitfalls in production? Select all that apply.
Q4. You are debugging "application X cannot reach service Y". The host is dual-stack. The service has both A and AAAA records. Both protocols are enabled. What is the fastest diagnostic step?
Passing score: 75%. Answers are checked in this browser.