This post will describe how NAT works. The reason for doing a blog post on NAT
is twofold. There is a lack of good documents out there describing NAT and I
want to do some learning for myself as well.

When we are talking about NAT we have some terminology that is commonly used.
Since the address is changed along the way we need to describe which address
we are referencing when talking about the IP address. The terminology is this.

Inside local address โ€“ This is the address as seen on the LAN (inside) before
the translation occurs.

Inside global address โ€“ This is the address as seen by other hosts on the Internet.
This is the address after translation has occurred.

Outside local โ€“ This is the address on the LAN of the other side. Note that if other side
is not running NAT the outside local and global may be the same. In the diagram the other
side is running public IP addresses on the inside (a valid design).

Outside global โ€“ The IP address as seen by other hosts on the Internet, this may be the
same as the inside local depending on if NAT is used or not.

When using NAT we need to define inside and outside interfaces (except if NVI is used).
The LAN interface(s) are the inside interfaces and the WAN interface(s) are the outside
interfaces. Translation is done when traffic is going from an inside interface to an
outside interface or vice versa.

The most basic NAT we can do is a 1:1 static NAT where the inside local address is
translated to an inside global address. We can map to an IP directly or to the
outside interface. To NAT to an interface the syntax is:

ip nat inside source static INSIDE_LOCAL interface OUTSIDE_INTERFACE

ip nat inside source static 192.168.1.11 interface f0/1
sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
--- 193.10.0.2         192.168.1.11       ---                ---

Traffic sourcing from the inside local address 192.168.1.11 will be translated
to 193.10.0.2. When we are doing static NAT there is a bidirectional translation
so when traffic is coming back in the outside interface the destination is
translated from inside global to inside local address. We can see this when we
debug ip nat detail.

NAT*: i: icmp (192.168.1.11, 6) -> (184.10.0.4, 6) [12]
NAT*: s=192.168.1.11->193.10.0.2, d=184.10.0.4 [12]
NAT*: o: icmp (184.10.0.4, 6) -> (193.10.0.2, 6) [12]
NAT*: s=184.10.0.4, d=193.10.0.2->192.168.1.11 [12]

First we see the ICMP packet coming in and the source gets translated and then
the return packet comes in and the destination address is translated. This is
how the translation table looks like.

R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
icmp 193.10.0.2:6      192.168.1.11:6     184.10.0.4:6       184.10.0.4:6
--- 193.10.0.2         192.168.1.11       ---                ---

We can also do a static NAT and choose what the inside global address should be. The syntax is this:

ip nat inside source static INSIDE_LOCAL INSIDE_GLOBAL

ip nat inside source static 192.168.1.11 193.10.0.254
R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
--- 193.10.0.254       192.168.1.11       ---                ---

We now see that the source is translated to 193.10.0.254.

NAT*: i: icmp (192.168.1.11, 8) -> (184.10.0.4, 8) [14]
NAT*: s=192.168.1.11->193.10.0.254, d=184.10.0.4 [14]
NAT*: o: icmp (184.10.0.4, 8) -> (193.10.0.254, 8) [14]
NAT*: s=184.10.0.4, d=193.10.0.254->192.168.1.11 [14]

The translation table looks like this:

R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
icmp 193.10.0.254:8    192.168.1.11:8     184.10.0.4:8       184.10.0.4:8
--- 193.10.0.254       192.168.1.11       ---                ---

When doing regular static NAT like this we can only map one inside
local to one inside global. What if we want to map several outside
addresses to the same inside address? To do that we need to create
extendable NAT translations. The syntax is the same but with the keyword
extendable at the end.

ip nat inside source static 192.168.1.11 193.10.0.254 extendable
ip nat inside source static 192.168.1.11 193.10.0.253 extendable

The translation table now looks like this:

R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
--- 193.10.0.254       192.168.1.11       ---                ---
--- 193.10.0.253       192.168.1.11       ---                ---

If we ping 193.10.0.253 from the other side we will see that being translated to
192.168.1.11.

NAT*: s=192.168.1.11->193.10.0.253, d=184.10.0.4 [3]
NAT*: o: icmp (184.10.0.4, 0) -> (193.10.0.253, 0) [4]
NAT*: s=184.10.0.4, d=193.10.0.253->192.168.1.11 [4]

