In this article, we will look at the NAT order of operation on Cisco IOS. We will restrict ourselves to the operation of routing and NAT. Two types of NAT can be configured on the Cisco IOS: Traditional (or domain-based) NAT and the newer NAT Virtual Interface (NVI) and we will see how these differ in their operations.

CCNA Training – Resources (Intense)

Traditional NAT

Traditional NAT is domain-based; i.e., you need to have at least one interface configured as “inside” and another interface configured as “outside.” When traditional NAT is configured, the direction of traffic determines the order of operation and it can be summarized in the following two points:

  • For inside to outside flow, routing occurs before the packets are translated.
  • For outside to inside flow, packets are first translated before routing occurs.

Let’s use the diagram below to illustrate these points:

In this network, we will not configure routing (static or dynamic routes) on the routers but there should be network-wide connectivity. To achieve this, we will configure R2 (192.168.12.2) to appear as 192.168.13.2 to R3 and we will configure R3 (192.168.13.3) to appear as 192.168.12.3 to R2. Since R2 will see R3’s IP address as an address in its network, we don’t need to configure any routes on R2. The same goes for R3.

R1 will be the router performing NAT and the configuration on this router is as follows:

hostname R1
!
interface FastEthernet0/0
 ip address 192.168.12.1 255.255.255.0
 ip nat inside
 no ip route-cache
!
interface FastEthernet0/1
 ip address 192.168.13.1 255.255.255.0
 ip nat outside
 no ip route-cache
!
ip nat inside source static 192.168.12.2 192.168.13.2
ip nat outside source static 192.168.13.3 192.168.12.3

Note: I have disabled fast switching (no ip route-cache) on both interfaces so that packets will be process-switched, i.e., we will be able to see packets passing through the router when we turn on debugging.

By default, when we configure a NAT rule, the router will add an alias for the translated address so that it will be able to reply to ARP requests for that IP address (proxy ARP). We can see this using the show ip aliases command:

Now let’s test our NAT rules. From R2 we will ping 192.168.12.3 and from R3 we will ping 192.168.13.2.

R2#ping 192.168.12.3 re 2
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.12.3, timeout is 2 seconds:
.!
Success rate is 50 percent (1/2), round-trip min/avg/max = 16/16/16 ms

R3#ping 192.168.13.2 re 2
Type escape sequence to abort.
Sending 2, 100-byte ICMP Echos to 192.168.13.2, timeout is 2 seconds:
..
Success rate is 0 percent (0/2)

We see above that the ping from R2 succeeded but the one from R3 failed. As we will soon discover, the ping that succeeded from R2 is actually “false” in the sense of it.

We will first consider the ping from R2 to 192.168.12.3 (R3’s NAT IP address). We will turn on NAT debugging and also IP packet debugging on R1 to see what happens.

R1#debug ip nat detailed
IP NAT detailed debugging is on
R1#debug ip packet detail
IP packet debugging is on (detailed)
R1#
R1#
*Mar  1 00:40:51.739: IP: tableid=0, s=192.168.12.2 (FastEthernet0/0), d=192.168.12.3 (FastEthernet0/0), routed via RIB
*Mar  1 00:40:51.739: IP: s=192.168.12.2 (FastEthernet0/0), d=192.168.12.3 (FastEthernet0/0), len 100, rcvd 3
*Mar  1 00:40:51.739:     ICMP type=8, code=0
*Mar  1 00:40:51.743: ICMP: echo reply sent, src 192.168.12.3, dst 192.168.12.2
*Mar  1 00:40:51.743: IP: tableid=0, s=192.168.12.3 (local), d=192.168.12.2 (FastEthernet0/0), routed via FIB
*Mar  1 00:40:51.743: IP: s=192.168.12.3 (local), d=192.168.12.2 (FastEthernet0/0), len 100, sending
*Mar  1 00:40:51.747:     ICMP type=0, code=0

Since this traffic is flowing from inside to outside, then routing should occur first before NAT takes place. From the first debug output, we see that R1 routed the packet to the same interface on which it came in (Fa0/0) and since it has an alias for that IP address, it responds on behalf of that address. So in summary, the reply that R2 got to its ping came from R1 and not from R3. Also notice that there are no NAT debug messages – this is because the NAT translation rule was never triggered!

We can confirm that the ping never got to R3 by debugging ICMP packets on both R1 and R3.

R2#ping 192.168.12.3 re 1
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 192.168.12.3, timeout is 2 seconds:
!
Success rate is 100 percent (1/1), round-trip min/avg/max = 29/29/29 ms

R1#und all
All possible debugging has been turned off
R1#
R1#debug ip icmp
ICMP packet debugging is on
R1#
*Mar 1 00:17:39.447: ICMP: echo reply sent, src 192.168.12.3, dst 192.168.12.2
R1#

R3#debug ip icmp
ICMP packet debugging is on
R3#

Before we resolve this issue, let us look at the other flow: R3 pinging 192.168.13.2.

