Using a Raspberry Pi as a NAT64 gateway


For many purposes, it is been a while since I wanted to have a NAT64 gateway inside my LAN. Sadly, at the time of this writing, my home router does not propose that kind of service. So why not trying to run it on a Raspberry Pi ?

NAT64 gateway is mainly used to reach IPv4 network from IPv6 network. There are many possible applications, such as opening an IPv6 access to a server farm that was only designed to operate through IPv4, or giving an access to IPv4 Internet for hosts only having an IPv6 configuration.

This article describes how to configure a Raspberry Pi as a working NAT64 gateway with the help of the Tayga software.

Table of contents


Disclaimer 1: If you are not using a Debian Linux (or Raspbian) nor installing Tayga through aptitude software, I could not guarantee that you will match the same conditions that I have encountered.

Disclaimer 2: This article only focus on NAT64 and Tayga. Topics as DNS64, Router Advertisement, or firewall are voluntary avoided and would be explored through future articles.


Hardware and software used

  • Router: Mikrotik hEX S model RB760iGS with firmware 6.47.1.
  • Switch: unmanaged NETGEAR GS108 switch
  • Raspberry Pi: model 4B with 4 gigabytes of ram, a quad-core CPU, and with Raspbian 10.4.
  • NAT64 software: Tayga 0.9.2-8

Network topology

Only pertinent addresses used through this article have been put on this diagram.

Here is a simplified representation of the LAN used. This is a really common dual stack LAN that could be found at any home. The default gateway is the router for both IPv4 and IPv6. Everything has access to Internet through IPv4 and IPV6. The router provide an Internet access through a NAT44 gateway because the Internet service provider provides only a single IPv4 address. Every hosts have a public IPv6 address because the Internet service provider provides a /64 IPv6 prefix.

The Raspberry Pi is not directly connected to the router simply because there are only two interfaces on this router model. It would have been more practical to have it directly attached to the router but a router with more than 2 interfaces is pretty expensive for a home usage.

What does the software Tayga ?

This software has one major purpose: converting incoming packet from IPv6 to IPv4 or from IPv4 to IPv6.

To achieve this goals, Tayga uses a Linux kernel feature called TUN devices. The principle of a TUN device is to link a Virtual Network Interface (VNI) to a file descriptor. On the file descriptor side, an application can make raw communication with the network world. And on the VNI side, the kernel can exchange packets between the VNI and the other network interfaces.

For the understanding of the rest of this article, it is important to realize that a TUN device has two ends:

  • The VNI that appears on the interface list and is used by the kernel to exchange data between Tayga and the other network interfaces
  • The file descriptor that is used by Tayga to exchange data with the kernel

From a Kernel point of view, a TUN device could be represented like this:

In terms of OSI model, Tayga interacts with two layers: the network layer and the transport layer.

At the network layer, Tayga only manages IPv4 and IPv6 protocols, if something manages to send another network protocol inside the VNI, it is dropped. (see function read_from_tun in file tayga.c)

At the transport layer, Tayga manages ICMPv4 if network layer is IPv4, ICMPv6 if network layer is IPv6, TCP with both network layers, and UDP with both network layers too. If the payload is something else, it is just copied from IPv4 to IPv6 payload or from IPv6 to IPv4 payload. (see functions xlate_payload_6to4 or xlate_payload_4to6 in file nat64.c)

Tayga addressing scheme

To operate properly, Tayga needs four things: an IPv6 prefix, an IPv4 pool, an IPv6 address and an IPv4 address.

The IPv4 pool

This pool is used to map IPv6 source addresses from the LAN to IPv4 addresses. It is better to pick a different subnet than any subnet used in the LAN. And the pool must be big enough to be mapped to every host in the LAN.

The default configuration of the Debian package comes with the following pre defined pool:

This subnet is different than any subnet used in the LAN and allow 254 mapping, that is more than enough, so let’s use it!

The IPv4 address

