I took the IPv6 NAT64 Challenge

It’s been a long time since I played around with IPv6. I haven’t really messed with it since 2016 or so. Moving from one fastlane dev job to another, it was enough to just keep my home network working, let alone experiment.

But now that I finally found time to play around, I figured it’s time to see how far IPv6 has come.

Also, a warning: This is more "how it feels" than a guide. And the TL;DR is: Don’t bother with NAT64 unless you’re an iOS dev who needs to test their app.

6to4#

It felt a bit silly to start with NAT64 without first getting proper IPv6 setup on my LAN, so with the help of a friend, I got my Ubiquiti EdgeRouterX all setup for 6to4 anycast. Testing showed about a 10ms penalty of using the IPv6 connection over the IPv4 for various sites.

What worked:
  • ipv6.google.com

  • facebook’s ipv6

What didn’t work:
  • ipv6.reddit.com

  • github ipv6

It turns out the 6to4 anycast relay has been deprecated since 2015. Without any SLA guarantees, nobody was motivated to fix it, and there were several other issues with home routers and things. You can read the RFC for more details, but this explains why a bunch of sites didn’t work for me. Note that 6to4 itself is not deprecated. You could still use it for site-to-site communications between two ipv6 sites over an ipv4 backbone. It’s just the relay that’s been deprecated. It still half-works, and you probably won’t notice any slowdowns because of the happy eyeballs algorithm (browser goes to ipv4 if ipv6 takes too long), but why use a half-broken solution?

Hurricane Electric Tunnel#

Checking traceroute and seeing that all my 6to4 traffic was going through he.net anyway, I realized that I could probably get the same latency with a proper tunnel. Signing up at he.net was pretty straightforward, and setting up my EdgeRouterX only took about 10 minutes or so. It’s a bit easier than setting up 6to4, actually. Latency was the same as before, but now ipv6.reddit.com and github ipv6 were working properly. And everything else that had ipv6 connectivity seemed to be working well.

IMHO, the main downside to this setup is the ipv6 prefix rarely changes. One of the features of home ISP ipv4 service is the dynamic address that gets changed and reassigned occasionally. I feel like that’s a good privacy feature. 6to4 prefixes will rotate with the ipv4 address. he.net prefixes are kind of constant, unless you rotate them yourself, which they probably don’t want you to do.

In any case, make sure you enable the ipv6 privacy extensions on your operating system. Windows has them on by default. MacOS probably has them on by default. On linux, you need to enable them specifically. I used systemd-networkd to configure this, but your method depends on what you use. Privacy extensions give you an ipv6 address that does not reveal your MAC address. It’s like getting back the privacy that you had with hiding behind a IPv4 NAT address.

NAT64#

Now, for the big one. It took me a few hours to figure out how to configure Tayga. Nothing made sense until I found the README file on the site. The Archlinux package didn’t come with the README, and the home page, man page, and help don’t do a full job of explaining it. The confusing bit was that the nat64 tun device needs both an ipv4 and ipv6 address, and tayga itself needs a different ipv4 and ipv6 address in its config file, and none of these addresses should be the addresses of the machine running tayga. Oh, and the prefix example they give you is the documentation prefix. You shouldn’t actually use that one. Which prefix should you use? "an unused /96 prefix from your site’s address range", according to the documentation. What does that mean? I never figured it out. Just use 64:ff9b::/96 and be done with it. They’ll give you warnings and etc. about how you can’t NAT64 between that prefix and private ipv4 address ranges, but this probably doesn’t matter to you. I don’t need my ipv6 machine to talk to my ipv4 machine’s 192.168.x.x address via the NAT64 64:ff9b::192.168.x.x address.

Oh, and don’t forget to enable ip forwarding on the real (non-tun) devices.

So now I have the EdgeRouterX doing my he.net tunnel and handing out prefixes, and a separate linux server doing the NAT64 via tayga, and I used Cloudflare’s DNS64 for DNS. Note that Cloudflare’s DNS64 uses the 64:ff9b::/96 prefix. And then I shut down my IPv4 DHCP server and rebooted. Time for fun!