R1#
*Mar  1 00:12:47.567: NAT*: o: icmp (192.168.13.3, 2) -> (192.168.13.2, 2) [7]
*Mar  1 00:12:47.571: NAT*: o: icmp (192.168.13.3, 2) -> (192.168.13.2, 2) [7]
*Mar  1 00:12:47.571: NAT*: s=192.168.13.3->192.168.12.3, d=192.168.13.2 [7]
*Mar  1 00:12:47.571: NAT*: s=192.168.12.3, d=192.168.13.2->192.168.12.2 [7]
*Mar  1 00:12:47.575: IP: tableid=0, s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), routed via FIB
*Mar  1 00:12:47.575: IP: s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), g=192.168.12.2, len 100, forward
*Mar  1 00:12:47.579:     ICMP type=8, code=0
R1#
*Mar  1 00:12:47.603: IP: tableid=0, s=192.168.12.2 (FastEthernet0/0), d=192.168.12.3 (FastEthernet0/0), routed via RIB
*Mar  1 00:12:47.603: IP: s=192.168.12.2 (FastEthernet0/0), d=192.168.12.3 (FastEthernet0/0), len 100, rcvd 3
*Mar  1 00:12:47.603:     ICMP type=0, code=0
R1#

The first message we see in our debug output is NAT-related. This confirms that when traffic is flowing from the outside to the inside, the packet is first translated before routing takes place. Therefore, R1 translates the packet from {s=192.168.13.3, d=192.168.13.2} to {s=192.168.12.3, d=192.168.12.2}.

After the NAT debug messages, the next message shows that R1 routes and forwards the packet from Fa0/1 to Fa0/0 towards R2. We can confirm that R2 received this ping and replied by debugging ICMP on that router.

R2#
*Jun 24 10:40:01.912: ICMP: echo reply sent, src 192.168.12.2, dst 192.168.12.3, topology BASE, dscp 0 topoid 0
R2#

Continuing with the debug output on R1, we see this ICMP reply sent by R2 towards 192.168.12.3. Since we are back to the inside-to-outside flow, R1 tries to route the packet first before NAT rules are applied. Because of the same issue we already discussed, this packet never gets to R3 and so the ping fails.

Having seen the problem, let us now resolve it. We have discovered that routing is what is causing the problem, so we need to tell R1 that to get to 192.168.12.3, it needs to use its Fa0/1 interface, not the Fa0/0 interface.

Resolution #1: Static route

One way to do this will be to configure a static route for this address pointing towards the right interface or towards R3.

ip route 192.168.12.3 255.255.255.255 fa0/1

Let’s test this now by ping from R2 to 192.168.12.3 again. I will turn on NAT and IP packet debugging on R1 as we did before.

R2#ping 192.168.12.3 re 1
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 192.168.12.3, timeout is 2 seconds:
!
Success rate is 100 percent (1/1), round-trip min/avg/max = 29/29/29 ms

The debug output on R1 is as follows:

R1#
*Mar  1 00:30:49.063: IP: tableid=0, s=192.168.12.2 (FastEthernet0/0), d=192.168.12.3 (FastEthernet0/1), routed via RIB
*Mar  1 00:30:49.067: NAT: i: icmp (192.168.12.2, 4) -> (192.168.12.3, 4) [9]
*Mar  1 00:30:49.067: NAT: s=192.168.12.2->192.168.13.2, d=192.168.12.3 [9]
*Mar  1 00:30:49.067: NAT: s=192.168.13.2, d=192.168.12.3->192.168.13.3 [9]
*Mar  1 00:30:49.071: IP: s=192.168.13.2 (FastEthernet0/0), d=192.168.13.3 (FastEthernet0/1), g=192.168.13.3, len 100, forward
*Mar  1 00:30:49.071:     ICMP type=8, code=0
*Mar  1 00:30:49.099: NAT*: o: icmp (192.168.13.3, 4) -> (192.168.13.2, 4) [9]
R1#
*Mar  1 00:30:49.099: NAT*: s=192.168.13.3->192.168.12.3, d=192.168.13.2 [9]
*Mar  1 00:30:49.099: NAT*: s=192.168.12.3, d=192.168.13.2->192.168.12.2 [9]
*Mar  1 00:30:49.099: IP: tableid=0, s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), routed via FIB
*Mar  1 00:30:49.099: IP: s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), g=192.168.12.2, len 100, forward
*Mar  1 00:30:49.099:     ICMP type=0, code=0
R1#

Now we see that things work the way they should. The first debug message shows that R1 routed the packet to Fa0/1. This then triggered the NAT rules and the packet was translated from {s=192.168.12.2, d=192.168.12.3} to {s=192.168.13.2, d=192.168.13.3}. The packet was then forwarded to R3.

Note: “Routing” as used here just means determining the egress interface to use to forward the packet. “Forwarding” is the actual sending of the packet.

The ping reply from R3 is seen next and the packet is first translated before routing and forwarding take place. We can confirm that R3 received this ping packet and replied from the ICMP debug message:

