No CHILD SA appears in the status output, the tunnel looks dead, and then a ping to the far subnet brings it up. In an on-demand IPsec setup that sequence is the design, not a fault. A trap policy installs a selector and waits, and the first packet that matches it is what starts IKE and the CHILD SA negotiation.

Part of the confusion is vocabulary: "up" gets used for two different states. The configuration is loaded and the kernel policy exists, while the data association does not. A monitor that only counts active SAs will report a healthy site as down for as long as nobody sends traffic to it.

An idle on-demand tunnel has no CHILD SA to show

A trap entry authenticates nothing by itself. It asks the kernel to hold a policy for the selected traffic and to raise an acquisition when a packet needs that policy. The daemon receives the acquisition, matches it to the child definition, and negotiates from there. Until that point there is no IKE SA to display either, which is why both lists can be empty at once.

That order is what makes the logs readable. The acquisition comes first, then IKE_SA_INIT and IKE_AUTH, then the CHILD SA. If the log ends right after the acquisition, the trigger worked. The negotiation failed, and the next question is about credentials, proposals or reachability rather than the trap.

With the Linux kernel backend you can see the policy before any SA exists. Run ip xfrm policy and look for the selector with direction out and a template pointing at the connection. That entry is the on-demand state, and its presence is the first thing to confirm.

start_action decides whether anything waits at all

Each child in swanctl.conf carries a start_action. start initiates when the configuration loads, trap installs the policy and waits, and none leaves initiation to something else. Only trap produces the ping-and-it-comes-up behaviour described above.

children {
    office-lan {
        local_ts = 192.168.50.0/24
        remote_ts = 192.168.70.0/24
        start_action = trap
    }
}

The two traffic selectors define what counts as interesting, and a packet from a host outside those prefixes does not fire the trap; it routes as usual. Widening the selectors to make the trigger more eager also widens the scope the tunnel claims to protect, so derive them from traffic you actually intend to encrypt.

The swanctl.conf reference lists the accepted values, together with the separate close and DPD actions that govern what happens once the SA exists.

Find out whether the trigger fired at all

Start with swanctl --list-conns and confirm the child is loaded with the action you intended. Then send one packet from a host inside local_ts to an address inside remote_ts and watch swanctl --list-sas. If the output does not change, no acquisition reached the daemon.

Two ordinary reasons for a silent trap: the packet was source-NATed before it hit the policy and no longer matched local_ts, or the connection never loaded because of a syntax or credential error. Re-run swanctl --load-all and read its output before touching selectors. A connection that failed to load installs no policy, so there is nothing for traffic to trigger.

Direction matters too: a trap installed for outbound traffic does not initiate on an inbound packet. If the remote side is supposed to bring the tunnel up, it needs its own trap, or this side needs start.

Waiting for the first packet is not always the right trade

Interactive sessions notice the setup cost. The first request pays for the IKE exchange and the CHILD negotiation, and a client with a short deadline can give up before the SA exists. Proactive start removes that first-packet delay. It keeps a tunnel up for traffic that may never arrive.

Three facts settle the choice: how often the path is used, whether the first request has a tight deadline, and whether the peer is reachable at boot. Occasional batch traffic and a peer that may be offline at boot both favour trap. A latency-sensitive client and a peer that is always reachable favour start. If both sides can initiate, agree on which one owns the trigger so you do not get duplicate negotiations.

An installed policy is not a leak-proof guarantee either. If the requirement is fail-closed, the firewall must drop traffic that has no SA, and that rule lives outside the child definition.

Record the three lines that end the next argument

Write down the selectors, the startup action, and the command output that shows the policy present with no SA. Those three lines separate "on demand and idle" from "not loading" the next time someone reports the tunnel as down. They cost a minute to capture. Without them the same investigation restarts from zero.

Test from a host that is genuinely inside the selector. A ping from the router itself often follows a different path and proves nothing about the policy protecting the subnet behind it.

Allakkat attuumassuteqartut