Previously, it could happen that child_rekey() was triggered twice for
the same "old" SA. For listeners that would mean they'd loose track as
they'd be tracking a new SA that wasn't relevant anymore and for which
no updown event would ever get triggered (it was the redundant SA in a
collision). This new assert ensures that events are triggered in a
predictable way and listeners can track SAs properly.
As the winner of a rekey collision, we previously always triggered the
child_rekey() event once when creating the redundant SA on behalf of the
peer in the passive child-rekey task and then a second time when
creating the winning SA in the active task. However, both calls passed
the replaced CHILD_SA as "old". This made tracking CHILD_SAs impossible
because there was no transition from the redundant, "new" SA of the
first event to the "new", winning SA of the second. Of course, when the
second event was triggered, the redundant SA might not have existed
anymore because the peer is expected to delete it, which could happen
before the CREATE_CHILD_SA response arrives at the initiator.
This refactoring ensures that the child_rekey() event is triggered in
a way that makes the CHILD_SAs trackable in all reasonable (and even
some unreasonable) scenarios. The event is generally only triggered
once after installing the outbound SA for the new/winning CHILD_SA.
This can be when processing the CREATE_CHILD_SA in the active child-rekey
task, or when processing the DELETE for the old SA in a passive
child-delete task. There are some cases where the event is still
triggered twice, but it is now ensured that listeners can properly
transition to the winning SA.
Some corner cases are now also handled correctly, e.g. if a responder's
DELETE for the new CHILD_SA arrives before its CREATE_CHILD_SA response
that actually creates it on the initiator. Also handled properly are
responders of rekeyings that incorrectly send a DELETE for the old
CHILD_SA (previously this caused both, the new and the old SA, to get
deleted).
The empty array of rules for `assert_message_empty()` and the resulting
size 0 triggers warnings like these:
allocation of insufficient size '0' for type 'listener_message_rule_t' with size '12'
Using calloc() with `nmemb` set to 0 triggers the same warning.
Instead of just adding the offset internally, this way the reported
base address is always the first assignable address (e.g. for
192.168.0.0/24 vs. 192.168.0.1/24).
Closesstrongswan/strongswan#2264
If a base address is configured, we don't expect the pool to be empty,
so reject the creation (e.g. with the broadcast address as base).
References strongswan/strongswan#2205
When using the statement expression and a stack object along with
clang-11 and libasan, we get quite a lot of errors about reading
invalid memory. This is due to clang making the actual listener_t local
to the block, such that the access outside of the macros using
_assert_payload is (correctly) considered an error.
By using a heap allocated object, we can destroy it once the listener
returns FALSE (cleaning up properly), and since bus_t does not touch the
listener after that, we don't get any errors from libasan.
Co-authored-by: Tobias Brunner <tobias@strongswan.org>
This could happen if an acquire is triggered while we respond to a
CREATE_CHILD_SA request from the peer, or if an acquire is triggered
while an IKE_SA (with its existing CHILD_SAs) is reestablished (also
with break-before-make reauthentication). Also catches multiple
manual initiations.
Note that this ignores the traffic selectors from acquires (narrowing to
them seems rare in practice anyway).
Duplicates can still get created if e.g. both peers initiate them
concurrently.
This is useful for kernel implementations where the ordering of SAs
is unpredictable and the new SA might otherwise not be used until the
DELETE response has been received, which is not ideal as the responder
might not keep the old SA around that long. On Linux, it makes no
difference as we switch to the new outbound SA immediately because the
updated outbound policy references its SPI.
We don't actually check that SA out (i.e. it's not registered with the
manager). That was originally different but had to be changed with
86993d6b90 to avoid that SAs created for rekeying don't block other
threads on the manager.
The peer might not have seen the CREATE_CHILD_SA response yet, receiving a
DELETE for the SA could then trigger it to abort the rekeying, causing
the deletion of the newly established SA (it can't know whether the
DELETE was sent due to an expire or because the user manually deleted
it). We just treat this SA as if we received a DELETE for it. This is
not an ideal situation anyway, as it causes some traffic to get dropped,
so it should usually be avoided by setting appropriate soft and hard limits.
References #2815.
This is currently not an issue for CHILD_SA rekeying tests as these only
check rekeyings of the CHILD_SA created with the IKE_SA, i.e. there is
no previous DH group to reuse.
This allows us to use it without having to initialize libcharon, which
was required for the logging (we probably could have included debug.h
instead of daemon.h to workaround that but this seems more correct).
This splits the SA installation also on the initiator, so we can avoid
installing the outbound SA if we lost a rekey collision, which might
have caused traffic loss depending on the timing of the DELETEs that are
sent in both directions.
After deleting a rekeyed CHILD_SA we uninstall the outbound SA but don't
destroy the CHILD_SA (and the inbound SA) immediately. We delay it
a few seconds or until the SA expires to allow delayed packets to get
processed. The CHILD_SA remains in state CHILD_DELETING until it finally
gets destroyed.