- finish functionality of ike_sa_manager
[strongswan.git] / Source / charon / ike_sa_manager.c
index 3fbe501..dac03c2 100644 (file)
@@ -1,8 +1,8 @@
 /**
  * @file ike_sa_manager.c
- * 
+ *
  * @brief Central point for managing IKE-SAs (creation, locking, deleting...)
- * 
+ *
  */
 
 /*
@@ -23,6 +23,7 @@
 #include <pthread.h>
 #include <string.h>
 
+#include "allocator.h"
 #include "ike_sa_manager.h"
 #include "linked_list.h"
 #include "ike_sa_id.h"
@@ -33,7 +34,7 @@
 typedef struct ike_sa_entry_s ike_sa_entry_t;
 struct ike_sa_entry_s {
        /**
-        * destructor, recursivly destroys ike_sa
+        * destructor, also destroys ike_sa
         */
        status_t (*destroy) (ike_sa_entry_t *this);
        /**
@@ -49,42 +50,54 @@ struct ike_sa_entry_s {
         */
        bool checked_out;
        /**
+        * does this SA let new treads in?
+        */
+       bool driveout_new_threads;
+       /**
+        * does this SA drives out new threads?
+        */
+       bool driveout_waiting_threads;;
+       /**
         * identifiaction of ike_sa (SPIs)
         */
-       ike_sa_id_t *ike_sa_id; 
+       ike_sa_id_t *ike_sa_id;
        /**
         * the contained ike_sa
         */
        ike_sa_t *ike_sa;
 };
 
+/**
+ * @see ike_sa_entry_t.destroy
+ */
 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);