The translation table is below.

R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
icmp 193.10.0.253:0    192.168.1.11:0     184.10.0.4:0       184.10.0.4:0
icmp 193.10.0.254:10   192.168.1.11:10    184.10.0.4:10      184.10.0.4:10
--- 193.10.0.254       192.168.1.11       ---                ---
--- 193.10.0.253       192.168.1.11       ---                ---

When we are doing static NAT translations we can also match on an access-list.
The good thing about matching on an ACL is that we can specify which hosts we
want to have translated and which we want to leave alone. We can create an ACL
so that traffic to 184.10.0.4 gets translated but traffic to 4.4.4.4 will arrive
with its original source address. The syntax is:

ip nat inside source list LIST_NAME interface INTERFACE_NAME

We can only translate to an interface or a pool of addresses when using a list
as the source.

R2(config)#ip access-list extended NAT
R2(config-ext-nacl)#deny ip host 192.168.1.11 host 4.4.4.4
R2(config-ext-nacl)#permit ip any any
R2(config-ext-nacl)#ip nat inside source list NAT interface f0/1

We will debug on the destination to see which address the ICMP packet coming in has.

ICMP: echo reply sent, src 4.4.4.4, dst 192.168.1.11
ICMP: echo reply sent, src 4.4.4.4, dst 192.168.1.11
ICMP: echo reply sent, src 184.10.0.4, dst 193.10.0.2
ICMP: echo reply sent, src 184.10.0.4, dst 193.10.0.2

Once again we look at the translation table.

R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
icmp 193.10.0.2:12     192.168.1.11:12    184.10.0.4:12      184.10.0.4:12

So we can see that when sending traffic to 4.4.4.4 it does not get
translated but traffic to 184.10.0.4 does. We can confirm by looking
at the ACL counters.

R2#sh ip access-lists NAT
Extended IP access list NAT
    10 deny ip host 192.168.1.11 host 4.4.4.4 (2 matches)
    20 permit ip any any (1 match)

We can also configure NAT to NAT 1:1 for an entire network. This means that
the inside local address 192.168.1.11 will be translated to 193.10.0.11. If
we were sourcing traffic from .20 then that would be translated to .20 so the
addressing consistency is kept. This can be useful if we have like a web server
that is reachable from 192.168.1.30 on the inside and when we want to access
it from the outside we now that it will have the IP 193.10.0.30. We should
rely on DNS for reaching web servers but knowing the IP can be good in case
of a DNS failure. Use the following syntax.

ip nat inside source static network INSIDE_LOCAL_NETWORK INSIDE_GLOBAL_NETWORK PREFIX_LENGTH_OR_MASK

R2(config)#ip nat inside source static network 192.168.1.0 193.10.0.0 /24

Now when we ping we should see the source getting translated to 193.10.0.11.

NAT*: s=192.168.1.11->193.10.0.11, d=184.10.0.4 [22]
NAT*: s=184.10.0.4, d=193.10.0.11->192.168.1.11 [22]

And the translation table.

R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
icmp 193.10.0.11:14    192.168.1.11:14    184.10.0.4:14      184.10.0.4:14
--- 193.10.0.11        192.168.1.11       ---                ---
--- 193.10.0.0         192.168.1.0        ---                ---

We can also do NAT for a pool of addresses. Say that we have been granted
a new pool of addresses from our ISP. The pool is 193.12.0.0/29. We create
a NAT pool matching this and then we enable NAT for the 192.168.1.11 address.
The syntax is:

ip nat inside source list ACL_NAME pool POOL_NAME

R2(config)#ip nat pool NAT_POOL 193.12.0.1 193.12.0.6 prefix-length 29
R2(config)#ip nat inside source list NAT pool NAT_POOL

We do a ping to look at the translation.

NAT*: s=192.168.1.11->193.12.0.1, d=184.10.0.4 [25]
NAT*: s=184.10.0.4, d=193.12.0.1->192.168.1.11 [25]

We can see that the source address got translated. As you can see
we can do NAT for networks that are not configured on the router.
This is the translation table.

R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
icmp 193.12.0.1:16     192.168.1.11:16    184.10.0.4:16      184.10.0.4:16
--- 193.12.0.1         192.168.1.11       ---                ---

