How NAT64 Works: Translating IPv6 to IPv4 at Scale

NAT64 is a stateful protocol translation mechanism that allows IPv6-only clients to reach IPv4-only servers. Unlike tunneling approaches that encapsulate one protocol inside the other, NAT64 rewrites packet headers at a translation gateway, converting between IPv6 and IPv4 in-flight. Its companion, DNS64, synthesizes AAAA records for destinations that only have A records, giving the IPv6-only client an IPv6 address to connect to -- an address that routes to the NAT64 translator rather than directly to the destination. Together, NAT64 and DNS64 (defined in RFC 6146 and RFC 6147) form the dominant IPv6 transition technology deployed at mobile carriers today, enabling billions of IPv6-only devices to reach the long tail of IPv4-only content without requiring dual-stack addressing on the access network.

This article covers the mechanics of stateful NAT64 translation, DNS64 synthetic record generation, prefix selection, packet translation lifecycle, fragment handling, ALG issues, DNSSEC interaction, prefix discovery, and real-world deployment patterns.

DNS64: Synthesizing AAAA Records

An IPv6-only client cannot connect to an IPv4 address directly. It needs an IPv6 destination address. DNS64 solves this by intercepting DNS queries and, when a domain has only an A record (no native AAAA), fabricating a synthetic AAAA record by embedding the IPv4 address inside an IPv6 prefix. The client never knows the difference -- it sees a normal AAAA response and connects to it as it would to any IPv6 destination.

The synthesis process works as follows:

  1. The IPv6-only client sends a AAAA query for example.com to the DNS64 resolver.
  2. The resolver queries the authoritative nameserver for both AAAA and A records.
  3. If a real AAAA record exists, the resolver returns it unmodified. Native IPv6 is always preferred.
  4. If only an A record exists (say 93.184.216.34), the resolver synthesizes a AAAA record by concatenating the NAT64 prefix with the IPv4 address. Using the well-known prefix 64:ff9b::/96, the synthesized address becomes 64:ff9b::5db8:d822 (where 5db8:d822 is the hex representation of 93.184.216.34).
  5. The client receives the synthesized AAAA and initiates a connection to 64:ff9b::5db8:d822.
  6. The network routes this address to the NAT64 translator, which extracts the embedded IPv4 address and forwards the packet to 93.184.216.34 over IPv4.

The DNS64 function can run on the recursive resolver itself (most common), as a separate DNS proxy between the client and the recursive resolver, or even on the client. Running it on the recursive resolver is simplest operationally -- the operator configures their existing resolver (BIND, Unbound, Knot Resolver) to perform DNS64 synthesis when returning results to clients in the IPv6-only access network.

Synthesis Rules and Prefix Lengths

RFC 6052 defines how an IPv4 address is embedded within an IPv6 address for NAT64 purposes. The prefix length determines where the 32-bit IPv4 address is placed. The supported prefix lengths and their embedding positions are:

The /96 prefix length is overwhelmingly the most common in practice because it is the simplest: the IPv4 address occupies the last 32 bits of the IPv6 address, making it trivial to extract. The well-known prefix 64:ff9b::/96 uses this format.

The Well-Known Prefix: 64:ff9b::/96

RFC 6052 reserves 64:ff9b::/96 as the "well-known prefix" for NAT64 translation. Any IPv6 address in 64:ff9b::/96 encodes an IPv4 destination in its last 32 bits. This prefix has special properties:

Network-Specific Prefixes

Operators who need to translate traffic destined to non-global IPv4 addresses (e.g., internal RFC 1918 servers), or who want to use a prefix from their own allocation for policy or diagnostic reasons, deploy a Network-Specific Prefix (NSP). RFC 8215 later added 64:ff9b:1::/48 as an additional well-known prefix specifically for local-use NAT64 in environments like enterprises and data centers, where the /96 well-known prefix's restriction on non-global IPv4 addresses is impractical.

An operator might assign 2001:db8:64:ff64::/96 from their own PA or PI allocation as their NAT64 prefix. The DNS64 resolver and the NAT64 translator must agree on which prefix to use -- the resolver synthesizes addresses using it, and the translator's routing ensures traffic for that prefix reaches the translation device.

