ASA

Cisco ASA Twice NAT Explained with Real Examples

Cisco ASA Twice NAT Explained with Real Examples
In: ASA

Twice NAT is the rule type you reach for when one source-only or destination-only translation is not enough. The classic case: when source 10.10.10.0/24 talks to destination 172.20.0.0/24 (a partner network), the source should be PATted to a specific public address that the partner has whitelisted - but only when the destination is the partner network. For all other destinations, ordinary outbound PAT applies.

That conditional ("when source is X and destination is Y") is what manual NAT, also known as twice NAT, exists for. This article walks the configuration and the verification on Cisco ASA software 9.x with real captures from the PingLabz reference lab. For the cluster context, see the Cisco ASA pillar; for the broader NAT picture, see Cisco ASA NAT Explained; for the order in which manual and auto NAT rules get evaluated, see NAT Order of Operations.

What Twice NAT Actually Means

"Twice NAT" is a Cisco term for a single NAT rule that can specify both source and destination match conditions, and optionally translate either or both. Compare to object NAT (auto NAT), which can only match on source.

The "twice" naming is a little misleading. Twice NAT does not necessarily translate twice. It just means the rule has two halves: a source half and a destination half. Each half can be:

  • Static: pre-existing fixed binding (one IP to one IP, or one subnet to another).
  • Dynamic: many-to-one or many-to-few (PAT pool, dynamic IP pool).
  • Identity: source/destination match without translation. Used heavily for VPN exemption (see Identity NAT for VPNs).

In configuration, the keyword is nat (real-int,mapped-int) source <type> ... destination <type> ... at global config, not nested inside an object. That distinguishes it from auto NAT, which is always nested.

Manual NAT lands in Section 1 of the NAT table by default, which means it gets evaluated before any auto NAT rule. That ordering is the entire reason engineers reach for it: when a more specific manual rule should win over a broader auto NAT, write the manual rule and ordering is solved.

The Lab Topology and the Goal

All output below is from a live ASAv running Cisco ASA 9.23(1). The relevant pieces:

  • Inside subnet INSIDE-NET: 10.10.10.0/24, behind ASA interface inside.
  • Outside interface: 203.0.113.2/30, default route to ISP at 203.0.113.1.
  • Public IP space: 198.51.100.0/24, routed to this ASA.
  • Default outbound rule: dynamic PAT to the outside interface IP (Section 2 auto NAT).

The new requirement: when 10.10.10.0/24 talks to a partner network at 172.20.0.0/24 (reachable via the upstream provider), the partner has whitelisted only one of our public IPs - 198.51.100.99. So we need to source-NAT to that specific address, but only on flows destined for the partner. All other outbound flows should keep using the standard PAT-to-interface rule.

The Twice NAT Rule

Two extra objects (the partner subnet and the whitelisted source IP) and one twice NAT rule does it:

object network INSIDE-NET
 subnet 10.10.10.0 255.255.255.0

object network PARTNER-NET
 subnet 172.20.0.0 255.255.255.0

object network INSIDE-PARTNER-PAT
 host 198.51.100.99

nat (inside,outside) source dynamic INSIDE-NET INSIDE-PARTNER-PAT destination static PARTNER-NET PARTNER-NET

Read the rule left to right:

  • (inside,outside) - real interface, mapped interface.
  • source dynamic INSIDE-NET INSIDE-PARTNER-PAT - match source against object INSIDE-NET (10.10.10.0/24), translate using object INSIDE-PARTNER-PAT (the host 198.51.100.99). Dynamic with a single host means PAT, source ports rewritten as needed.
  • destination static PARTNER-NET PARTNER-NET - match destination against object PARTNER-NET (172.20.0.0/24), do not translate destination (the same object on both sides means identity, no rewrite). The "static" keyword tells the ASA the destination match is exact, not dynamic.

That last detail is the most counter-intuitive piece for newcomers: destination static PARTNER-NET PARTNER-NET looks like it is doing destination NAT, but with the same object on both sides it just means "the destination must be in PARTNER-NET; do not change it". This is the conditional that makes the rule fire only for partner-bound flows.

Verifying the Rule Loaded