When doing NAT pools we can also make the host portion of the address match
if we want to. We do that like this.

ip nat pool prefix-length type match-host

ip nat pool NAT_POOL 194.10.0.1 194.10.0.30 prefix-length 27 type match-host
ip nat inside source list NAT pool NAT_POOL

Now when we ping the IP should get translated to 194.10.0.11.

NAT*: s=192.168.1.11->194.10.0.11, d=184.10.0.4 [27]
NAT*: s=184.10.0.4, d=194.10.0.11->192.168.1.11 [27]

Which it did. So even with pools we can match the host part of the address.
This is the translation table.

R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
icmp 194.10.0.11:17    192.168.1.11:17    184.10.0.4:17      184.10.0.4:17
--- 194.10.0.11        192.168.1.11       ---                ---

With NAT pools we can also do rotary assignments if we want or overload
the pool if we want to do Port Address Translation (PAT).

Now we will also create a scenario where using a route-map. Using route-maps
we can create more advanced scenarios. For this scenario telnet traffic
going to 184.10.0.4 will get one source IP and HTTP traffic will get
another source address.

R2(config)#ip access-list extended ISP1
R2(config-ext-nacl)#permi tcp any host 184.10.0.4 eq telnet
R2(config-ext-nacl)#ip access-list extended ISP2
R2(config-ext-nacl)#permit tcp any host 184.10.0.4 eq www
R2(config-ext-nacl)#ip nat pool POOL_ISP1 11.11.11.1 11.11.11.1 prefix-length 24
R2(config)#ip nat pool POOL_ISP2 22.22.22.2 22.22.22.2 prefix-length 24
R2(config)#route-map RM_ISP1
R2(config-route-map)#match ip add ISP1
R2(config-route-map)#route-map RM_ISP2
R2(config-route-map)#match ip add ISP2
R2(config-route-map)#ip nat inside source route-map RM_ISP1 pool POOL_ISP1
R2(config)#ip nat inside source route-map RM_ISP2 pool POOL_ISP2

Now to verify the configuration, first we send telnet to 184.10.0.4.

NAT: map match RM_ISP1
NAT*: i: tcp (192.168.1.11, 29183) -> (184.10.0.4, 23) [42518]
NAT*: i: tcp (192.168.1.11, 29183) -> (184.10.0.4, 23) [42518]
NAT*: s=192.168.1.11->11.11.11.1, d=184.10.0.4 [42518]

Now we send to port 80 instead.

NAT: map match RM_ISP2
NAT*: i: tcp (192.168.1.11, 15942) -> (184.10.0.4, 80) [30734]
NAT*: i: tcp (192.168.1.11, 15942) -> (184.10.0.4, 80) [30734]
NAT*: s=192.168.1.11->22.22.22.2, d=184.10.0.4 [30734]

And the translation table.

R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
tcp 22.22.22.2:27143   192.168.1.11:27143 184.10.0.4:80      184.10.0.4:80
tcp 11.11.11.1:64511   192.168.1.11:64511 184.10.0.4:23      184.10.0.4:23

So using route-maps we can do more advanced scenarios. If we have multiple
inside interfaces we could even match on those.

NAT can also be used to do a form of basic load balancing. Several
inside local addresses will be mapped to one inside global address.
A pool of inside local addresses will be created and handed out in a
rotary fashion.

R2(config)#access-list 1 permit 222.2.2.2
R2(config)#ip nat pool ROTARY_POOL 10.0.0.1 10.0.0.3 prefix-length 24 type rotary
R2(config)#ip nat inside destination list 1 pool ROTARY_POOL

IP addresses 10.0.0.1, 10.0.0.2 and 10.0.0.3 will now be handed out in a rotary
fashion when someone tries to access the IP 222.2.2.2. We can see this when
debugging the NAT translation.

NAT*: s=184.10.0.4, d=222.2.2.2->10.0.0.2 [42645]
NAT*: s=184.10.0.4, d=222.2.2.2->10.0.0.3 [57551]
NAT*: s=184.10.0.4, d=222.2.2.2->10.0.0.1 [26254]

So this performs a basic form of load balancing. The only thing different here is
that we are using the ip nat inside destination command. This translates the
destination of the packet. Usually we translate the source of the packet but
since static NAT is bidirectional in the other direction the destination of
the packet will be translated. When doing this form of NAT we need to trigger
it by sending TCP packets. Just sending ICMP will not trigger the NAT translation.

