diff --git a/libreswan-5.3-outstanding-ike-auth-crossing.patch b/libreswan-5.3-outstanding-ike-auth-crossing.patch index 6d37dad..754161a 100644 --- a/libreswan-5.3-outstanding-ike-auth-crossing.patch +++ b/libreswan-5.3-outstanding-ike-auth-crossing.patch @@ -1,7 +1,7 @@ From a671b9ce5ff86ffc31142436e9c93031083d7221 Mon Sep 17 00:00:00 2001 From: Ondrej Moris Date: Fri, 21 Nov 2025 22:42:33 +0100 -Subject: [PATCH 1/2] Add option reject-simultaneous-ike-auth +Subject: [PATCH 1/5] Add option reject-simultaneous-ike-auth Newly added option (default values is yes) decides what to do when IKE_AUTH request is received from the peer while having @@ -193,7 +193,7 @@ index a5a95045bf..5e47e009b8 100644 From 6d1d06ef085cbdfdfeb24f677cec9d4540db2903 Mon Sep 17 00:00:00 2001 From: Ondrej Moris Date: Fri, 21 Nov 2025 22:42:33 +0100 -Subject: [PATCH 2/2] ikev2: reject simultaneous IKE_AUTH requests +Subject: [PATCH 2/5] ikev2: reject simultaneous IKE_AUTH requests If initiator has another IKE SA with IKE_AUTH request outstanding for the same permanent connection then send @@ -352,3 +352,546 @@ index ba5aeace88..08f7c79739 100644 -- 2.52.0 + +From 437d0ff3a4b32b22b2272347881f49a333e9ffbf Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Sat, 17 Jan 2026 13:28:39 +0900 +Subject: [PATCH 3/5] pluto: reject crossing IKE_AUTH request only for the one + side + +When an IKE_AUTH request is crossed, both sides previously rejected it +by sending AUTHENTICATION_FAILED, resulting in no IKE_SA being +established. This patch relaxes the condition and make the one outlive +the other. + +Suggested-by: Ondrej Moris +Signed-off-by: Daiki Ueno +--- + include/ike_spi.h | 1 + + programs/pluto/ike_spi.c | 11 +++++++++++ + programs/pluto/ikev2_auth.c | 10 +++++----- + programs/pluto/ikev2_auth.h | 2 +- + programs/pluto/ikev2_child.c | 35 +++++++++++++++++++++++++++++---- + programs/pluto/ikev2_ike_auth.c | 35 +++++++++++++++++++++++++++++---- + 6 files changed, 80 insertions(+), 14 deletions(-) + +diff --git a/include/ike_spi.h b/include/ike_spi.h +index 0c05542bae..376c120c0e 100644 +--- a/include/ike_spi.h ++++ b/include/ike_spi.h +@@ -39,6 +39,7 @@ typedef struct { + } ike_spis_t; + + bool ike_spis_eq(const ike_spis_t *lhs, const ike_spis_t *rhs); ++bool ike_spis_gt(const ike_spis_t *lhs, const ike_spis_t *rhs); + + /* + * Need to handle two cases: +diff --git a/programs/pluto/ike_spi.c b/programs/pluto/ike_spi.c +index 312c835bed..d7bb73a6e8 100644 +--- a/programs/pluto/ike_spi.c ++++ b/programs/pluto/ike_spi.c +@@ -42,6 +42,17 @@ bool ike_spis_eq(const ike_spis_t *lhs, const ike_spis_t *rhs) + ike_spi_eq(&lhs->responder, &rhs->responder)); + } + ++bool ike_spis_gt(const ike_spis_t *lhs, const ike_spis_t *rhs) ++{ ++ int d = memcmp(lhs->initiator.bytes, rhs->initiator.bytes, ++ sizeof(lhs->initiator.bytes)); ++ if (d == 0) { ++ d = memcmp(lhs->responder.bytes, rhs->responder.bytes, ++ sizeof(lhs->responder.bytes)); ++ } ++ return d > 0; ++} ++ + static struct { + uint8_t bytes[SHA2_256_DIGEST_SIZE]; + } ike_spi_secret; +diff --git a/programs/pluto/ikev2_auth.c b/programs/pluto/ikev2_auth.c +index 2e1b6df655..ee36ef8b03 100644 +--- a/programs/pluto/ikev2_auth.c ++++ b/programs/pluto/ikev2_auth.c +@@ -1050,18 +1050,18 @@ lset_t proposed_v2AUTH(struct ike_sa *ike, + * IKE_AUTH request outstanding. This is useful to detect potential + * IKE_AUTH crossing streams scenarios. + */ +-bool has_outstanding_ike_auth_request(const struct connection *c, ++struct ike_sa *get_sa_with_outstanding_ike_auth_request(const struct connection *c, + const struct ike_sa *ike, + const struct msg_digest *md) + { + /* Check can be disabled in a connection config */ + if (!c->config->reject_simultaneous_ike_auth) { +- return false; ++ return NULL; + } + + /* Connection must be permanent and request must be incoming */ + if (v2_msg_role(md) != MESSAGE_REQUEST || !is_permanent(c)) { +- return false; ++ return NULL; + } + + struct state_filter sf = { +@@ -1092,8 +1092,8 @@ bool has_outstanding_ike_auth_request(const struct connection *c, + if (outstanding_request != NULL && outstanding_request->type == ISAKMP_v2_IKE_AUTH) { + llog(RC_LOG, ike->sa.logger, "IKE SA "PRI_SO" has outstanding IKE_AUTH request", + pri_so(simultaneous_ike->sa.st_serialno)); +- return true; ++ return simultaneous_ike; + } + } +- return false; ++ return NULL; + } +diff --git a/programs/pluto/ikev2_auth.h b/programs/pluto/ikev2_auth.h +index d616534507..bb659ce88f 100644 +--- a/programs/pluto/ikev2_auth.h ++++ b/programs/pluto/ikev2_auth.h +@@ -85,7 +85,7 @@ struct crypt_mac v2_remote_id_hash(const struct ike_sa *ike, const char *why, + + lset_t proposed_v2AUTH(struct ike_sa *ike, struct msg_digest *md); + +-bool has_outstanding_ike_auth_request(const struct connection *c, ++struct ike_sa *get_sa_with_outstanding_ike_auth_request(const struct connection *c, + const struct ike_sa *ike, + const struct msg_digest *md); + #endif +diff --git a/programs/pluto/ikev2_child.c b/programs/pluto/ikev2_child.c +index 5b2c0d7028..63df00956f 100644 +--- a/programs/pluto/ikev2_child.c ++++ b/programs/pluto/ikev2_child.c +@@ -69,6 +69,7 @@ + #include "ikev2_auth.h" + #include "iface.h" + #include "nat_traversal.h" ++#include "terminate.h" + + static bool emit_v2_child_response_payloads(struct ike_sa *ike, + const struct child_sa *child, +@@ -1036,10 +1037,36 @@ static v2_notification_t process_v2_IKE_AUTH_request_child_sa_payloads(struct ik + * This is to prevent potential crossing streams scenario for + * IKE AUTH exchange. + */ +- if (has_outstanding_ike_auth_request(child->sa.st_connection, ike, md)) { +- record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, +- empty_shunk, ENCRYPTED_PAYLOAD); +- return v2N_AUTHENTICATION_FAILED; ++ struct ike_sa *simultaneous_ike = ++ get_sa_with_outstanding_ike_auth_request(child->sa.st_connection, ike, md); ++ if (simultaneous_ike != NULL) { ++ /* Compare our initiated SPI vs their initiated SPI ++ * from the message. Note that the ordering doesn't ++ * matter, but it ensures that both sides have the ++ * same consensus on which IKE SA should be dropped. ++ */ ++ if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis, ++ &ike->sa.st_ike_spis)) { ++ /* Our IKE SA with oustanding IKE AUTH request ++ * has SPI higher, delete this IKE SA, keep ++ * the simultaneous_ike. ++ */ ++ ldbg(ike->sa.logger, "preferring the outstanding "PRI_SO" over the current "PRI_SO, ++ pri_so(simultaneous_ike->sa.st_serialno), ++ pri_so(ike->sa.st_serialno)); ++ record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, ++ empty_shunk, ENCRYPTED_PAYLOAD); ++ return v2N_AUTHENTICATION_FAILED; ++ } else { ++ /* IKE SA for the current IKE AUTH request has ++ * SPI higher, continue with IKE AUTH reply ++ * and drop the other one. ++ */ ++ ldbg(ike->sa.logger, "preferring the current "PRI_SO" over the outstanding "PRI_SO"", ++ pri_so(ike->sa.st_serialno), ++ pri_so(simultaneous_ike->sa.st_serialno)); ++ terminate_ike_family(&simultaneous_ike, REASON_SUPERSEDED_BY_NEW_SA, HERE); ++ } + } + + n = process_childs_v2SA_payload("IKE_AUTH responder matching remote ESP/AH proposals", +diff --git a/programs/pluto/ikev2_ike_auth.c b/programs/pluto/ikev2_ike_auth.c +index 08f7c79739..acd268e89a 100644 +--- a/programs/pluto/ikev2_ike_auth.c ++++ b/programs/pluto/ikev2_ike_auth.c +@@ -76,6 +76,7 @@ + #include "ikev2_notification.h" + #include "peer_id.h" + #include "ddos.h" ++#include "terminate.h" + + static ikev2_state_transition_fn process_v2_IKE_AUTH_request; + +@@ -682,10 +683,36 @@ stf_status process_v2_IKE_AUTH_request_standard_payloads(struct ike_sa *ike, str + * IKE_AUTH request and terminate current IKE SA. This is to + * prevent potential crossing streams scenario. + */ +- if (has_outstanding_ike_auth_request(c, ike, md)) { +- record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, +- empty_shunk, ENCRYPTED_PAYLOAD); +- return STF_FATAL; ++ struct ike_sa *simultaneous_ike = ++ get_sa_with_outstanding_ike_auth_request(c, ike, md); ++ if (simultaneous_ike != NULL) { ++ /* Compare our initiated SPIs vs their initiated SPIs ++ * from the message. Note that the ordering doesn't ++ * matter, but it ensures that both sides have the ++ * same consensus on which IKE SA should be dropped. ++ */ ++ if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis, ++ &ike->sa.st_ike_spis)) { ++ /* Our IKE SA with oustanding IKE AUTH request ++ * has SPI higher, delete this IKE SA, keep ++ * the simultaneous_ike. ++ */ ++ ldbg(ike->sa.logger, "preferring the outstanding "PRI_SO" over the current "PRI_SO, ++ pri_so(simultaneous_ike->sa.st_serialno), ++ pri_so(ike->sa.st_serialno)); ++ record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, ++ empty_shunk, ENCRYPTED_PAYLOAD); ++ return STF_FATAL; ++ } else { ++ /* IKE SA for the current IKE AUTH request has ++ * SPI higher, continue with IKE AUTH reply ++ * and drop the other one. ++ */ ++ ldbg(ike->sa.logger, "preferring the current "PRI_SO" over the outstanding "PRI_SO"", ++ pri_so(ike->sa.st_serialno), ++ pri_so(simultaneous_ike->sa.st_serialno)); ++ terminate_ike_family(&simultaneous_ike, REASON_SUPERSEDED_BY_NEW_SA, HERE); ++ } + } + + /* +-- +2.52.0 + + +From e812e32c2d58d0a96ebfb38cd5a03ea4bf99161e Mon Sep 17 00:00:00 2001 +From: Ondrej Moris +Date: Sun, 25 Jan 2026 19:41:25 +0100 +Subject: [PATCH 4/5] ikev2: refactor simultaneous IKE_AUTH logic + +1. Moved decision which IKE to reject into the function. +2. Renamed function to better capture its new structure. + +Signed-off-by: Ondrej Moris +--- + programs/pluto/ikev2_auth.c | 80 +++++++++++++++++++++++---------- + programs/pluto/ikev2_auth.h | 6 +-- + programs/pluto/ikev2_child.c | 44 +++++------------- + programs/pluto/ikev2_ike_auth.c | 43 +++++------------- + 4 files changed, 80 insertions(+), 93 deletions(-) + +diff --git a/programs/pluto/ikev2_auth.c b/programs/pluto/ikev2_auth.c +index ee36ef8b03..f0c08fb27f 100644 +--- a/programs/pluto/ikev2_auth.c ++++ b/programs/pluto/ikev2_auth.c +@@ -1046,13 +1046,23 @@ lset_t proposed_v2AUTH(struct ike_sa *ike, + } + + /* +- * Check if a given permanent connection has another IKE SA with +- * IKE_AUTH request outstanding. This is useful to detect potential +- * IKE_AUTH crossing streams scenarios. ++ * Check relevant simultaneous IKE_AUTH requests and decide ++ * which IKE SA is going to be rejected (if any). This is goint ++ * to be called when processing IKE_AUTH request from the responder ++ * ++ * Relevant: ++ * - same connection ++ * - initiating IKE ++ * - non-established with IKE_AUTH request waiting for reply ++ * ++ * Returns: ++ * - ike: reject current IKE_AUTH request ++ * - other IKE SA than ike: terminate it, keep ike ++ * - NULL: no simultaneous IKE SA + */ +-struct ike_sa *get_sa_with_outstanding_ike_auth_request(const struct connection *c, +- const struct ike_sa *ike, +- const struct msg_digest *md) ++struct ike_sa *check_simultaneous_ike_auth(const struct connection *c, ++ const struct ike_sa *ike, ++ const struct msg_digest *md) + { + /* Check can be disabled in a connection config */ + if (!c->config->reject_simultaneous_ike_auth) { +@@ -1065,35 +1075,57 @@ struct ike_sa *get_sa_with_outstanding_ike_auth_request(const struct connection + } + + struct state_filter sf = { +- .connection_serialno = c->serialno, +- .search = { +- .order = NEW2OLD, +- .verbose.logger = ike->sa.logger, +- .where = HERE, +- }, ++ .connection_serialno = c->serialno, ++ .search = { ++ .order = NEW2OLD, ++ .verbose.logger = ike->sa.logger, ++ .where = HERE, ++ }, + }; + ++ struct ike_sa *simultaneous_ike = NULL; ++ + while (next_state(&sf)) { + if (!IS_IKE_SA(sf.st)) { + continue; + } + +- struct ike_sa *simultaneous_ike = pexpect_ike_sa(sf.st); +- if (simultaneous_ike == NULL || simultaneous_ike == ike) { +- continue; +- } else if (simultaneous_ike->sa.st_sa_role != SA_INITIATOR) { ++ struct ike_sa *candidate = pexpect_ike_sa(sf.st); ++ ++ if (candidate == NULL || candidate == ike) { + continue; +- } else if (!v2_msgid_request_outstanding(simultaneous_ike)) { ++ } else if (candidate->sa.st_sa_role != SA_INITIATOR) { + continue; + } + +- const struct v2_exchange *outstanding_request = +- simultaneous_ike->sa.st_v2_msgid_windows.initiator.exchange; +- if (outstanding_request != NULL && outstanding_request->type == ISAKMP_v2_IKE_AUTH) { +- llog(RC_LOG, ike->sa.logger, "IKE SA "PRI_SO" has outstanding IKE_AUTH request", +- pri_so(simultaneous_ike->sa.st_serialno)); +- return simultaneous_ike; ++ /* Does candidate has IKE_AUTH request outstanding? */ ++ if (v2_msgid_request_outstanding(candidate)) { ++ const struct v2_exchange *outstanding_request = candidate->sa.st_v2_msgid_windows.initiator.exchange; ++ if (outstanding_request != NULL && outstanding_request->type == ISAKMP_v2_IKE_AUTH) { ++ llog(RC_LOG, ike->sa.logger, "IKE SA "PRI_SO" has outstanding IKE_AUTH request", ++ pri_so(candidate->sa.st_serialno)); ++ simultaneous_ike = candidate; ++ break; ++ } + } + } +- return NULL; ++ ++ /* No simultaneous IKE found */ ++ if (simultaneous_ike == NULL) { ++ return NULL; ++ } ++ ++ /* Simultaneous IKE found, we need to decide if we keep that one or current IKE. ++ * ++ * We keep one with higher SPI. ++ */ ++ if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis, &ike->sa.st_ike_spis)) { ++ ldbg(ike->sa.logger, "preferring the simultaneous IKE SA "PRI_SO" over the current IKE SA "PRI_SO, ++ pri_so(simultaneous_ike->sa.st_serialno), pri_so(ike->sa.st_serialno)); ++ return (struct ike_sa *) ike; ++ } else { ++ ldbg(ike->sa.logger, "preferring the current IKE SA "PRI_SO" over the simultanoues IKE SA "PRI_SO"", ++ pri_so(ike->sa.st_serialno), pri_so(simultaneous_ike->sa.st_serialno)); ++ return simultaneous_ike; ++ } + } +diff --git a/programs/pluto/ikev2_auth.h b/programs/pluto/ikev2_auth.h +index bb659ce88f..1c25479ed7 100644 +--- a/programs/pluto/ikev2_auth.h ++++ b/programs/pluto/ikev2_auth.h +@@ -85,7 +85,7 @@ struct crypt_mac v2_remote_id_hash(const struct ike_sa *ike, const char *why, + + lset_t proposed_v2AUTH(struct ike_sa *ike, struct msg_digest *md); + +-struct ike_sa *get_sa_with_outstanding_ike_auth_request(const struct connection *c, +- const struct ike_sa *ike, +- const struct msg_digest *md); ++struct ike_sa *check_simultaneous_ike_auth(const struct connection *c, ++ const struct ike_sa *ike, ++ const struct msg_digest *md); + #endif +diff --git a/programs/pluto/ikev2_child.c b/programs/pluto/ikev2_child.c +index 63df00956f..1205622b27 100644 +--- a/programs/pluto/ikev2_child.c ++++ b/programs/pluto/ikev2_child.c +@@ -1032,41 +1032,19 @@ static v2_notification_t process_v2_IKE_AUTH_request_child_sa_payloads(struct ik + } + + /* It is possible that Child SA switched to permanent connection +- * where initiator has IKE SA with IKE_AUTH request outstanding, +- * in that case send AUTHENTICATION_FAILED and terminate this IKE SA. +- * This is to prevent potential crossing streams scenario for ++ * where initiator has IKE SA with IKE_AUTH request outstanding ++ * (or recently established IKE SA) in that case keep only one of ++ * them. This is to prevent potential crossing streams scenario for + * IKE AUTH exchange. + */ +- struct ike_sa *simultaneous_ike = +- get_sa_with_outstanding_ike_auth_request(child->sa.st_connection, ike, md); +- if (simultaneous_ike != NULL) { +- /* Compare our initiated SPI vs their initiated SPI +- * from the message. Note that the ordering doesn't +- * matter, but it ensures that both sides have the +- * same consensus on which IKE SA should be dropped. +- */ +- if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis, +- &ike->sa.st_ike_spis)) { +- /* Our IKE SA with oustanding IKE AUTH request +- * has SPI higher, delete this IKE SA, keep +- * the simultaneous_ike. +- */ +- ldbg(ike->sa.logger, "preferring the outstanding "PRI_SO" over the current "PRI_SO, +- pri_so(simultaneous_ike->sa.st_serialno), +- pri_so(ike->sa.st_serialno)); +- record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, +- empty_shunk, ENCRYPTED_PAYLOAD); +- return v2N_AUTHENTICATION_FAILED; +- } else { +- /* IKE SA for the current IKE AUTH request has +- * SPI higher, continue with IKE AUTH reply +- * and drop the other one. +- */ +- ldbg(ike->sa.logger, "preferring the current "PRI_SO" over the outstanding "PRI_SO"", +- pri_so(ike->sa.st_serialno), +- pri_so(simultaneous_ike->sa.st_serialno)); +- terminate_ike_family(&simultaneous_ike, REASON_SUPERSEDED_BY_NEW_SA, HERE); +- } ++ struct ike_sa *ike_to_reject = check_simultaneous_ike_auth(child->sa.st_connection, ike, md); ++ if (ike_to_reject == ike) { ++ /* Reject current IKE_AUTH request */ ++ record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, empty_shunk, ENCRYPTED_PAYLOAD); ++ return v2N_AUTHENTICATION_FAILED; ++ } else if (ike_to_reject != NULL) { ++ /* Terminate the other IKE SA, continue with current */ ++ terminate_ike_family(&ike_to_reject, REASON_SUPERSEDED_BY_NEW_SA, HERE); + } + + n = process_childs_v2SA_payload("IKE_AUTH responder matching remote ESP/AH proposals", +diff --git a/programs/pluto/ikev2_ike_auth.c b/programs/pluto/ikev2_ike_auth.c +index acd268e89a..fc35ecc200 100644 +--- a/programs/pluto/ikev2_ike_auth.c ++++ b/programs/pluto/ikev2_ike_auth.c +@@ -678,41 +678,18 @@ stf_status process_v2_IKE_AUTH_request_standard_payloads(struct ike_sa *ike, str + const struct connection *c = ike->sa.st_connection; + + /* If initiator has another IKE SA with IKE_AUTH request +- * outstanding for the same permanent connection then send +- * AUTHENTICATION_FAILED instead of IKE_AUTH response for this +- * IKE_AUTH request and terminate current IKE SA. This is to ++ * outstanding for the same permanent connection (or recently ++ * established one) then we keep only one of them. This is to + * prevent potential crossing streams scenario. + */ +- struct ike_sa *simultaneous_ike = +- get_sa_with_outstanding_ike_auth_request(c, ike, md); +- if (simultaneous_ike != NULL) { +- /* Compare our initiated SPIs vs their initiated SPIs +- * from the message. Note that the ordering doesn't +- * matter, but it ensures that both sides have the +- * same consensus on which IKE SA should be dropped. +- */ +- if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis, +- &ike->sa.st_ike_spis)) { +- /* Our IKE SA with oustanding IKE AUTH request +- * has SPI higher, delete this IKE SA, keep +- * the simultaneous_ike. +- */ +- ldbg(ike->sa.logger, "preferring the outstanding "PRI_SO" over the current "PRI_SO, +- pri_so(simultaneous_ike->sa.st_serialno), +- pri_so(ike->sa.st_serialno)); +- record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, +- empty_shunk, ENCRYPTED_PAYLOAD); +- return STF_FATAL; +- } else { +- /* IKE SA for the current IKE AUTH request has +- * SPI higher, continue with IKE AUTH reply +- * and drop the other one. +- */ +- ldbg(ike->sa.logger, "preferring the current "PRI_SO" over the outstanding "PRI_SO"", +- pri_so(ike->sa.st_serialno), +- pri_so(simultaneous_ike->sa.st_serialno)); +- terminate_ike_family(&simultaneous_ike, REASON_SUPERSEDED_BY_NEW_SA, HERE); +- } ++ struct ike_sa *ike_to_reject = check_simultaneous_ike_auth(c, ike, md); ++ if (ike_to_reject == ike) { ++ /* Reject current IKE_AUTH request */ ++ record_v2N_response(ike->sa.logger, ike, md, v2N_AUTHENTICATION_FAILED, empty_shunk, ENCRYPTED_PAYLOAD); ++ return STF_FATAL; ++ } else if (ike_to_reject != NULL) { ++ /* Terminate the other IKE SA, continue with current */ ++ terminate_ike_family(&ike_to_reject, REASON_SUPERSEDED_BY_NEW_SA, HERE); + } + + /* +-- +2.52.0 + + +From 47fc2d3bb650d4ecfd066ab04ebc454bcefb91d1 Mon Sep 17 00:00:00 2001 +From: Ondrej Moris +Date: Sun, 25 Jan 2026 19:46:45 +0100 +Subject: [PATCH 5/5] ikev2: extend check for simultaneous IKE_AUTH + +On initiator, if simultaneous IKE receives IKE_AUTH response +shortly before the current IKE_AUTH request is received, +then this simultaneous IKE gets established and hence it +incorrectly missed by the current check. This commit extends +the check for simultaneous IKE to capture such cases and if +there is such simultaneous IKE it will be keps and the current +one will be dropped. + +Signed-off-by: Ondrej Moris +--- + programs/pluto/ikev2_auth.c | 21 +++++++++++++++++++-- + 1 file changed, 19 insertions(+), 2 deletions(-) + +diff --git a/programs/pluto/ikev2_auth.c b/programs/pluto/ikev2_auth.c +index f0c08fb27f..c1fed3bcdd 100644 +--- a/programs/pluto/ikev2_auth.c ++++ b/programs/pluto/ikev2_auth.c +@@ -1054,6 +1054,7 @@ lset_t proposed_v2AUTH(struct ike_sa *ike, + * - same connection + * - initiating IKE + * - non-established with IKE_AUTH request waiting for reply ++ * - established within 1 second of processing this IKE_AUTH request + * + * Returns: + * - ike: reject current IKE_AUTH request +@@ -1108,6 +1109,17 @@ struct ike_sa *check_simultaneous_ike_auth(const struct connection *c, + break; + } + } ++ ++ /* Was candidate established within the last second of the current IKE_AUTH request arrival? */ ++ if (candidate->sa.st_state->kind == STATE_V2_ESTABLISHED_IKE_SA) { ++ deltatime_t age = monotime_diff(mononow(), candidate->sa.st_v2_msgid_windows.initiator.last_recv); ++ if (deltatime_cmp(age, <, deltatime(1))) { ++ llog(RC_LOG, ike->sa.logger, "IKE SA "PRI_SO" recently established", ++ pri_so(candidate->sa.st_serialno)); ++ simultaneous_ike = candidate; ++ break; ++ } ++ } + } + + /* No simultaneous IKE found */ +@@ -1117,9 +1129,14 @@ struct ike_sa *check_simultaneous_ike_auth(const struct connection *c, + + /* Simultaneous IKE found, we need to decide if we keep that one or current IKE. + * +- * We keep one with higher SPI. ++ * If simultaneous IKE is already established, we keep it. Otherwise we keep one ++ * with higher SPI. + */ +- if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis, &ike->sa.st_ike_spis)) { ++ if (simultaneous_ike->sa.st_state->kind == STATE_V2_ESTABLISHED_IKE_SA) { ++ llog(RC_LOG, ike->sa.logger, "rejecting IKE_AUTH request; IKE SA "PRI_SO" already established", ++ pri_so(simultaneous_ike->sa.st_serialno)); ++ return (struct ike_sa *) ike; ++ } else if (ike_spis_gt(&simultaneous_ike->sa.st_ike_spis, &ike->sa.st_ike_spis)) { + ldbg(ike->sa.logger, "preferring the simultaneous IKE SA "PRI_SO" over the current IKE SA "PRI_SO, + pri_so(simultaneous_ike->sa.st_serialno), pri_so(ike->sa.st_serialno)); + return (struct ike_sa *) ike; +-- +2.52.0 +