How 464XLAT Works: IPv4 over IPv6-Only Networks

464XLAT is the most widely deployed IPv6 transition mechanism on mobile networks, used by billions of devices daily to access IPv4-only services over IPv6-only cellular infrastructure. Defined in RFC 6877, 464XLAT combines two translation stages -- a stateless SIIT translator on the customer device (CLAT) and a stateful NAT64 translator in the provider network (PLAT) -- to give legacy IPv4 applications a working IPv4 socket while the actual wire traffic is pure IPv6. Every major mobile carrier in the world, including T-Mobile US, AT&T, Verizon, NTT Docomo, KDDI, SK Telecom, and Reliance Jio, runs 464XLAT today. If you are reading this on a mobile device, there is a strong chance 464XLAT is translating your traffic right now.

Why Pure NAT64 Is Not Enough

Before understanding 464XLAT, you need to understand the problem it solves. NAT64 (RFC 6146) allows an IPv6-only host to reach IPv4-only servers by translating IPv6 packets to IPv4 at a network gateway. Combined with DNS64 (RFC 6147), which synthesizes AAAA records for IPv4-only domains, this works well for most traffic: the application resolves a hostname, DNS64 returns a synthetic IPv6 address (typically in the 64:ff9b::/96 prefix), the host sends an IPv6 packet, and the NAT64 gateway translates it to IPv4.

The problem is applications that use IPv4 address literals -- hardcoded addresses like 93.184.216.34 embedded directly in application code, configuration files, or protocols. These applications never issue a DNS query, so DNS64 never gets a chance to synthesize an address. The IPv6-only host has no IPv4 stack, so it cannot construct an IPv4 packet. The application simply fails. This is not an edge case. Significant categories of real-world software use IPv4 literals:

A carrier deploying IPv6-only with NAT64+DNS64 would break all of these applications. 464XLAT solves this by providing the device with a real IPv4 address and a local translation layer, so applications can use IPv4 literals freely -- the CLAT translates them to IPv6 before they hit the wire.

The 464XLAT Architecture

The name "464XLAT" describes the protocol journey: IPv4 from the application, translated to IPv6 for the network, translated back to IPv4 at the provider edge. The two translators are:

The CLAT and PLAT do not communicate directly or share any state. The CLAT simply generates IPv6 packets using the well-known NAT64 prefix (64:ff9b::/96 or a provider-specific prefix discovered via DNS64 or Router Advertisement options). The PLAT processes these packets identically to any other NAT64 traffic. This stateless coupling is what makes 464XLAT elegant: the PLAT does not even know whether its traffic came from a CLAT or from a native IPv6 application using DNS64-synthesized addresses.

464XLAT: Double Translation Architecture IPv4 IPv6-only IPv4 Application dst: 93.184.216.34 src: 192.0.0.4 CLAT Stateless SIIT IPv4 → IPv6 on device (tun0) IPv6-only Mobile Network PLAT Stateful NAT64 IPv6 → IPv4 carrier gateway IPv4 Server Packet Headers at Each Stage 1. App → CLAT (IPv4) Src: 192.0.0.4 Dst: 93.184.216.34 Proto: TCP/80 2. CLAT → PLAT (IPv6) Src: 2001:db8::c000:4 (CLAT addr) Dst: 64:ff9b::5db8:d822 Proto: TCP/80 (no port change) 3. PLAT → Server (IPv4) Src: 198.51.100.1:49152 (NAT) Dst: 93.184.216.34 Proto: TCP/80 CLAT: stateless 1:1 translation, no port mapping PLAT: stateful NAT64, port mapping, shared IPv4 pool (like CGNAT) 64:ff9b::5db8:d822 = 93.184.216.34 embedded in NAT64 prefix 192.0.0.4 = RFC 7335 CLAT address (not routable on the wire)

CLAT: The Customer-Side Translator in Detail

The CLAT is the piece that distinguishes 464XLAT from plain NAT64+DNS64. It runs on the end-user device itself and creates a local IPv4 interface that applications can bind to. The translation is stateless SIIT (Stateless IP/ICMP Translation, RFC 7915): every IPv4 packet entering the CLAT is converted to an IPv6 packet by mechanically rewriting headers, with no connection tracking or port mapping. The inverse happens for return traffic. Key details of the CLAT:

