Optimized NAT46 Config on a FortiGate

Johannes published a basic NAT46 configuration for a Fortigate firewall with FortiOS 7.0 some time ago. I run such a service (legacy IPv4 access to IPv6-only resources) since FortiOS 5.6, which means more than six years; lastly with FortiOS 6.4. It’s running for more than 100 servers without any other problems as we see them with IPv4 only or dual stack services.

But we weren’t happy with the basic configuration example by Fortinet. We wanted some NAT46 sample configuration with more details, that is: including the original source IPv4 address within the synthesized/SNATted IPv6 address. More in this post, after a short story about my way to a running nat46 configuration with port forwarding in FortiOS 7.2.x.

Note that this post is one of many related to IPv6. Click here for a structured list.

(Please note: this post is about NAT46, not NAT64, which is more common. :))

We operate a firewall which is doing NAT46 for many of our hosting services. All servers have IPv6 GUA (Global Unicast Addresses) and we are happy with the configuration. It looks like this:

Bear the Challenge and Update the FortiGate

Now, Fortinet support asked me, because of an issue we opened a ticket, to update my productive firewall (with the nat46 configuration!) to FortiOS 7.0 or 7.2 soon. I didn’t want to do this step since I read the first 7.0.1 release notes in summer 2021. This is because Fortinet changed the whole NAT64 and NAT46 configuration with policy46, vip46 and vip64 and so on. You will lose all of your configuration for IPv4-IPv6 translation features, not written there word by word, but as a result. They call this “simplify policy and routing configurations” at Fortinet in their Release Notes. And of course, as you already expect: the transfer of the nat46/nat64 configuration from a 6.x operating system to the new 7.x configuration is not a simple clicking around.

During the update process, there is no automatic transfer of the ip pools for nat64 and nat46, and no transfer of the nat64 and nat46 policies. After the first boot with 7.0.x, you only get a hint like this on the console:

As I always strongly recommend when you get this line: run the debug command before the next reboot! There you find a short hint about the ignored transfer of some configuration sections; in this case, as announced in the release notes:

In my case, the configuration had around 800 lines less… Ouch!

I didn’t want to wait and keep many services of our hosting offline for a longer time, so an additional reboot and an update from 7.0.13 to 7.2.6 was the next task.

With the help of a self-written search-and-replace script and some additional, manual changes, I had a text file with my whole nat46 configuration which I could import without any warnings or errors. BE HAPPY.

Optimized nat46 Configuration

Unfortunately, there are no examples with the well-known NAT46/SIIT IPv6-prefix 64:ff9b::/96 in many documentations. This 64:ff9b::/96 range is 32-bit wide (96 + 32 = 128 bit) and represents the whole (32-bit wide) IPv4 range in IPv6, used for NAT46 (and NAT64) and SIIT (see RFC 6052 and RFC 7755 and many others).

The IPv4 address is shown as a hexadecimal address in the last two hextets of the IPv6 address. This means: if you have the (example) IPv4 address 9.9.9.9 it converts to 64:ff9b::909:909 in IPv6 if you use it for NAT46/NAT64; IPv4 address 10.11.12.13 is translated to 64:ff9b::a0b:c0d.  And one more example: IPv4 192.0.2.240 is 64:ff9b::c000:2f0. This is also called a synthesized IPv4 address when the IPv4 address is included in the 64:ff9b address.

If you create an ippool6 object in FortiOS with “startip 64:ff9b::” and “endip 64:ff9b::ffff:ffff“, (remember: the whole 32-bit IPv4 Internet!) and use this IPv6 range as a pool for the NAT46 (poolname6 in the policy for NAT46), the Fortigate will include the synthesized IPv4 source address as the IPv6 source address.

That is: you can still see the original source address in your server logs rather than just one single SNATted address from the firewall itself.

The IPv6-only service you operate behind your nat46 firewall can write this address to the log and may help you solve problems. This is the way IPv6-only servers can still provide the service for IPv4-only clients.

Configuration Example with NAT46 and Port-Forwarding

Here is my configuration example, with port-forwarding by a nat46 policy. First, we must create an IPv6 pool with a size of 32 bit to nat the (whole source) IPv4 address to a (destination) IPv6 address:

The next step is a service object. We need one virtual IP (vip) object for each port/service which has to be mapped from an IPv4 address to an IPv6 address. Here is the example for HTTP and HTTPS:

Now we create an object for port-forwarding within the NAT46 object. The IPv4-address is 192.0.2.39, IPv4 port is 22200 and the IPv6 port is 22 (default SSH):

For this type of port-forwarding an additional custom service object is required. This example is to forward port 22200, but not defined for which destination port:

Finally, we can combine all the created objects in policies:

In policy 462 you can see the port-forwarding configuration. We need the service object nat46fwd22200tcp for the IPv4 mapped port and the SSH service object for the destination IPv6 service. Read the next section to see the internal process to understand, why the configuration needs this.

With this configuration, the IPv6-only daemon for SSH or HTTP/HTTPS can write the synthesized IPv4 address in the log and the marketing department can follow a client connection through the logs.

Until now I could not test the real effect of changing srcaddr6 and dstaddr6 from all to other values. Looking forward to get some feedback!

Bonus: Start of a Data Flow of a nat46 Connection

If you have a detailed look at the following lines (built-in tcpdump in FortiOS), you can see a new, hidden interface called naf.root (I expect root is the name of the vdom, it might be different if you have other vdoms) which is in use to help to translate IPv4 in IPv6 and back:

  • In line 4 the test-ssh-client opens an IPv4 connection to port 22200 with a syn.
  • In line 5 the syn packet is routed through the naf.root interface (still IPv4) and comes back in line 6 as an IPv6 packet to destination port 22 for SSH.
  • The packet was sent to the vlan 503 (line 7) which uses a port channel (line 8) and leaves the unit on physical interface x3 (line 9).

All other lines are very similar and I am only publishing a part of the connection here. And I’m very sorry: I did not understand why there is a naf.root out in line 5 and why it is a naf.root in in line 6; same for all other lines with naf.root. Feedback is welcome!

Further sources: SIIT task of Tore Anderson on RIPE69 (Nov. 2014).

Photo by Ian Taylor on Unsplash.

About Ulrich Hauser

I'm a network engineer and I have to operate the network infrastructure which I have designed, planned and built before. And I like wasting IPv6 networks instead of conserving IPv4 addresses.

Leave a Reply

Your email address will not be published. Required fields are marked *