From 472217f114aed3671d408dbc7cf991497d74d113 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 8 Nov 2005 16:24:18 +0000 Subject: [PATCH] - untested implementation of ike_sa_manager --- Source/charon/ike_sa.c | 9 + Source/charon/ike_sa.h | 8 + Source/charon/ike_sa_id.c | 26 +- Source/charon/ike_sa_id.h | 21 ++ Source/charon/ike_sa_manager.c | 542 +++++++++++++++++++++++++++++++++ Source/charon/ike_sa_manager.h | 65 ++++ 6 files changed, 667 insertions(+), 4 deletions(-) create mode 100644 Source/charon/ike_sa_manager.c create mode 100644 Source/charon/ike_sa_manager.h diff --git a/Source/charon/ike_sa.c b/Source/charon/ike_sa.c index 32c833cbe..a45e7d90f 100644 --- a/Source/charon/ike_sa.c +++ b/Source/charon/ike_sa.c @@ -113,6 +113,14 @@ static status_t process_configuration (private_ike_sa_t *this,configuration_t *c return SUCCESS; } +/** + * @brief implements function private_ike_sa_t.get_id + */ +static ike_sa_id_t* get_id(private_ike_sa_t *this) +{ + return this->ike_sa_id; +} + /** * @brief implements function destroy of private_ike_sa_t */ @@ -147,6 +155,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) /* Public functions */ this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message; this->public.process_configuration = (status_t(*)(ike_sa_t*, configuration_t*)) process_configuration; + this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id; this->public.destroy = (status_t(*)(ike_sa_t*))destroy; diff --git a/Source/charon/ike_sa.h b/Source/charon/ike_sa.h index 75f09a74c..2c925c0ac 100644 --- a/Source/charon/ike_sa.h +++ b/Source/charon/ike_sa.h @@ -56,6 +56,14 @@ struct ike_sa_s { * @return SUCCESSFUL if succeeded, FAILED otherwise */ status_t (*process_configuration) (ike_sa_t *this,configuration_t *configuration); + + /** + * @brief Get the id of the SA + * + * @param this ike_sa_t-message_t object object + * @return ike_sa's ike_sa_id_t + */ + ike_sa_id_t* (*get_id) (ike_sa_t *this); /** * @brief Destroys a ike_sa_t object diff --git a/Source/charon/ike_sa_id.c b/Source/charon/ike_sa_id.c index c8065e875..56cacced0 100644 --- a/Source/charon/ike_sa_id.c +++ b/Source/charon/ike_sa_id.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -65,14 +66,16 @@ struct private_ike_sa_id_s { */ static status_t set_responder_spi (private_ike_sa_id_t *this, spi_t responder_spi) { - if (this == NULL) - { - return FAILED; - } this->responder_spi = responder_spi; return SUCCESS; } +static status_t set_initiator_spi(private_ike_sa_id_t *this, spi_t initiator_spi) +{ + this->initiator_spi = initiator_spi; + return SUCCESS; +} + /** * @brief implements function initiator_spi_is_set of ike_sa_id_t */ @@ -133,6 +136,19 @@ status_t replace_values (private_ike_sa_id_t *this, private_ike_sa_id_t *other) return SUCCESS; } +/** + * @brief implements function ike_sa_id_t.get_values + */ +static status_t get_values(private_ike_sa_id_t *this, spi_t *initiator, spi_t *responder, ike_sa_role_t *role) +{ + memcpy(initiator, &(this->initiator_spi), sizeof(initiator)); + memcpy(responder, &(this->responder_spi), sizeof(responder)); + *role = this->role; + + return SUCCESS; +} + + /** * @brief implements function clone of ike_sa_id_t */ @@ -174,10 +190,12 @@ ike_sa_id_t * ike_sa_id_create(spi_t initiator_spi, spi_t responder_spi, ike_sa_ /* Public functions */ this->public.set_responder_spi = (status_t(*)(ike_sa_id_t*,spi_t)) set_responder_spi; + this->public.set_initiator_spi = (status_t(*)(ike_sa_id_t*,spi_t)) set_initiator_spi; this->public.responder_spi_is_set = (bool(*)(ike_sa_id_t*)) responder_spi_is_set; this->public.initiator_spi_is_set = (bool(*)(ike_sa_id_t*)) initiator_spi_is_set; this->public.equals = (status_t(*)(ike_sa_id_t*,ike_sa_id_t*,bool*)) equals; this->public.replace_values = (status_t(*)(ike_sa_id_t*,ike_sa_id_t*)) replace_values; + this->public.get_values = (status_t(*)(ike_sa_id_t*,spi_t*,spi_t*,ike_sa_role_t*)) get_values; this->public.clone = (status_t(*)(ike_sa_id_t*,ike_sa_id_t**)) clone; this->public.destroy = (status_t(*)(ike_sa_id_t*))destroy; diff --git a/Source/charon/ike_sa_id.h b/Source/charon/ike_sa_id.h index 3b83672be..eaaa8112b 100644 --- a/Source/charon/ike_sa_id.h +++ b/Source/charon/ike_sa_id.h @@ -51,6 +51,16 @@ struct ike_sa_id_s { * @return SUCCESSFUL if succeeded, FAILED otherwise */ status_t (*set_responder_spi) (ike_sa_id_t *this, spi_t responder_spi); + + /** + * @brief Sets the SPI of the initiator. + * + * + * @param this ike_sa_id_t-object + * @param initiator_spi SPI to set + * @return SUCCESSFUL if succeeded, FAILED otherwise + */ + status_t (*set_initiator_spi) (ike_sa_id_t *this, spi_t initiator_spi); /** * @brief Returns TRUE if the initiator spi is set (not zero) @@ -88,6 +98,17 @@ struct ike_sa_id_s { */ status_t (*replace_values) (ike_sa_id_t *this,ike_sa_id_t *other); + /** + * @brief get spis and role of an ike_sa_id + * + * @param this ike_sa_id_t-object + * @param initiator address to write initator spi + * @param responder address to write responder spi + * @param role address to write role + * @return SUCCESSFUL if succeeded, FAILED otherwise + */ + status_t (*get_values) (ike_sa_id_t *this, spi_t *initiator, spi_t *responder, ike_sa_role_t *role); + /** * @brief Clones a given ike_sa_id_t-object * diff --git a/Source/charon/ike_sa_manager.c b/Source/charon/ike_sa_manager.c new file mode 100644 index 000000000..3fbe501c2 --- /dev/null +++ b/Source/charon/ike_sa_manager.c @@ -0,0 +1,542 @@ +/** + * @file ike_sa_manager.c + * + * @brief Central point for managing IKE-SAs (creation, locking, deleting...) + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include + +#include "ike_sa_manager.h" +#include "linked_list.h" +#include "ike_sa_id.h" + +/** + * @brief An entry in the linked list, contains IKE_SA, locking and lookup data. + */ +typedef struct ike_sa_entry_s ike_sa_entry_t; +struct ike_sa_entry_s { + /** + * destructor, recursivly destroys ike_sa + */ + status_t (*destroy) (ike_sa_entry_t *this); + /** + * Number of threads waiting for this ike_sa + */ + int waiting_threads; + /** + * is this SA flagged for deleting ? + */ + pthread_cond_t condvar; + /** + * is this ike_sa currently checked out? + */ + bool checked_out; + /** + * identifiaction of ike_sa (SPIs) + */ + ike_sa_id_t *ike_sa_id; + /** + * the contained ike_sa + */ + ike_sa_t *ike_sa; +}; + +static status_t ike_sa_entry_destroy(ike_sa_entry_t *this) +{ + this->ike_sa->destroy(this->ike_sa); + this->ike_sa_id->destroy(this->ike_sa_id); + pfree(this); + return SUCCESS; + +} + + +/** + * @brief creates a new entry for the ike_sa list + * + * This constructor additionaly creates a new and empty SA + * + * @param ike_sa_id the associated ike_sa_id_t, NOT cloned + * @return created entry, with ike_sa and ike_sa_id + */ +ike_sa_entry_t *ike_sa_entry_create(ike_sa_id_t *ike_sa_id) +{ + ike_sa_entry_t *this = alloc_thing(ike_sa_entry_t, "ike_sa_entry_t"); + + this->destroy = ike_sa_entry_destroy; + this->waiting_threads = 0; + pthread_cond_init(&(this->condvar), NULL); + /* we set checkout flag when we really give it out */ + this->checked_out = FALSE; + this->ike_sa_id = ike_sa_id; + this->ike_sa = ike_sa_create(ike_sa_id); + return this; +} + +/** + * Additional private members to ike_sa_manager_t + */ +typedef struct private_ike_sa_manager_s private_ike_sa_manager_t; +struct private_ike_sa_manager_s { + /** + * Public members + */ + ike_sa_manager_t public; + + /** + * @brief get next spi + * + * we give out SPIs incremental + * + * @param this the ike_sa_manager + * @param spi[out] spi will be written here + * @return SUCCESS or, + * OUT_OF_RES when we already served 2^64 SPIs ;-) + */ + status_t (*get_next_spi) (private_ike_sa_manager_t *this, spi_t *spi); + /** + * @brief find the ike_sa_entry in the list by SPIs + * + * This function simply iterates over the linked list. A hash-table + * would be more efficient when storing a lot of IKE_SAs... + * + * @param this the ike_sa_manager containing the list + * @param ike_sa_id id of the ike_sa, containing SPIs + * @param entry[out] pointer to set to the found entry + * @return SUCCESS when found, + * NOT_FOUND when no such ike_sa_id in list + */ + status_t (*get_entry_by_id) (private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_entry_t **entry); + /** + * @brief find the ike_sa_entry in the list by pointer to SA. + * + * This function simply iterates over the linked list. A hash-table + * would be more efficient when storing a lot of IKE_SAs... + * + * @param this the ike_sa_manager containing the list + * @param ike_sad pointer to the ike_sa + * @param entry[out] pointer to set to the found entry + * @return SUCCESS when found, + * NOT_FOUND when no such ike_sa_id in list + */ + status_t (*get_entry_by_sa) (private_ike_sa_manager_t *this, ike_sa_t *ike_sa, ike_sa_entry_t **entry); + /** + * @brief + */ + status_t (*delete_entry) (private_ike_sa_manager_t *this, ike_sa_entry_t *entry); + /** + * lock for exclusivly accessing the manager + */ + pthread_mutex_t mutex; + + /** + * Linked list with entries for the ike_sa + */ + linked_list_t *list; + /** + * Next SPI, needed for incremental creation of SPIs + */ + spi_t next_spi; +}; + + +/** + * @see private_ike_sa_manager_t.get_ike_sa_entry + */ +static status_t get_ike_sa_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_entry_t **entry) +{ + linked_list_t *list = this->list; + linked_list_iterator_t *iterator; + list->create_iterator(list, &iterator, TRUE); + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *current; + bool are_equal = FALSE; + iterator->current(iterator, (void**)¤t); + current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id, &are_equal); + if (are_equal) + { + *entry = current; + iterator->destroy(iterator); + return SUCCESS; + } + } + iterator->destroy(iterator); + return NOT_FOUND; +} + +/** + * @see private_ike_sa_manager_t.get_ike_sa_entry_by_sa + */ +static status_t get_ike_sa_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa, ike_sa_entry_t **entry) +{ + linked_list_t *list = this->list; + linked_list_iterator_t *iterator; + list->create_iterator(list, &iterator, TRUE); + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *current; + iterator->current(iterator, (void**)¤t); + if (current->ike_sa == ike_sa) + { + *entry = current; + iterator->destroy(iterator); + return SUCCESS; + } + } + iterator->destroy(iterator); + return NOT_FOUND; +} + + +/** + * @see private_ike_sa_manager_t.get_next_spi + */ +static status_t get_next_spi(private_ike_sa_manager_t *this, spi_t *spi) +{ + this->next_spi.low ++; + if (this->next_spi.low == 0) { + /* overflow of lower int in spi */ + this->next_spi.high ++; + if (this->next_spi.high == 0) { + /* our software ran so incredible stable, we have no more + * SPIs to give away :-/. */ + return OUT_OF_RES; + } + } + *spi = this->next_spi; + return SUCCESS; +} + + +/** + * @see ike_sa_manager_s.checkout_ike_sa + */ +static status_t checkout_ike_sa(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_t **ike_sa) +{ + bool responder_spi_set; + bool initiator_spi_set; + status_t retval; + + pthread_mutex_lock(&(this->mutex)); + + responder_spi_set = ike_sa_id->responder_spi_is_set(ike_sa_id); + initiator_spi_set = ike_sa_id->initiator_spi_is_set(ike_sa_id); + + if (initiator_spi_set && responder_spi_set) + { + /* we SHOULD have an IKE_SA for these SPIs in the list, + * if not, we cant handle the request... + */ + ike_sa_entry_t *entry; + /* look for the entry */ + if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + { + /* is this IKE_SA already checked out ?? */ + while (entry->checked_out) + { + /* so wait until we can get it for us. + * we register us as waiting. + */ + entry->waiting_threads++; + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + entry->waiting_threads--; + } + /* ok, this IKE_SA is finally ours */ + entry->checked_out = TRUE; + *ike_sa = entry->ike_sa; + /* DON'T use return, we must unlock the mutex! */ + retval = SUCCESS; + + } + else + { + /* looks like there is no such IKE_SA, better luck next time... */ + + /* DON'T use return, we must unlock the mutex! */ + retval = NOT_FOUND; + } + } + else if (initiator_spi_set && !responder_spi_set) + { + /* an IKE_SA_INIT from an another endpoint, + * he is the initiator. + * For simplicity, we do NOT check for retransmitted + * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT- + * Request (even a retransmitted one) will result in a + * IKE_SA. This could be improved... + */ + spi_t responder_spi; + ike_sa_id_t *new_ike_sa_id; + ike_sa_entry_t *new_ike_sa_entry; + + /* set SPIs, we are the responder */ + ike_sa_id->clone(ike_sa_id, &new_ike_sa_id); + this->get_next_spi(this, &responder_spi); + new_ike_sa_id->set_responder_spi(new_ike_sa_id, responder_spi); + /* we also set arguments spi, so its still valid */ + ike_sa_id->set_responder_spi(ike_sa_id, responder_spi); + + /* create entry */ + new_ike_sa_entry = ike_sa_entry_create(new_ike_sa_id); + this->list->insert_last(this->list, new_ike_sa_entry); + + /* check ike_sa out */ + new_ike_sa_entry->checked_out = TRUE; + *ike_sa = new_ike_sa_entry->ike_sa; + + /* DON'T use return, we must unlock the mutex! */ + retval = SUCCESS; + } + else if (!initiator_spi_set && !responder_spi_set) + { + /* creation of an IKE_SA from local site, + * we are the initiator! + */ + spi_t initiator_spi, responder_spi; + ike_sa_id_t *new_ike_sa_id; + ike_sa_entry_t *new_ike_sa_entry; + + /* set SPIs */ + memset(&responder_spi, 0, sizeof(spi_t)); + this->get_next_spi(this, &initiator_spi); + + /* we also set arguments SPI, so its still valid */ + ike_sa_id->set_initiator_spi(ike_sa_id, initiator_spi); + + /* create entry */ + new_ike_sa_id = ike_sa_id_create(initiator_spi, responder_spi, INITIATOR); + new_ike_sa_entry = ike_sa_entry_create(new_ike_sa_id); + this->list->insert_last(this->list, new_ike_sa_entry); + + /* check ike_sa out */ + new_ike_sa_entry->checked_out = TRUE; + *ike_sa = new_ike_sa_entry->ike_sa; + + /* DON'T use return, we must unlock the mutex! */ + retval = SUCCESS; + } + else + { + /* responder set, initiator not: here is something seriously wrong! */ + + /* DON'T use return, we must unlock the mutex! */ + retval = INVALID_ARG; + } + + pthread_mutex_unlock(&(this->mutex)); + /* OK, unlocked... */ + return retval; +} + +static status_t checkin_ike_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +{ + /* to check the SA back in, we look for the pointer of the ike_sa + * in all entries. + * We can't search by SPI's since the MAY have changed (e.g. on reception + * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary... + */ + status_t retval; + ike_sa_entry_t *entry; + + pthread_mutex_lock(&(this->mutex)); + + + /* look for the entry */ + if (this->get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) + { + /* ike_sa_id must be updated */ + entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa)); + /* signal waiting threads */ + entry->checked_out = FALSE; + pthread_cond_signal(&(entry->condvar)); + retval = SUCCESS; + } + else + { + /* this SA is no more, this REALLY should not happen */ + retval = NOT_FOUND; + } + pthread_mutex_unlock(&(this->mutex)); + return retval; +} + + + +static status_t delete_ike_sa_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +{ + /* deletion is a bit complex, we must garant that no thread is waiting for + * this SA. + * We take this SA from the list, and start signaling while threads + * are in the condvar. + */ + linked_list_t *list = this->list; + linked_list_iterator_t *iterator; + ike_sa_entry_t *entry; + bool found = FALSE; + status_t retval; + + pthread_mutex_lock(&(this->mutex)); + + /* remove SA from list */ + list->create_iterator(list, &iterator, TRUE); + while (iterator->has_next(iterator)) + { + iterator->current(iterator, (void**)&entry); + if (entry->ike_sa == ike_sa) + { + list->remove(list, iterator); + found = TRUE; + break; + } + } + iterator->destroy(iterator); + + if (found) + { + /* wait until all workers have done their work */ + while (entry->waiting_threads) + { + /* wake up all */ + pthread_cond_signal(&(entry->condvar)); + /* and the nice thing, they will wake us again when their work is done */ + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + } + + /* ok, we are alone now, no threads waiting in the entry's condvar */ + entry->destroy(entry); + retval = SUCCESS; + } + else + { + retval = NOT_FOUND; + } + + pthread_mutex_unlock(&(this->mutex)); + return retval; +} + +static status_t delete_ike_sa_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) +{ + /* deletion is a bit complex, we must garant that no thread is waiting for + * this SA. + * We take this SA from the list, and start signaling while threads + * are in the condvar. + */ + linked_list_t *list = this->list; + linked_list_iterator_t *iterator; + ike_sa_entry_t *entry; + bool found = FALSE; + status_t retval; + + pthread_mutex_lock(&(this->mutex)); + + /* remove SA from list */ + list->create_iterator(list, &iterator, TRUE); + while (iterator->has_next(iterator)) + { + bool are_equal = FALSE; + iterator->current(iterator, (void**)&entry); + entry->ike_sa_id->equals(entry->ike_sa_id, ike_sa_id, &are_equal); + if (are_equal) + { + list->remove(list, iterator); + found = TRUE; + break; + } + } + iterator->destroy(iterator); + + if (found) + { + /* wait until all workers have done their work */ + while (entry->waiting_threads) + { + /* wake up all */ + pthread_cond_signal(&(entry->condvar)); + /* and the nice thing, they will wake us again when their work is done */ + pthread_cond_wait(&(entry->condvar), &(this->mutex)); + } + + /* ok, we are alone now, no threads waiting in the entry's condvar */ + entry->destroy(entry); + retval = SUCCESS; + } + else + { + retval = NOT_FOUND; + } + + pthread_mutex_unlock(&(this->mutex)); + return retval; +} + +static status_t destroy(private_ike_sa_manager_t *this) +{ + /* destroy all list entries */ + linked_list_t *list = this->list; + linked_list_iterator_t *iterator; + list->create_iterator(list, &iterator, TRUE); + while (iterator->has_next(iterator)) + { + ike_sa_entry_t *entry; + iterator->current(iterator, (void**)&entry); + entry->destroy(entry); + } + iterator->destroy(iterator); + + list->destroy(list); + + pfree(this); + + return SUCCESS; +} + + +ike_sa_manager_t *ike_sa_manager_create() +{ + private_ike_sa_manager_t *this = alloc_thing(private_ike_sa_manager_t, "private_ike_sa_manager_t"); + + /* assign public functions */ + this->public.destroy = (status_t(*)(ike_sa_manager_t*))destroy; + this->public.checkout_ike_sa = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t *sa_id, ike_sa_t **sa))checkout_ike_sa; + this->public.checkin_ike_sa = (status_t(*)(ike_sa_manager_t*, ike_sa_t *sa))checkin_ike_sa; + this->public.delete_ike_sa_by_id = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t *sa_id))delete_ike_sa_by_id; + this->public.delete_ike_sa_by_sa = (status_t(*)(ike_sa_manager_t*, ike_sa_t *ike_sa))delete_ike_sa_by_sa; + + /* initialize private data */ + this->get_next_spi = get_next_spi; + this->get_entry_by_sa = get_ike_sa_entry_by_sa; + this->get_entry_by_id = get_ike_sa_entry_by_id; + + this->list = linked_list_create(); + if (this->list == NULL) + { + pfree(this); + return NULL; + } + + pthread_mutex_init(&(this->mutex), NULL); + + this->next_spi.low = 1; + this->next_spi.high = 0; + + return (ike_sa_manager_t*)this; +} diff --git a/Source/charon/ike_sa_manager.h b/Source/charon/ike_sa_manager.h new file mode 100644 index 000000000..ffd9d95cf --- /dev/null +++ b/Source/charon/ike_sa_manager.h @@ -0,0 +1,65 @@ +/** + * @file ike_sa_manager.h + * + * @brief Central point for managing IKE-SAs (creation, locking, deleting...) + * + */ + +/* + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IKE_SA_MANAGER_H_ +#define IKE_SA_MANAGER_H_ + +#include "types.h" +#include "ike_sa.h" + + +/** + * @brief The IKE_SA-Manager manages the IKE_SAs ;-). + * + * To avoid access from multiple threads, IKE_SAs must be checked out from + * the manager, and checked back in after usage. + * The manager also handles deletion of SAs. + * + */ +typedef struct ike_sa_manager_s ike_sa_manager_t; +struct ike_sa_manager_s { + + status_t (*checkout_ike_sa) (ike_sa_manager_t*, ike_sa_id_t *sa_id, ike_sa_t **ike_sa); + status_t (*checkin_ike_sa) (ike_sa_manager_t*, ike_sa_t *ike_sa); + status_t (*delete_ike_sa_by_id) (ike_sa_manager_t*, ike_sa_id_t *ike_sa_id); + status_t (*delete_ike_sa_by_sa) (ike_sa_manager_t*, ike_sa_t *ike_sa); + + /** + * @brief Destroys a linked_list object + * + * @warning all items are removed before deleting the list. The + * associated values are NOT destroyed. + * Destroying an list which is not empty may cause + * memory leaks! + * + * @param linked_list calling object + * @returns SUCCESS if succeeded, FAILED otherwise + */ + status_t (*destroy) (ike_sa_manager_t *isam); +}; + +/** + * @brief Create the IKE_SA-Manager + */ +ike_sa_manager_t *ike_sa_manager_create(); + +#endif /*IKE_SA_MANAGER_H_*/