Android CLAT Implementation

Android has included a CLAT implementation since Android 4.3 (2013), making it the earliest and most widely deployed CLAT. The implementation is a userspace daemon called clatd, located in the AOSP source tree at packages/modules/Connectivity/clatd/. Here is how it works:

  1. Detection -- When Android connects to a network that provides only IPv6 (no IPv4 address via DHCP or PPP), the connectivity stack detects this and starts clatd.
  2. NAT64 prefix discovery -- clatd discovers the NAT64 prefix either via DNS64 synthesis probing (it queries ipv4only.arpa, which always resolves to 192.0.0.170/171, and examines the synthesized AAAA response to extract the NAT64 prefix) or via the PREF64 Router Advertisement option (RFC 8781).
  3. Tun interface creation -- clatd creates a v4-ifname tun interface (e.g., v4-wlan0 or v4-rmnet0). This interface gets the address 192.0.0.4 with a /29 netmask.
  4. Routing -- A default route for IPv4 traffic is installed pointing at the tun interface. Any IPv4 packet from any application is captured by this route and delivered to clatd.
  5. Translation -- clatd translates each IPv4 packet to IPv6 using SIIT rules. In modern Android (11+), this translation has been moved from userspace to an eBPF program running in the kernel for performance, but the logic is identical.
  6. Egress -- The translated IPv6 packet is injected into the actual network interface (e.g., rmnet0) and sent to the carrier network as a normal IPv6 packet.

You can observe this on a rooted Android device:

# Show the CLAT tun interface
$ ip addr show v4-rmnet0
    inet 192.0.0.4/29 scope global v4-rmnet0

# Show the routing table -- IPv4 default route via CLAT
$ ip route show table main
default dev v4-rmnet0 proto static

# Show the IPv6 address used for CLAT
$ ip -6 addr show rmnet0
    inet6 2607:fb90:abcd:1234::1/128 scope global   # regular addr
    inet6 2607:fb90:abcd:1234::f/128 scope global    # CLAT addr

The eBPF-based CLAT in modern Android is notably efficient. It translates packets in the kernel's TC (traffic control) layer, avoiding the overhead of copying packets to userspace and back. Google's measurements show that this adds less than 5 microseconds of latency per packet -- negligible compared to cellular radio latency of 10-50 ms.

iOS CLAT Implementation

Apple added CLAT support in iOS 12.2 (2019), later than Android but covering iPhones, iPads, and Apple Watch (with cellular). Apple's implementation differs from Android in several ways:

Apple also requires App Store apps to work on IPv6-only networks (a policy enforced since 2016), which pushed app developers to avoid IPv4 literals. However, 464XLAT remains essential for the long tail of apps, system services, and protocols that still rely on IPv4 sockets.

Windows CLAT Support

Windows added CLAT support in Windows 10 version 1903 (May 2019 Update) for cellular connections, and extended it to Wi-Fi and Ethernet in Windows 11. The Windows CLAT implementation:

Windows CLAT adoption lagged behind mobile platforms because wired/Wi-Fi networks rarely ran IPv6-only configurations until recently. As enterprises begin deploying IPv6-only Wi-Fi with NAT64 (following the success of mobile deployments), Windows CLAT support is becoming more relevant.

PLAT: The Provider-Side Translator

The PLAT is a standard stateful NAT64 gateway (RFC 6146) deployed in the carrier's network. It maintains connection-tracking state for every active flow, mapping each (IPv6 source, source port) tuple to a (public IPv4 address, translated port) tuple -- identical to how CGNAT works but translating between protocol families rather than just rewriting addresses within IPv4.

Key characteristics of the PLAT:

From the PLAT's perspective, traffic from a CLAT is indistinguishable from traffic generated by a native IPv6 application that obtained its destination address via DNS64. The PLAT does not know or care whether the client used CLAT. This means a carrier can deploy PLAT (NAT64) infrastructure once and serve both DNS64-capable native IPv6 apps and CLAT-translated IPv4 apps simultaneously.

Interaction with DNS64

