NAT and PAT Troubleshooting on Cisco IOS XE: An Empty Translation Table

The NAT config looks correct and nothing is translating. One command splits the problem in two: no entry means the traffic never matched, an entry means the failure is downstream. Captured on IOS XE 17.18.2 in CML, where a single missing ip nat inside leaves the table empty at 0 hits.

Terminal card showing an empty Cisco NAT translation table with zero hits, then the same table populated with PAT entries sharing one public address

The ticket says the 192.168.1.0/24 subnet cannot reach the internet. You log into the border router and the configuration looks correct: there is an ip nat inside source list 1 interface Ethernet0/1 overload line, access list 1 permits the right subnet, there is a default route out. Nothing is obviously wrong. And nothing is being translated.

This article starts at that moment, not before it. It is not a configuration walkthrough. It assumes you already have NAT configured, that it is not working, and that you now have to work backwards from a broken translation to the reason. If you are still building the configuration, the prerequisite is separate: go and set up inside source NAT and PAT overload from scratch first, then come back here when it refuses to translate. For where address translation sits alongside DHCP, NTP and the rest, the IP services configuration and troubleshooting pillar is the map.

Everything below was captured on a live CML lab running iol-xe on IOS XE 17.18.2: R1 as the inside host, R2 doing PAT, R3 as the outside world holding 8.8.8.8, with the break baked in deliberately and repaired on the box while the captures ran.

One command splits the problem in two

Before you read a single line of the NAT configuration, run show ip nat translations while the failing traffic is actually flowing. That output puts you in one of exactly two worlds, and the two worlds have almost nothing in common.

No entry at all. The router is not translating this flow. Either the packet is not arriving, or it is arriving and NAT is declining to act on it. Nothing downstream of the router matters yet, because nothing downstream has been given a chance to fail.

An entry exists, and traffic still fails. NAT is doing its job. The translation was built, the packet left with a public source, and the failure is past the translation point: return routing, a filter on the far side, or an asymmetric path that bypasses the NAT router on the way back.

Most wasted hours come from skipping that split and rereading the NAT configuration regardless of which world you are in. Here is world one, captured on R2 with R1 pinging 8.8.8.8 from 192.168.1.1 every fifteen seconds:

R2# show ip nat translations
(empty)
R2# show ip nat statistics
Total active translations: 0 (0 static, 0 dynamic; 0 extended)
Hits: 0  Misses: 0                          <-- traffic is flowing and NAT has seen none of it

Note what makes that reading trustworthy: the pings were live at the time. An empty table on an idle router tells you nothing at all.

No entry: the traffic never matched

Three things must be true before IOS XE builds a translation. The packet arrives on an interface marked ip nat inside, it leaves via one marked ip nat outside, and it matches the ACL, pool or route-map named in the ip nat inside source statement. Miss any one and you get silence, not an error. Check them in that order, because that is the order of how often they are the culprit.

Check the interface roles first, and check them backwards

The single most common cause of a NAT that never fires is the inside and outside tags: one missing, or the two on the wrong interfaces. It happens on renumbering, on interface swaps, on a config restore, and above all when someone adds a second WAN link and tags the new interface without touching the old one.

Do not read this from show running-config top to bottom. Ask each interface directly:

show run interface Ethernet0/0 | include nat
show run interface Ethernet0/1 | include nat

You want ip nat inside on the interface facing your private hosts and ip nat outside on the interface facing the internet, and you want to say out loud which physical interface each of those actually is. Reversed tags are the cruel version, because the configuration looks complete: both keywords present, both on interfaces, and nothing translated, because IOS XE translates on the inside-to-outside path and you have told it the internet is your inside network.

In the lab the break was the simpler version of this: R2's Ethernet0/0, the interface facing R1, was never marked. One line repaired it.

R2(config)# interface Ethernet0/0
R2(config-if)#  ip nat inside

A related trap: if the inside interface lives in a VRF, a plain global NAT statement will not pick that traffic up at all, so everything above looks correct while nothing translates. That is a different rule set, covered in translating traffic that lives inside a VRF. Grep the interface for a vrf forwarding line before going further down this branch.

Then check what selects the traffic

If both roles are correct, the next suspect is the selector. Pull the ACL and compare it to the source address of the traffic that is actually failing, not to the subnet you believe your users are on:

show ip access-lists 1
show run | include ip nat inside source

Things that bite here: the ACL permits 192.168.1.0 0.0.0.255 but the host sits on a secondary subnet outside that range; the wildcard mask is wrong by one bit; an edit has put a deny above the permit; or the NAT statement references list 1 while somebody has been carefully maintaining list 101. With a route-map instead of an ACL, remember a route-map whose match clause points at a non-existent ACL matches nothing while looking perfectly reasonable in the config.

The fast confirmation is the ACL's own hit counters. Run show ip access-lists 1 twice, thirty seconds apart, with the failing traffic running. If the match count does not move, the traffic is not reaching that ACL and you are still in the interface-role branch.

Then confirm the packet is even arriving

If the roles are right, the ACL is matching and the table is still empty, stop assuming the packet reaches the router. Check the inside host's route to the destination and confirm the NAT router is genuinely the next hop, not something the host learned from a competing default. Being able to read the routing table and work out which next hop actually wins settles that in seconds, for both halves of the path.