DNS64 + NAT64 Translation Flow 1. Client queries AAAA for example.com IPv6-only Client 2001:db8::1 No IPv4 stack DNS64 Resolver No AAAA found for example.com AAAA? 64:ff9b::5db8:d822 2. DNS64 synthesizes AAAA from A record (93.184.216.34) Prefix (/96): 64:ff9b:0:0:0:0 (96 bits) IPv4 embedded: 5db8:d822 = 93.184.216.34 (32 bits) 3. Client connects to synthesized address; packet reaches NAT64 translator Client sends to 64:ff9b::5db8:d822 IPv6 packet NAT64 Translator Strips IPv6 header Creates IPv4 header + NAT IPv4 packet IPv4 Server 93.184.216.34 4. Return path: server replies to NAT64's IPv4 address, translator rebuilds IPv6 packet IPv4 reply IPv6 reply Translate v4 → v6 Server never sees IPv6. It replies to NAT64's IPv4 pool address.

Stateful NAT64: Packet Translation Lifecycle

NAT64 as defined in RFC 6146 is stateful. It maintains a binding information base (BIB) and a session table, analogous to the connection tracking table in a traditional NAT gateway. Understanding the lifecycle of a translated session is essential for troubleshooting and capacity planning.

Session Establishment (IPv6 to IPv4)

When the NAT64 translator receives an IPv6 packet destined for its NAT64 prefix, it performs the following steps:

  1. Prefix match. The translator checks whether the destination IPv6 address matches its configured NAT64 prefix (e.g., 64:ff9b::/96). If it does not match, the packet is forwarded normally as IPv6.
  2. IPv4 address extraction. The translator extracts the embedded IPv4 destination address from the appropriate bit positions (for /96, the last 32 bits).
  3. BIB lookup. The translator checks its Binding Information Base for an existing mapping of the source IPv6 address and transport-layer port to an IPv4 address and port from its public pool. If no mapping exists, it creates one, allocating a public IPv4 address and port from its pool -- this is the stateful part, identical in concept to how CGNAT allocates mappings.
  4. Session table entry. A session entry is created recording the full 5-tuple: source IPv6 address/port, destination IPv4 address/port (extracted from the NAT64 prefix), allocated IPv4 source address/port, and protocol (TCP/UDP/ICMP).
  5. Header translation. The IPv6 header is removed and an IPv4 header is constructed. Key field mappings:
    • Version: 6 → 4
    • Traffic Class (IPv6) → Type of Service / DSCP (IPv4): copied directly (both are 8-bit fields in the same position semantically)
    • Flow Label: discarded (no IPv4 equivalent)
    • Payload Length (IPv6) → Total Length (IPv4): adjusted for the 20-byte IPv4 header vs. the 40-byte IPv6 header
    • Hop Limit (IPv6) → TTL (IPv4): decremented by 1
    • Next Header (IPv6) → Protocol (IPv4): direct mapping (TCP=6, UDP=17, ICMP mapped from ICMPv6)
    • Source address: IPv6 source → allocated IPv4 address from the NAT pool
    • Destination address: extracted IPv4 address from the NAT64 prefix
  6. Checksum recomputation. The IPv4 header checksum is computed (IPv6 has no header checksum). TCP and UDP checksums are recalculated because the pseudo-header changes (IPv6 pseudo-header uses 128-bit addresses; IPv4 uses 32-bit). For UDP, if the original IPv6 UDP checksum was mandatory (it is -- unlike IPv4 where UDP checksum can be zero), the IPv4 UDP checksum is computed. The translator can optionally set the IPv4 UDP checksum to zero if the transport is UDP.
  7. Forwarding. The resulting IPv4 packet is forwarded toward the destination via normal IPv4 routing.

Return Path (IPv4 to IPv6)

When the IPv4 server responds, the packet arrives at the NAT64 translator's IPv4 address (the allocated pool address). The translator:

  1. Looks up the session table by the destination IPv4 address/port and source IPv4 address/port.
  2. Finds the corresponding IPv6 source address and port.
  3. Constructs an IPv6 header with the original client's IPv6 address as destination and the NAT64 prefix + server's IPv4 address as source.
  4. Recomputes checksums for the IPv6 pseudo-header.
  5. Forwards the IPv6 packet to the client.

The session table entry has idle timeouts: 2 hours 4 minutes for established TCP sessions (matching the TCP connection timeout convention), 5 minutes for UDP, and variable for ICMP. When a TCP FIN/RST exchange completes, the session is removed after a shorter timeout (typically 4 minutes for TIME_WAIT).

ICMP Translation

ICMP requires special handling because ICMPv4 and ICMPv6 are different protocols with different type/code numbering. The translator maps between them according to RFC 7915 (IP/ICMP Translation Algorithm). Echo Request/Reply (used by ping) is translated bidirectionally. Error messages (Destination Unreachable, Time Exceeded, Packet Too Big) are translated with their type/code values remapped. Critically, ICMP error messages contain a copy of the offending packet's header in their payload -- the translator must also translate this inner header, which means looking up the inner packet's addresses in the session table.

