Read a status line saying Security Associations (1 up, 0 connecting) and it looks like a report of one working tunnel. Two subnets are passing traffic through it at the same time, so the number appears to be lying. It is not lying; it is counting IKE associations, and one of those can host several children. Read the inventories before you touch a proposal or a route.

Why the headline and the traffic disagree

The legacy stroke output labels its total as security associations and derives it from the IKE association manager. Every established IKE association contributes one, regardless of how many CHILD SAs sit underneath it. A single IKEv2 parent commonly carries several children, one per traffic-selector pair, and a child's negotiated selectors can cover more than one address or prefix. The number of addresses in your configuration therefore has no fixed relationship to the number in the headline.

Modern output sometimes shows a total instead of the old "up" wording, and a swanctl-managed daemon is not driven by legacy stroke settings at all. Assume neither the wording nor the counting rule across versions.

Compare three inventories before changing anything

Start with what is loaded, then what is negotiated, then what is actually moving packets. Those are three different questions and each has its own command.

swanctl --list-conns
swanctl --list-sas

The first lists connection definitions the daemon accepted. The second lists established IKE associations with their children, which is where the real hierarchy appears. A definition present in a file but absent from the first listing was never loaded, and no amount of initiator retries will fix that. If you run the legacy interface instead, the equivalent pair is ipsec statusall and the same swanctl listing depending on which plugin owns the configuration. Do not mix tooling across two daemons on one host and expect a single coherent answer.

swanctl --list-sas --raw | head -40
ip -s xfrm state

The raw form keeps identifiers and selectors visible for comparison, and the kernel state listing shows what the stack believes it installed. When the daemon and the kernel disagree, the daemon restarted around an existing child or the kernel object was removed underneath it.

Read the hierarchy, not the total

An illustrative established parent with two children looks like one node with two children beneath it, each labelled with its own state and mode. Dump the actual output and count the children yourself instead of reading a summary somebody pasted from a dashboard.

swanctl --list-sas
swanctl --list-conns --raw | grep -A4 'accounting'

One parent with two installed children is a healthy two-subnet tunnel. One parent with one installed child and one absent child is a different situation, and the missing child's selectors are the thing to compare against the remote side's proposal. During rekey, an old child and its replacement can both appear for a short window. Keep the timestamp with the snapshot, because a count taken mid-rekey looks like duplication and a count taken after looks like loss.

Loaded, installed, and passing traffic

A child in the installed state has negotiated parameters and a kernel entry. It has not necessarily carried a packet: selectors can be narrower than the traffic you expect, and a route can send that traffic elsewhere before the tunnel ever sees it. Send a flow that matches the negotiated selectors from an authorised host, then read the child's counters rather than the headline. A counter that stays at zero while you generate matching traffic points at routing or policy, not at IKE.

swanctl --initiate --child accounting
swanctl --list-sas --raw | grep -E 'bytes|packets'

Initiating a named child is a deliberate action with a visible result, and it separates "the connection is idle because nobody asked" from "the connection cannot come up". An on-demand trap policy behaves exactly like the first case, so read the connection's initiation setting before calling it a fault.

When the count really is wrong

If a definition is missing from the loaded inventory, look at configuration loading and at which daemon instance is running. If it is loaded but never negotiates, compare proposals, authentication context and selectors with the peer, one child at a time. Change one element per attempt and record the identifier you changed. Rolling back a proposal is trivial when you know which one; it is guesswork after three edits that were never separated.

Fix the misreading first. Most reports of a "missing" second tunnel end with two installed children under one parent, and the only thing that was ever broken was the summary line people trusted instead of the hierarchy.

Lahatsoratra mifandraika