IPv6-only at home + NAT64 and DNS64

I’ve broken this topic out from the Time to get serious about my router discussion over the last few months.

Inspired by:

Just kidding, @jdownie :rofl:! But, as one of the group’s resident IPv6 nutters enthusiasts, I thought it was worth a post for those HLBers who might be hesitant to dip their toe into the IPv6 waters, or even want to leap straight in and go IPv6-only!

As I mentioned in that thread, I’ve been running IPv6 in various forms (e.g., native DHCPv6-PD, via Tunnel Brokers) for close to 20 years now, but they’ve all been Dual Stack (i.e., along side IPv4). This weekend was my first foray into a completely IPv6-only deployment at home and I thought it was worth opening the discussion for anyone else who wants to take the plunge and ditch IPv4.

Assuming that your ISP provides a v6 prefix, getting IPv6 Dual Stack going is straightforward. There may be some minor configuration (e.g., prefix length) on your router, but there’s a very good chance that you’re already using IPv6 without realising it. However, IPv6 adoption is an ongoing process, and the global adoption rate is about 50%, with Australia sitting somewhat below that overall (Source: Internet Society Pulse). Some large websites and services also have limited adoption or even no IPv6 adoption (e.g., GitHub remains IPv4 only).

Going fully IPv6-only at home will therefore “break stuff”, specifically “IPv4-only stuff”. To address this, there are various transition mechanisms, but I chose to use NAT64 and DNS64 on this occasion. Tayga and Jool both provide NAT64, and I’ve used Jool.

As implied in the name, the NAT64 service provides NAT between the IPv6 only network and an IPv4 network for services that don’t have IPv6. How does this work?

The 64:ff9b::/96 prefix is reserved and is used to map into IPv4 space. For example, 10.0.0.1 becomes 64:ff9b::a00:1 (0a00:0001 being a hexadecimal representation of the octets 10.0.0.1). The router on my IPv6-only network forwards this 64:ff9b::/96 prefix to the NAT64 host, and that NAT64 host sends IPv4 packets out to the IPv4-only host.

On my IPv6-only network (separate VLAN & SSID), I’ve got no IPv4 addresses configured on anything, and no DHCP (v4), etc. at all. I installed an Alpine Linux VM to serve as my NAT64 translator. For what it’s worth, I did the entire Alpine install using IPv6 only - very impressive!

Once installed, I set up a separate interface on the Alpine VM which connects via IPv4 to my existing Dual Stack LAN. Next:

  1. Enable IP forwarding so the machine acts as a router:
sysctl -w net.ipv4.conf.all.forwarding=1
sysctl -w net.ipv6.conf.all.forwarding=1
  1. Set a static IPv6 ULA address on the NAT64 machine:
auto eth0
iface eth0 inet6 static
    address fd00::64
    netmask 64

(eth1 left as DHCP - connected to the IPv4 network).

  1. Install Jool (Alpine Linux instructions)
  2. Configure Jool. My basic config is:
{
    "instance": "nat64-minimal",
    "framework": "netfilter",

    "global": {
        "pool6": "64:ff9b::/96"
    }
}
  1. Set a route from your router for 64:ff9b::/96 to your NAT64 machine (in my case fd00::64) and on my MikroTik router
    ipv6 route add comment=NAT64 dst-address=64:ff9b::/96 gateway=fd00::64

Happy days - NAT64 works. Example ping to 8.8.8.8 below:

ping6 64:ff9b::8.8.8.8
PING6(56=40+8+8 bytes) <my IPv6 address> --> 64:ff9b::808:808
16 bytes from 64:ff9b::808:808, icmp_seq=0 hlim=118 time=22.463 ms
16 bytes from 64:ff9b::808:808, icmp_seq=1 hlim=118 time=24.962 ms
16 bytes from 64:ff9b::808:808, icmp_seq=2 hlim=118 time=21.277 ms
16 bytes from 64:ff9b::808:808, icmp_seq=3 hlim=118 time=26.514 ms
16 bytes from 64:ff9b::808:808, icmp_seq=4 hlim=118 time=26.176 ms

But what about DNS? DNS64 is the partner mechanism to NAT64 where A records are synthesised into AAAA records for NAT64 purposes. I understand that several self-hostable DNS servers (e.g., Bind, Technetium, and AdGuard Home) have this capability, but for this weekend’s testing I simply updated my IPv6 ND to advertise Cloudflare’s DNS64 server onto that IPv6-only VLAN. I’ll come back to hosting something here over the coming days, probably on that same NAT64 VM.

