Subnet-to-Subnet SNAT/DNAT on Fortinet Firewalls with Central NAT

Implementing SNAT/DNAT on Fortinet Firewalls has never been straightforward as on other platforms like Checkpoint, in my opinion, at least before the introduction of Central NAT. Let’s see how to implement a subnet-to-subnet 1-to-1 translation with deterministic mappings, using VirtualIPs (DNAT) and Fixed-Port-Range IP-Pools (SNAT).

Gianni Costanzi
Nerd For Tech
7 min readApr 26, 2021

--

Introduction

I’ve recently had to migrate some firewall security and NAT rules from Checkpoint to Fortinet firewalls and faced some challenges due to the different behavior of the two technologies. Checkpoint applies security policies first (at least the version I’m using) and then it checks the NAT policies and applies both source and destination NAT. Fortinet instead has a different order of operations, more like Linux with Iptables: the packet arrives from the incoming interface, there is a pre-routing step where Destination NAT (DNAT from now on) happens, follows a routing decision (since now the final destination is known) that determines the outgoing interface, security policies (IPv4 policies) are examined and finally Source NAT (SNAT from now on) takes place and the packet can be sent out on the outgoing interface (more detail can be found here, I’ve omitted a lot of other intermediate steps).

During the rules’ migration, I’ve had the necessity to implement SNAT and DNAT rules to map a /22 network onto another /22 network, with deterministic mappings. Let’s take the following as an example:

On the right there is a partner’s network, 10.0.100.0/22 (i.e. with IPs ranging from 10.0.100.0 to 10.0.103.255) for which our firewall has a known route. We have some rules that required communication from our network on the left toward the partner network but via an alias network 10.0.200.0/22, which is routed in our network to our border firewall. What is important is that the mapping, in both directions, must be deterministic, i.e. 10.0.101.55 must be SNATted to 10.0.201.55 and vice versa, or in a more generic way, 10.0.10x.y must be mapped to 10.0.20x.y (and vice versa). To make things more complex, SNAT from the partner network to us must happen only for some IPs within 10.0.100.0/22.

NOTE: examples and images shown are taken on FortiManager, the Interface/VM used to manage FortiGate firewalls. You can apply the same concepts directly on FortiGate firewalls (maybe there is a slightly different terminology).

Central NAT

As I’ve mentioned before, implementing DNAT and SNAT on Fortinet FortiGate firewalls has never been simple as on other platforms, but they’ve made a big step forward with Central NAT, that adds two policy tables to the standard IPv4 table with security rules that we usually had on FortiManager. There is still something that reminds the old way of configuring nat (Central DNAT rules create VirtualIP objects that where used before to implement DNAT, but now you do not have to apply them as destinations in standard security rules), but it is more easy to use and to read (and less prone to errors).

Security Rules

As I’ve explained before, DNAT happens before security policies are evaluated, so when you write the security policies you must take into account that DNAT already translated destination addresses. On the opposite, SNAT has still to happen, so original source addresses must be used. In order to implement bidirectional communications we’ve shown in the example figure above, we need two rules:

IPv4 Security Policies

As you can see, rule #1 has 10.0.100.0/22 as destination, because DNAT from 10.0.200.0/22 to 10.0.100.0/22 has already been applied.

DNAT

DNAT is quite easy to implement, it requires a single rule in Central DNAT table, which in turn creates a single VIP Object:

DNAT 10.0.200.0/22 to 10.0.100.0/22

As you can see you set the range of IP addresses of the /22 network that we “know” on our side and then you specify only the first address of the real Mapped (or Internal) network of the partner. The GUI computes the final IP of that network in order to have a 1-to-1 static and deterministic mapping.

SNAT and the Fixed Port Range IP-Pools

I faced some challenges to implement the opposite, since in order to migrate the rules from the old firewall we had to be able to implement a deterministic SNAT of 10.0.10x.y to 10.0.20x.y IP addresses, but only for specific sources. Source NAT is implemented in Central SNAT table, where you write a policy, like the security ones, specifying source/destination addresses and ports of the traffic to match (with post-DNAT destinations) and then you select to SNAT the traffic onto the IP of the outgoing interface or onto an IP pool. The IP Pool can be of different kinds: overload, One-to-One, Fixed Port Range and Port Block Allocation. One-to-One was the one that seemed right for me, since we want to implement a 1-to-1 mapping between two subnets… but that object allows you to specify a single range of IP addresses, so the FortiGate has no way of knowing which source IP (that is something you specify in the Central SNAT policy, a separate object) will be mapped on which IP specified in the IP-Pool object. So in my first test I’ve implemented a Central SNAT policy that said “traffic from test-outside and with source IP in 10.0.100.0/22 has to be SNATted onto an IP-Pool One-to-One object that specifies 10.0.200.0/22 as range”. Then I made some tests, with a flow from 10.0.100.10 SNATted onto 10.0.200.1, a second flow from 10.0.100.x SNATted onto 10.0.200.2 and so on, so I could not achieve a deterministic 1-to-1 mapping as I needed.

I’ve searched a bit through the documentation and I’ve read how Fixed Port Range IP-Pool object works: Fixed Port Range allows you to map for example a /22 network onto a /24 network, so with a 4:1 ratio of IP addresses, in a deterministic way, reserving 1/4 of the ports for each IP. You can find more details on the Official Fortinet Documentation. What you can see is that if you have a 1:1 ratio of IP addresses between the real network and the mapped one, you can achieve a deterministic 1-to-1 mapping of 10.0.10x.y port w to 10.0.20x.y port w with Fixed Port Range SNAT.

The following formulas are found in section The Equations on official docs:

Image taken from https://docs.fortinet.com/document/fortigate/5.4.0/cookbook/414467/fixed-port-range-ip-pools-algorithm

Let’s try with our particular case, with a partner’s source IP of 10.0.102.55:

Here you are: 10.0.102.55 is translated to 10.0.202.55! Bingo!

Math with subnets and IP addresses may not seem so intuitive at first, but it should be quite clear in this example I’ve shown.

As you can see in the following image, with Fixed Port Range you specify the exact mapping, by telling FortiManager what is the External IP Range (the IP addresses as known on our side) and the Internal (or mapped) IP addresses (the real IP addresses on the partner’s network):

Fixed Port range IP-Pool

Now we can use this IP-pool object in a Central SNAT rule where we determine for which IP addresses of the partner network we want the deterministic NAT to happen:

Central SNAT rule details
Central SNAT rule

Obviously you can implement the above SNAT with 3 rules each one with its own Overload/One-to-One IP-pool that translates a single IP onto the corresponding one, but in my example you have a deterministic 1-to-1 mapping of the original /22 network onto the target /22 network and then you determine with SNAT policies when it has to take place.

With the above rule, traffic from 10.0.100.5 arrives into our network with source 10.0.200.5, as expected, while traffic from 10.0.101.100 for example arrives with its original IP address since it is not matched by the Central SNAT policy.

In a simpler setup, you can make the SNAT happen for the whole 10.0.100.0/22 by setting it as source address in the Central SNAT rule:

SNAT for the whole partner’s network

Conclusions

I wanted to share this bit of knowledge because I had some headaches before finding the solution to my problem for this deterministic SNAT between subnets and maybe it can be useful for someone that would have never thought (like me) to use a Fixed Port Range IP-pool object to implement it, until I’ve read the documentation and understood how it worked in our corner case with 1:1 ratio of IP addresses.

--

--

Gianni Costanzi
Nerd For Tech

Network Engineer, Music Lover, Motorbike Rider, Amateur Photographer, Nerd-inside