What works
  • Linux

  • Windows

  • MacOS

  • Android phones

  • Web browsing

  • Spotify

  • Discord text chat (not voice calls)

  • Slack

  • Zoom

  • Element matrix.org client

  • TheLounge IRC bouncer

What doesn’t work
  • My job’s VPN

  • Vizio smart TV features

  • Nintendo Switch

  • Steam

  • Rocket League

  • Discord voice calls

  • Synergy

So uh…​ Nintendo Switch grabs an IPv6 address but doesn’t know how to use it at all. The switch and all games claim there’s no internet connectivity. Vizio smart TV also grabs an IPv6 address, but the Vizio home screen and all Vizio apps (Amazon Prime Video, Netflix, etc) fail to use it. My job’s VPN seems to be configured only for IPv4, but I haven’t dug into it much. Discord voice calls fail on making a webrtc connection on both the standalone electron-based client and in the web page (tested via Firefox). Synergy crashes when given an IPv6 address.

Steam has partial IPv6 support, but a few critical DNS queries are hard-coded to look for A records. You can check out the github issues to see angry nerds complaining about this since 2013: 3372 and 2912, but Do not add another "me too" post! Steam devs do not watch this, so you’d only be annoying other IPv6-enthusiasts. I think it could be worked around by making a custom TUN service and an entry in the /etc/hosts file. Maybe redirect the server it’s looking for to a service running on the local machine that will connect to a NAT64 address on the outgoing side. I thought about playing around with it, but there’s really no point.

What I mean is, NAT64 is just a dead-end. As others have found before me, the biggest blocker is client-side application support. In my brief testing, I found major show-stoppers. Every device in my home either has software I don’t want to do without (Steam), or the device itself can’t handle ipv6 (Nintendo, Vizio, etc.). The irony is that the devices that don’t support IPv6 are using a networking subsystem that does. So, essentially, app devs didn’t bother to enable it. Which probably means that there are a lot fewer ipv6-only networks than we’re led to believe.

Oh, and tayga is a bit slow. When I turned IPv4 DHCP back on and played some Rocket League, my normal ping of <30ms jumped up above 110ms for us-east servers. At that point, the ball and all the other cars twitch all over in every collision. The extra translations between IPv6, IPv4, and back again are too much. We can reduce latency to an acceptable level by routing NAT64 traffic directly to the tayga server (my traffic would go to the router, bounce back to the tayga server, then back to the router again), but we’re still up at least 10ms, and for absolutely no gain. There are other, faster NAT64 solutions, like Jool, but it still brings no advantage over dual-stack.

The only use of NAT64 right now, in my opinion, is to test out your client software to make sure it’s compatible with IPv6. iOS developers sometimes do this so they can ensure they meet this app store requirement.

For anyone who wants to repeat my experiment, here are the tayga config and service files that worked for me. Note that my LAN’s IPv4 range is 192.168.1.0/24, and that I added a route to my EdgeRouter to point 192.168.255.0/24 to my tayga server.

/etc/tayga.conf
tun-device nat64
ipv4-addr 192.168.255.1
ipv6-addr fc00::1:20
prefix 64:ff9b::/96
dynamic-pool 192.168.255.0/24
data-dir /var/db/tayga
/etc/systemd/system/tayga.service
[Unit]
Description=TAYGA NAT64
After=network.target

[Service]
ExecStart=/usr/bin/tayga --nodetach --config /etc/tayga.conf
ExecStartPost=/usr/bin/ip link set nat64 up
ExecStartPost=/usr/bin/ip ad add dev nat64 192.168.0.1
ExecStartPost=/usr/bin/ip ad add dev nat64 fc00::2:20
ExecStartPost=/usr/bin/ip rou add 192.168.255.0/24 dev nat64
ExecStartPost=/usr/bin/ip rou add 64:ff9b::/96 dev nat64
Restart=always

[Install]
WantedBy=multi-user.target