We have been through a lot of scenarios so far. Almost all of the scenarios
describe how to translate the source IP of the packet going out from the local
network. What if we want to translate the source of the address coming in on
the outside interface instead? This can be useful in scenarios
where there are overlapping subnets, e.g. the same subnet is used on two different
companies and they need to connect through a VPN tunnel or such. Syntax is the
following.

ip nat outside source static OUTSIDE_GLOBAL OUTSIDE_LOCAL

NAT: s=184.10.0.4->4.4.4.4, d=10.0.0.1 [5]

Here we see that the source of the packet is translated when coming in
on the outside. And this is the translation table.

R2#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
--- ---                ---                4.4.4.4            184.10.0.4
icmp 10.0.0.1:1        10.0.0.1:1         4.4.4.4:1          184.10.0.4:1

The final scenario I want to describe is NAT on a stick. It’s not a very
common scenario but the idea is this. Look at the topology below.

R3 has only one interface which leads to a problem because we need to define
one interface as inside and the other as outside. How can we solve this?
We will use what is called NAT on a stick. R3 will do policy routing and
send traffic to its loopback to trigger the NAT process. R1 and R2 need
to have default routes towards R3 which will be doing the NAT. When R1
pings with a source of its loopback (10.1.1.1) that should be translated
to 100.1.1.1. When R2 pings from its loopback (10.2.2.2) then it should
be translated to 200.2.2.2. We start by setting up the policy routing.
We create an access-list matching traffic from 10.1.1.1 to 200.2.2.2.
Then we create a route-map matching the ACL and set the interface to
loopback0. The loopback interface will be the NAT inside interface.

R3(config)#access-list 100 permit ip host 10.1.1.1 host 200.2.2.2
R3(config)#route-map PBR
R3(config-route-map)#match ip add 100
R3(config-route-map)#set interface lo0
R3(config-route-map)#exit
R3(config)#int f0/0
R3(config-if)#no ip redirects
R3(config-if)#ip policy route-map PBR
R3#sh ip policy
Interface      Route map
Fa0/0          PBR

We also disable ICMP redirects so that R1 does not bypass R3 when
sending traffic to R2. We need to add a few routes on R3 for the
scenario to work. The network 10.1.1.0 is routed to R1. Then
10.2.2.0 and 200.2.2.0 is routed to R2. Why do we need to route
the 200.2.2.0 network to R2? This is because the order of operations
in Cisco routers. On inside to outside policy routing is done first, then routing
and then NAT. On outside to inside NAT is done first, then policy routing
and after that routing. Before we add the rest of the configuration
lets think about the traffic flow.

R1 sends an ICMP packet with (S= 10.1.1.1 , D= 200.2.2.2). The packet comes
inbound on R3 on Fa0/0. The traffic coming in matches the policy and the packet
is looped through R3 lo0. Loopback0 has ip nat inside so this triggers the
NAT process. The source IP 10.1.1.1 is translated to 100.1.1.1 and the destination
is translated to 10.2.2.2, then the packet is sent out Fa0/0.
The packet reaches R2 with (S= 100.1.1.1, D= 10.2.2.2). R2 sends
an ICMP reply with (S= 10.2.2.2, D= 100.1.1.1). The packet comes in on R3
Fa0/0 which is the NAT outside interface. That triggers a translation of the
source from 10.2.2.2 to 200.2.2.2. The destination is also translated from
100.1.1.1 to 10.1.1.1. R3 then checks the routing table and
sends the packet back out Fa0/0 to R1. The packet reaches R1 with
(S= 200.2.2.2 , D=10.1.1.1). And that finishes the flow. Now to
configure it.

R3(config)#ip route 10.1.1.0 255.255.255.0 131.1.123.1
R3(config)#ip route 10.2.2.0 255.255.255.0 131.1.123.2
R3(config)#ip route 200.2.2.0 255.255.255.0 131.1.123.2
R3(config)#int lo0
R3(config-if)#ip nat inside
R3(config-if)#int fa0/0
R3(config-if)#ip nat outside

Take a look at the translation table.

