aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2012-02-29 18:15:42 +0100
committerTobias Brunner <tobias@strongswan.org>2012-03-20 17:31:40 +0100
commit71cf97871f9565635b77379f6d2f09f5d6b375ff (patch)
tree1021afda689ebe77d6a11a42436a8305433cd824 /src
parent68611395dc11fecdf736893c06bb26b68e05fc06 (diff)
downloadstrongswan-71cf97871f9565635b77379f6d2f09f5d6b375ff.tar.bz2
strongswan-71cf97871f9565635b77379f6d2f09f5d6b375ff.tar.xz
Added separate hashtable for hashes of initial IKE messages.
This does not require us to do a lookup for an SA by SPI first.
Diffstat (limited to 'src')
-rw-r--r--src/libcharon/sa/ike_sa_manager.c203
1 files changed, 139 insertions, 64 deletions
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 3a06f1d1f..2e9297604 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -157,17 +157,6 @@ static entry_t *entry_create()
}
/**
- * Function that matches entry_t objects by initiator SPI and the hash of the
- * IKE_SA_INIT message.
- */
-static bool entry_match_by_hash(entry_t *entry, ike_sa_id_t *id, chunk_t *hash)
-{
- return id->get_responder_spi(id) == 0 &&
- id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id) &&
- chunk_equals(*hash, entry->init_hash);
-}
-
-/**
* Function that matches entry_t objects by ike_sa_id_t.
*/
static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id)
@@ -358,6 +347,16 @@ struct private_ike_sa_manager_t {
shareable_segment_t *connected_peers_segments;
/**
+ * Hash table with chunk_t objects.
+ */
+ linked_list_t **init_hashes_table;
+
+ /**
+ * Segments of the "hashes" hash table.
+ */
+ segment_t *init_hashes_segments;
+
+ /**
* RNG to get random SPIs for our side
*/
rng_t *rng;
@@ -649,17 +648,6 @@ static status_t get_entry_by_id(private_ike_sa_manager_t *this,
}
/**
- * Find an entry by initiator SPI and IKE_SA_INIT hash.
- * Note: On SUCCESS, the caller has to unlock the segment.
- */
-static status_t get_entry_by_hash(private_ike_sa_manager_t *this,
- ike_sa_id_t *ike_sa_id, chunk_t hash, entry_t **entry, u_int *segment)
-{
- return get_entry_by_match_function(this, ike_sa_id, entry, segment,
- (linked_list_match_t)entry_match_by_hash, ike_sa_id, &hash);
-}
-
-/**
* Find an entry by IKE_SA pointer.
* Note: On SUCCESS, the caller has to unlock the segment.
*/
@@ -901,6 +889,87 @@ static void remove_connected_peers(private_ike_sa_manager_t *this, entry_t *entr
}
/**
+ * Check if we already have created an IKE_SA based on the initial IKE message
+ * with the given hash. If not the hash is stored.
+ *
+ * @returns TRUE if the message with the given hash was seen before
+ */
+static bool check_and_put_init_hash(private_ike_sa_manager_t *this,
+ chunk_t init_hash)
+{
+ chunk_t *clone;
+ linked_list_t *list;
+ u_int row, segment;
+ mutex_t *mutex;
+
+ row = chunk_hash(init_hash) & this->table_mask;
+ segment = row & this->segment_mask;
+ mutex = this->init_hashes_segments[segment].mutex;
+ mutex->lock(mutex);
+ list = this->init_hashes_table[row];
+ if (list)
+ {
+ chunk_t *current;
+
+ if (list->find_first(list, (linked_list_match_t)chunk_equals_ptr,
+ (void**)&current, &init_hash) == SUCCESS)
+ {
+ mutex->unlock(mutex);
+ return TRUE;
+ }
+ }
+ else
+ {
+ list = this->init_hashes_table[row] = linked_list_create();
+ }
+
+ INIT(clone,
+ .len = init_hash.len,
+ .ptr = malloc(init_hash.len),
+ );
+ memcpy(clone->ptr, init_hash.ptr, clone->len);
+ list->insert_last(list, clone);
+
+ mutex->unlock(mutex);
+ return FALSE;
+}
+
+/**
+ * Remove the hash of an initial IKE message from the cache.
+ */
+static void remove_init_hash(private_ike_sa_manager_t *this, chunk_t init_hash)
+{
+ linked_list_t *list;
+ u_int row, segment;
+ mutex_t *mutex;
+
+ row = chunk_hash(init_hash) & this->table_mask;
+ segment = row & this->segment_mask;
+ mutex = this->init_hashes_segments[segment].mutex;
+ mutex->lock(mutex);
+ list = this->init_hashes_table[row];
+ if (list)
+ {
+ enumerator_t *enumerator;
+ chunk_t *current;
+
+ enumerator = list->create_enumerator(list);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (chunk_equals_ptr(current, &init_hash))
+ {
+ list->remove_at(list, enumerator);
+ chunk_free(current);
+ free(current);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ mutex->unlock(mutex);
+}
+
+/**
* Get a random SPI for new IKE_SAs
*/
static u_int64_t get_spi(private_ike_sa_manager_t *this)
@@ -977,6 +1046,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
bool is_init = FALSE;
id = message->get_ike_sa_id(message);
+ /* clone the IKE_SA ID so we can modify the initiator flag */
id = id->clone(id);
id->switch_initiator(id);
@@ -1009,61 +1079,45 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
}
if (is_init && this->hasher)
- {
- /* First request. Check for an IKE_SA with such a message hash. */
+ { /* initial request. checking for the hasher prevents crashes once
+ * flush() has been called */
chunk_t hash;
this->hasher->allocate_hash(this->hasher,
message->get_packet_data(message), &hash);
- if (get_entry_by_hash(this, id, hash, &entry, &segment) == SUCCESS)
+ /* ensure this is not a retransmit of an already handled init message */
+ if (check_and_put_init_hash(this, hash))
{
- if (message->get_exchange_type(message) == IKE_SA_INIT &&
- entry->message_id == 0)
- {
- unlock_single_segment(this, segment);
- chunk_free(&hash);
- id->destroy(id);
- DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing");
- return NULL;
- }
- else if (wait_for_entry(this, entry, segment))
- {
- entry->checked_out = TRUE;
- entry->message_id = message->get_message_id(message);
- ike_sa = entry->ike_sa;
- DBG2(DBG_MGR, "IKE_SA %s[%u] checked out by hash",
- ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
- chunk_free(&hash);
- }
- unlock_single_segment(this, segment);
+ chunk_free(&hash);
+ id->destroy(id);
+ DBG1(DBG_MGR, "ignoring %s, already processing",
+ ike_version == IKEV2 ? "IKE_SA_INIT" : "initial IKE message");
+ return NULL;
}
- if (ike_sa == NULL)
+ /* no IKE_SA yet, create a new one */
+ id->set_responder_spi(id, get_spi(this));
+ ike_sa = ike_sa_create(id, FALSE, ike_version);
+ if (ike_sa)
{
- /* no IKE_SA found, create a new one */
- id->set_responder_spi(id, get_spi(this));
- ike_sa = ike_sa_create(id, FALSE, ike_version);
- if (ike_sa)
- {
- entry = entry_create();
- /* a new SA checked out by message is a responder SA */
- entry->ike_sa = ike_sa;
- entry->ike_sa_id = id->clone(id);
+ entry = entry_create();
+ entry->ike_sa = ike_sa;
+ entry->ike_sa_id = id->clone(id);
- segment = put_entry(this, entry);
- entry->checked_out = TRUE;
- unlock_single_segment(this, segment);
+ segment = put_entry(this, entry);
+ entry->checked_out = TRUE;
+ unlock_single_segment(this, segment);
- entry->message_id = message->get_message_id(message);
- entry->init_hash = hash;
+ entry->message_id = message->get_message_id(message);
+ entry->init_hash = hash;
- DBG2(DBG_MGR, "created IKE_SA %s[%u]",
- ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
- }
+ DBG2(DBG_MGR, "created IKE_SA %s[%u]",
+ ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
}
- if (ike_sa == NULL)
+ else
{
+ remove_init_hash(this, hash);
chunk_free(&hash);
DBG1(DBG_MGR, "ignoring message, no such IKE_SA");
}
@@ -1449,6 +1503,10 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
{
remove_connected_peers(this, entry);
}
+ if (entry->init_hash.ptr)
+ {
+ remove_init_hash(this, entry->init_hash);
+ }
entry_destroy(entry);
@@ -1744,6 +1802,10 @@ METHOD(ike_sa_manager_t, flush, void,
{
remove_connected_peers(this, entry);
}
+ if (entry->init_hash.ptr)
+ {
+ remove_init_hash(this, entry->init_hash);
+ }
remove_entry_at((private_enumerator_t*)enumerator);
entry_destroy(entry);
}
@@ -1767,19 +1829,23 @@ METHOD(ike_sa_manager_t, destroy, void,
DESTROY_IF(this->ike_sa_table[i]);
DESTROY_IF(this->half_open_table[i]);
DESTROY_IF(this->connected_peers_table[i]);
+ DESTROY_IF(this->init_hashes_table[i]);
}
free(this->ike_sa_table);
free(this->half_open_table);
free(this->connected_peers_table);
+ free(this->init_hashes_table);
for (i = 0; i < this->segment_count; i++)
{
this->segments[i].mutex->destroy(this->segments[i].mutex);
this->half_open_segments[i].lock->destroy(this->half_open_segments[i].lock);
this->connected_peers_segments[i].lock->destroy(this->connected_peers_segments[i].lock);
+ this->init_hashes_segments[i].mutex->destroy(this->init_hashes_segments[i].mutex);
}
free(this->segments);
free(this->half_open_segments);
free(this->connected_peers_segments);
+ free(this->init_hashes_segments);
free(this);
}
@@ -1856,8 +1922,8 @@ ike_sa_manager_t *ike_sa_manager_create()
"charon.ikesa_table_segments", DEFAULT_SEGMENT_COUNT));
this->segment_count = max(1, min(this->segment_count, this->table_size));
this->segment_mask = this->segment_count - 1;
- this->ike_sa_table = calloc(this->table_size, sizeof(linked_list_t*));
+ this->ike_sa_table = calloc(this->table_size, sizeof(linked_list_t*));
this->segments = (segment_t*)calloc(this->segment_count, sizeof(segment_t));
for (i = 0; i < this->segment_count; i++)
{
@@ -1883,6 +1949,15 @@ ike_sa_manager_t *ike_sa_manager_create()
this->connected_peers_segments[i].count = 0;
}
+ /* and again for the table of hashes of seen initial IKE messages */
+ this->init_hashes_table = calloc(this->table_size, sizeof(linked_list_t*));
+ this->init_hashes_segments = calloc(this->segment_count, sizeof(segment_t));
+ for (i = 0; i < this->segment_count; i++)
+ {
+ this->init_hashes_segments[i].mutex = mutex_create(MUTEX_TYPE_RECURSIVE);
+ this->init_hashes_segments[i].count = 0;
+ }
+
this->reuse_ikesa = lib->settings->get_bool(lib->settings,
"charon.reuse_ikesa", TRUE);
return &this->public;