show running-config nat shows the manual rule at the top, before the object NAT rules:

ASA-PERIM# show running-config nat
nat (inside,outside) source static INSIDE-NET-FULL INSIDE-NET-FULL destination static REMOTE-VPN-NET REMOTE-VPN-NET no-proxy-arp route-lookup
nat (inside,outside) source dynamic INSIDE-NET INSIDE-PARTNER-PAT destination static PARTNER-NET PARTNER-NET
!
object network INSIDE-NET
 nat (inside,outside) dynamic interface
object network INSIDE-TRANSIT
 nat (inside,outside) dynamic interface
object network DMZ-WEB
 nat (dmz,outside) static 198.51.100.10

Two manual NAT rules at the top (an identity NAT for VPN and our partner twice NAT), three object NAT rules below (the DMZ static and the two outbound PATs).

show nat detail tells you which Section each rule lands in, and the per-rule hit counters:

ASA-PERIM# show nat detail

Manual NAT Policies (Section 1)
1 (inside) to (outside) source static INSIDE-NET-FULL INSIDE-NET-FULL  destination static REMOTE-VPN-NET REMOTE-VPN-NET no-proxy-arp route-lookup
    translate_hits = 1, untranslate_hits = 1
    Source - Origin: 10.10.0.0/16, Translated: 10.10.0.0/16
    Destination - Origin: 10.99.99.0/24, Translated: 10.99.99.0/24
2 (inside) to (outside) source dynamic INSIDE-NET INSIDE-PARTNER-PAT  destination static PARTNER-NET PARTNER-NET
    translate_hits = 1, untranslate_hits = 1
    Source - Origin: 10.10.10.0/24, Translated: 198.51.100.99/32
    Destination - Origin: 172.20.0.0/24, Translated: 172.20.0.0/24

Auto NAT Policies (Section 2)
1 (dmz) to (outside) source static DMZ-WEB 198.51.100.10
    translate_hits = 0, untranslate_hits = 7
2 (inside) to (outside) source dynamic INSIDE-TRANSIT interface
    translate_hits = 0, untranslate_hits = 0
3 (inside) to (outside) source dynamic INSIDE-NET interface
    translate_hits = 46, untranslate_hits = 0

Section 1 has the two manual rules. Section 2 has the auto NAT rules. Within each section, the order shown is the order in which they will be evaluated. Across sections, Section 1 is always evaluated first.

Proving It Hits the Right Rule

The most important verification on a manual NAT is "did the right rule match my traffic?", because manual NAT changes the order of operations. packet-tracer is the cleanest way to prove it.

Send a hypothetical packet from inside (10.10.10.50:33000) to the partner (172.20.0.50:80):

ASA-PERIM# packet-tracer input inside tcp 10.10.10.50 33000 172.20.0.50 80

Phase: 1
Type: UN-NAT
Subtype: static
Result: ALLOW
Config:
nat (inside,outside) source dynamic INSIDE-NET INSIDE-PARTNER-PAT destination static PARTNER-NET PARTNER-NET
Additional Information:
NAT divert to egress interface outside
Untranslate 172.20.0.50/80 to 172.20.0.50/80

Phase: 2
Type: NAT
Subtype:
Result: ALLOW
Config:
nat (inside,outside) source dynamic INSIDE-NET INSIDE-PARTNER-PAT destination static PARTNER-NET PARTNER-NET
Additional Information:
Dynamic translate 10.10.10.50/33000 to 198.51.100.99/33000

Phase: 6
Type: NAT
Subtype: rpf-check
Result: ALLOW
Config:
nat (inside,outside) source dynamic INSIDE-NET INSIDE-PARTNER-PAT destination static PARTNER-NET PARTNER-NET

Phase: 10
Type: FLOW-CREATION
Subtype:
Result: ALLOW
New flow created with id 53, packet dispatched to next module

Result:
input-interface: inside
input-status: up
input-line-status: up
output-interface: outside
output-status: up
output-line-status: up
Action: allow