464XLAT and DNS64 are complementary, not competing. On a 464XLAT-enabled device, traffic takes one of two paths depending on how the destination address was obtained:

  1. DNS64 path (no CLAT involved) -- An application resolves example.com, the DNS64 server synthesizes an AAAA record 64:ff9b::5db8:d822, and the application sends native IPv6 to this address. The traffic goes directly to the PLAT (NAT64) without touching the CLAT. This is the preferred path because it avoids the CLAT translation overhead entirely.
  2. CLAT path -- An application uses an IPv4 literal (e.g., connect(93.184.216.34)) or an application explicitly requests an A record and gets an IPv4 address. The IPv4 packet is routed to the CLAT tun interface, translated to IPv6, and then sent to the PLAT. The PLAT translates it to IPv4 identically to the DNS64 path.

On Android and iOS, the system DNS resolver is aware of NAT64 and will preferentially use AAAA records (either real or DNS64-synthesized) when available. The CLAT path is a fallback for traffic that bypasses DNS entirely or explicitly uses IPv4 sockets. In practice, the majority of traffic on a 464XLAT network flows via the DNS64 path. Google's measurements from Android devices show that roughly 90-95% of traffic uses DNS64/native IPv6, with only 5-10% going through the CLAT. But that 5-10% includes critical applications that would completely fail without CLAT.

Carrier Deployments

464XLAT has achieved near-universal deployment on mobile networks. The progression:

The economics are straightforward. An IPv4 address on the open market costs $30-50 (as of 2025). A carrier with 100 million subscribers would need $3-5 billion in IPv4 address costs for 1:1 allocation. With 464XLAT and NAT64, that same carrier can serve 100 million subscribers with a few hundred thousand IPv4 addresses on the PLAT, costing a fraction of a percent of the dual-stack alternative. Combined with eliminating the operational complexity of running dual-stack routing, DHCP, and per-subscriber IPv4 state across the radio and core network, the savings are substantial.

464XLAT on 5G Networks

5G NR networks have universally adopted 464XLAT. The 3GPP 5G core (5GC) specifications explicitly support IPv6-only PDU sessions with CLAT, and all 5G chipset vendors (Qualcomm, MediaTek, Samsung) implement CLAT in their modem firmware. In 5G standalone (SA) deployments, 464XLAT is the default IPv4 connectivity mechanism -- carriers no longer provision IPv4 addresses to individual PDU sessions at all.

The 5G architecture introduces some optimizations relevant to 464XLAT:

DNS64 Path vs CLAT Path Mobile Device App A (uses DNS) getaddrinfo("example.com") → gets AAAA via DNS64 App B (IPv4 literal) connect("93.184.216.34") → IPv4 socket to 192.0.0.4 CLAT (v4-rmnet0) SIIT: IPv4 → IPv6 192.0.0.4 → CLAT v6 addr native IPv6 translated IPv6 IPv6-only Network All traffic is IPv6 here (~90% DNS64 + ~10% CLAT) DNS64 Server Synthesizes AAAA records PLAT NAT64 IPv6 → IPv4 Stateful + ports Shared IPv4 pool IPv4 Internet Traffic split (typical) ~90-95% DNS64 path (native IPv6) ~5-10% CLAT path (IPv4 literals)

Performance Overhead

The natural question about double translation is: what does it cost? The answer is surprisingly little, for several reasons:

CLAT overhead is minimal. The CLAT performs a stateless header rewrite -- swapping a 20-byte IPv4 header for a 40-byte IPv6 header (or vice versa), recalculating checksums, and remapping ICMP types. There is no connection-tracking table to look up, no port allocation to perform, and no logging. On modern mobile processors, this adds single-digit microseconds per packet. Android's eBPF-based CLAT performs the translation in the kernel's TC hook, avoiding any context switch to userspace. Apple's kernel-resident CLAT is similarly efficient.

PLAT overhead is real but amortized. The PLAT (NAT64) does maintain per-flow state and performs port allocation, identical to CGNAT. However, carriers already needed CGNAT for IPv4 address conservation regardless of 464XLAT -- the PLAT replaces CGNAT rather than adding to it. The net overhead compared to dual-stack with CGNAT is actually lower because the carrier no longer needs to run IPv4 routing infrastructure in the radio and core network.

Throughput impact: Multiple carrier benchmarks show less than 1% throughput reduction from 464XLAT compared to native IPv4. The bottleneck on mobile networks is always the radio interface, not the CLAT or PLAT translation. Even on high-throughput 5G connections (1+ Gbps), the CLAT translation is not the limiting factor.