+       allocator_free(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");
-       
+       ike_sa_entry_t *this = allocator_alloc_thing(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->checked_out = FALSE;
+       this->driveout_new_threads = FALSE;
+       this->driveout_waiting_threads = FALSE;
        this->ike_sa_id = ike_sa_id;
        this->ike_sa = ike_sa_create(ike_sa_id);
        return this;
@@ -99,12 +112,12 @@ 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,
@@ -113,10 +126,10 @@ struct private_ike_sa_manager_s {
         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
@@ -126,26 +139,31 @@ struct private_ike_sa_manager_s {
         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 ike_sa                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 
+         * @brief delete an entry from the linked list
+         *
+         * @param this                 the ike_sa_manager containing the list
+         * @param entry                entry to delete
+         * @return                             SUCCESS when found,
+         *                                     NOT_FOUND when no such ike_sa_id in list
          */
         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
          */
@@ -153,14 +171,14 @@ struct private_ike_sa_manager_s {
         /**
          * Next SPI, needed for incremental creation of SPIs
          */
-        spi_t next_spi;        
+        spi_t next_spi;
 };
 
 
 /**
- * @see private_ike_sa_manager_t.get_ike_sa_entry
+ * @see private_ike_sa_manager_t.get_entry_by_id
  */
-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)
+static status_t get_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;
@@ -183,9 +201,9 @@ static status_t get_ike_sa_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id
 }
 
 /**
- * @see private_ike_sa_manager_t.get_ike_sa_entry_by_sa
+ * @see private_ike_sa_manager_t.get_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)
+static status_t get_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;
@@ -194,7 +212,7 @@ static status_t get_ike_sa_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t
        {
                ike_sa_entry_t *current;
                iterator->current(iterator, (void**)&current);
-               if (current->ike_sa == ike_sa) 
+               if (current->ike_sa == ike_sa)
                {
                        *entry = current;
                        iterator->destroy(iterator);
@@ -205,6 +223,30 @@ static status_t get_ike_sa_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t
        return NOT_FOUND;
 }
 
+/**
+ * @see private_ike_sa_manager_t.delete_entry
+ */
+static status_t delete_entry(private_ike_sa_manager_t *this, 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**)&current);
+               if (current == entry) 
+               {
+                       list->remove(list, iterator);
+                       entry->destroy(entry);
+                       iterator->destroy(iterator);
+                       return SUCCESS;
+               }
+       }
+       iterator->destroy(iterator);
+       return NOT_FOUND;       
+}
+
 
 /**
  * @see private_ike_sa_manager_t.get_next_spi
@@ -229,47 +271,64 @@ static status_t get_next_spi(private_ike_sa_manager_t *this, spi_t *spi)
 /**
  * @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)
+static status_t checkout(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) 
+
+       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) 
+                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--;
+                       /* can we give this out to new requesters? */
+                       if (entry->driveout_new_threads)
+                       {
+                               retval = NOT_FOUND;
+                       }
+                       else
+                       {
+                               /* is this IKE_SA already checked out ?? 
+                                * are we welcome to get this SA ? */
+                               while (entry->checked_out && !entry->driveout_waiting_threads)  
+                               { 
+                                       /* 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--;
+                               }
+                               /* hm, a deletion request forbids us to get this SA, go home */
+                               if (entry->driveout_waiting_threads)
+                               {
+                                       /* we must signal here, others are interested that we leave */
+                                       pthread_cond_signal(&(entry->condvar));
+                                       retval = NOT_FOUND;
+                               }
+                               else
+                               {
+                                       /* 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; 
+                               }
                        }
-                       /* 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;
                 }
@@ -286,22 +345,22 @@ static status_t checkout_ike_sa(private_ike_sa_manager_t *this, ike_sa_id_t *ike
                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;
        }
@@ -309,34 +368,35 @@ static status_t checkout_ike_sa(private_ike_sa_manager_t *this, ike_sa_id_t *ike
        {
                /* 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);
                
+               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 
+       else
        {
                /* responder set, initiator not: here is something seriously wrong! */
-               
+
                /* DON'T use return, we must unlock the mutex! */
                retval = INVALID_ARG;
        }
@@ -346,9 +406,9 @@ static status_t checkout_ike_sa(private_ike_sa_manager_t *this, ike_sa_id_t *ike
        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 
+static status_t checkin(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...
@@ -358,20 +418,19 @@ static status_t checkin_ike_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
        
        pthread_mutex_lock(&(this->mutex));
 
-
        /* look for the entry */
-       if (this->get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) 
+       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; 
+               retval = SUCCESS;
        }
        else
        {
-               /* this SA is no more, this REALLY should not happen */ 
+               /* this SA is no more, this REALLY should not happen */
                retval = NOT_FOUND;
        }
        pthread_mutex_unlock(&(this->mutex));
@@ -380,110 +439,80 @@ static status_t checkin_ike_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
 
 
 
-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. 
+static status_t checkin_and_delete(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.  
+        * 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) 
+
+       if (this->get_entry_by_sa(this, ike_sa, &entry) == SUCCESS)
        {
+               /* mark it, so now new threads can acquire this SA */
+               entry->driveout_new_threads = TRUE;
+               /* additionaly, drive out waiting threads */
+               entry->driveout_waiting_threads = TRUE;
+
                /* wait until all workers have done their work */
                while (entry->waiting_threads)
                {
-                       /* wake up all */
+                       /* let the other threads do some work*/
                        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));           
+                       pthread_cond_wait(&(entry->condvar), &(this->mutex));
                }
-               
                /* ok, we are alone now, no threads waiting in the entry's condvar */
-               entry->destroy(entry);
+               this->delete_entry(this, 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. 
+static status_t delete(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.  
+        * 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) 
+
+       if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
        {
+               /* mark it, so now new threads can acquire this SA */
+               entry->driveout_new_threads = TRUE;
+
                /* 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));           
+                       pthread_cond_wait(&(entry->condvar), &(this->mutex));
                }
-               
                /* ok, we are alone now, no threads waiting in the entry's condvar */
-               entry->destroy(entry);
+               this->delete_entry(this, entry);
                retval = SUCCESS;
        }
        else
        {
                retval = NOT_FOUND;
        }
-               
+
        pthread_mutex_unlock(&(this->mutex));
        return retval;
 }
@@ -493,18 +522,48 @@ 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;
+       
+       pthread_mutex_lock(&(this->mutex));
+       
+       /* Step 1: drive out all waiting threads  */
        list->create_iterator(list, &iterator, TRUE);
        while (iterator->has_next(iterator))
        {
                ike_sa_entry_t *entry;
                iterator->current(iterator, (void**)&entry);
-               entry->destroy(entry);
+               /* do not accept new threads, drive out waiting threads */
+               entry->driveout_new_threads = TRUE;
+               entry->driveout_waiting_threads = TRUE; 
+       }
+       /* Step 2: wait until all are gone */
+       iterator->reset(iterator);
+       while (iterator->has_next(iterator))
+       {
+               ike_sa_entry_t *entry;
+               iterator->current(iterator, (void**)&entry);
+               while (entry->waiting_threads)
+               {
+                       /* wake up all */
+                       pthread_cond_signal(&(entry->condvar));
+                       /* go sleeping until they are gone */
+                       pthread_cond_wait(&(entry->condvar), &(this->mutex));           
+               }
+       }
+       /* Step 3: delete all entries */
+       iterator->reset(iterator);
+       while (iterator->has_next(iterator))
+       {
+               ike_sa_entry_t *entry;
+               iterator->current(iterator, (void**)&entry);
+               this->delete_entry(this, entry);
        }
        iterator->destroy(iterator);
-       
+
        list->destroy(list);
        
-       pfree(this);
+       pthread_mutex_unlock(&(this->mutex));
+
+       allocator_free(this);
 
        return SUCCESS;
 }
@@ -512,31 +571,32 @@ static status_t destroy(private_ike_sa_manager_t *this)
 
 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");
-       
+       private_ike_sa_manager_t *this = allocator_alloc_thing(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;
-       
+       this->public.checkout = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t *sa_id, ike_sa_t **sa))checkout;
+       this->public.checkin = (status_t(*)(ike_sa_manager_t*, ike_sa_t *sa))checkin;
+       this->public.delete = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t *sa_id))delete;
+       this->public.checkin_and_delete = (status_t(*)(ike_sa_manager_t*, ike_sa_t *ike_sa))checkin_and_delete;
+
        /* 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->get_entry_by_sa = get_entry_by_sa;
+       this->get_entry_by_id = get_entry_by_id;
+       this->delete_entry = delete_entry;
+
        this->list = linked_list_create();
        if (this->list == NULL)
        {
-               pfree(this);
-               return NULL;    
+               allocator_free(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;
 }