Symptoms
- Two NVAs (firewalls) sit behind a Standard internal load balancer (ILB) in the hub. Most traffic works, but some connections hang after the SYN, reset after a few seconds, or work only on retry.
- The firewall logs show the SYN on NVA-1 and the SYN-ACK arriving on NVA-2, dropped as "no session" or "out of state".
- Problems started after adding a second NVA, a new spoke, or an on-premises route.
Stateful firewalls must see both directions of a flow. If the request goes through NVA-1 and the reply through NVA-2, NVA-2 drops it. That is asymmetric routing.
How the path is decided
Every hop that can choose differently is a place where asymmetry starts:
| Hop | Decided by |
|---|---|
| Spoke VM -> hub | UDR on the spoke subnet (next hop = ILB frontend IP) |
| ILB -> which NVA | Load balancer hash of the flow (5-tuple) |
| NVA -> destination | NVA's own routes and the hub subnet's UDRs |
| Gateway -> hub (return from on-prem) | UDR on GatewaySubnet |
| Peered spoke -> spoke | UDRs on both spokes |
If one direction is sent to the ILB and the other goes directly to an NVA IP, or bypasses the NVAs entirely, the flow breaks.
Step 1: map the real path both ways
Use Network Watcher next hop from each side:
# Spoke VM to on-prem
az network watcher show-next-hop -g rg-spoke1 --vm vm-app01 \
--source-ip 10.1.1.4 --dest-ip 192.168.10.20
# What a hub NVA does with the reply direction
az network watcher show-next-hop -g rg-hub --vm nva-01 \
--source-ip 10.0.1.4 --dest-ip 10.1.1.4
Check effective routes on each relevant NIC:
az network nic show-effective-route-table -g rg-spoke1 -n vm-app01-nic -o table
az network nic show-effective-route-table -g rg-hub -n nva-01-trust-nic -o table
Look for:
- A spoke UDR pointing to
10.0.1.4(one NVA's IP) instead of the ILB frontend. - A missing or wrong UDR on
GatewaySubnet, so traffic from on-prem goes straight to the spoke. - More specific system routes (peering, private endpoints) beating your UDR.
Step 2: confirm on the NVAs
Capture on both appliances at the same time, filtered to one client:
# Linux-based NVA example
tcpdump -ni eth1 host 10.1.1.4 and host 192.168.10.20
SYN on one box, SYN-ACK on the other = proven asymmetry.
Step 3: fix the routing
Route every direction to the ILB frontend, never to an NVA IP
az network route-table route create -g rg-spoke1 --route-table-name rt-spoke1 \
-n to-onprem --address-prefix 192.168.0.0/16 \
--next-hop-type VirtualAppliance --next-hop-ip-address 10.0.1.100 # ILB frontend
GatewaySubnet UDR for traffic coming from on-premises
az network route-table create -g rg-hub -n rt-gateway
az network route-table route create -g rg-hub --route-table-name rt-gateway \
-n to-spoke1 --address-prefix 10.1.0.0/16 \
--next-hop-type VirtualAppliance --next-hop-ip-address 10.0.1.100
az network vnet subnet update -g rg-hub --vnet-name vnet-hub -n GatewaySubnet --route-table rt-gateway
Warning
Don't disable gateway route propagation on the route table attached to
GatewaySubnet, and don't add a0.0.0.0/0route there. Both can break the gateway. Add specific spoke prefixes only.
Use HA ports on the ILB
An HA ports rule (protocol All, port 0) balances every protocol and port, so all flows are handled the same way:
az network lb rule create -g rg-hub --lb-name ilb-nva -n ha-ports \
--protocol All --frontend-port 0 --backend-port 0 \
--frontend-ip-name fe-trust --backend-pool-name be-nva --probe-name probe-nva \
--floating-ip false
For a flow that crosses the ILB in both directions through the same frontend, the load balancer keeps it on the same backend. Problems start when the two directions use different frontends or load balancers (for example a separate "untrust" ILB), because each makes its own choice.
Make the NVA forward
az network nic update -g rg-hub -n nva-01-trust-nic --ip-forwarding true
Also enable forwarding inside the OS or appliance.
Step 4: when symmetry can't be guaranteed, SNAT
If traffic enters through one load balancer and returns through another (common with separate public and internal ILBs, or east-west between spokes), configure the NVAs to SNAT traffic to their own interface IP. The reply then comes back to the NVA that owns that IP, whatever the routing.
| Pattern | Symmetric without SNAT? |
|---|---|
| Spoke ↔ on-prem through one ILB frontend, UDRs both sides | Yes |
| Spoke ↔ spoke through the same ILB frontend | Yes, if both spoke UDRs point to it |
| Internet inbound via public LB, return via internal LB | No: SNAT on the NVA |
| Traffic to private endpoints | Often not: SNAT recommended |
SNAT hides the original client IP from the server; make sure logging on the NVA keeps it.
Step 5: health probes
If the probe sees an NVA as up while its data path is broken (or the reverse), flows land on a dead box. Probe a port that reflects the forwarding plane, not just the management plane, and check that the probe source (168.63.129.16) is allowed by the NVA.
az network lb show -g rg-hub -n ilb-nva --query "probes[].{name:name,port:port,proto:protocol,interval:intervalInSeconds}"
Prevent it next time
- One route table design document: every subnet, every prefix, next hop always the ILB frontend.
- Use Azure Policy or pipeline checks to block UDRs that point at individual NVA IPs.
- Test new spokes with a flow both ways and a packet capture before handing them over.
- Prefer SNAT for any path where two load balancers are involved.