(Edit: I came back to this and set up Bind as a DNS64 server on the same VM. I only picked Bind because I was being lazy and there was an Alpine package for it, but not for Technetium or AdGuard Home. Bind is set up very close to the Jool documentation on DNS64, with the exception that I’ve set it up as a forwarder to my current ad-blocking self-hosted IPv4 DNS rather than a recursive resolver. DNS64 working with ad blocking still in place is the best of both worlds!)

An IPv6-only (and IPv6 capable) client will ask for AAAA DNS records. In cases where AAAA records don’t exist, then an IPv6-only client doesn’t know how to access the service, even with NAT64 in place. A DNS64 server synthesises these records out of A records. For example, there are no AAAA records for discourse.homelabbrisbane.com.au, however a DNS64 server will return a valid record of
discourse.homelabbrisbane.com.au. IN AAAA 64:ff9b::5df:2e20, which has been converted from discourse.homelabbrisbane.com.au. IN A 5.223.46.32 (Where **5.223.46.32** maps into the NAT64 address of 64:ff9b::**5df:2e20**. The IPv6-only client then accesses the service via that synthesised IPv6 address via the NAT64 service. (I promise I’m not picking on the HLB discourse for being IPv4-only - it was just a nice example!)

Everything just worked. Admittedly, I haven’t tested a Windows machine (merely because I don’t have one handy), but Android/iOS/macOS clients worked straight away and I haven’t noticed the difference. I’ve noticed no speed difference for IPv4-only services going via NAT64, and that Alpine Linux VM has barely been breaking a sweat. CPU usage is low, it’s using around 130MB RAM total, and network usage is at line speed for the Proxmox host.

I’ll try and find a way to collect some statistics at some stage about the amount of traffic going via the NAT64 service (i.e., how much IPv4-only traffic is left from the day to day services being used at home).

Hope that helps others and encourages others to get onboard the IPv6 train :grin:.

The next virtual homelab meeting is on 6 January 2026, 19.30 AEST. I think ipv6 would be a good topic for that night.

@Belfry, doable?

Shamed into it.

~$ dig AAAA discourse.homelabbrisbane.com.au +short
2a01:4ff:2f0:193d::1

That’s doable. Happy to put something together on IPv6.

:rofl: that honestly wasn’t my intention, but yay! Thank you!

(Now do GitHub next :thinking::joy:)

A brief update with some notes over the past three weeks. Everything has been working flawlessly (other than the below note), and I’d essentially forgotten that this was a single stack IPv6 network at all. Unfortunately I had to reboot all my equipment about 4 days ago for an unrelated issue, but for those curious about the stats since then (i.e., over the past four days):

  • ~11GB transmitted over the IPv6 only VLAN
  • ~4.2GB went through the NAT64 setup ethernet’s interface (this machine does nothing else)

Although that’s not at all granular or super scientific, it certainly “feels” about right - 38% IPv4 traffic and 62% IPv6. I’m probably a little biased towards using more IPv6 purely by the nature of things I engage with online, but that’s certainly in line with the “50-50ish split” reflected worldwide overall.

Another note for anyone playing at home: I realised this morning that it’d been weeks since the phones at home had received any inbound calls. No missed calls, no nothin’. It’s been utterly delightful, if you ask me, but not something that could continue long-term :joy:.

Tracked it down to the Wi-Fi Calling feature (Telstra) being enabled, and the phones being on the IPv6 only network. It wasn’t completely dead for inbound calls, but was definitely intermittent, bordering on the “doesn’t work at all” side of the scale. SMS etc. were unaffected. Didn’t end up going too far down the diagnostic rabbit hole, but when it was working I saw some traffic on port 4500 UDP, consistent with docs online, and several other references online that port 500 UDP was also relevant to Wi-Fi Calling. Fixed with a quick firewall rule addition, and documented here in case anyone else runs into the same thing. Unfortunately, the phones ring again now :joy:.

I’ve got a hunch that it was working sometimes because of the established/related/etc. firewall rules allowing the traffic if the phone initiated it first, and then that tracked connection possibly timing out and/or some privacy addresses rotating on the phone and the new IPv6 address not corresponding to the tracked connection in the firewall and/or possibly to do with moving from one end of the house to the other and jumping between access points causing that connection not to be tracked. Very odd that Wi-Fi Calling relies on IPv4 still (certainly for Telstra - they don’t publish any AAAA records in DNS for it, only A) given that Telstra Mobile is a single stack IPv6 network. Haven’t done a deep dive on the why, but happy to do so if someone on here is curious.

I finally got full dual-stack going again after switching to FTTP. As suspected at least half of my issues were the dodgy VDSL modem I was running in bridge mode. Everything seems to work now, finally.

1 Like