For control purpose, and more precisely error management, Tayga must be able to send ICMPv4 packet. To do so, it needs an IPv4 source address. The default configuration coming with the Debian package suggests the use of the address Even if this address is included in the IPv4 pool, it is allowed to use it. This address will be removed from the pool and won’t be used for any translation. The pool size has now a length of 253, still more than enough.

The IPv6 prefix

The IPv6 prefix is the most interesting part because it embeds the IPv4 destination address to translate. And Tayga needs to know from which prefix it has to extract IPv4 addresses.

Because an IPv4 address is 32-bit long, the IPv6 prefix can not be bigger than 96 bits. (an IPv6 address of 128 bits minus an IPv4 address of 32 bits is equal to 96 bits) Tayga follows the rules defined by RFC 6052, the prefix must have one of the following sizes: 32, 40, 48, 56, 64 or 96 bits.

The RFC 6052 also proposes an IPv6 prefix reserved for embedded IPv4 and NAT64 gateway: 64:ff9b::/96
It is called the Well-Known Prefix. Even if it is not mandatory to use it, why not taking that one.

The RFC 6052 comes with a set of rules and good practices that justify some of the implementation choices of Tayga. This is not the purpose of this article to talk about it, but that RFC remains an interesting reading.

The IPv6 address

The reason why Tayga needs an IPv6 address is the same as for the IPv4 address. It needs an IPv6 source address to send ICMPv6 packet in case of issue. But this address is more complicated to determine because of all the rules it has. It can not be an address from the prefix 64:ff9b::/96, not a link local address, not a multicast address, not an address from the prefix 0::/8, …

I am adding one more rule! I do not want it to be a Global Unicast Address (GUA). The reason is quite simple, the NAT44 gateway to reach Internet is managed by the router and not by Tayga. Because of that, Tayga never exchanges directly with hosts out of the LAN. It only exchanges data with LAN hosts or with the router. So there is no need to set an IPv6 that could be forwarded on Internet.

What’s left ? Unique Local Address (ULA). It is not against the rules and it is not a Global Unicast Address.

The IPv6 address of Tayga will be fdaa:bb:1::1.

Simplified use case

With all these settings defined, here is a simplified use case of how things should occur.

A packet arrives at the VNI interface with the IPv6 source address 2001:DB8::100 and the IPv6 destination address 64:ff9b::0808:0808. Tayga translates the source address to the IPv4 address and the destination address to the IPv4 address The payload remains mostly unchanged.

The address is randomly picked. Let’s say it is the first available address in the IPv4 pool.

During the process, Tayga also maps the address to 2001:DB8::100 during at least 2 hours. So if any IPv4 packet comes back, it will be sent to the correct IPv6 destination.

And the destination IPv4 address is extracted from the IPv6 address 64:ff9b::0808:0808. Even if is not obvious, the last 32 bits of the IPv6 address 0808:0808 are the IPv4 address. Groups of 4 hexadecimal characters are used to represent an IPv6 address while the Dot Decimal Notation is used to represent an IPv4 address, this is why it does not look exactly the same, but it is the same value.

The next section describe a more complete and detailed use case.


