On an Active/Active Azure Virtual WAN VPN gateway doing Source‑NAT, a flow that SNATs on one instance but whose return lands on the other instance is silently dropped — the SNAT table is per‑instance, and without BGP nothing pins the return to the instance that did the SNAT. Symptom: intermittent packet loss ("sometimes the reply comes back to Instance 1 after leaving Instance 0").
| Root cause | Dynamic (PAT) SNAT state is per‑instance. With static routing the SNAT/external address is reachable over both tunnels, so the remote gateway ECMPs the return; replies landing on the non‑SNAT instance have no reverse mapping → dropped. The request reaches the app; only the reply is lost. |
| Why intermittent | Forward and return hash independently — only flows where forward‑instance ≠ return‑instance break (~50%). |
| Why BGP avoids it | BGP route‑translation advertises the external mapping from only the owning instance, pinning the return to the SNAT instance. So this only bites static / no‑BGP designs. |
| Best fix | Static (1:1) NAT — stateless and applied to both instances, so either can un‑NAT. Verified 60/60 under the same condition that dropped ~50% with Dynamic. |
vWAN secured hub (Azure Firewall Basic, routing‑intent) + Active/Active S2S VPN gateway ↔ isolated remote VNet with an Active/Standby gateway, internal LB and app VM. Two tunnels, one per hub instance. NAT = EgressSnat 10.0.0.0/8 → 10.110.14.8/32.
The app logs the client source IP, so it doubles as a per‑instance forward probe: 10.110.14.8 = the flow egressed Instance 0 (SNAT'd); 10.11.0.4 = Instance 1 (not NAT'd).
| Test | Configuration | Result |
|---|---|---|
| A | No NAT (baseline) | 60/60 OK |
| B | Dynamic NAT, return via both tunnels (ECMP) — the customer's setup | intermittent drops (~13/60) |
| C | Dynamic NAT, return forced to the wrong instance | ~50% dropped (every SNAT'd flow) |
| D | Dynamic NAT, return pinned to the SNAT instance | 60/60 OK |
| E | Static 1:1 NAT, return via both tunnels (ECMP) | 60/60 OK |
Dropped requests are logged by the app (so they arrive) — only the reply is lost. With BGP route‑translation the bug is structurally prevented (Azure advertises the external from only the owning instance and rejects the same external on both).
| Option | Idea | Trade‑off |
|---|---|---|
| ★ Static (1:1) NAT | Stateless, applied to both instances → either can un‑NAT; immune to the asymmetry. Keeps NAT on the A/A gateway. | 1:1 only (no many→one PAT); external pool = internal size. |
| Enable BGP route‑translation | Pins the return to the owning instance automatically. | Requires BGP on the S2S. |
| Distinct external mapping per instance | Each external routes to only its instance → the external IP pins the return (static equivalent of BGP). | More addresses/rules to manage. |
| NAT off the A/A gateway — on the Active/Standby side, an NVA, or Azure Firewall | Centralises state on one instance/device. | Depends which side overlaps; extra hop/cost. |
| Remove the IP overlap (re‑IP / dedicated range) | No overlap ⇒ no NAT ⇒ no per‑instance state. | Often impractical. |
| Private Link to the app | Bypasses the S2S NAT path entirely. | Only for Private‑Link‑capable services. |
How the return was pinned in the lab: each remote local‑network‑gateway maps 1:1 to a hub instance, and its localAddressPrefixes are the static return routes — so putting the SNAT prefix on one LNG vs both decides which instance (or ECMP) the return uses.
Partially. The building blocks are: vWAN VPN NAT (Static vs Dynamic, per‑instance ipConfigurationId for Dynamic vs Static applied to both, BGP route‑translation) and the VPN Gateway NAT overview. The specific "A/A + Dynamic NAT drops asymmetric returns" failure is not a named warning — it follows from the per‑instance design plus the rule that stateful NAT needs symmetric routing.