Latency impact: The CLAT adds negligible latency (microseconds) relative to cellular radio latency (milliseconds). The PLAT adds latency comparable to any NAT/CGNAT device -- typically 0.1-0.5 ms for the translation and state lookup. Total end-to-end impact is under 1 ms in most deployments.

Where overhead matters: The main concern is not per-packet cost but PLAT state table capacity. Each active flow consumes state in the PLAT. Heavy users (those with many concurrent connections, such as BitTorrent users) can create thousands of flows, consuming PLAT resources. Carriers configure per-subscriber port limits (typically 1,000-4,000 ports) and session timeouts to manage this. UDP flows without explicit teardown are particularly problematic because the PLAT must hold state until a timer expires.

Debugging 464XLAT with Packet Captures

Debugging connectivity issues on 464XLAT networks requires capturing packets at the right point in the translation chain. Here is what you see at each stage:

Capture on the CLAT tun interface (before translation):

# On Android (rooted) -- capture on the CLAT tun
$ tcpdump -i v4-rmnet0 -n
15:30:01.123 IP 192.0.0.4.49152 > 93.184.216.34.443: Flags [S], seq 12345
15:30:01.178 IP 93.184.216.34.443 > 192.0.0.4.49152: Flags [S.], seq 67890, ack 12346
15:30:01.179 IP 192.0.0.4.49152 > 93.184.216.34.443: Flags [.], ack 67891

This looks like normal IPv4 traffic. The application sees exactly this -- a standard TCP handshake to an IPv4 address.

Capture on the physical interface (after CLAT translation):

# On Android (rooted) -- capture on the real interface
$ tcpdump -i rmnet0 -n ip6
15:30:01.123 IP6 2607:fb90:abcd:1234::f.49152 > 64:ff9b::5db8:d822.443: Flags [S], seq 12345
15:30:01.178 IP6 64:ff9b::5db8:d822.443 > 2607:fb90:abcd:1234::f.49152: Flags [S.], seq 67890, ack 12346
15:30:01.179 IP6 2607:fb90:abcd:1234::f.49152 > 64:ff9b::5db8:d822.443: Flags [.], ack 67891

Same TCP handshake, same sequence numbers, same ports -- but now it is IPv6. The source is the CLAT IPv6 address, the destination embeds the original IPv4 address in the NAT64 prefix. Note that the port numbers are unchanged -- this is because the CLAT is stateless SIIT, not NAT.

Capture at the PLAT egress (after NAT64 translation):

# On the carrier's PLAT -- capture on the IPv4 egress
$ tcpdump -i eth0 -n host 93.184.216.34
15:30:01.124 IP 198.51.100.7.23456 > 93.184.216.34.443: Flags [S], seq 12345
15:30:01.177 IP 93.184.216.34.443 > 198.51.100.7.23456: Flags [S.], seq 67890, ack 12346
15:30:01.180 IP 198.51.100.7.23456 > 93.184.216.34.443: Flags [.], ack 67891

Now the traffic is IPv4 again, but the source address and port have changed -- the PLAT has NATted the connection, mapping the subscriber's CLAT IPv6 address to a shared public IPv4 address (198.51.100.7) with a translated source port (23456). The destination server sees a normal IPv4 connection from the carrier's NAT pool.

Common debugging scenarios:

Stateless vs Stateful: Why This Split Matters

A critical design decision in 464XLAT is making the CLAT stateless and the PLAT stateful. This was not accidental -- it directly addresses the scaling constraints of each component:

The CLAT runs on the end-user device -- a phone with limited memory, battery, and CPU. Making it stateless means it does not need to maintain a connection-tracking table, does not need timers for flow expiry, does not need to allocate ports, and does not need to handle table exhaustion. A stateless CLAT can translate any packet in O(1) time with zero per-flow memory. This is essential when the device might have hundreds of concurrent connections across dozens of apps.

The PLAT runs in the carrier's data center on dedicated hardware. Making it stateful is acceptable because (a) the carrier already needs stateful CGNAT infrastructure for IPv4 address conservation, (b) the PLAT hardware is purpose-built for high-speed state tracking, and (c) the PLAT is the natural place to multiplex many subscribers onto shared IPv4 addresses, which inherently requires state.

