Traffic leaves one site and never comes back, both gateways report a completed negotiation, and the only clue is a pile of outbound packets with no replies. On a Linux endpoint, the usual cause is not cipher strength, MTU or a routing table. It is the granularity of the policy entries and the number that links each policy to a security association. The runbook below reads those two databases before touching anything else.

Split the symptom before changing anything

A one-way failure with a completed IKE negotiation narrows the problem to the data path. Either the outbound policy matched and the reply was dropped, or the reply never arrived because the far side had nothing to send it through. Both look identical from an application, and the xfrm databases distinguish them.

Do not start with MTU. A wrong MTU produces stalls and fragmentation symptoms, not a clean direction split with rising outbound counters.

Read the policy database and the state database

Encryption on Linux is decided by policy entries, so a matching policy, not a route, is what puts a packet into the tunnel. Run ip xfrm policy to see the selectors, direction, priority and template for each entry, and ip xfrm state to see the established associations with their reqids. The policy and the association are joined by that reqid.

Look at all three directions. Outbound and inbound cover traffic to and from this host; the forward direction matters when the host routes for others. A missing or mismatched entry in one direction explains a one-way result without any cryptographic fault.

The classic asymmetry: one reqid for several subnets

A common failure comes from a peer that installs policies for all negotiated subnets under a single reqid, which happens with racoon's generate_policy require. Traffic then leaves through whichever association was established last, while the far side keeps a separate association per subnet. Replies arrive on an association whose reqid does not identify the policy that ought to accept them, and the kernel drops them after decryption.

The fix is a configuration change, not a firewall rule: generate_policy unique; gives each policy its own reqid, so the peer's per-policy associations have matching partners. Apply it, renegotiate, and count the distinct reqids for the subnets you expect before declaring success.

If two subnets still share a reqid while the far side expects two, you have found the asymmetry. Record the mapping, because it is the evidence that the next change either fixed or preserved it.

Selectors come from the negotiation, not your config

The protected ranges are whatever both ends agreed on, not what either side asked for. A client offering a host pair against a broad remote range receives the gateway's narrowed version, and the client cannot widen it. Read the installed selectors rather than the ones in your local file.

When the goal is the opposite, keeping one destination outside the tunnel, add a bypass policy with a lower priority number so it wins over the automatically installed entry. A policy without a template permits plaintext, which makes it a deliberate exception for a change record rather than a debugging step.

Verify in this order, and know how to back out

List policies for every subnet in both directions, including forward if the host routes. Count distinct reqids and confirm one per policy on the side that expects it. Compare installed selectors with the negotiated ones. Re-read per-association counters after a rekey, because the association you inspected may already be gone. Check routes last, since a missing route to the remote subnet produces a similar one-way symptom with clean policy counters.

Before changing the peer, save the current output of both xfrm commands. If the new setting does not restore the return direction, restoring that output is a faster rollback than rebuilding the tunnel from memory, and it keeps the comparison honest.

Winndannde jowitiiɗe heen