OpenBSD nat64

I’m a bit of a fan of ipv6. I would like to move as many of my systems to be ipv6-only but in the current world of dual stack that’s just not completely possible. Nat64 helps allow ipv6 hosts connect to the ipv4 internet.

Normally you have:

ipv4 <-- | ------- |--> ipv4 internet
         |         |
host     | gateway |
         |         |
ipv6 <-- | ------- |--> ipv6 internet

The two protocols are kept seperate, and you need both to connect to the network.

In a Nat64 setup, your gate way defines a magic prefix that is routed to it, that is at least a /96 - in other words, it contains a /32 that you can populate with ipv4 addresses. So at home I have a /56:


Inside of this I have reserved a network:


Now, if you change the last 32 bits to an ipv4 address such as:


Or in “pure” ipv6


This traffic is sent via the default route, and the gateway picks it up. It sees the prefix of 2001:db8:0:64::/96 on the packet, it then removes the last 32 bits and forms an ipv4 address. The data of the packet is encapsulated to ipv4, a session table built and the data sent out. When a response comes back, the session table is consulted, the data is mapped back to the origin ipv6 address and re-encapsulated back to the client.

Thus you see:

ping6 2001:db8:0:64::
PING 2001:db8:0:64:: 56 data bytes
64 bytes from 2001:db8:0:64::808:808: icmp_seq=1 ttl=57 time=123 ms

Or from our previous diagram, you can now construct:

ipv4  X  |     ---- | --> ipv4 internet
         |    /     |
host     |   /      |
         |  /       |
ipv6 <-- | -------- | --> ipv6 internet

However, you need a supporting technology. If you were to use this normally, applications don’t know how to attach the ipv4 data into the ipv6 prefix. So you need to support this application with DNS64. This allows hostnames that have no AAAA record, to automatically have the A data appended to the Nat64 prefix. For example with out DNS64:

dig  +short A
dig  +short AAAA

Now, if we add DNS64

dig  +short AAAA

Now we can contact this:

PING 2001:db8:0:64:: 56 data bytes
64 bytes from 2001:db8:0:64::817f:908d: icmp_seq=1 ttl=64 time=130 ms

So, lets get into the meat of the configuration.

First, you need a working Nat64. I’m using openBSD 5.7 on my router, so this is configured purely in pf.conf

pass in quick on $interface_int_r0 inet6 from any to 2001:db8:0:64::/96 af-to inet from (egress:0) keep state rtable 0

That’s all it is! Provided you have working ipv6 already, the addition of this will enable a /96 to now function as your nat64 prefix. Remember, you DO NOT need this /96 on an interface or routed. It exists “in the ether” and pf recognises traffic to the prefix and will automatically convert it to ipv4 traffic exiting on your egress device.

Next you need to configure dns64. I like bind9 so here is the config you need:

options {
    //.... snip .....
            dns64 2001:db8:0:64::/96 {
                clients { any; };
                //Exclude prviate networks we connect to.
                mapped { !; !; any; };
                suffix ::;
                recursive-only yes;


Once you restart named, you will have working DNS64, and able to contact the ipv4 internet from ipv4 hosts.

The only gotcha I have found is with VPNs. When you VPN from the site or into the site with DNS64/Nat64, often you will find that your DNS will resolve hosts to ipv6 addresses, for example, 2001:db8:0:64:: and then will be put onto your network egress port, rather than down the VPN: Not ideal at all! So I exclude the ipv4 ranges from my local networks and my work place to avoid these issues.