The test case scenario consists of sending an ICMPv6 request (ping) to Google DNS ( through the NAT64 gateway and then receiving the ICMPv6 reply. The corresponding IPv6 address of the IPv4 embedded into the Well-Known IPv6 prefix is 64:ff9b::, or 64:ff9b::0808:0808, or 64:ff9b::808:808.

Part 1: the ICMP request

The first part of the scenario is the ICMPv6 request being sent from a random host inside the LAN, then being converted into an ICMPv4 request, and finally being sent to Internet.

The red lines is the outgoing ICMPv6 request, the green lines is the outgoing ICMPv4 request, and the blue line is an ICMPv6 redirect packet.

The host sends an ICMPv6 request to the router and not the Raspberry Pi because the only routing rule known by a host is the default route. That means sending every traffic not “on link” to the default gateway. Then the router sends the frame containing the ICMPv6 request to the Raspberry Pi. The Raspberry Pi converts the ICMPv6 request into an ICMPv4 request and sends it to the router. And finally the router sends the ICMPv4 request on Internet.

The router also sends an ICMPv6 redirect (type 137) telling the host it was not the best routing decision to send this ICMPv6 request to the router, it should send it directly to the Raspberry Pi. With that information, the host will send future ICMPv6 request directly to the Raspberry Pi.

The following table describes MAC addresses, IP addresses and payload used at each step:

StepMAC srcMAC destIP versionIP srcIP destPayload
101-00-5E-00-00-6401-00-5E-00-00-0162001:db8::10064:ff9b::808:808ICMP6, echo request
201-00-5E-00-00-6401-00-5E-00-00-0262001:db8::10064:ff9b::808:808ICMP6, echo request
2′01-00-5E-00-00-0101-00-5E-00-00-646fe80::12001:db8::100ICMP6 redirect
301-00-5E-00-00-0201-00-5E-00-00-014192.168.255.2198.8.8.8ICMP echo request
4….4203. echo request

Part 2: the ICMP reply

The second part of the scenario is an ICMPv4 reply coming back from Internet, being converted into an ICMPv6 reply, and being sent back to the specific host that was at the origin of the ICMPv6 request.

The green lines is the incoming ICMPv4 request and the red line is the incoming ICMPv6 request.

The reply path is a little bit simpler than the request path for two reasons:

  • The router knows a route to the IPv4 range used by the NAT64 via the Raspberry Pi
  • The Raspberry Pi replies the ICMPv6 reply to an IPv6 “on link” address and sends it directly to the host on the LAN

The following table describes MAC addresses, IP addresses and payload used at each step:

StepMAC srcMAC destIP versionIP srcIP destPayload echo reply
201-00-5E-00-00-0101-00-5E-00-00-0248.8.8.8192.168.255.219ICMP echo reply
301-00-5E-00-00-0201-00-5E-00-00-64664:ff9b::808:8082001:db8::100ICMP6 echo reply


With all these informations in mind, the configuration is pretty straightforward.

On the Raspberry Pi

Step 1: installing Tayga

Install Tayga through Aptitude packet manager.

pi@raspberrypi:~$ sudo apt install tayga
Reading package lists... Done
Setting up tayga (0.9.2-8) ...

Step 2: configuring Tayga, application side (file /etc/tayga.conf)

The configuration file of Tayga is located at /etc/tayga.conf.

tun-device nat64
ipv6-addr 2001:db8::2
prefix ::/96
data-dir /var/spool/tayga

Almost all of these settings are discussed in the Tayga Addressing Scheme section: ipv4-addr, ipv6-addr, prefix, and dynamic-pool.

The setting tun-device is the VNI interface created and used by Tayga. Except if you know what you are doing and want to do some experimentation, the best is to keep the value nat64. There is no need to manually create this interface, scripts provided with the Debian package do it for you.

The setting data-dir is a path to a directory where Tayga keeps track of dynamic mappings. So in case of a reload of Tayga or if the Raspberry Pi restarts, informations about existing mapping are resilient.

Step 3: configuring Tayga, VNI side (file /etc/default/tayga)

Another configuration file that needs to be updated is /etc/default/tayga, this is not an official Tayga configuration file, but an extra configuration file added by the packaging team. This file is used by the script /etc/init.d/tayga mainly to set the VNI side. To be precise, this is the configuration of the Linux side of the TUN device, Tayga does not read nor use that file at all. (see section explaining how Tayga works)

Here is the content of this file:


The setting CONFIGURE_IFACE must be kept to yes. It instructs scripts to create and set up the VNI interface defined in Tayga configuration file, see tun-device parameter. If this setting was set to no, it would have need to be done manually.

The setting CONFIGURE_NAT44 must be set to no. The authors of this script have though the service