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.c914
1 files changed, 594 insertions, 320 deletions
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 731ae6007..a396235c2 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2005-2011 Martin Willi
* Copyright (C) 2011 revosec AG
- * Copyright (C) 2008 Tobias Brunner
+ * Copyright (C) 2008-2012 Tobias Brunner
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
@@ -157,18 +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->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
- 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)
@@ -179,7 +167,6 @@ static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id)
}
if ((id->get_responder_spi(id) == 0 ||
entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0) &&
- id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id))
{
/* this is TRUE for IKE_SAs that we initiated but have not yet received a response */
@@ -201,8 +188,19 @@ static bool entry_match_by_sa(entry_t *entry, ike_sa_t *ike_sa)
*/
static u_int ike_sa_id_hash(ike_sa_id_t *ike_sa_id)
{
- /* we always use initiator spi as key */
- return ike_sa_id->get_initiator_spi(ike_sa_id);
+ /* IKEv2 does not mandate random SPIs (RFC 5996, 2.6), they just have to be
+ * locally unique, so we use our randomly allocated SPI whether we are
+ * initiator or responder to ensure a good distribution. The latter is not
+ * possible for IKEv1 as we don't know whether we are original initiator or
+ * not (based on the IKE header). But as RFC 2408, section 2.5.3 proposes
+ * SPIs (Cookies) to be allocated near random (we allocate them randomly
+ * anyway) it seems safe to always use the initiator SPI. */
+ if (ike_sa_id->get_ike_version(ike_sa_id) == IKEV1_MAJOR_VERSION ||
+ ike_sa_id->is_initiator(ike_sa_id))
+ {
+ return ike_sa_id->get_initiator_spi(ike_sa_id);
+ }
+ return ike_sa_id->get_responder_spi(ike_sa_id);
}
typedef struct half_open_t half_open_t;
@@ -227,14 +225,6 @@ static void half_open_destroy(half_open_t *this)
free(this);
}
-/**
- * Function that matches half_open_t objects by the given IP address chunk.
- */
-static bool half_open_match(half_open_t *half_open, chunk_t *addr)
-{
- return chunk_equals(*addr, half_open->other);
-}
-
typedef struct connected_peers_t connected_peers_t;
struct connected_peers_t {
@@ -262,15 +252,25 @@ static void connected_peers_destroy(connected_peers_t *this)
/**
* Function that matches connected_peers_t objects by the given ids.
*/
-static bool connected_peers_match(connected_peers_t *connected_peers,
+static inline bool connected_peers_match(connected_peers_t *connected_peers,
identification_t *my_id, identification_t *other_id,
- uintptr_t family)
+ int family)
{
return my_id->equals(my_id, connected_peers->my_id) &&
other_id->equals(other_id, connected_peers->other_id) &&
- family == connected_peers->family;
+ (!family || family == connected_peers->family);
}
+typedef struct init_hash_t init_hash_t;
+
+struct init_hash_t {
+ /** hash of IKE_SA_INIT or initial phase1 message (data is not cloned) */
+ chunk_t hash;
+
+ /** our SPI allocated for the IKE_SA based on this message */
+ u_int64_t our_spi;
+};
+
typedef struct segment_t segment_t;
/**
@@ -298,6 +298,20 @@ struct shareable_segment_t {
u_int count;
};
+typedef struct table_item_t table_item_t;
+
+/**
+ * Instead of using linked_list_t for each bucket we store the data in our own
+ * list to save memory.
+ */
+struct table_item_t {
+ /** data of this item */
+ void *value;
+
+ /** next item in the overflow list */
+ table_item_t *next;
+};
+
typedef struct private_ike_sa_manager_t private_ike_sa_manager_t;
/**
@@ -312,7 +326,7 @@ struct private_ike_sa_manager_t {
/**
* Hash table with entries for the ike_sa_t objects.
*/
- linked_list_t **ike_sa_table;
+ table_item_t **ike_sa_table;
/**
* The size of the hash table.
@@ -342,7 +356,7 @@ struct private_ike_sa_manager_t {
/**
* Hash table with half_open_t objects.
*/
- linked_list_t **half_open_table;
+ table_item_t **half_open_table;
/**
* Segments of the "half-open" hash table.
@@ -352,7 +366,7 @@ struct private_ike_sa_manager_t {
/**
* Hash table with connected_peers_t objects.
*/
- linked_list_t **connected_peers_table;
+ table_item_t **connected_peers_table;
/**
* Segments of the "connected peers" hash table.
@@ -360,6 +374,16 @@ struct private_ike_sa_manager_t {
shareable_segment_t *connected_peers_segments;
/**
+ * Hash table with init_hash_t objects.
+ */
+ table_item_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;
@@ -379,10 +403,10 @@ struct private_ike_sa_manager_t {
* Acquire a lock to access the segment of the table row with the given index.
* It also works with the segment index directly.
*/
-static void lock_single_segment(private_ike_sa_manager_t *this, u_int index)
+static inline void lock_single_segment(private_ike_sa_manager_t *this,
+ u_int index)
{
mutex_t *lock = this->segments[index & this->segment_mask].mutex;
-
lock->lock(lock);
}
@@ -390,10 +414,10 @@ static void lock_single_segment(private_ike_sa_manager_t *this, u_int index)
* Release the lock required to access the segment of the table row with the given index.
* It also works with the segment index directly.
*/
-static void unlock_single_segment(private_ike_sa_manager_t *this, u_int index)
+static inline void unlock_single_segment(private_ike_sa_manager_t *this,
+ u_int index)
{
mutex_t *lock = this->segments[index & this->segment_mask].mutex;
-
lock->unlock(lock);
}
@@ -456,9 +480,14 @@ struct private_enumerator_t {
u_int row;
/**
- * enumerator for the current table row
+ * current table item
+ */
+ table_item_t *current;
+
+ /**
+ * previous table item
*/
- enumerator_t *current;
+ table_item_t *prev;
};
METHOD(enumerator_t, enumerate, bool,
@@ -473,33 +502,23 @@ METHOD(enumerator_t, enumerate, bool,
{
while (this->row < this->manager->table_size)
{
+ this->prev = this->current;
if (this->current)
{
- entry_t *item;
-
- if (this->current->enumerate(this->current, &item))
- {
- *entry = this->entry = item;
- *segment = this->segment;
- return TRUE;
- }
- this->current->destroy(this->current);
- this->current = NULL;
- unlock_single_segment(this->manager, this->segment);
+ this->current = this->current->next;
}
else
{
- linked_list_t *list;
-
lock_single_segment(this->manager, this->segment);
- if ((list = this->manager->ike_sa_table[this->row]) != NULL &&
- list->get_count(list))
- {
- this->current = list->create_enumerator(list);
- continue;
- }
- unlock_single_segment(this->manager, this->segment);
+ this->current = this->manager->ike_sa_table[this->row];
}
+ if (this->current)
+ {
+ *entry = this->entry = this->current->value;
+ *segment = this->segment;
+ return TRUE;
+ }
+ unlock_single_segment(this->manager, this->segment);
this->row += this->manager->segment_count;
}
this->segment++;
@@ -517,7 +536,6 @@ METHOD(enumerator_t, enumerator_destroy, void,
}
if (this->current)
{
- this->current->destroy(this->current);
unlock_single_segment(this->manager, this->segment);
}
free(this);
@@ -546,19 +564,23 @@ static enumerator_t* create_table_enumerator(private_ike_sa_manager_t *this)
*/
static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry)
{
- linked_list_t *list;
+ table_item_t *current, *item;
u_int row, segment;
+ INIT(item,
+ .value = entry,
+ );
+
row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask;
segment = row & this->segment_mask;
lock_single_segment(this, segment);
- list = this->ike_sa_table[row];
- if (!list)
- {
- list = this->ike_sa_table[row] = linked_list_create();
+ current = this->ike_sa_table[row];
+ if (current)
+ { /* insert at the front of current bucket */
+ item->next = current;
}
- list->insert_last(list, entry);
+ this->ike_sa_table[row] = item;
this->segments[segment].count++;
return segment;
}
@@ -569,28 +591,30 @@ static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry)
*/
static void remove_entry(private_ike_sa_manager_t *this, entry_t *entry)
{
- linked_list_t *list;
+ table_item_t *item, *prev = NULL;
u_int row, segment;
row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask;
segment = row & this->segment_mask;
- list = this->ike_sa_table[row];
- if (list)
+ item = this->ike_sa_table[row];
+ while (item)
{
- entry_t *current;
- enumerator_t *enumerator;
-
- enumerator = list->create_enumerator(list);
- while (enumerator->enumerate(enumerator, &current))
+ if (item->value == entry)
{
- if (current == entry)
+ if (prev)
{
- list->remove_at(list, enumerator);
- this->segments[segment].count--;
- break;
+ prev->next = item->next;
}
+ else
+ {
+ this->ike_sa_table[row] = item->next;
+ }
+ this->segments[segment].count--;
+ free(item);
+ break;
}
- enumerator->destroy(enumerator);
+ prev = item;
+ item = item->next;
}
}
@@ -602,9 +626,21 @@ static void remove_entry_at(private_enumerator_t *this)
this->entry = NULL;
if (this->current)
{
- linked_list_t *list = this->manager->ike_sa_table[this->row];
- list->remove_at(list, this->current);
+ table_item_t *current = this->current;
+
this->manager->segments[this->segment].count--;
+ this->current = this->prev;
+
+ if (this->prev)
+ {
+ this->prev->next = current->next;
+ }
+ else
+ {
+ this->manager->ike_sa_table[this->row] = current->next;
+ unlock_single_segment(this->manager, this->segment);
+ }
+ free(current);
}
}
@@ -614,26 +650,26 @@ static void remove_entry_at(private_enumerator_t *this)
*/
static status_t get_entry_by_match_function(private_ike_sa_manager_t *this,
ike_sa_id_t *ike_sa_id, entry_t **entry, u_int *segment,
- linked_list_match_t match, void *p1, void *p2)
+ linked_list_match_t match, void *param)
{
- entry_t *current;
- linked_list_t *list;
+ table_item_t *item;
u_int row, seg;
row = ike_sa_id_hash(ike_sa_id) & this->table_mask;
seg = row & this->segment_mask;
lock_single_segment(this, seg);
- list = this->ike_sa_table[row];
- if (list)
+ item = this->ike_sa_table[row];
+ while (item)
{
- if (list->find_first(list, match, (void**)&current, p1, p2) == SUCCESS)
+ if (match(item->value, param))
{
- *entry = current;
+ *entry = item->value;
*segment = seg;
/* the locked segment has to be unlocked by the caller */
return SUCCESS;
}
+ item = item->next;
}
unlock_single_segment(this, seg);
return NOT_FOUND;
@@ -647,18 +683,7 @@ static status_t get_entry_by_id(private_ike_sa_manager_t *this,
ike_sa_id_t *ike_sa_id, 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_id, ike_sa_id, NULL);
-}
-
-/**
- * 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);
+ (linked_list_match_t)entry_match_by_id, ike_sa_id);
}
/**
@@ -669,7 +694,7 @@ static status_t get_entry_by_sa(private_ike_sa_manager_t *this,
ike_sa_id_t *ike_sa_id, ike_sa_t *ike_sa, 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_sa, ike_sa, NULL);
+ (linked_list_match_t)entry_match_by_sa, ike_sa);
}
/**
@@ -707,44 +732,43 @@ static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry,
*/
static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
{
- half_open_t *half_open = NULL;
- linked_list_t *list;
- chunk_t addr;
+ table_item_t *item;
u_int row, segment;
rwlock_t *lock;
+ half_open_t *half_open;
+ chunk_t addr;
addr = entry->other->get_address(entry->other);
row = chunk_hash(addr) & this->table_mask;
segment = row & this->segment_mask;
lock = this->half_open_segments[segment].lock;
lock->write_lock(lock);
- list = this->half_open_table[row];
- if (list)
+ item = this->half_open_table[row];
+ while (item)
{
- half_open_t *current;
+ half_open = item->value;
- if (list->find_first(list, (linked_list_match_t)half_open_match,
- (void**)&current, &addr) == SUCCESS)
+ if (chunk_equals(addr, half_open->other))
{
- half_open = current;
half_open->count++;
- this->half_open_segments[segment].count++;
+ break;
}
- }
- else
- {
- list = this->half_open_table[row] = linked_list_create();
+ item = item->next;
}
- if (!half_open)
+ if (!item)
{
INIT(half_open,
.other = chunk_clone(addr),
.count = 1,
);
- list->insert_last(list, half_open);
- this->half_open_segments[segment].count++;
+ INIT(item,
+ .value = half_open,
+ .next = this->half_open_table[row],
+ );
+ this->half_open_table[row] = item;
}
+ this->half_open_segments[segment].count++;
lock->unlock(lock);
}
@@ -753,37 +777,41 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
*/
static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
{
- linked_list_t *list;
- chunk_t addr;
+ table_item_t *item, *prev = NULL;
u_int row, segment;
rwlock_t *lock;
+ chunk_t addr;
addr = entry->other->get_address(entry->other);
row = chunk_hash(addr) & this->table_mask;
segment = row & this->segment_mask;
lock = this->half_open_segments[segment].lock;
lock->write_lock(lock);
- list = this->half_open_table[row];
- if (list)
+ item = this->half_open_table[row];
+ while (item)
{
- half_open_t *current;
- enumerator_t *enumerator;
+ half_open_t *half_open = item->value;
- enumerator = list->create_enumerator(list);
- while (enumerator->enumerate(enumerator, &current))
+ if (chunk_equals(addr, half_open->other))
{
- if (half_open_match(current, &addr))
+ if (--half_open->count == 0)
{
- if (--current->count == 0)
+ if (prev)
{
- list->remove_at(list, enumerator);
- half_open_destroy(current);
+ prev->next = item->next;
}
- this->half_open_segments[segment].count--;
- break;
+ else
+ {
+ this->half_open_table[row] = item->next;
+ }
+ half_open_destroy(half_open);
+ free(item);
}
+ this->half_open_segments[segment].count--;
+ break;
}
- enumerator->destroy(enumerator);
+ prev = item;
+ item = item->next;
}
lock->unlock(lock);
}
@@ -793,28 +821,28 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
*/
static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
{
- connected_peers_t *connected_peers = NULL;
- chunk_t my_id, other_id;
- linked_list_t *list;
+ table_item_t *item;
u_int row, segment;
rwlock_t *lock;
+ connected_peers_t *connected_peers;
+ chunk_t my_id, other_id;
+ int family;
my_id = entry->my_id->get_encoding(entry->my_id);
other_id = entry->other_id->get_encoding(entry->other_id);
+ family = entry->other->get_family(entry->other);
row = chunk_hash_inc(other_id, chunk_hash(my_id)) & this->table_mask;
segment = row & this->segment_mask;
lock = this->connected_peers_segments[segment].lock;
lock->write_lock(lock);
- list = this->connected_peers_table[row];
- if (list)
+ item = this->connected_peers_table[row];
+ while (item)
{
- connected_peers_t *current;
+ connected_peers = item->value;
- if (list->find_first(list, (linked_list_match_t)connected_peers_match,
- (void**)&current, entry->my_id, entry->other_id,
- (uintptr_t)entry->other->get_family(entry->other)) == SUCCESS)
+ if (connected_peers_match(connected_peers, entry->my_id,
+ entry->other_id, family))
{
- connected_peers = current;
if (connected_peers->sas->find_first(connected_peers->sas,
(linked_list_match_t)entry->ike_sa_id->equals,
NULL, entry->ike_sa_id) == SUCCESS)
@@ -822,22 +850,24 @@ static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
lock->unlock(lock);
return;
}
+ break;
}
- }
- else
- {
- list = this->connected_peers_table[row] = linked_list_create();
+ item = item->next;
}
- if (!connected_peers)
+ if (!item)
{
INIT(connected_peers,
.my_id = entry->my_id->clone(entry->my_id),
.other_id = entry->other_id->clone(entry->other_id),
- .family = entry->other->get_family(entry->other),
+ .family = family,
.sas = linked_list_create(),
);
- list->insert_last(list, connected_peers);
+ INIT(item,
+ .value = connected_peers,
+ .next = this->connected_peers_table[row],
+ );
+ this->connected_peers_table[row] = item;
}
connected_peers->sas->insert_last(connected_peers->sas,
entry->ike_sa_id->clone(entry->ike_sa_id));
@@ -850,54 +880,61 @@ static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
*/
static void remove_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
{
- chunk_t my_id, other_id;
- linked_list_t *list;
+ table_item_t *item, *prev = NULL;
u_int row, segment;
rwlock_t *lock;
+ chunk_t my_id, other_id;
+ int family;
my_id = entry->my_id->get_encoding(entry->my_id);
other_id = entry->other_id->get_encoding(entry->other_id);
+ family = entry->other->get_family(entry->other);
+
row = chunk_hash_inc(other_id, chunk_hash(my_id)) & this->table_mask;
segment = row & this->segment_mask;
lock = this->connected_peers_segments[segment].lock;
lock->write_lock(lock);
- list = this->connected_peers_table[row];
- if (list)
+ item = this->connected_peers_table[row];
+ while (item)
{
- connected_peers_t *current;
- enumerator_t *enumerator;
+ connected_peers_t *current = item->value;
- enumerator = list->create_enumerator(list);
- while (enumerator->enumerate(enumerator, &current))
+ if (connected_peers_match(current, entry->my_id, entry->other_id,
+ family))
{
- if (connected_peers_match(current, entry->my_id, entry->other_id,
- (uintptr_t)entry->other->get_family(entry->other)))
- {
- ike_sa_id_t *ike_sa_id;
- enumerator_t *inner;
+ enumerator_t *enumerator;
+ ike_sa_id_t *ike_sa_id;
- inner = current->sas->create_enumerator(current->sas);
- while (inner->enumerate(inner, &ike_sa_id))
+ enumerator = current->sas->create_enumerator(current->sas);
+ while (enumerator->enumerate(enumerator, &ike_sa_id))
+ {
+ if (ike_sa_id->equals(ike_sa_id, entry->ike_sa_id))
{
- if (ike_sa_id->equals(ike_sa_id, entry->ike_sa_id))
- {
- current->sas->remove_at(current->sas, inner);
- ike_sa_id->destroy(ike_sa_id);
- this->connected_peers_segments[segment].count--;
- break;
- }
+ current->sas->remove_at(current->sas, enumerator);
+ ike_sa_id->destroy(ike_sa_id);
+ this->connected_peers_segments[segment].count--;
+ break;
}
- inner->destroy(inner);
- if (current->sas->get_count(current->sas) == 0)
+ }
+ enumerator->destroy(enumerator);
+ if (current->sas->get_count(current->sas) == 0)
+ {
+ if (prev)
{
- list->remove_at(list, enumerator);
- connected_peers_destroy(current);
+ prev->next = item->next;
}
- break;
+ else
+ {
+ this->connected_peers_table[row] = item->next;
+ }
+ connected_peers_destroy(current);
+ free(item);
}
+ break;
}
- enumerator->destroy(enumerator);
+ prev = item;
+ item = item->next;
}
lock->unlock(lock);
}
@@ -907,13 +944,143 @@ static void remove_connected_peers(private_ike_sa_manager_t *this, entry_t *entr
*/
static u_int64_t get_spi(private_ike_sa_manager_t *this)
{
- u_int64_t spi = 0;
+ u_int64_t spi;
+
+ if (this->rng &&
+ this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi))
+ {
+ return spi;
+ }
+ return 0;
+}
+
+/**
+ * Calculate the hash of the initial IKE message. Memory for the hash is
+ * allocated on success.
+ *
+ * @returns TRUE on success
+ */
+static bool get_init_hash(private_ike_sa_manager_t *this, message_t *message,
+ chunk_t *hash)
+{
+ if (!this->hasher)
+ { /* this might be the case when flush() has been called */
+ return FALSE;
+ }
+ if (message->get_exchange_type(message) == ID_PROT)
+ { /* include the source for Main Mode as the hash will be the same if
+ * SPIs are reused by two initiators that use the same proposal */
+ host_t *src = message->get_source(message);
+
+ if (!this->hasher->allocate_hash(this->hasher,
+ src->get_address(src), NULL))
+ {
+ return FALSE;
+ }
+ }
+ return this->hasher->allocate_hash(this->hasher,
+ message->get_packet_data(message), hash);
+}
+
+/**
+ * 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, the hash data is not(!) cloned.
+ *
+ * Also, the local SPI is returned. In case of a retransmit this is already
+ * stored together with the hash, otherwise it is newly allocated and should
+ * be used to create the IKE_SA.
+ *
+ * @returns ALREADY_DONE if the message with the given hash has been seen before
+ * NOT_FOUND if the message hash was not found
+ * FAILED if the SPI allocation failed
+ */
+static status_t check_and_put_init_hash(private_ike_sa_manager_t *this,
+ chunk_t init_hash, u_int64_t *our_spi)
+{
+ table_item_t *item;
+ u_int row, segment;
+ mutex_t *mutex;
+ init_hash_t *init;
+ u_int64_t spi;
- if (this->rng)
+ row = chunk_hash(init_hash) & this->table_mask;
+ segment = row & this->segment_mask;
+ mutex = this->init_hashes_segments[segment].mutex;
+ mutex->lock(mutex);
+ item = this->init_hashes_table[row];
+ while (item)
{
- this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi);
+ init_hash_t *current = item->value;
+
+ if (chunk_equals(init_hash, current->hash))
+ {
+ *our_spi = current->our_spi;
+ mutex->unlock(mutex);
+ return ALREADY_DONE;
+ }
+ item = item->next;
+ }
+
+ spi = get_spi(this);
+ if (!spi)
+ {
+ return FAILED;
}
- return spi;
+
+ INIT(init,
+ .hash = {
+ .len = init_hash.len,
+ .ptr = init_hash.ptr,
+ },
+ .our_spi = spi,
+ );
+ INIT(item,
+ .value = init,
+ .next = this->init_hashes_table[row],
+ );
+ this->init_hashes_table[row] = item;
+ *our_spi = init->our_spi;
+ mutex->unlock(mutex);
+ return NOT_FOUND;
+}
+
+/**
+ * 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)
+{
+ table_item_t *item, *prev = NULL;
+ 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);
+ item = this->init_hashes_table[row];
+ while (item)
+ {
+ init_hash_t *current = item->value;
+
+ if (chunk_equals(init_hash, current->hash))
+ {
+ if (prev)
+ {
+ prev->next = item->next;
+ }
+ else
+ {
+ this->init_hashes_table[row] = item->next;
+ }
+ free(current);
+ free(item);
+ break;
+ }
+ prev = item;
+ item = item->next;
+ }
+ mutex->unlock(mutex);
}
METHOD(ike_sa_manager_t, checkout, ike_sa_t*,
@@ -941,25 +1108,38 @@ METHOD(ike_sa_manager_t, checkout, ike_sa_t*,
}
METHOD(ike_sa_manager_t, checkout_new, ike_sa_t*,
- private_ike_sa_manager_t* this, bool initiator)
+ private_ike_sa_manager_t* this, ike_version_t version, bool initiator)
{
ike_sa_id_t *ike_sa_id;
ike_sa_t *ike_sa;
+ u_int8_t ike_version;
+ u_int64_t spi;
+
+ ike_version = version == IKEV1 ? IKEV1_MAJOR_VERSION : IKEV2_MAJOR_VERSION;
+
+ spi = get_spi(this);
+ if (!spi)
+ {
+ DBG1(DBG_MGR, "failed to allocate SPI for new IKE_SA");
+ return NULL;
+ }
if (initiator)
{
- ike_sa_id = ike_sa_id_create(get_spi(this), 0, TRUE);
+ ike_sa_id = ike_sa_id_create(ike_version, spi, 0, TRUE);
}
else
{
- ike_sa_id = ike_sa_id_create(0, get_spi(this), FALSE);
+ ike_sa_id = ike_sa_id_create(ike_version, 0, spi, FALSE);
}
- ike_sa = ike_sa_create(ike_sa_id);
+ ike_sa = ike_sa_create(ike_sa_id, initiator, version);
ike_sa_id->destroy(ike_sa_id);
- DBG2(DBG_MGR, "created IKE_SA %s[%u]", ike_sa->get_name(ike_sa),
- ike_sa->get_unique_id(ike_sa));
-
+ if (ike_sa)
+ {
+ DBG2(DBG_MGR, "created IKE_SA %s[%u]", ike_sa->get_name(ike_sa),
+ ike_sa->get_unique_id(ike_sa));
+ }
return ike_sa;
}
@@ -970,94 +1150,118 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
entry_t *entry;
ike_sa_t *ike_sa = NULL;
ike_sa_id_t *id;
+ ike_version_t ike_version;
+ 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);
DBG2(DBG_MGR, "checkout IKE_SA by message");
- if (message->get_request(message) &&
- message->get_exchange_type(message) == IKE_SA_INIT &&
- this->hasher)
+ if (id->get_responder_spi(id) == 0)
{
- /* IKE_SA_INIT request. Check for an IKE_SA with such a message hash. */
- chunk_t data, hash;
-
- data = message->get_packet_data(message);
- this->hasher->allocate_hash(this->hasher, data, &hash);
- chunk_free(&data);
-
- if (get_entry_by_hash(this, id, hash, &entry, &segment) == SUCCESS)
+ if (message->get_major_version(message) == IKEV2_MAJOR_VERSION)
{
- if (entry->message_id == 0)
+ if (message->get_exchange_type(message) == IKE_SA_INIT &&
+ message->get_request(message))
{
- unlock_single_segment(this, segment);
- chunk_free(&hash);
- id->destroy(id);
- DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing");
- return NULL;
+ ike_version = IKEV2;
+ is_init = TRUE;
}
- else if (wait_for_entry(this, entry, segment))
+ }
+ else
+ {
+ if (message->get_exchange_type(message) == ID_PROT ||
+ message->get_exchange_type(message) == AGGRESSIVE)
{
- 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));
+ ike_version = IKEV1;
+ is_init = TRUE;
+ if (id->is_initiator(id))
+ { /* not set in IKEv1, switch back before applying to new SA */
+ id->switch_initiator(id);
+ }
}
- unlock_single_segment(this, segment);
+ }
+ }
+
+ if (is_init)
+ {
+ u_int64_t our_spi;
+ chunk_t hash;
+
+ if (!get_init_hash(this, message, &hash))
+ {
+ DBG1(DBG_MGR, "ignoring message, failed to hash message");
+ id->destroy(id);
+ return NULL;
}
- if (ike_sa == NULL)
+ /* ensure this is not a retransmit of an already handled init message */
+ switch (check_and_put_init_hash(this, hash, &our_spi))
{
- if (id->get_responder_spi(id) == 0 &&
- message->get_exchange_type(message) == IKE_SA_INIT)
- {
- /* no IKE_SA found, create a new one */
- id->set_responder_spi(id, get_spi(this));
- entry = entry_create();
- entry->ike_sa = ike_sa_create(id);
- entry->ike_sa_id = id->clone(id);
+ case NOT_FOUND:
+ { /* we've not seen this packet yet, create a new IKE_SA */
+ id->set_responder_spi(id, our_spi);
+ ike_sa = ike_sa_create(id, FALSE, ike_version);
+ if (ike_sa)
+ {
+ 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;
- ike_sa = entry->ike_sa;
+ 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));
+ }
+ else
+ {
+ remove_init_hash(this, hash);
+ chunk_free(&hash);
+ DBG1(DBG_MGR, "ignoring message, no such IKE_SA");
+ }
+ id->destroy(id);
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
}
- else
- {
+ case FAILED:
+ { /* we failed to allocate an SPI */
chunk_free(&hash);
- DBG1(DBG_MGR, "ignoring message, no such IKE_SA");
+ id->destroy(id);
+ DBG1(DBG_MGR, "ignoring message, failed to allocate SPI");
+ return NULL;
}
+ case ALREADY_DONE:
+ default:
+ break;
}
- else
- {
- chunk_free(&hash);
- }
- id->destroy(id);
- charon->bus->set_sa(charon->bus, ike_sa);
- return ike_sa;
+ /* it looks like we already handled this init message to some degree */
+ id->set_responder_spi(id, our_spi);
+ chunk_free(&hash);
}
if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS)
{
- /* only check out if we are not processing this request */
+ /* 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)
{
- DBG1(DBG_MGR, "ignoring request with ID %d, already processing",
+ DBG1(DBG_MGR, "ignoring request with ID %u, already processing",
entry->message_id);
}
else if (wait_for_entry(this, entry, segment))
{
- ike_sa_id_t *ike_id = entry->ike_sa->get_id(entry->ike_sa);
+ ike_sa_id_t *ike_id;
+
+ ike_id = entry->ike_sa->get_id(entry->ike_sa);
entry->checked_out = TRUE;
entry->message_id = message->get_message_id(message);
if (ike_id->get_responder_spi(ike_id) == 0)
@@ -1089,7 +1293,7 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
if (!this->reuse_ikesa)
{ /* IKE_SA reuse disable by config */
- ike_sa = checkout_new(this, TRUE);
+ ike_sa = checkout_new(this, peer_cfg->get_ike_version(peer_cfg), TRUE);
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
}
@@ -1125,7 +1329,7 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
if (!ike_sa)
{ /* no IKE_SA using such a config, hand out a new */
- ike_sa = checkout_new(this, TRUE);
+ ike_sa = checkout_new(this, peer_cfg->get_ike_version(peer_cfg), TRUE);
}
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
@@ -1244,6 +1448,7 @@ static bool enumerator_filter_wait(private_ike_sa_manager_t *this,
if (wait_for_entry(this, *in, *segment))
{
*out = (*in)->ike_sa;
+ charon->bus->set_sa(charon->bus, *out);
return TRUE;
}
return FALSE;
@@ -1260,17 +1465,26 @@ static bool enumerator_filter_skip(private_ike_sa_manager_t *this,
!(*in)->checked_out)
{
*out = (*in)->ike_sa;
+ charon->bus->set_sa(charon->bus, *out);
return TRUE;
}
return FALSE;
}
+/**
+ * Reset threads SA after enumeration
+ */
+static void reset_sa(void *data)
+{
+ charon->bus->set_sa(charon->bus, NULL);
+}
+
METHOD(ike_sa_manager_t, create_enumerator, enumerator_t*,
private_ike_sa_manager_t* this, bool wait)
{
return enumerator_create_filter(create_table_enumerator(this),
wait ? (void*)enumerator_filter_wait : (void*)enumerator_filter_skip,
- this, NULL);
+ this, reset_sa);
}
METHOD(ike_sa_manager_t, checkin, void,
@@ -1290,7 +1504,7 @@ METHOD(ike_sa_manager_t, checkin, void,
ike_sa_id = ike_sa->get_id(ike_sa);
my_id = ike_sa->get_my_id(ike_sa);
- other_id = ike_sa->get_other_id(ike_sa);
+ other_id = ike_sa->get_other_eap_id(ike_sa);
other = ike_sa->get_other_host(ike_sa);
DBG2(DBG_MGR, "checkin IKE_SA %s[%u]", ike_sa->get_name(ike_sa),
@@ -1340,9 +1554,21 @@ METHOD(ike_sa_manager_t, checkin, void,
}
/* apply identities for duplicate test */
- if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
+ if ((ike_sa->get_state(ike_sa) == IKE_ESTABLISHED ||
+ ike_sa->get_state(ike_sa) == IKE_PASSIVE) &&
entry->my_id == NULL && entry->other_id == NULL)
{
+ if (ike_sa->get_version(ike_sa) == IKEV1)
+ {
+ /* If authenticated and received INITIAL_CONTACT,
+ * delete any existing IKE_SAs with that peer. */
+ if (ike_sa->has_condition(ike_sa, COND_INIT_CONTACT_SEEN))
+ {
+ this->public.check_uniqueness(&this->public, ike_sa, TRUE);
+ ike_sa->set_condition(ike_sa, COND_INIT_CONTACT_SEEN, FALSE);
+ }
+ }
+
entry->my_id = my_id->clone(my_id);
entry->other_id = other_id->clone(other_id);
if (!entry->other)
@@ -1376,6 +1602,16 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
if (get_entry_by_sa(this, ike_sa_id, ike_sa, &entry, &segment) == SUCCESS)
{
+ if (entry->driveout_waiting_threads && entry->driveout_new_threads)
+ { /* it looks like flush() has been called and the SA is being deleted
+ * anyway, just check it in */
+ DBG2(DBG_MGR, "ignored check-in and destroy of IKE_SA during shutdown");
+ entry->checked_out = FALSE;
+ entry->condvar->broadcast(entry->condvar);
+ unlock_single_segment(this, segment);
+ return;
+ }
+
/* drive out waiting threads, as we are in hurry */
entry->driveout_waiting_threads = TRUE;
/* mark it, so no new threads can get this entry */
@@ -1399,6 +1635,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);
@@ -1412,65 +1652,81 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
charon->bus->set_sa(charon->bus, NULL);
}
-METHOD(ike_sa_manager_t, check_uniqueness, bool,
- private_ike_sa_manager_t *this, ike_sa_t *ike_sa, bool force_replace)
+/**
+ * Cleanup function for create_id_enumerator
+ */
+static void id_enumerator_cleanup(linked_list_t *ids)
{
- bool cancel = FALSE;
- peer_cfg_t *peer_cfg;
- unique_policy_t policy;
- linked_list_t *list, *duplicate_ids = NULL;
- enumerator_t *enumerator;
- ike_sa_id_t *duplicate_id = NULL;
- identification_t *me, *other;
+ ids->destroy_offset(ids, offsetof(ike_sa_id_t, destroy));
+}
+
+METHOD(ike_sa_manager_t, create_id_enumerator, enumerator_t*,
+ private_ike_sa_manager_t *this, identification_t *me,
+ identification_t *other, int family)
+{
+ table_item_t *item;
u_int row, segment;
rwlock_t *lock;
-
- peer_cfg = ike_sa->get_peer_cfg(ike_sa);
- policy = peer_cfg->get_unique_policy(peer_cfg);
- if (policy == UNIQUE_NO && !force_replace)
- {
- return FALSE;
- }
-
- me = ike_sa->get_my_id(ike_sa);
- other = ike_sa->get_other_id(ike_sa);
+ linked_list_t *ids = NULL;
row = chunk_hash_inc(other->get_encoding(other),
chunk_hash(me->get_encoding(me))) & this->table_mask;
segment = row & this->segment_mask;
- lock = this->connected_peers_segments[segment & this->segment_mask].lock;
+ lock = this->connected_peers_segments[segment].lock;
lock->read_lock(lock);
- list = this->connected_peers_table[row];
- if (list)
+ item = this->connected_peers_table[row];
+ while (item)
{
- connected_peers_t *current;
- host_t *other_host;
+ connected_peers_t *current = item->value;
- other_host = ike_sa->get_other_host(ike_sa);
- if (list->find_first(list, (linked_list_match_t)connected_peers_match,
- (void**)&current, me, other,
- (uintptr_t)other_host->get_family(other_host)) == SUCCESS)
+ if (connected_peers_match(current, me, other, family))
{
- /* clone the list, so we can release the lock */
- duplicate_ids = current->sas->clone_offset(current->sas,
- offsetof(ike_sa_id_t, clone));
+ ids = current->sas->clone_offset(current->sas,
+ offsetof(ike_sa_id_t, clone));
+ break;
}
+ item = item->next;
}
lock->unlock(lock);
- if (!duplicate_ids)
+ if (!ids)
+ {
+ return enumerator_create_empty();
+ }
+ return enumerator_create_cleaner(ids->create_enumerator(ids),
+ (void*)id_enumerator_cleanup, ids);
+}
+
+METHOD(ike_sa_manager_t, check_uniqueness, bool,
+ private_ike_sa_manager_t *this, ike_sa_t *ike_sa, bool force_replace)
+{
+ bool cancel = FALSE;
+ peer_cfg_t *peer_cfg;
+ unique_policy_t policy;
+ enumerator_t *enumerator;
+ ike_sa_id_t *id = NULL;
+ identification_t *me, *other;
+ host_t *other_host;
+
+ peer_cfg = ike_sa->get_peer_cfg(ike_sa);
+ policy = peer_cfg->get_unique_policy(peer_cfg);
+ if (policy == UNIQUE_NEVER || (policy == UNIQUE_NO && !force_replace))
{
return FALSE;
}
+ me = ike_sa->get_my_id(ike_sa);
+ other = ike_sa->get_other_eap_id(ike_sa);
+ other_host = ike_sa->get_other_host(ike_sa);
- enumerator = duplicate_ids->create_enumerator(duplicate_ids);
- while (enumerator->enumerate(enumerator, &duplicate_id))
+ enumerator = create_id_enumerator(this, me, other,
+ other_host->get_family(other_host));
+ while (enumerator->enumerate(enumerator, &id))
{
status_t status = SUCCESS;
ike_sa_t *duplicate;
- duplicate = checkout(this, duplicate_id);
+ duplicate = checkout(this, id);
if (!duplicate)
{
continue;
@@ -1520,7 +1776,6 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
}
}
enumerator->destroy(enumerator);
- duplicate_ids->destroy_offset(duplicate_ids, offsetof(ike_sa_id_t, destroy));
/* reset thread's current IKE_SA after checkin */
charon->bus->set_sa(charon->bus, ike_sa);
return cancel;
@@ -1530,7 +1785,7 @@ METHOD(ike_sa_manager_t, has_contact, bool,
private_ike_sa_manager_t *this, identification_t *me,
identification_t *other, int family)
{
- linked_list_t *list;
+ table_item_t *item;
u_int row, segment;
rwlock_t *lock;
bool found = FALSE;
@@ -1538,16 +1793,17 @@ METHOD(ike_sa_manager_t, has_contact, bool,
row = chunk_hash_inc(other->get_encoding(other),
chunk_hash(me->get_encoding(me))) & this->table_mask;
segment = row & this->segment_mask;
- lock = this->connected_peers_segments[segment & this->segment_mask].lock;
+ lock = this->connected_peers_segments[segment].lock;
lock->read_lock(lock);
- list = this->connected_peers_table[row];
- if (list)
+ item = this->connected_peers_table[row];
+ while (item)
{
- if (list->find_first(list, (linked_list_match_t)connected_peers_match,
- NULL, me, other, family) == SUCCESS)
+ if (connected_peers_match(item->value, me, other, family))
{
found = TRUE;
+ break;
}
+ item = item->next;
}
lock->unlock(lock);
@@ -1573,8 +1829,8 @@ METHOD(ike_sa_manager_t, get_count, u_int,
METHOD(ike_sa_manager_t, get_half_open_count, u_int,
private_ike_sa_manager_t *this, host_t *ip)
{
- linked_list_t *list;
- u_int segment, row;
+ table_item_t *item;
+ u_int row, segment;
rwlock_t *lock;
chunk_t addr;
u_int count = 0;
@@ -1584,17 +1840,19 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int,
addr = ip->get_address(ip);
row = chunk_hash(addr) & this->table_mask;
segment = row & this->segment_mask;
- lock = this->half_open_segments[segment & this->segment_mask].lock;
+ lock = this->half_open_segments[segment].lock;
lock->read_lock(lock);
- if ((list = this->half_open_table[row]) != NULL)
+ item = this->half_open_table[row];
+ while (item)
{
- half_open_t *current;
+ half_open_t *half_open = item->value;
- if (list->find_first(list, (linked_list_match_t)half_open_match,
- (void**)&current, &addr) == SUCCESS)
+ if (chunk_equals(addr, half_open->other))
{
- count = current->count;
+ count = half_open->count;
+ break;
}
+ item = item->next;
}
lock->unlock(lock);
}
@@ -1602,7 +1860,7 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int,
{
for (segment = 0; segment < this->segment_count; segment++)
{
- lock = this->half_open_segments[segment & this->segment_mask].lock;
+ lock = this->half_open_segments[segment].lock;
lock->read_lock(lock);
count += this->half_open_segments[segment].count;
lock->unlock(lock);
@@ -1651,16 +1909,18 @@ METHOD(ike_sa_manager_t, flush, void,
while (enumerator->enumerate(enumerator, &entry, &segment))
{
charon->bus->set_sa(charon->bus, entry->ike_sa);
- /* as the delete never gets processed, fire down events */
- switch (entry->ike_sa->get_state(entry->ike_sa))
- {
- case IKE_ESTABLISHED:
- case IKE_REKEYING:
- case IKE_DELETING:
- charon->bus->ike_updown(charon->bus, entry->ike_sa, FALSE);
- break;
- default:
- break;
+ if (entry->ike_sa->get_version(entry->ike_sa) == IKEV2)
+ { /* as the delete never gets processed, fire down events */
+ switch (entry->ike_sa->get_state(entry->ike_sa))
+ {
+ case IKE_ESTABLISHED:
+ case IKE_REKEYING:
+ case IKE_DELETING:
+ charon->bus->ike_updown(charon->bus, entry->ike_sa, FALSE);
+ break;
+ default:
+ break;
+ }
}
entry->ike_sa->delete(entry->ike_sa);
}
@@ -1680,6 +1940,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);
}
@@ -1698,24 +1962,22 @@ METHOD(ike_sa_manager_t, destroy, void,
{
u_int i;
- for (i = 0; i < this->table_size; i++)
- {
- DESTROY_IF(this->ike_sa_table[i]);
- DESTROY_IF(this->half_open_table[i]);
- DESTROY_IF(this->connected_peers_table[i]);
- }
+ /* these are already cleared in flush() above */
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);
}
@@ -1757,6 +2019,7 @@ ike_sa_manager_t *ike_sa_manager_create()
.check_uniqueness = _check_uniqueness,
.has_contact = _has_contact,
.create_enumerator = _create_enumerator,
+ .create_id_enumerator = _create_id_enumerator,
.checkin = _checkin,
.checkin_and_destroy = _checkin_and_destroy,
.get_count = _get_count,
@@ -1782,17 +2045,19 @@ ike_sa_manager_t *ike_sa_manager_create()
return NULL;
}
- this->table_size = get_nearest_powerof2(lib->settings->get_int(lib->settings,
- "charon.ikesa_table_size", DEFAULT_HASHTABLE_SIZE));
+ this->table_size = get_nearest_powerof2(lib->settings->get_int(
+ lib->settings, "%s.ikesa_table_size",
+ DEFAULT_HASHTABLE_SIZE, charon->name));
this->table_size = max(1, min(this->table_size, MAX_HASHTABLE_SIZE));
this->table_mask = this->table_size - 1;
- this->segment_count = get_nearest_powerof2(lib->settings->get_int(lib->settings,
- "charon.ikesa_table_segments", DEFAULT_SEGMENT_COUNT));
+ this->segment_count = get_nearest_powerof2(lib->settings->get_int(
+ lib->settings, "%s.ikesa_table_segments",
+ DEFAULT_SEGMENT_COUNT, charon->name));
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(table_item_t*));
this->segments = (segment_t*)calloc(this->segment_count, sizeof(segment_t));
for (i = 0; i < this->segment_count; i++)
{
@@ -1801,7 +2066,7 @@ ike_sa_manager_t *ike_sa_manager_create()
}
/* we use the same table parameters for the table to track half-open SAs */
- this->half_open_table = calloc(this->table_size, sizeof(linked_list_t*));
+ this->half_open_table = calloc(this->table_size, sizeof(table_item_t*));
this->half_open_segments = calloc(this->segment_count, sizeof(shareable_segment_t));
for (i = 0; i < this->segment_count; i++)
{
@@ -1810,7 +2075,7 @@ ike_sa_manager_t *ike_sa_manager_create()
}
/* also for the hash table used for duplicate tests */
- this->connected_peers_table = calloc(this->table_size, sizeof(linked_list_t*));
+ this->connected_peers_table = calloc(this->table_size, sizeof(table_item_t*));
this->connected_peers_segments = calloc(this->segment_count, sizeof(shareable_segment_t));
for (i = 0; i < this->segment_count; i++)
{
@@ -1818,7 +2083,16 @@ 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(table_item_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);
+ "%s.reuse_ikesa", TRUE, charon->name);
return &this->public;
}