Back to blog

Broken by Design  ·  Layer 3 · How Networks Fail · October 2026

Broken by Design
The Return Path That Vanished

Pings work. DNS works. But half your TCP connections hang at “connecting…” and never finish. The cause is almost invisible: traffic leaves by one path and tries to come back by another, and a stateful firewall on the return path drops it because it never saw the connection start. This is asymmetric routing meeting stateful inspection — and it is maddening until you can see it.

Broken by Design Asymmetric Routing Stateful Firewall Dual-WAN Conntrack PBR

A stateful firewall remembers every connection it has seen: when a client sends a TCP SYN outbound, the firewall writes a conntrack entry so it can recognise the matching SYN-ACK coming back and let it in. That is the whole value of stateful inspection — you only have to allow the outbound, and the return is permitted automatically because the firewall saw it start.

Asymmetric routing breaks that assumption. If the packet leaves by one path and the reply comes back by another — a second WAN, a second firewall, an ECMP hash landing differently each way — then the box on the return path never saw the SYN, has no state for the flow, and drops the SYN-ACK as an unsolicited packet. The connection half-opens and hangs. Nothing is “down”; the path simply has a one-way valve nobody designed on purpose.

Part of

01

The Half-Open Connection

The SYN goes out one way; the SYN-ACK comes back the other, into a firewall that has no record of the flow, and dies there.

Client R1 (WAN-A) Server FW (WAN-B) × SYN out → via R1 SYN-ACK back → via FW: no state, dropped The firewall on the return path never saw the SYN, so the reply looks unsolicited — and the connection hangs.

The giveaway is that something works. Stateless traffic (much of UDP, ICMP) sails through because there is no state to miss, so ping and often DNS are fine. TCP is where it bites: the three-way handshake cannot complete because the SYN-ACK is dropped, so the client sits retransmitting its SYN until it gives up.

02

Why It Happens

Each ingredient is normal. Put two exits and a stateful box together without pinning the path, and you have built a one-way valve.

CauseHow the path splits
Dual-WAN without flow pinningOut via WAN-A (default route), reply arrives on WAN-B because the server was reached via B is address — or the return is routed by a rule that differs from the forward decision.
Two firewalls / HA pair without state syncForward through one node, return through the other; the second has no conntrack entry for the flow.
ECMP / multipathPer-packet or re-hashed load balancing sends the two directions down different next-hops through different stateful boxes.
Policy routing that only marks one directionA PBR rule steers outbound but the return is left to the normal table, so it comes back elsewhere.

03

The Symptoms

“Ping works but nothing loads” plus a maddening inconsistency — some destinations fine, others dead — points straight here.

What you seeWhat it tells you
Ping and DNS fine, TCP connections hangStateless passes, stateful return is dropped — the signature.
Some sites work, others never doWhich path a flow takes depends on the destination and the hash — symmetric ones work, asymmetric ones do not.
Conntrack shows SYN_SENT stuckThe firewall recorded the outbound SYN but no reply ever matched it.
Started after a second WAN / HA pair went inThe change that introduced a second path is the cause.

04

The Fix: Make Flows Symmetric

Pick one of two strategies — pin the return to the forward path, or make the stateful boxes share state — and apply it deliberately.

The usual fix on a dual-WAN edge is connection marking: mark a connection when its first packet arrives on a WAN, then force every packet of that connection — including the return — back out the same WAN, regardless of the routing table. This is the single most useful pattern for keeping an edge symmetric.

Pin each connection to the WAN it arrived on (RouterOS)
# remember which WAN a NEW connection used, and mark its packets
/ip firewall mangle
# mark the connection by the WAN its first packet arrived on
add chain=prerouting in-interface=ether1-wanA connection-state=new \
    action=mark-connection new-connection-mark=from-A passthrough=yes
# replies from a server behind us: force them back out that same WAN (transit)
add chain=prerouting connection-mark=from-A in-interface-list=LAN \
    action=mark-routing new-routing-mark=to-A passthrough=no
# replies from the router's own services go via output, not prerouting
add chain=output connection-mark=from-A action=mark-routing new-routing-mark=to-A
# route to-A marked traffic back out WAN-A; same pair for WAN-B
/ip route add dst-address=0.0.0.0/0 gateway=wanA-gw routing-mark=to-A

On Cisco the equivalent is policy-based routing that matches the connection and sets the egress, or keeping the firewall pair state-synchronised (a failover/HA cluster that shares its connection table so either node recognises the flow). For an HA firewall, enabling state sync is the correct fix; for ECMP, make the hash per-flow (not per-packet) and ensure both directions hash the same, or remove the second stateful box from one direction.

⚠ Gotcha — the fix that hides it: loosening the firewall

It is tempting to “fix” asymmetry by telling the firewall to accept invalid/out-of-state packets, or to not track the flow at all. That makes the symptom disappear and throws away the entire point of a stateful firewall — now unsolicited SYN-ACKs from anywhere are accepted. Fix the path, not the firewall. If you must relax state, scope it to the exact flow, never globally.

05

Catch It in 30 Seconds Field check

Prove the direction: the SYN leaves, the SYN-ACK is seen on the wire but never on the firewall that should pass it.

Confirm asymmetry fast
# RouterOS: a connection stuck in SYN_SENT with no reply bytes
/ip firewall connection print where tcp-state=syn-sent
# capture on BOTH WANs at once: SYN out on A, SYN-ACK in on B = asymmetric
/tool sniffer quick interface=ether1-wanA,ether2-wanB protocol=tcp

# Linux/host side: SYN retransmits with no handshake completion
sudo tcpdump -ni any 'tcp[tcpflags] & (tcp-syn) != 0 and host SERVER'

If the SYN goes out one interface and the SYN-ACK arrives on another — or arrives at a firewall that logs it as invalid/no-state — you have asymmetry. Pin the flow and it completes immediately.

Takeaways

  1. Stateful firewalls permit the return because they saw the start. Send the return a different way and it looks unsolicited — dropped.
  2. The signature is “ping works, TCP hangs” plus some destinations fine and others dead — stateless passes, asymmetric stateful flows do not.
  3. Dual-WAN, HA firewall pairs and ECMP are the usual causes — a second path introduced without pinning direction.
  4. Fix the path, not the firewall: connection-mark flows back out the WAN they arrived on, or state-sync the firewall pair.
  5. Never “fix” it by accepting invalid packets globally — that discards the firewall is purpose.
  6. Prove it by capturing both paths at once: SYN out here, SYN-ACK in there = asymmetric, case closed.

TCP that half-connects and hangs for no clear reason?

Asymmetric paths through a stateful firewall are one of the hardest faults to spot from the symptom. I trace the flow both directions, find where the return vanishes, and pin the paths so connections complete.

Book a Discovery Call →