Originally published as "Curbing the Evil of NAT (in IOS)"

Introduction

NAT is a necessary evil.

It is necessary because the IPv4 address space would have been exhausted before the end of the 20th century and it can be argued that the Internet as we know it today would never have come to fruition.

It is evil for a number of reasons, not the least of which is that it overcomplicates network configuration and troubleshooting by adding a layer of translation that applications may or may not tolerate well.
This, like so many other necessary evils in networking, is something we have come to accept or at least grudgingly tolerate because there really aren't any ways to avoid it.

What we can do is try to keep it from becoming unnecessarily complex, adding unnecessary evil to the existing maleficence of the technology.

Rant

Too many times I've seen networks, especially where policy-based IPSec VPNs and publicly-addressed DMZ networks are used, where the NAT rules are incredibly complex... bringing a new level of gratuitous diabolism to the aforementioned evil.

Sifting through the NAT definitions and reconciling them with the various IPSec ACLs may be something akin to an accountant's dream, but it's the sort of thing that makes me wake up in a cold sweat.

There has to be a better way.

Strategy

Keeping a simplified NAT configuration that needs little to no modification as new networks are added shouldn't be too complicated if we approach things from a perspective of the possible needs rather than just looking at what we need now.
Typically, our requirements are going to fall into three categories: addresses that should never be subject to NAT, addresses that would normally be subject to NAT that we need to make an exception for, and addresses that always need to be subject to NAT.

If we establish these categories as the basis of our NAT configuration, we should only need to make adjustments under exceptional circumstances and not every single time we add a new IPSec VPN or publicly-addressed DMZ network.

NAT: Never

Communication between RFC 1918 addresses and other RFC1918 addresses will almost never be subject to NAT, so we can easily establish that category as the basis for our new configuration.

First, we define an object group (you'll need a minimum of IOS 12.4.20T to use these) that defines all RFC 1918 private addresses.

object-group network OG_RFC1918
 10.0.0.0 /8
 172.16.0.0 /12
 192.168.0.0 /16

Then we'll establish an extended access control list to ensure that traffic between these addresses are not subject to NAT.

ip access-list extended ACL_NAT
 deny ip object-group OG_RFC1918 object-group OG_RFC1918

NAT: Normally, but not here

Occasionally, we'll need to disable NAT to a particular destination that we would normally perform translation for. This happens under circumstances where there is a DMZ network that uses public addressing but needs to be able to communicate with hosts on a LAN that are using private addresses. There are a few other cases where this might be necessary too, but we'll leave those alone for now.

Assuming that our theoretical DMZ network is using 203.0.113.0/24, we can define another object group for this.

object-group network OG_NAT_Exceptions
 203.0.113.0 /24

Then we add this to our list of addresses to be left untranslated.

ip access-list extended ACL_NAT
 deny ip object-group OG_RFC1918 object-group OG_NAT_Exceptions

NAT: Always

This leaves us with the third category of communications in our strategy: the traffic that we always want to be subject to translation. Because we've already defined all of our exceptions, we don't need to create any more object groups. We only need to add one more statement to our NAT access list.

ip access-list extended ACL_NAT
 permit ip object-group OG_RFC1918 any

Summary

By dividing our NAT strategy into three different categories, we have created an implementation that rarely requires modification as new networks are added and provides the added bonus of simplified troubleshooting.

object-group network OG_RFC1918
 10.0.0.0 /8
 172.16.0.0 /12
 192.168.0.0 /16
!
object-group network OG_NAT_Exceptions
 203.0.113.0 /24
!
ip access-list extended ACL_NAT
 deny ip object-group OG_RFC1918 object-group OG_RFC1918
 deny ip object-group OG_RFC1918 object-group OG_NAT_Exceptions
 permit ip object-group OG_RFC1918 any

Once this is done, ACL_NAT can be used to define our NAT process either directly:

ip nat inside source list ACL_NAT interface GigabitEthernet0/0 overload

Or, using a route map:

route-map RM_NAT_WAN
 match ip address ACL_NAT
 match interface GigabitEthernet0/0
!
ip nat inside source route-map RM_NAT_WAN interface GigabitEthernet0/0 overload

The same technique works equally well with NVI (NAT Virtual Interface) configurations. We just need to omit the "inside" keyword in the NAT definition.

The Whisper in the Wires

NAT is still evil, but we can take some steps to mitigate it and perhaps save that last shred of sanity that remains in the next network specialist that works on our configurations.

Originally published on the Cisco Support Communities site.