Automatic CLAT on macOS
We’re an all-Apple campus on the client side (we mostly run Linux on the server side). We noticed something interesting on our macs while testing IPv6-only connectivity using the NAT64 servers that we set up for testing IPv4 legacy protocols with IPv6-only connections.
One day I absent-mindedly tried to ping an IPv4 address literal (18.104.22.168). Normally, this doesn’t work with NAT64 because a literal address doesn’t involve a query to DNS, so DNS64 doesn’t get the chance to synthesize the v4-embedded address.
But in this case, I was able to ping and get responses! I thought
perhaps I had turned v4 on by accident (I had manually turned it off
to test native IPv6), but I confirmed it was still off. Then, I
noticed this in
en0: flags=88e3<UP,BROADCAST,SMART,RUNNING,NOARP,SIMPLEX,MULTICAST> mtu 1500 options=6463<RXCSUM,TXCSUM,TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM> ether <redacted> inet6 fe80::<redacted>%en0 prefixlen 64 secured scopeid 0xf inet6 <redacted> prefixlen 64 autoconf secured inet 192.0.0.2 netmask 0xffffffff broadcast 192.0.0.2 inet6 <redacted> prefixlen 64 clat46 nat64 prefix 64:ff9b:: prefixlen 96 nd6 options=201<PERFORMNUD,DAD> media: autoselect status: active
That 192.0.0.2 address is pulled from the DS-Lite range, and represents a locally-assigned address that only works on the host. Checking the route tables, you can see that this address has a next-hop gateway that’s in the same range:
% netstat -f inet -rn Routing tables Internet: Destination Gateway Flags Netif Expire default 192.0.0.1 UGScg en0 127 127.0.0.1 UCS lo0 127.0.0.1 127.0.0.1 UH lo0 192.0.0.1/32 link#15 UCS en0 ! 192.0.0.1 link#15 UHLWIir en0 ! 192.0.0.2/32 link#15 UCS en0 ! 192.0.0.2 f0:2f:4b:13:ed:bd UHLWIi lo0 224.0.0/4 link#15 UmCS en0 ! 22.214.171.124 1:0:5e:0:0:fb UHmLWI en0 126.96.36.199 1:0:5e:7f:ff:fa UHmLWI en0 255.255.255.255/32 link#15 UCS en0 !
You’ll note that the
192.0.0.1 address is the default gateway for
v4, so as far as the node is concerned, it has a default route to the
v4 internet. Traffic to that address is handled by a client site
translator (CLAT) process
that converts the packets from v4 to v6 and embeds the v4 destination
in the v6 address (just like DNS64 synthesis would). The benefit here
is that no DNS is required, and the host has (what appears to be) a
valid IPv4 address, so applications that are v4-only on the host
continue to operate correctly even though the host doesn’t have a
routable v4 address.
I wasn’t the only one to see this behavior. Ondřej Caletka of the RIPE NCC observed this CLAT behavior in macOS and documented it in his article about IPv6-mostly networks. However, at the time of his article (2022), the CLAT was only activated in response to DHCPv4 providing the IPv6-only Preferred option, along with a PREF64 Advertisement in the v6 Router Advertisement.
In my case, I don’t have these set up (yet). So in 2023 it seems Apple has relaxed the requirements for activating the CLAT. My best guess is that when macOS detects both the absence of a v4 address (in my case, because I’ve explicitly disabled it) and detects a PREF64 value per RFC7050 it activates the CLAT. This is good, and helpful because the RA flag has not been adopted by any router vendors we have tested (Juniper, Arista, Aruba). I reached out to Ondřej and he confirmed my findings; he also said that he observed macOS 13 (Ventura) complaining unless it had a valid IPv4 address or a functioning CLAT/NAT64 (so it seems Apple is requiring IPv4 functionality to consider itself “connected”).
It’s worth noting that this feature is not 100% reliable. If I have
multiple network connections enabled (ethernet and wifi), and then
disconnect from ethernet, the CLAT doesn’t automatically pick up on
wifi. I usually have to bring wifi down and back up, and sometimes
toggle v4 on and back off before the functionality returns. I’ve
noticed that when it’s broken I sometimes see the CLAT output in
netstat shows no default v4 route installed, so the
packets won’t flow correctly.
Even though it looks like macOS doesn’t quite have all the kinks worked out, I’m still excited about this feature! I was prepared to deal with the loss of IPv4 literals when we switch to pure IPv6 with NAT64. However, having this CLAT functionality will help the transition by preventing immediate breakage of programs that still rely on raw IPv4. On the downside, it means that broken apps can stay broken longer and there will be less impetus to update them. But, given that some apps may not be able to be upgraded (e.g., because they’re discontinued), this prevents those apps from not working at all and possibly preventing a migration to IPv6-only.