An alternative design -- stateful CLAT (NAT44) plus stateful PLAT (NAT64) -- would create "double NAT" with all its problems: port exhaustion at two levels, compounding log requirements, and making inbound connections essentially impossible. The stateless CLAT avoids all of these issues. The total system has exactly one layer of stateful NAT (the PLAT), same as a simple CGNAT deployment.

464XLAT vs Other Transition Mechanisms

How does 464XLAT compare to the other IPv6 transition technologies?

NAT64 Prefix Discovery

The CLAT needs to know the NAT64 prefix to construct IPv6 destination addresses. Three mechanisms exist for this:

  1. DNS64 probing (RFC 7050) -- The CLAT queries the DNS for ipv4only.arpa, which has hardcoded A records returning 192.0.0.170 and 192.0.0.171 (defined by IANA). If the network's DNS64 server synthesizes AAAA records for these addresses, the CLAT can extract the NAT64 prefix from the synthesized response. For example, if the AAAA response is 64:ff9b::c000:aa, the prefix is 64:ff9b::/96. This is the original discovery method and works with any DNS64 deployment.
  2. PREF64 Router Advertisement option (RFC 8781) -- The router sends the NAT64 prefix directly in IPv6 Router Advertisements. This is faster than DNS probing (available immediately at network attachment, before DNS is even configured) and more reliable (does not depend on the DNS path). Most modern carrier deployments use PREF64.
  3. Static configuration -- The prefix is provisioned by the carrier in the device's APN/profile settings. This is a fallback for networks that support neither DNS64 probing nor PREF64.

Android tries PREF64 first (if available in the RA), then falls back to DNS64 probing. iOS follows the same priority. The well-known prefix 64:ff9b::/96 (RFC 6052) is used by most deployments, but carriers can use provider-specific prefixes (e.g., 2001:db8:64::/96) if they need to route NAT64 traffic to specific PLAT instances.

The 192.0.0.0/29 Address Block

The CLAT needs an IPv4 address to present to local applications. Using any routable IPv4 address would risk conflicts with real destinations. RFC 7335 reserves the 192.0.0.0/29 block specifically for IANA IPv4 Special-Purpose Address Registry use, and 192.0.0.4 through 192.0.0.7 are designated for CLAT. The choice of 192.0.0.4 as the default CLAT address is by convention -- all major implementations (Android, iOS, Windows) use it.

This address is only meaningful within the device itself. It never appears on the wire (the CLAT rewrites it to the device's IPv6 CLAT address). Applications that call getsockname() will see 192.0.0.4 as their local address, and applications that call getpeername() will see the original IPv4 destination. To the application, the connection looks completely normal -- a standard IPv4 TCP/UDP socket with no indication that two protocol translations are happening underneath.

Limitations and Edge Cases

464XLAT handles the vast majority of real-world traffic, but some scenarios remain challenging:

464XLAT and the Future of IPv4

464XLAT is arguably the technology that made IPv6-only networks viable at scale. Before 464XLAT, every IPv6 transition plan stumbled on the same obstacle: the long tail of IPv4-dependent applications. Carriers could not deploy IPv6-only because too many apps broke, and they could not afford to maintain dual-stack because IPv4 addresses were exhausted. 464XLAT broke this deadlock by providing a transparent compatibility layer that requires no application changes.

The trajectory is clear. As more content becomes available over native IPv6, the percentage of traffic flowing through the CLAT decreases. Eventually, the CLAT will handle near-zero traffic and can be disabled. The PLAT (NAT64) will handle the remaining IPv4-only long tail. And when the last IPv4-only servers are decommissioned -- likely decades from now -- the PLAT can be retired too.

Until then, 464XLAT remains the quiet workhorse that lets billions of mobile devices use IPv4 applications over IPv6 networks, consuming no IPv4 addresses at the subscriber level, with overhead so low that users never notice the double translation happening on every packet.

Try looking up your mobile carrier's IP address and BGP routes to see whether your connection uses IPv6 -- if it does, 464XLAT is almost certainly the reason you can still reach IPv4-only websites.

See BGP routing data in real time

Open Looking Glass
More Articles
What is DNS? The Internet's Phone Book
What is an IP Address?
IPv4 vs IPv6: What's the Difference?
What is a Network Prefix (CIDR)?
How Does Traceroute Work?
What is a CDN? Content Delivery Networks Explained