The trace tells the entire story:

  • Phase 1 (UN-NAT static) matches the destination half of the rule. The destination 172.20.0.50 matches PARTNER-NET, untranslated to itself (no destination rewrite). The packet is diverted to egress interface outside.
  • Phase 2 (NAT) matches the source half. Dynamic translate 10.10.10.50/33000 to 198.51.100.99/33000 - source rewritten to the whitelisted IP. The same rule is referenced.
  • Phase 6 (rpf-check) validates the reverse path matches the same rule, so the return packet from the partner can be untranslated correctly.
  • Phase 10 (FLOW-CREATION) creates the conn entry with the source and destination as they appear after translation.

Now compare a packet from the same source to a non-partner destination - the standard PAT-to-interface rule should win instead:

ASA-PERIM# packet-tracer input inside tcp 10.10.10.50 49000 8.8.8.8 80

Phase: 2
Type: NAT
Subtype:
Result: ALLOW
Config:
object network INSIDE-NET
 nat (inside,outside) dynamic interface
Additional Information:
Dynamic translate 10.10.10.50/49000 to 203.0.113.2/49000

Same source IP, different destination, different rule. Phase 2 here matches the auto NAT rule (Section 2) instead, because the partner-network destination match in the manual rule fails. Source gets translated to the outside interface IP (203.0.113.2), exactly as it should.

That side-by-side is the working proof that twice NAT does what we wanted: conditional source translation, falling through to the auto NAT default for everything else.

When Twice NAT Is the Right Tool

Reach for twice NAT (manual NAT in Section 1) when one of these is true:

  • Different source translation depending on destination. The partner-whitelist case above. Different B2B partners want to see different source IPs, all from the same set of inside hosts.
  • Different destination translation depending on source. Some inside groups should see DMZ-WEB at one IP, others at another. (Less common, but real.)
  • NAT exemption for VPN traffic. Source and destination both match VPN-relevant subnets, no translation. This is identity NAT, the topic of Identity NAT for VPNs.
  • Forcing rule order. A specific match needs to win over a broader auto NAT rule. Manual NAT in Section 1 is evaluated before any Section 2 auto NAT.
  • Bidirectional / same-IP scenarios. When the same address appears on both sides of the wire (overlapping address spaces between organizations), only twice NAT can express the bidirectional rewrite.

If your requirement is just "translate this one source subnet to the outside interface", reach for object NAT (Section 2) instead. It is shorter, lives next to the source object, and reads more naturally.

Failure Modes You Will Actually See

  • Wrong section. The author wrote the rule as auto NAT (nested in an object) thinking it would beat another auto NAT, but it lands in Section 2 alongside the other rule. Fix: rewrite as manual NAT to land in Section 1.
  • Source object covers more than intended. The rule fires for traffic the author did not expect, breaking unrelated flows. Fix: show nat detail with hit counters tells you exactly what the rule is matching; tighten the source object.
  • Forgotten destination static identity. The author wrote destination static PARTNER-NET PARTNER-PUBLIC-NET instead of PARTNER-NET PARTNER-NET, accidentally rewriting the destination. Symptom: partner sees traffic at an unexpected destination IP. Fix: same object on both sides if no destination rewrite is wanted.
  • Out-of-order rules within Section 1. Multiple manual NAT rules and the wrong one wins. Fix: use the optional rule line number (nat (inside,outside) 1 source ...) to pin order.
  • RPF check failing. packet-tracer phase 6 fails with "rpf-check". Usually a routing or symmetry problem, not a NAT problem. Confirm the route to the destination exits via the same interface the rule uses.

Where to Go Next

Key Takeaways

  • Twice NAT (manual NAT) is the only ASA NAT type that can match on both source and destination. It is the right tool when translation depends on what destination is being reached.
  • Manual NAT lands in Section 1 and is always evaluated before Section 2 auto NAT. Use that ordering to your advantage.
  • Identity translation (same object on both sides of source or destination) is how you express "match but do not rewrite". It is the building block for both partner-conditional PAT and full VPN NAT exemption.
  • packet-tracer is the diagnostic of choice for confirming which rule fires for a given source/destination pair. Run two flows side by side: one that should hit the manual rule, one that should fall through to auto NAT.
  • If you do not need conditional behavior, use object NAT instead. Manual NAT is more flexible but also more error-prone.
Written by
More from Ping Labz
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Ping Labz.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.