mirror of
https://github.com/strongswan/strongswan.git
synced 2025-10-06 00:00:47 -04:00
duplicheck: track multiple IKE_SAs in checking state to avoid any races
When two consequent duplicates have been detected, track state of each checking IKE_SA separately, avoiding potential race conditions between the active SA and the different SAs in checking state.
This commit is contained in:
parent
93f53a78b5
commit
9c84bbcbc0
@ -60,8 +60,8 @@ struct private_duplicheck_listener_t {
|
||||
typedef struct {
|
||||
/** peer identity */
|
||||
identification_t *id;
|
||||
/** IKE_SA identifier */
|
||||
ike_sa_id_t *sa;
|
||||
/** list of IKE_SA identifiers, ike_sa_id_t */
|
||||
linked_list_t *sas;
|
||||
} entry_t;
|
||||
|
||||
/**
|
||||
@ -70,7 +70,7 @@ typedef struct {
|
||||
static void entry_destroy(entry_t *this)
|
||||
{
|
||||
this->id->destroy(this->id);
|
||||
this->sa->destroy(this->sa);
|
||||
this->sas->destroy_offset(this->sas, offsetof(ike_sa_id_t, destroy));
|
||||
free(this);
|
||||
}
|
||||
|
||||
@ -90,27 +90,101 @@ static bool equals(identification_t *a, identification_t *b)
|
||||
return a->equals(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an IKE_SA identifier to hashtable
|
||||
*/
|
||||
static void put(hashtable_t *table, identification_t *id, ike_sa_id_t *sa)
|
||||
{
|
||||
entry_t *entry;
|
||||
|
||||
entry = table->get(table, id);
|
||||
if (!entry)
|
||||
{
|
||||
INIT(entry,
|
||||
.id = id->clone(id),
|
||||
.sas = linked_list_create(),
|
||||
);
|
||||
table->put(table, entry->id, entry);
|
||||
}
|
||||
entry->sas->insert_last(entry->sas, sa->clone(sa));
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge an entry from table if it has no IKE_SA identifiers
|
||||
*/
|
||||
static void remove_if_empty(hashtable_t *table, entry_t *entry)
|
||||
{
|
||||
if (entry->sas->get_count(entry->sas) == 0)
|
||||
{
|
||||
entry = table->remove(table, entry->id);
|
||||
if (entry)
|
||||
{
|
||||
entry_destroy(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the first entry found in the table for the given id
|
||||
*/
|
||||
static ike_sa_id_t *remove_first(hashtable_t *table, identification_t *id)
|
||||
{
|
||||
ike_sa_id_t *sa = NULL;
|
||||
entry_t *entry;
|
||||
|
||||
entry = table->get(table, id);
|
||||
if (entry)
|
||||
{
|
||||
entry->sas->remove_first(entry->sas, (void**)&sa);
|
||||
remove_if_empty(table, entry);
|
||||
}
|
||||
return sa;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific IKE_SA ID for the given identity
|
||||
*/
|
||||
static bool remove_specific(hashtable_t *table, identification_t *id,
|
||||
ike_sa_id_t *sa)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
bool found = FALSE;
|
||||
entry_t *entry;
|
||||
ike_sa_id_t *current;
|
||||
|
||||
entry = table->get(table, id);
|
||||
if (entry)
|
||||
{
|
||||
enumerator = entry->sas->create_enumerator(entry->sas);
|
||||
while (enumerator->enumerate(enumerator, ¤t))
|
||||
{
|
||||
if (sa->equals(sa, current))
|
||||
{
|
||||
entry->sas->remove_at(entry->sas, enumerator);
|
||||
current->destroy(current);
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
if (found)
|
||||
{
|
||||
remove_if_empty(table, entry);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
METHOD(listener_t, ike_rekey, bool,
|
||||
private_duplicheck_listener_t *this, ike_sa_t *old, ike_sa_t *new)
|
||||
{
|
||||
identification_t *id;
|
||||
ike_sa_id_t *sa;
|
||||
entry_t *entry;
|
||||
|
||||
sa = new->get_id(new);
|
||||
id = new->get_other_id(new);
|
||||
|
||||
INIT(entry,
|
||||
.id = id->clone(id),
|
||||
.sa = sa->clone(sa),
|
||||
);
|
||||
this->mutex->lock(this->mutex);
|
||||
entry = this->active->put(this->active, entry->id, entry);
|
||||
|
||||
remove_specific(this->active, old->get_other_id(old), old->get_id(old));
|
||||
put(this->active, new->get_other_id(new), new->get_id(new));
|
||||
|
||||
this->mutex->unlock(this->mutex);
|
||||
if (entry)
|
||||
{
|
||||
entry_destroy(entry);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -119,58 +193,41 @@ METHOD(listener_t, ike_updown, bool,
|
||||
{
|
||||
identification_t *id;
|
||||
ike_sa_id_t *sa;
|
||||
entry_t *entry;
|
||||
job_t *job;
|
||||
|
||||
sa = ike_sa->get_id(ike_sa);
|
||||
id = ike_sa->get_other_id(ike_sa);
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
if (up)
|
||||
{
|
||||
INIT(entry,
|
||||
.id = id->clone(id),
|
||||
.sa = sa->clone(sa),
|
||||
);
|
||||
this->mutex->lock(this->mutex);
|
||||
entry = this->active->put(this->active, entry->id, entry);
|
||||
this->mutex->unlock(this->mutex);
|
||||
if (entry)
|
||||
/* another IKE_SA for this identity active? */
|
||||
sa = remove_first(this->active, id);
|
||||
if (sa)
|
||||
{
|
||||
DBG1(DBG_CFG, "detected duplicate IKE_SA for '%Y', "
|
||||
"triggering delete for old IKE_SA", id);
|
||||
job = (job_t*)delete_ike_sa_job_create(entry->sa, TRUE);
|
||||
this->mutex->lock(this->mutex);
|
||||
entry = this->checking->put(this->checking, entry->id, entry);
|
||||
this->mutex->unlock(this->mutex);
|
||||
lib->processor->queue_job(lib->processor, job);
|
||||
if (entry)
|
||||
{
|
||||
entry_destroy(entry);
|
||||
}
|
||||
put(this->checking, id, sa);
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)delete_ike_sa_job_create(sa, TRUE));
|
||||
sa->destroy(sa);
|
||||
}
|
||||
/* register IKE_SA as the new active */
|
||||
sa = ike_sa->get_id(ike_sa);
|
||||
put(this->active, id, sa);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->mutex->lock(this->mutex);
|
||||
entry = this->checking->remove(this->checking, id);
|
||||
this->mutex->unlock(this->mutex);
|
||||
if (entry)
|
||||
sa = ike_sa->get_id(ike_sa);
|
||||
/* check if closing an IKE_SA currently in checking state */
|
||||
if (remove_specific(this->checking, id, sa))
|
||||
{
|
||||
DBG1(DBG_CFG, "delete for duplicate IKE_SA '%Y' timed out, "
|
||||
"keeping new IKE_SA", id);
|
||||
entry_destroy(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->mutex->lock(this->mutex);
|
||||
entry = this->active->remove(this->active, id);
|
||||
this->mutex->unlock(this->mutex);
|
||||
if (entry)
|
||||
{
|
||||
entry_destroy(entry);
|
||||
}
|
||||
}
|
||||
/* check normal close of IKE_SA */
|
||||
remove_specific(this->active, id, sa);
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -181,29 +238,32 @@ METHOD(listener_t, message_hook, bool,
|
||||
if (incoming && plain && !message->get_request(message))
|
||||
{
|
||||
identification_t *id;
|
||||
entry_t *entry;
|
||||
ike_sa_id_t *sa;
|
||||
|
||||
id = ike_sa->get_other_id(ike_sa);
|
||||
sa = ike_sa->get_id(ike_sa);
|
||||
|
||||
this->mutex->lock(this->mutex);
|
||||
entry = this->checking->remove(this->checking, id);
|
||||
this->mutex->unlock(this->mutex);
|
||||
if (entry)
|
||||
if (remove_specific(this->checking, id, sa))
|
||||
{
|
||||
DBG1(DBG_CFG, "got a response on a duplicate IKE_SA for '%Y', "
|
||||
"deleting new IKE_SA", id);
|
||||
charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP);
|
||||
entry_destroy(entry);
|
||||
this->mutex->lock(this->mutex);
|
||||
entry = this->active->remove(this->active, id);
|
||||
this->mutex->unlock(this->mutex);
|
||||
if (entry)
|
||||
sa = remove_first(this->active, id);
|
||||
if (sa)
|
||||
{
|
||||
lib->processor->queue_job(lib->processor,
|
||||
(job_t*)delete_ike_sa_job_create(entry->sa, TRUE));
|
||||
entry_destroy(entry);
|
||||
(job_t*)delete_ike_sa_job_create(sa, TRUE));
|
||||
sa->destroy(sa);
|
||||
}
|
||||
this->mutex->unlock(this->mutex);
|
||||
|
||||
this->notify->send(this->notify, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->mutex->unlock(this->mutex);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user