Fragment Handling

Fragment translation is one of the most complex aspects of NAT64, because IPv4 and IPv6 handle fragmentation differently:

When translating a fragmented IPv4 packet to IPv6, the translator must add a Fragment Extension Header. When translating a fragmented IPv6 packet to IPv4, it must populate the IPv4 fragmentation fields. The translator must also track fragment state: the first fragment contains the transport-layer header (TCP/UDP ports) needed for NAT, but subsequent fragments do not. The translator must reassemble enough of the fragmented flow to identify which session it belongs to, or alternatively buffer fragments and reassemble before translation.

RFC 6146 recommends that NAT64 translators perform virtual reassembly: buffer all fragments of a packet, perform the session lookup on the reassembled packet, then translate and re-fragment for the outgoing protocol. This is memory-intensive and creates a potential denial-of-service vector (fragment flooding), so implementations impose limits on reassembly buffer size and timeout (typically 2 seconds per RFC). In practice, most traffic is TCP with Path MTU Discovery working correctly, so fragmentation is rare. But when it occurs -- particularly with UDP-heavy protocols like DNS over UDP with large responses, or certain VPN protocols -- fragment handling is a frequent source of NAT64 failures.

Application Layer Gateway Issues

NAT64 breaks any application-layer protocol that embeds IP addresses in its payload. This is the same class of problem that plagues traditional NAT, but worse, because NAT64 must translate between two different address families. The affected protocols include:

FTP

FTP's PORT and PASV commands embed IPv4 addresses and port numbers as ASCII text in the data stream. An IPv6-only client using FTP through NAT64 faces a fundamental mismatch: the client's FTP control implementation speaks IPv6 (using EPRT/EPSV from RFC 2428), but the server may only understand IPv4-era PORT/PASV. A NAT64 ALG must rewrite these commands in-flight, converting between EPRT/EPSV and PORT/PASV, adjusting the embedded addresses and ports to match the translated addresses. This is fragile, fails with FTP over TLS (FTPS) where the control channel is encrypted, and is one reason FTP usage has declined significantly.

SIP

SIP (Session Initiation Protocol) embeds IP addresses in SDP (Session Description Protocol) bodies, Via headers, Contact headers, and Record-Route headers. A SIP ALG on a NAT64 device must parse the SIP message, identify all address-bearing fields, translate between IPv6 and IPv4 representations, and adjust the Content-Length header because IPv6 addresses are longer as text strings than IPv4 addresses. SIP ALGs are notoriously unreliable even on regular NAT devices; on NAT64, the added complexity of cross-protocol address rewriting makes them even more problematic. Many VoIP deployments solve this by using Session Border Controllers (SBCs) that are NAT64-aware rather than relying on generic ALG support.

Protocols That Embed Addresses

Other protocols affected include: IPsec AH (the Authentication Header covers the IP header, so translation invalidates it -- ESP in transport mode has similar issues), peer-to-peer protocols that exchange addresses in application-layer signaling, game protocols with embedded IP addresses, and any protocol using IP-address-based referrals. The general solution is to avoid address embedding at the application layer, use domain names instead of IP literals, and rely on the DNS64/NAT64 infrastructure to handle the translation transparently.

IP Literals in URLs and APIs