Interface counters close it out. If input packets on the inside interface are not incrementing while the host is pinging, the problem is upstream of NAT and you have been troubleshooting the wrong device.

An entry exists and it still fails

Now the other world. show ip nat translations lists your flow, the inside global address is what you expect, and the user still reports failure. NAT has done everything asked of it, so rereading its configuration cannot help you. Three things to check, in order.

Return routing to the inside global address. The outside world has to be able to reach whatever you translated to. Translating to an interface address on a link the upstream device already routes for is usually fine. Translating to a pool address on a subnet nobody upstream knows about means the request leaves and the reply dies out there with no route home. On the lab topology R3 can reach 203.0.113.1 only because that address sits on the directly connected link.

A filter on the far side. An ACL on the outside interface, an upstream firewall, or a policy on the destination itself. Because the far side sees the translated source, this is also where you discover a permit list that was written against the private address and never updated for the public one. The tell is translation counters climbing while nothing comes back, which is exactly what a silently discarded return packet looks like.

Asymmetry. If the return path does not cross the same NAT router, that router's translation table is never consulted and the reply reaches the host with a public destination address it does not own. Redundant edge routers with independent NAT tables produce this reliably, and it presents as intermittent failure rather than total failure, which is what makes it unpleasant to chase.

Hits, misses, and the fixed state

show ip nat statistics is the second half of the diagnosis and it is routinely ignored. Hits count packets that matched an existing translation, misses count packets that needed a new one built. Zero for both under live traffic is the clean signature of a NAT configuration gap rather than a routing problem, because the router is not even attempting to translate.

Here is the same lab after the one-line repair, with the pings still running:

R2# show ip nat translations
Pro Inside global      Inside local       Outside local      Outside global
icmp 203.0.113.1:1024  192.168.1.1:9      8.8.8.8:9          8.8.8.8:1024
icmp 203.0.113.1:1025  192.168.1.1:10     8.8.8.8:10         8.8.8.8:1025
icmp 203.0.113.1:1026  192.168.1.1:11     8.8.8.8:11         8.8.8.8:1026
...
R2# show ip nat statistics
Total active translations: 5 (0 static, 5 dynamic; 5 extended)
Hits: 240  Misses: 0

Read the columns. Inside local is the real private address, inside global is what the outside world sees, and here one public address is shared across many port numbers. That is overload, which is what PAT means, and why the entries are marked extended. For a known-good build to compare a suspect configuration against, a working PAT overload lab you can rebuild in an evening gives you one.

Note the five active translations for one pinging host. Each ICMP echo gets its own entry keyed on the ICMP identifier, so a busy host inflates that count fast. Never read a large translation count as evidence of a large number of users.

What this was captured on

CML, three iol-xe nodes on IOS XE 17.18.2. R1 is the inside host with Loopback0 at 192.168.1.1/24 and Ethernet0/0 at 10.0.12.1/30, defaulting to R2. R2 is the PAT router: Ethernet0/0 at 10.0.12.2 facing inside, Ethernet0/1 at 203.0.113.1 facing outside, running ip nat inside source list 1 interface Ethernet0/1 overload with access list 1 permitting 192.168.1.0/24, and a static route to 8.8.8.8 via R3. R3 is the outside, with Loopback0 as 8.8.8.8/32.

The break was that R2's Ethernet0/0 was missing ip nat inside. Everything else was correct, which is what makes it a fair reproduction of the real ticket. Captures were taken on-box with EEM applets writing show output to syslog while a second applet generated the pings, so every command ran against genuinely live traffic.

Gotchas

ICMP translations age out fast, so ping and show together. This one produces phantom bugs. You ping, it fails, you run show ip nat translations a minute later, the table is empty and you conclude NAT is broken when the entry existed and simply timed out. Keep a repeating ping running in another window, or script the ping and the show as one action, and only then trust an empty table.

Zero hits and zero misses is a specific signal. Rising misses with no successful translations points at a pool or an ACL problem. Zero of both means the packet is never being offered to NAT at all, which sends you straight to the interface roles rather than into the selector.

Both roles or nothing. Marking only ip nat outside is the classic half-configuration, and it fails completely silently. There is no log message, no error at the CLI and no counter anywhere that says "I would have translated this if you had told me which side was inside".

Clearing translations is a diagnostic, not a fix. clear ip nat translation * tears down stale state and can genuinely resolve a stuck entry, but if the table repopulates and the same flow fails again, you have only learned that the entries were not stale.

Key takeaways

  • Run show ip nat translations while the failing traffic is live. No entry and an existing entry are two different investigations.
  • No entry sends you to the interface roles first. Missing or reversed ip nat inside and ip nat outside is the most common cause and it fails silently.
  • If the roles are right, check the ACL, pool or route-map that selects the traffic, and confirm its hit counters are moving.
  • An entry that exists means NAT worked. Look downstream: return routing to the inside global address, a filter on the far side, or an asymmetric return path.
  • show ip nat statistics at 0 hits and 0 misses under live traffic is the signature of a NAT configuration gap, not a routing problem.
  • ICMP entries expire quickly, so pair the ping with the show command or you will chase a table that emptied itself.

NAT sits at the boundary where a routing problem, a filtering problem and a translation problem all look identical from the user's desk, which is why a fixed order of checks beats intuition. The other edge services that fail this quietly follow the same pattern: read the state table first, then decide which half of the path you are actually in.

Read next