Documentation > Architectures > 464XLAT



  1. Introduction
  2. Problem Statement
  3. Sample Network
  4. Expected Packet Flow
  5. Testing
  6. Closing words


This document is a summary of the 464XLAT architecture (RFC 6877), collapsed into a walkthrough that uses Jool.

Problem Statement

Barring RFC 6384, NAT64 only translates network (IPv4, IPv6 and ICMP) and transport headers (UDP and TCP). Unfortunately, some protocols on top of UDP and TCP have a bad habit of including IP addresses (“IP literals”) along their conversations. Because NAT64 only translates lower protocols, these addresses will slip past the NAT64 unmodified.

For example, some IPv6-unaware website, which would normally contain this HTML:

<a href="">Link to something.</a>

Could be poorly coded like this:

<a href="">Link to something.</a>

This address lies within the body of an HTML file, not a network or transport header. It is not viable for a NAT64 to support translation of all existing application protocols.

Clicking the latter version of the link from an IPv6-only node via a NAT64 will result in failure, because the node doesn’t have an IPv4 stack which to access with. works fine because the DNS64 appends the NAT64 prefix once the node asks about it; on the other hand, if all the node has is, it can’t really tell it’s talking via a NAT64, much less know which prefix should be appended.

464XLAT is a technique meant to address this limitation. It appends an SIIT to the path, which mirrors the NAT64’s work, which grants a controlled amount of clients a fallback IPv4 stack to access IP literals with.

Sample Network

Fig.1 - 464 Needed

The red box would be your domain. n6 stands for “IPv6 node” and R is “router”. Say your ISP gives you only IPv6 addresses, but it also grants you access to IPv4 via a Stateful NAT64 (PLAT; “Provider-side Translator”). n4 is a random IPv4 Internet node.

Say your user from n6 clicks a link towards n6 does not have an IPv4 stack, so the request has nowhere to go. The situation could be amended by manually appending the NAT64 prefix to the address, but the user doesn’t know that. Of course, a DNS64 would be the ideal and transparent solution, but unfortunately the site provided an address and not a domain name, so n6 is not querying the DNS.

In broad terms, the solution is to provide n6 with a “fake” IPv4 stack whose packets will be translated into IPv6 before reaching PLAT. In other words, an SIIT service (in 464XLAT terms called “CLAT”; “Customer-side Translator”) will be sort of undoing PLAT’s work.

If n6 is a lone case and you want to isolate the mirror hack as much as possible, n6 itself can be the CLAT. If you want to provide this feature to several nodes however, R is a better candidate:

Fig.2 - 464XLAT'd Network

I also removed the clouds to simplify routing in the example. The dual translation idea has really nothing to do with routing, so this is unimportant.

Expected Packet Flow

This is the normal flow an IPv6-sourced packet would traverse. It is a typical Stateful NAT64 flow and the Dual Translation presented in this configuration will not interfere with it: Notice we’ve chosen 64:ff9b::/96 as PLAT’s NAT64 prefix:

Figure 3 - Normal Stateful Flow

The 464XLAT flow we want to achieve follows. n6 will use its IPv4 address to try to query the literal (or whatever IPv4 Internet address):

Figure 4 - Literal

R will SIIT the packet into IPv6 so it can traverse the IPv6-only chunk. Address will be translated using the EAMT, and will receive the pool6 prefix treatment to mirror PLAT’s.

Figure 5 - SIIT'd packet

PLAT will do its magic and send the packet to the IPv4 Internet:

Figure 6 - Stateful NAT64'd packet

And the mangling will be mirrored for the response:

Figure 7 - Mirror


n6 doesn’t know it kind of owns another IPv6 address in the 2001:db8:2::/96 network. It never sees this traffic, because R always translates it towards

service network-manager stop

ip link set eth0 up
ip addr add 2001:db8:1::8/64 dev eth0
ip addr add dev eth0

ip route add default via 2001:db8:1::1
ip route add default via

This is R:

service network-manager stop

ip link set eth0 up
ip addr add dev eth0
ip addr add 2001:db8:1::1/64 dev eth0

ip link set eth1 up
ip addr add 2001:db8:100::1/64 dev eth1

# Traffic headed to the real IPv4 Internet goes via PLAT.
ip route add 64:ff9b::/96 via 2001:db8:100::2

# Enable SIIT.
# We're masking the private network using an EAMT entry.
# Traffic towards the Internet is to be appended PLAT's prefix.
# Recall that the EAMT has higher precedence than the prefix.
modprobe jool_siit pool6=64:ff9b::/96
jool_siit --eamt --add 2001:db8:2::/125

n6’s packet will have addresses and The former will be translated using the EAMT entry (since it matches and the latter will use the pool6 prefix (because it doesn’t match).

Also note that R is an average SIIT implementation and you shouldn’t think of this installation of Jool as anything other than that.

For completeness sake, here’s PLAT’s network configuration:

service network-manager stop

ip link set eth0 up
ip addr add 2001:db8:100::2/64 dev eth0
# I'm pretending the ISP gave us these two prefixes to play with.
ip route add 2001:db8:1::/64 via 2001:db8:100::1
ip route add 2001:db8:2::/64 via 2001:db8:100::1

ip link set eth1 up
ip addr add dev eth1
ip addr add dev eth1

modprobe jool pool6=64:ff9b::/96 pool4=

And n4 is thoroughly boring:

service network-manager stop

ip link set eth0 up
ip addr add dev eth0
ip route add default via


Ping n4 via IPv4 from n6:

$ ping -c 1
PING ( 56(84) bytes of data.
64 bytes from icmp_seq=1 ttl=62 time=4.13 ms

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

Ping n4 via IPv6 from n6:

$ ping6 64:ff9b:: -c 1
PING 64:ff9b:: 56 data bytes
64 bytes from 64:ff9b::cb00:7118: icmp_seq=1 ttl=62 time=14.0 ms

--- 64:ff9b:: ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 14.053/14.053/14.053/0.000 ms

Closing words

Though at this point you can see how you can defend yourself against IP literals and legacy IPv4-only appliances, you might want to be forewarned that at least one application protocol out there is so poorly designed it works differently depending on whether it’s sitting on top of IPv6 or IPv4. Therefore, addressing IP literals in this case is not sufficient to make FTP work via NAT64.

On the other hand, some network-aware protocols only partially depend on literals, and the NAT64 is not going to get in the way of the features that don’t. FTP’s “extended passive” mode falls in this category.

You can make active FTP work by deploying a fully stateless dual translation environment such as siit-dc-2xlat. It works because both the client and server are both using IPv4 sockets, the IPv4 addresses are unchanged end-to-end, and it’s fully bi-directional, so active and passive FTP on arbitrary ports work fine. In siit-dc-2xlat, the IPv6 network in the middle becomes an invisible “tunnel” through which IPv4 is transported.

Here’s a list of protocols that are known to use IP literals. You might also want to see RFC 6586.