A common breakage scenario: an application connects to an API endpoint specified as an IPv4 literal (e.g., http://203.0.113.5/api/data) rather than a hostname. DNS64 cannot help here because no DNS query is made. The client tries to connect to 203.0.113.5, but it has no IPv4 stack. On Apple platforms (iOS, macOS), the system provides an address synthesis API (getaddrinfo with AI_ADDRCONFIG) that can convert IPv4 literals to their NAT64-mapped IPv6 equivalents. Apple requires that all apps submitted to the App Store work correctly on IPv6-only networks with NAT64 -- a requirement that has driven significant improvement in IPv4-literal avoidance across the iOS ecosystem.

DNSSEC Interaction with DNS64

DNS64 synthesis creates a fundamental conflict with DNSSEC. When the DNS64 resolver synthesizes a AAAA record, that record was not signed by the domain's authoritative nameserver. A DNSSEC-validating client will reject the synthesized record because it cannot verify its authenticity -- the RRSIG covering the real AAAA RRset (if it existed) does not cover the synthetic address, and there is no RRSIG for a fabricated record.

RFC 6147 addresses this with the following approach:

In practice, very few end-user devices perform their own DNSSEC validation, so this is not a widespread problem. But it is a real architectural limitation: DNS64 and end-to-end DNSSEC are fundamentally incompatible.

Pref64::/n Discovery

An IPv6-only client needs to know the NAT64 prefix in use to perform address synthesis locally (for handling IPv4 literals, for example). Two standardized discovery mechanisms exist:

RFC 7050: DNS-Based Discovery

The client queries for the well-known name ipv4only.arpa with a AAAA query. This domain has a real A record pointing to 192.0.0.170 and 192.0.0.171 (well-known IPv4 addresses assigned by IANA for this purpose) but no real AAAA record. If the client is behind a DNS64 resolver, the resolver will synthesize a AAAA record by embedding 192.0.0.170 into the NAT64 prefix. The client can then compare the returned AAAA record against the known IPv4 address to extract the NAT64 prefix.

For example, if the DNS64 uses 64:ff9b::/96, the query for ipv4only.arpa returns 64:ff9b::c000:aa. The client knows the real IPv4 address is 192.0.0.170 (c000:00aa in hex), so it can determine that the prefix is 64:ff9b::/96. This mechanism works with any prefix length, not just /96, because the client can test multiple embedding positions.

Router Advertisement Pref64 Option (RFC 8781)

RFC 8781 defines a new option in IPv6 Router Advertisements (RA) that carries the NAT64 prefix directly. The router sends the prefix and its length as part of the standard RA that clients already process for SLAAC. This is more reliable than DNS-based discovery because it works even if the client uses a non-DNS64 resolver (e.g., a public resolver like 8.8.8.8 or 1.1.1.1 that does not perform DNS64 synthesis). The client learns the NAT64 prefix from the RA and can use it for local address synthesis regardless of which DNS resolver it uses.

Apple's iOS and macOS support both discovery mechanisms. Android added support for the RA Pref64 option in Android 12. When a device discovers the NAT64 prefix via either method, it can synthesize IPv6 addresses from IPv4 literals without relying on DNS64 -- enabling applications that use IP literals directly to work on IPv6-only networks.

NAT64 Prefix Discovery Mechanisms Method 1: DNS-based (RFC 7050) Client IPv6-only DNS64 Resolver Synthesizes AAAA AAAA ipv4only.arpa? Response: 64:ff9b::c000:00aa Known IPv4: 192.0.0.170 = c000:00aa Extracted prefix: 64:ff9b::/96 Requires DNS64-aware resolver in path. Fails with public resolvers (8.8.8.8, 1.1.1.1). + Works with any prefix length + No router changes needed - Depends on DNS64 resolver being in path - Fails if client uses non-DNS64 resolver Method 2: Router Advertisement (RFC 8781) IPv6 Router Sends RA + Pref64 Client IPv6-only RA with Pref64 option RA Option Type: 38 (Pref64) Prefix + Length: 64:ff9b::/96 Lifetime: 600s (default) Works regardless of DNS resolver choice. Client can use 8.8.8.8, 1.1.1.1, etc. + Independent of DNS resolver + Immediate at network attach (in RA) + Enables local address synthesis - Requires router support for RFC 8781

464XLAT: Solving the IPv4 Literal Problem

NAT64 with DNS64 handles the majority of traffic, but it fundamentally requires DNS to be in the path. Applications that use IPv4 literals, IPv4 socket APIs, or protocols that embed IPv4 addresses will not work with DNS64 alone. 464XLAT (RFC 6877) solves this by adding a client-side stateless NAT46 translator (CLAT) that gives applications a fake IPv4 interface.

The architecture has two translation layers:

The result is that the application sees IPv4, the network carries IPv6, and the PLAT translates to IPv4 at the edge -- hence "464": IPv4 → IPv6 → IPv4. Android has included a CLAT since Android 4.3 (Jelly Bean). Apple's iOS uses a similar mechanism internally. Windows 10/11 supports CLAT as well. 464XLAT is what makes it possible for IPv6-only mobile networks to run legacy IPv4-only apps without giving the device an actual IPv4 address.

Deployment at Mobile Carriers

NAT64 has seen its largest deployment at mobile carriers, where IPv4 address scarcity is acute (each subscriber's device needs an address) and the operator controls the full stack from the device to the packet gateway.

T-Mobile US

T-Mobile US was one of the first major carriers to deploy IPv6-only with NAT64/DNS64 at scale, starting around 2014. Their network assigns only an IPv6 address to mobile devices -- no IPv4 address at all. DNS64 is performed on T-Mobile's recursive resolvers, and NAT64 translators are deployed at their packet gateways. Combined with 464XLAT (CLAT on the device), this allows legacy IPv4 applications to function. T-Mobile reported that over 90% of their mobile traffic is native IPv6, with NAT64 handling the remaining IPv4-only destinations. The approach saved them from needing to deploy CGNAT for IPv4 address sharing, significantly reducing their IPv4 infrastructure costs.

Apple's IPv6 Requirement

Apple mandated in June 2016 that all apps submitted to the App Store must work on IPv6-only networks with DNS64/NAT64. Apple's review process tests apps on a simulated IPv6-only network with NAT64. This single policy decision had an outsized impact on the ecosystem: it forced millions of app developers to stop using IPv4 literals, adopt getaddrinfo properly, and avoid assumptions about address family. Apple's own network interfaces provide automatic address synthesis via the getaddrinfo API when a NAT64 prefix is discovered, handling IPv4 literals transparently for well-behaved applications.

Other Carriers

SKT, KT, and LG U+ in South Korea, EE and Three in the UK, Reliance Jio in India, and numerous carriers across Asia and Europe have deployed IPv6-only with NAT64 on their mobile networks. The pattern is consistent: assign only IPv6 to mobile devices, run DNS64 on the carrier's resolvers, deploy NAT64 at the packet gateway, and rely on 464XLAT for IPv4-literal compatibility. This is now the standard deployment model for new mobile network buildouts, and it is the primary reason global IPv6 adoption statistics have climbed rapidly since 2015.

Hairpinning

Hairpinning occurs when two clients behind the same NAT64 translator try to communicate via their NAT64-synthesized addresses rather than their native IPv6 addresses. For example, if client A at 2001:db8::1 tries to reach client B's IPv4 service via 64:ff9b::c0a8:0102, the traffic would go to the NAT64 translator, get translated to IPv4, and then... where? Client B might not be reachable via IPv4 at all if it is also on the IPv6-only network.

RFC 6146 requires NAT64 implementations to support hairpinning: when the translated IPv4 destination address maps back to a host behind the same translator, the translator should recognize this and forward the packet back, performing a double translation (IPv6 → IPv4 → IPv6). In practice, hairpinning through NAT64 is rare because both clients are on IPv6 and should communicate via native IPv6 addresses. DNS64 would not synthesize a AAAA for a destination that already has a real AAAA record, so the hairpin scenario primarily arises with IPv4 literals or misconfigured services.

Performance Characteristics

NAT64 introduces measurable but generally acceptable performance overhead:

Logging and Tracing Through NAT64

Every stateful NAT creates a logging challenge: the destination server sees the translator's IPv4 address, not the original client's IPv6 address. Tracing back to the original client requires correlating the server-side logs (external IPv4 address, port, timestamp) with the NAT64 translator's session logs (which IPv6 address was assigned that IPv4 address and port at that time).

This is identical to the CGNAT logging problem, and the same solutions apply:

For law enforcement and abuse tracking, the operator must be able to answer: "Who was using IPv4 address X, port Y, at time T?" This requires either real-time session logs or a deterministic mapping algorithm. Regulatory requirements vary by jurisdiction, but most require retention of sufficient data to identify the originating subscriber for a given external address and port at a given time.

Stateless NAT64 (RFC 6145)

While stateful NAT64 (RFC 6146) dominates deployments, stateless NAT64 (defined in RFC 6145, with the algorithm in RFC 7915) exists for scenarios where 1:1 address mapping is possible. In stateless NAT64, each IPv6 host has a dedicated IPv4 address (or a deterministic mapping), and no per-flow session state is maintained. The translator simply rewrites headers based on a fixed mapping table or algorithmic rule.

Stateless NAT64 is used in data center environments where servers need to be reachable from both IPv4 and IPv6 clients, and there are enough IPv4 addresses for 1:1 mappings. It avoids the scalability concerns of session tables but does not help with address conservation -- which is the primary reason operators deploy NAT64 in the first place.

NAT64 vs. Other Transition Mechanisms

How does NAT64 compare to other IPv6 transition technologies?

Operational Considerations

Deploying NAT64 in production involves several practical concerns beyond the protocol mechanics:

Limitations and the Path Forward

NAT64 is not a permanent solution. It exists because the IPv4 internet cannot be switched off overnight. Its limitations are well-understood:

The endgame is a fully IPv6 internet where NAT64 is unnecessary because every destination has an AAAA record. That day is approaching -- Google reports over 45% of its traffic arrives via IPv6 as of 2024, and the fraction of IPv4-only content that requires NAT64 translation is shrinking. But the long tail of legacy IPv4-only services, internal enterprise systems, and IoT devices means NAT64 will remain essential infrastructure for years to come. You can explore the IPv4 vs. IPv6 landscape and look up any IP address or prefix in the looking glass to see the current state of routing for both protocols.

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