R3#
*Jun 24 10:50:29.661: ICMP: echo reply sent, src 192.168.13.3, dst 192.168.13.2, topology BASE, dscp 0 topoid 0

This method of resolution will not work on routers with newer IOS versions because such routers install “local” routes for the translated addresses in their routing tables. Since directly connected routes are preferred over static routes, the static route will never be installed in the routing table.

An example of the routing table of a router running IOS version 15 with inside and outside NAT configured is shown below:

Resolution #2: “add-route” option

The second option is to use the “add-route” option when you configure an outside NAT. This option will automatically install a static route for the outside local address in the router’s routing table.

no ip nat outside source static 192.168.13.3 192.168.12.3
no ip route 192.168.12.3 255.255.255.255 fa0/1
ip nat outside source static 192.168.13.3 192.168.12.3 add-route

With this configuration, we will see the static route for 192.168.12.3/32 in R1’s routing table:

NAT Virtual Interface (NVI)

NVI gets rid of domain; i.e., we don’t have to define inside and outside domains any more. We will also see that with NVI-based NAT, the order of operation is different.

I will clear the NAT configuration on R1 and enable NVI-based NAT (ip nat enable).

interface FastEthernet0/0
 no ip nat inside
 ip nat enable
!
interface FastEthernet0/1
 no ip nat outside
 ip nat enable
!
no ip nat inside source static 192.168.12.2 192.168.13.2
no ip nat outside source static 192.168.13.3 192.168.12.3 add-route
!
ip nat source static 192.168.12.2 192.168.13.2
ip nat source static 192.168.13.3 192.168.12.3

Let’s now ping from R2 to 192.168.12.3 and watch the debug on R1.

R2#ping 192.168.12.3 re 1
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 192.168.12.3, timeout is 2 seconds:
!
Success rate is 100 percent (1/1), round-trip min/avg/max = 31/31/31 ms

R1#
*Mar 1 01:21:46.987: IP: tableid=0, s=192.168.12.2 (FastEthernet0/0), d=192.168.12.3 (FastEthernet0/0), routed via RIB
*Mar 1 01:21:46.987: NAT: i: icmp (192.168.12.2, 7) -> (192.168.12.3, 7) [12]
*Mar 1 01:21:46.991: NAT: s=192.168.12.2->192.168.13.2, d=192.168.12.3 [12]
*Mar 1 01:21:46.991: NAT: s=192.168.13.2, d=192.168.12.3->192.168.13.3 [12]
*Mar 1 01:21:46.991: IP: tableid=0, s=192.168.13.2 (FastEthernet0/0), d=192.168.13.3 (FastEthernet0/1), routed via RIB
*Mar 1 01:21:46.995: IP: s=192.168.13.2 (FastEthernet0/0), d=192.168.13.3 (FastEthernet0/1), g=192.168.13.3, len 100, forward
*Mar 1 01:21:46.995: ICMP type=8, code=0
R1#
*Mar 1 01:21:47.019: IP: tableid=0, s=192.168.13.3 (FastEthernet0/1), d=192.168.13.2 (FastEthernet0/1), routed via RIB
*Mar 1 01:21:47.019: NAT: i: icmp (192.168.13.3, 7) -> (192.168.13.2, 7) [12]
*Mar 1 01:21:47.019: NAT: s=192.168.13.3->192.168.12.3, d=192.168.13.2 [12]
*Mar 1 01:21:47.019: NAT: s=192.168.12.3, d=192.168.13.2->192.168.12.2 [12]
*Mar 1 01:21:47.019: IP: tableid=0, s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), routed via RIB
*Mar 1 01:21:47.019: IP: s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), g=192.168.12.2, len 100, forward
*Mar 1 01:21:47.019: ICMP type=0, code=0

Looking at the debug output: When the ICMP echo request packet comes in from R2, we see that R1 first takes a routing decision to send the packet to the virtual interface (since the destination is an address in the NAT table), translates the packet via NAT rules, takes another routing decision (egress interface is now Fa0/1), and then forwards the packet to R3.

If we look at the ICMP echo reply from R3, we see the same process again: routing decision, translates the packet, another routing decision (egress interface is now Fa0/0) and the forwards the packet to R2.

In summary, when NVI-based NAT is configured, the order of operation is symmetric for traffic flow in any direction and the order is routing, NAT, routing again and then forwarding.

Summary

In this article, we have considered the order of operation (routing and NAT) on Cisco IOS devices when NAT is configured. For traditional NAT, we discovered that when traffic is flowing from inside to outside, routing is performed first before NAT rules are applied. This can cause an issue if outside NAT is also configured and this problem can be fixed by configuring a static route (older IOS versions only) or using the “add-route” option. On the other hand, when traffic is flowing from the outside to the inside, NAT rules are first applied and then routing decision is made.

For NVI-based NAT, the order of operation is symmetric in any direction: routing first to virtual interface, NAT rules applied, routing again to determine egress interface and then forwarding.

I hope you have found this article insightful.

References and Further Reading