R3#sh ip nat trans
Pro Inside global      Inside local       Outside local      Outside global
--- ---                ---                200.2.2.2          10.2.2.2
--- 100.1.1.1          10.1.1.1           ---                ---

Now to see if it works. We will debug NAT on R3 to see what is happening while
pinging from R1.

NAT: s=10.1.1.1->100.1.1.1, d=200.2.2.2 [21]
NAT: s=100.1.1.1, d=200.2.2.2->10.2.2.2 [21]
NAT*: s=10.2.2.2->200.2.2.2, d=100.1.1.1 [21]
NAT*: s=200.2.2.2, d=100.1.1.1->10.1.1.1 [21]

Finally here is a drawing that is describing the traffic flow.

This has been
a very big post and I wrote it to have as a reference for my studies. You
don’t have to read the whole post at once but I hope that you find some
useful scenarios that you can try out for yourself. One final piece of advice
is that if you run NAT in Dynamips you should assign that router 256MB of
memory or you will see some strange things happening like sh run not working.

A look at NAT – inside, outside and NAT on a stick
Tagged on:                                 

13 thoughts on “A look at NAT – inside, outside and NAT on a stick

  • October 3, 2012 at 4:58 pm
    Permalink

    Thank you! This was an excellent post.

    Reply
    • October 3, 2012 at 5:02 pm
      Permalink

      Glad you liked it ๐Ÿ™‚ Good luck with your studies.

      Reply
  • October 3, 2012 at 10:58 pm
    Permalink

    hi.

    if a local IP address is not being translated into a global address when sending traffic to 4.4.4.4, how does the 4.4.4.4 know where to send a icmp echo reply? 192.168.x.x addresses could be behind every single router in the inter-network.

    Reply
    • October 4, 2012 at 8:15 am
      Permalink

      For this scenario I just had some default routes in the network. You would of course need a route back to the source. For a real network you would not use private addresses of course in that case. You would have a public range and you would make sure that it doesn’t go through NAT but you might have a private network that you do want to NAT as well.

      Reply
  • October 4, 2012 at 4:28 am
    Permalink

    hey Daniel – just a quick comment re: NAT-on-a-stick. Wouldn’t the interface loopback0 need to be ip nat outside, while the ingress interface (int fa0/0) be ip nat inside?

    Otherwise, it is a great post/review!

    Reply
    • October 4, 2012 at 8:23 am
      Permalink

      Hi,

      I think it is possible to do this scenario that way as well. You would just have to change the NAT statements since you would hit the outside first and then go to inside from R1 perspective. This is because PBR is done before NAT on ingress so the packet would go to loopback0 and hit the outside NAT statement and then go out Fa0/0 which is the inside. So it would be something like this:

      ip nat inside source static 10.2.2.2 200.2.2.2
      ip nat outside source static 10.1.1.1 100.1.1.1

      I’ll have to try it out ๐Ÿ™‚

      Reply
  • January 6, 2013 at 12:45 pm
    Permalink

    straight out of Narbik CCIE Workbook Vol2 ……Be Original

    Reply
    • January 6, 2013 at 3:06 pm
      Permalink

      Not sure which part you are referring to as I haven’t done many Narbik labs.

      Reply
  • May 25, 2016 at 10:15 pm
    Permalink

    Not many great Info on Nat out there, your is best I have read about NAT. Thanks for sharing with us!

    Reply
  • April 16, 2017 at 7:35 am
    Permalink

    “Loopback0 has ip nat inside so this triggers the
    NAT process. The source IP 10.1.1.1 is translated to 100.1.1.1 and the destination
    is translated to 10.2.2.2, then the packet is sent out Fa0/0.”

    As you said,the order of operation is to first route, then translate, when packet first hits the NAT inside interface. So, to be more precise, when packet is looped through the lo0 (according to PBR route map set interface statementโ€‹), it will first be routed to fa0/0 according to static route to destination 200.2.2.2/24, and only then, when hitting the fa0/0, R1 will translate the source and destination addresses according to the 2 nat inside and outside source static rules.
    Please, correct me, if I’m wrong. Also, could you add the nat rules to the configuration?

    And thanks for great post!

    Reply
  • Pingback:YA-NAT: Yet Another Network Address Translation Post – neckercube.com

Leave a Reply

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