summaryrefslogtreecommitdiff
path: root/src/libcharon/sa/ike_sa_manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa/ike_sa_manager.c')
-rw-r--r--src/libcharon/sa/ike_sa_manager.c76
1 files changed, 50 insertions, 26 deletions
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 2ac8c3123..4fbc4da8e 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -108,9 +108,9 @@ struct entry_t {
identification_t *other_id;
/**
- * message ID currently processing, if any
+ * message ID or hash of currently processing message, -1 if none
*/
- u_int32_t message_id;
+ u_int32_t processing;
};
/**
@@ -135,23 +135,12 @@ static status_t entry_destroy(entry_t *this)
*/
static entry_t *entry_create()
{
- entry_t *this = malloc_thing(entry_t);
-
- this->waiting_threads = 0;
- this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT);
-
- /* we set checkout flag when we really give it out */
- this->checked_out = FALSE;
- this->driveout_new_threads = FALSE;
- this->driveout_waiting_threads = FALSE;
- this->message_id = -1;
- this->init_hash = chunk_empty;
- this->other = NULL;
- this->half_open = FALSE;
- this->my_id = NULL;
- this->other_id = NULL;
- this->ike_sa_id = NULL;
- this->ike_sa = NULL;
+ entry_t *this;
+
+ INIT(this,
+ .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .processing = -1,
+ );
return this;
}
@@ -1171,6 +1160,20 @@ METHOD(ike_sa_manager_t, checkout_new, ike_sa_t*,
return ike_sa;
}
+/**
+ * Get the message ID or message hash to detect early retransmissions
+ */
+static u_int32_t get_message_id_or_hash(message_t *message)
+{
+ /* Use the message ID, or the message hash in IKEv1 Main/Aggressive mode */
+ if (message->get_major_version(message) == IKEV1_MAJOR_VERSION &&
+ message->get_message_id(message) == 0)
+ {
+ return chunk_hash(message->get_packet_data(message));
+ }
+ return message->get_message_id(message);
+}
+
METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
private_ike_sa_manager_t* this, message_t *message)
{
@@ -1246,7 +1249,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
entry->checked_out = TRUE;
unlock_single_segment(this, segment);
- entry->message_id = message->get_message_id(message);
+ entry->processing = get_message_id_or_hash(message);
entry->init_hash = hash;
DBG2(DBG_MGR, "created IKE_SA %s[%u]",
@@ -1290,12 +1293,11 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS)
{
- /* only check out in IKEv2 if we are not already processing it */
- if (message->get_request(message) &&
- message->get_message_id(message) == entry->message_id)
+ /* only check out if we are not already processing it. */
+ if (entry->processing == get_message_id_or_hash(message))
{
DBG1(DBG_MGR, "ignoring request with ID %u, already processing",
- entry->message_id);
+ entry->processing);
}
else if (wait_for_entry(this, entry, segment))
{
@@ -1305,7 +1307,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
entry->checked_out = TRUE;
if (message->get_first_payload_type(message) != FRAGMENT_V1)
{
- entry->message_id = message->get_message_id(message);
+ entry->processing = get_message_id_or_hash(message);
}
if (ike_id->get_responder_spi(ike_id) == 0)
{
@@ -1564,7 +1566,7 @@ METHOD(ike_sa_manager_t, checkin, void,
entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa));
/* signal waiting threads */
entry->checked_out = FALSE;
- entry->message_id = -1;
+ entry->processing = -1;
/* check if this SA is half-open */
if (entry->half_open && ike_sa->get_state(ike_sa) != IKE_CONNECTING)
{
@@ -1745,6 +1747,23 @@ METHOD(ike_sa_manager_t, create_id_enumerator, enumerator_t*,
(void*)id_enumerator_cleanup, ids);
}
+/**
+ * Move all CHILD_SAs from old to new
+ */
+static void adopt_children(ike_sa_t *old, ike_sa_t *new)
+{
+ enumerator_t *enumerator;
+ child_sa_t *child_sa;
+
+ enumerator = old->create_child_sa_enumerator(old);
+ while (enumerator->enumerate(enumerator, &child_sa))
+ {
+ old->remove_child_sa(old, enumerator);
+ new->add_child_sa(new, child_sa);
+ }
+ enumerator->destroy(enumerator);
+}
+
METHOD(ike_sa_manager_t, check_uniqueness, bool,
private_ike_sa_manager_t *this, ike_sa_t *ike_sa, bool force_replace)
{
@@ -1782,6 +1801,7 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
{
DBG1(DBG_IKE, "destroying duplicate IKE_SA for peer '%Y', "
"received INITIAL_CONTACT", other);
+ charon->bus->ike_updown(charon->bus, duplicate, FALSE);
checkin_and_destroy(this, duplicate);
continue;
}
@@ -1796,6 +1816,10 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
{
case UNIQUE_REPLACE:
charon->bus->alert(charon->bus, ALERT_UNIQUE_REPLACE);
+ if (duplicate->get_version(duplicate) == IKEV1)
+ {
+ adopt_children(duplicate, ike_sa);
+ }
DBG1(DBG_IKE, "deleting duplicate IKE_SA for peer "
"'%Y' due to uniqueness policy", other);
status = duplicate->delete(duplicate);