Photo by Jordan Harrison / Unsplash

IPv6 adventures 3: IPv6 only hosts

Network Engineering Feb 16, 2025

Go watch these videos for more in depth information about IPv6-only troubles and transition techniques. For me the problem is twofold:

  1. Some services are IPv4 only, but use proper DNS / hostnaming. This can be solved with NAT64 and DNS64. These will translate an IPv4 address to IPv6 so the IPv6 only host can still connect.
  2. Other services use IPv4 literals, hard-coded ip adresses, which cannot be "intercepted" by DNS and thus not translated to IPv6. This can be solved with CLAT.

References:

1a. NAT64 with Jool

I'm using pfSense as my router/internet gateway, so the second video above was not for me. However, the first NAT64 he mentions is Jool, which he will not be using as it is for Linux, not FreeBSD. So that's the one I configured and am using now. This is how.

Virtual machine

Start by creating a Debian or Ubuntu VM. Don't use LXC as Jool attaches to the kernel and gets confused when the LXC kernel version differs from the host version. I made my life easier by just installing Jool in a VM. I initially gave the VM 2 GB of memory for installation, but afterwards dialed that back to 1024MB, which should be more than plenty.

joep@jpl-nat64:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           960Mi       224Mi       728Mi       492Ki       140Mi       735Mi
Swap:          974Mi          0B       974Mi

Jool install

  • Install kernel headers
sudo apt install linux-headers-$(uname -r)
  • Install Jool
sudo apt install jool-dkms jool-tools
sudo modprobe jool

Jool config

  • Enable network forwarding:
    • Create file /etc/sysctl.d/90-forwarding.conf:
sudo nano /etc/sysctl.d/90-forwarding.conf
    • Add lines:
net.ipv4.conf.all.forwarding=1
net.ipv6.conf.all.forwarding=1
    • Apply:
sudo sysctl -p /etc/sysctl.d/90-forwarding.conf
sudo mkdir /etc/jool
sudo nano /etc/jool/jool.conf
    • Add minimal config:
{
	"instance": "nat64-minimal",
	"framework": "netfilter",

	"global": {
		"pool6": "64:ff9b::/96"
	}
}
  • Enable and start Jool
sudo systemctl enable jool --now

pfSense config

Now we need to route all traffic to 64:ff9b::/96 to the NAT64 VM. The VM lives on VLAN 30 and has ULA: fdc3:xxxx:xxxx:30:xxxx:xxxx:xxxx:xxxx.

In pfSense:

  • Go to System ā€“ Routing ā€“ Gateways
  • Copy the Gateway address of the IPv4 WAN gateway: xxx.xxx.xxx.xxx
  • Click the add button and enter:
šŸ’”
Interface: select the VLAN 30 interface
Address Family: IPv6
Name: NAT64_GW
Gateway: fdc3:xxxx:xxxx:30:xxxx:xxxx:xxxx:xxxx
Monitor IP: 64:ff9b::xxx.xxx.xxx.xxx
Description: NAT 64 Gateway
  • Save
  • Go to System ā€“ Routing ā€“ Static Routes
  • Click the add button and enter:
šŸ’”
Destination network: 64:ff9b:: / 96
Gateway: NAT64_GW - fdc3:xxxx:xxxx:30:xxxx:xxxx:xxxx:xxxx
Description: NAT 64 Gateway
  • Save

1b. DNS64

If you use the DNS resolver in pfSense, go to Services ā€“ DNS Resolver ā€“ Advanced Settings. Then scroll all the way down and enable DNS64 Support by checking the box Enable DNS64 (RFC 6147).

I use Technitium which uses a DNS64 "App".

  • Go to Apps ā€“ App store
  • Look for "DNS64" and press Install

1c. Results

If I now ping a site that is IPv4-only on a IPv6-only machine I get a proper response:

joeplaa@jpl-grafana:~$ ping -c 1 github.com
PING github.com (64:ff9b::8c52:7904) 56 data bytes
64 bytes from lb-140-82-121-4-fra.github.com (64:ff9b::8c52:7904): icmp_seq=1 ttl=52 time=11.7 ms

--- github.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 11.669/11.669/11.669/0.000 ms

On a dual-stack machine it still pings the IPv4 address directly:

joep@PCJOEP:~$ ping -c 1 github.com
PING github.com (140.82.121.4) 56(84) bytes of data.
64 bytes from lb-140-82-121-4-fra.github.com (140.82.121.4): icmp_seq=1 ttl=53 time=10.1 ms

--- github.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 10.091/10.091/10.091/0.000 ms

2a. CLAT / 464XLAT

Some applications have hardcoded IP addresses in their code. On mobile phones this is solved by using CLAT/464XLAT see the Wikipedia article about transistioning mechanisms. On PC's/servers, this is not (yet) implemented. For Linux you can install CLAT's (see Apalrd videos), on Windows you're screwed. Windows 10 only supports this for mobile devices (WWAN), Windows 11 support has been promised, but isn't there yet. So the only option so far is to keep your desktops dual-stack. Which is fine for me at home.

Spotify

I noticed that when I disabled IPv4 on my Window 10 machine in the living room, Spotify refused to connect. I tried to reproduce this scenario on my Ubuntu Desktop to try CLAT, but there Spotify works fine on IPv6. This seems to be an "old" issue, Spotify engineers already wrote about this in 2015:

Oh IPv6, Where Art Thou - Spotify Engineering
Oh IPv6, Where Art Thou - Spotify Engineering

There are also multiple requests which have been passed to the "internal teams" in 2021.

[Desktop] IPv6 support for the desktop client
Resubmitting the same idea as: https://community.spotify.com/t5/Closed-Ideas/Desktop-Support-IPv6-end-to-end/idi-p/1240314 https://community.spotify.com/t5/Closed-Ideas/Desktop-Desktop-client-IPv6-support/idi-p/1667179 https://community.spotify.com/t5/Closed-Ideas/Other-IPv6-Support/idi-p/4469460 sā€¦

However, it seems this still isn't fixed.

This Reddit thread discusses some issues back in 2019-2020. I tried to ping and nslookup the mentioned addresses and they all return IPv6 addresses. So I'm a bit confused why Spotify seems to work fine on Ubuntu, but not on Windows 10.

Other

When disabling IPv4 on my Ubuntu desktop, I noticed my Windows VM didn't start. The Virtual Machine Manager gives an error that it cannot to the spice address. It happens that Spice only listens on IPv4 localhost by default. This can be changed quite easily.

  • Open /etc/libvirt/qemu.conf
  • Find the line spice_listen = "0.0.0.0". This line should be commented out, if not, do so
  • Add line spice_listen = "::1"
# SPICE is configured to listen on 127.0.0.1 by default.
# To make it listen on all public interfaces, uncomment
# this next option.
#
# NB, strong recommendation to enable TLS + x509 certificate
# verification when allowing public access
#
#spice_listen = "0.0.0.0"
spice_listen = "::1"

Tags