2 * @file ike_sa_manager.c
4 * @brief Central point for managing IKE-SAs (creation, locking, deleting...)
9 * Copyright (C) 2005 Jan Hutter, Martin Willi
10 * Hochschule fuer Technik Rapperswil
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26 #include "ike_sa_manager.h"
29 #include <sa/ike_sa_id.h>
30 #include <utils/allocator.h>
31 #include <utils/logger.h>
32 #include <utils/logger_manager.h>
33 #include <utils/linked_list.h>
35 typedef struct ike_sa_entry_t ike_sa_entry_t
;
38 * @brief An entry in the linked list, contains IKE_SA, locking and lookup data.
40 struct ike_sa_entry_t
{
42 * destructor, also destroys ike_sa
44 status_t (*destroy
) (ike_sa_entry_t
*this);
46 * Number of threads waiting for this ike_sa
50 * condvar where threads can wait until it's free again
52 pthread_cond_t condvar
;
54 * is this ike_sa currently checked out?
58 * Does this SA drives out new threads?
60 bool driveout_new_threads
;
62 * Does this SA drives out waiting threads?
64 bool driveout_waiting_threads
;
66 * identifiaction of ike_sa (SPIs)
68 ike_sa_id_t
*ike_sa_id
;
70 * the contained ike_sa
76 * @see ike_sa_entry_t.destroy
78 static status_t
ike_sa_entry_destroy(ike_sa_entry_t
*this)
80 /* also destroy IKE SA */
81 this->ike_sa
->destroy(this->ike_sa
);
82 this->ike_sa_id
->destroy(this->ike_sa_id
);
88 * @brief creates a new entry for the ike_sa list
90 * This constructor additionaly creates a new and empty SA
92 * @param ike_sa_id the associated ike_sa_id_t, will be cloned
93 * @return created entry, with ike_sa and ike_sa_id
95 static ike_sa_entry_t
*ike_sa_entry_create(ike_sa_id_t
*ike_sa_id
)
97 ike_sa_entry_t
*this = allocator_alloc_thing(ike_sa_entry_t
);
104 /* destroy function */
105 this->destroy
= ike_sa_entry_destroy
;
107 this->waiting_threads
= 0;
108 pthread_cond_init(&(this->condvar
), NULL
);
110 /* we set checkout flag when we really give it out */
111 this->checked_out
= FALSE
;
112 this->driveout_new_threads
= FALSE
;
113 this->driveout_waiting_threads
= FALSE
;
115 /* ike_sa_id is always cloned */
116 ike_sa_id
->clone(ike_sa_id
, &(this->ike_sa_id
));
117 if (this->ike_sa_id
== NULL
)
119 allocator_free(this);
123 /* create new ike_sa */
124 this->ike_sa
= ike_sa_create(ike_sa_id
);
125 if (this->ike_sa
== NULL
)
127 this->ike_sa_id
->destroy(this->ike_sa_id
);
128 allocator_free(this);
135 typedef struct private_ike_sa_manager_t private_ike_sa_manager_t
;
138 * Additional private members to ike_sa_manager_t
140 struct private_ike_sa_manager_t
{
144 ike_sa_manager_t
public;
147 * @brief get next spi
149 * we give out SPIs incremental
151 * @param this the ike_sa_manager
152 * @param spi[out] spi will be written here
153 * @return SUCCESS or,
154 * OUT_OF_RES when we already served 2^64 SPIs ;-)
156 status_t (*get_next_spi
) (private_ike_sa_manager_t
*this, u_int64_t
*spi
);
159 * @brief find the ike_sa_entry in the list by SPIs
161 * This function simply iterates over the linked list. A hash-table
162 * would be more efficient when storing a lot of IKE_SAs...
164 * @param this the ike_sa_manager containing the list
165 * @param ike_sa_id id of the ike_sa, containing SPIs
166 * @param entry[out] pointer to set to the found entry
168 * - SUCCESS when found,
169 * - NOT_FOUND when no such ike_sa_id in list
172 status_t (*get_entry_by_id
) (private_ike_sa_manager_t
*this, ike_sa_id_t
*ike_sa_id
, ike_sa_entry_t
**entry
);
175 * @brief find the ike_sa_entry in the list by pointer to SA.
177 * This function simply iterates over the linked list. A hash-table
178 * would be more efficient when storing a lot of IKE_SAs...
180 * @param this the ike_sa_manager containing the list
181 * @param ike_sa pointer to the ike_sa
182 * @param entry[out] pointer to set to the found entry
184 * - SUCCESS when found,
185 * - NOT_FOUND when no such ike_sa_id in list
188 status_t (*get_entry_by_sa
) (private_ike_sa_manager_t
*this, ike_sa_t
*ike_sa
, ike_sa_entry_t
**entry
);
191 * @brief delete an entry from the linked list
193 * @param this the ike_sa_manager containing the list
194 * @param entry entry to delete
196 * - SUCCESS when found,
197 * - NOT_FOUND when no such ike_sa_id in list
199 status_t (*delete_entry
) (private_ike_sa_manager_t
*this, ike_sa_entry_t
*entry
);
202 * lock for exclusivly accessing the manager
204 pthread_mutex_t mutex
;
207 * Logger used for this IKE SA Manager
212 * Linked list with entries for the ike_sa
214 linked_list_t
*ike_sa_list
;
217 * Next SPI, needed for incremental creation of SPIs
224 * @see private_ike_sa_manager_t.get_entry_by_id
226 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
)
228 linked_list_t
*list
= this->ike_sa_list
;
229 linked_list_iterator_t
*iterator
;
232 /* create iterator over list of ike_sa's */
233 status
= list
->create_iterator(list
, &iterator
, TRUE
);
234 if (status
!= SUCCESS
)
236 this->logger
->log(this->logger
,ERROR
,"Fatal Error: could not create iterator: %s",mapping_find(status_m
,status
));
245 while (iterator
->has_next(iterator
))
247 ike_sa_entry_t
*current
;
248 bool are_equal
= FALSE
;
250 iterator
->current(iterator
, (void**)¤t
);
251 if (current
->ike_sa_id
->get_responder_spi(current
->ike_sa_id
) == 0) {
252 /* seems to be a half ready ike_sa */
253 if ((current
->ike_sa_id
->get_initiator_spi(current
->ike_sa_id
) == ike_sa_id
->get_initiator_spi(ike_sa_id
))
254 && (ike_sa_id
->is_initiator(ike_sa_id
) == current
->ike_sa_id
->is_initiator(current
->ike_sa_id
)))
256 this->logger
->log(this->logger
,CONTROL
| MOST
,"Found entry by initiator spi %d",ike_sa_id
->get_initiator_spi(ike_sa_id
));
262 current
->ike_sa_id
->equals(current
->ike_sa_id
, ike_sa_id
, &are_equal
);
265 this->logger
->log(this->logger
,CONTROL
| MOST
,"Found entry by full ID");
272 iterator
->destroy(iterator
);
277 * @see private_ike_sa_manager_t.get_entry_by_sa
279 static status_t
get_entry_by_sa(private_ike_sa_manager_t
*this, ike_sa_t
*ike_sa
, ike_sa_entry_t
**entry
)
281 linked_list_t
*list
= this->ike_sa_list
;
282 linked_list_iterator_t
*iterator
;
285 status
= list
->create_iterator(list
, &iterator
, TRUE
);
286 if (status
!= SUCCESS
)
288 this->logger
->log(this->logger
,ERROR
,"Fatal Error: could not create iterator: %s",mapping_find(status_m
,status
));
295 while (iterator
->has_next(iterator
))
297 ike_sa_entry_t
*current
;
298 iterator
->current(iterator
, (void**)¤t
);
299 /* only pointers are compared */
300 if (current
->ike_sa
== ike_sa
)
302 this->logger
->log(this->logger
,CONTROL
| MOST
,"Found entry by pointer");
308 iterator
->destroy(iterator
);
313 * @see private_ike_sa_manager_s.delete_entry
315 static status_t
delete_entry(private_ike_sa_manager_t
*this, ike_sa_entry_t
*entry
)
317 linked_list_t
*list
= this->ike_sa_list
;
318 linked_list_iterator_t
*iterator
;
321 status
= list
->create_iterator(list
, &iterator
, TRUE
);
323 if (status
!= SUCCESS
)
325 this->logger
->log(this->logger
,ERROR
,"Fatal Error: could not create iterator: %s",mapping_find(status_m
,status
));
331 while (iterator
->has_next(iterator
))
333 ike_sa_entry_t
*current
;
334 iterator
->current(iterator
, (void**)¤t
);
335 if (current
== entry
)
337 this->logger
->log(this->logger
,CONTROL
| MOST
,"Found entry by pointer. Going to delete it.");
338 iterator
->remove(iterator
);
339 entry
->destroy(entry
);
344 iterator
->destroy(iterator
);
350 * @see private_ike_sa_manager_t.get_next_spi
352 static status_t
get_next_spi(private_ike_sa_manager_t
*this, u_int64_t
*spi
)
355 if (this->next_spi
== 0) {
356 /* our software ran so incredible stable, we have no more
357 * SPIs to give away :-/. */
358 this->logger
->log(this->logger
,CONTROL
| MOST
,"No more SPI values available. WOW!");
361 *spi
= this->next_spi
;
366 * @see ike_sa_manager_s.checkout_ike_sa
368 static status_t
checkout(private_ike_sa_manager_t
*this, ike_sa_id_t
*ike_sa_id
, ike_sa_t
**ike_sa
)
370 bool responder_spi_set
;
371 bool initiator_spi_set
;
374 /* each access is locked */
375 pthread_mutex_lock(&(this->mutex
));
377 responder_spi_set
= (FALSE
!= ike_sa_id
->get_responder_spi(ike_sa_id
));
378 initiator_spi_set
= (FALSE
!= ike_sa_id
->get_initiator_spi(ike_sa_id
));
380 if (initiator_spi_set
&& responder_spi_set
)
382 /* we SHOULD have an IKE_SA for these SPIs in the list,
383 * if not, we can't handle the request...
385 ike_sa_entry_t
*entry
;
386 /* look for the entry */
387 if (this->get_entry_by_id(this, ike_sa_id
, &entry
) == SUCCESS
)
389 /* can we give this ike_sa out to new requesters?*/
390 if (entry
->driveout_new_threads
)
392 this->logger
->log(this->logger
,CONTROL
|MORE
,"Drive out new thread for existing IKE_SA");
398 /* is this IKE_SA already checked out ??
399 * are we welcome to get this SA ? */
400 while (entry
->checked_out
&& !entry
->driveout_waiting_threads
)
402 /* so wait until we can get it for us.
403 * we register us as waiting.
405 entry
->waiting_threads
++;
406 pthread_cond_wait(&(entry
->condvar
), &(this->mutex
));
407 entry
->waiting_threads
--;
410 /* hm, a deletion request forbids us to get this SA, go home */
411 if (entry
->driveout_waiting_threads
)
413 /* we must signal here, others are interested that we leave */
414 pthread_cond_signal(&(entry
->condvar
));
415 this->logger
->log(this->logger
,CONTROL
|MORE
,"Drive out waiting thread for existing IKE_SA");
420 this->logger
->log(this->logger
,CONTROL
|MOST
,"IKE SA successfully checked out");
421 /* ok, this IKE_SA is finally ours */
422 entry
->checked_out
= TRUE
;
423 *ike_sa
= entry
->ike_sa
;
424 /* DON'T use return, we must unlock the mutex! */
431 this->logger
->log(this->logger
,ERROR
| MORE
,"IKE SA not stored in known IKE_SA list");
432 /* looks like there is no such IKE_SA, better luck next time... */
433 /* DON'T use return, we must unlock the mutex! */
437 else if (initiator_spi_set
&& !responder_spi_set
)
439 /* an IKE_SA_INIT from an another endpoint,
440 * he is the initiator.
441 * For simplicity, we do NOT check for retransmitted
442 * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT-
443 * Request (even a retransmitted one) will result in a
444 * IKE_SA. This could be improved...
446 u_int64_t responder_spi
;
447 ike_sa_entry_t
*new_ike_sa_entry
;
450 /* set SPIs, we are the responder */
451 retval
= this->get_next_spi(this, &responder_spi
);
453 if (retval
== SUCCESS
)
454 { /* next SPI could be created */
456 /* we also set arguments spi, so its still valid */
457 ike_sa_id
->set_responder_spi(ike_sa_id
, responder_spi
);
460 new_ike_sa_entry
= ike_sa_entry_create(ike_sa_id
);
461 if (new_ike_sa_entry
!= NULL
)
463 retval
= this->ike_sa_list
->insert_last(this->ike_sa_list
, new_ike_sa_entry
);
464 if (retval
== SUCCESS
)
466 /* check ike_sa out */
467 this->logger
->log(this->logger
,CONTROL
| MORE
,"IKE_SA added to list of known IKE_SA's");
468 new_ike_sa_entry
->checked_out
= TRUE
;
469 *ike_sa
= new_ike_sa_entry
->ike_sa
;
471 /* DON'T use return, we must unlock the mutex! */
475 /* ike_sa_entry could not be added to list*/
476 this->logger
->log(this->logger
,ERROR
,"Fatal error: ike_sa_entry could not be added to list");
481 /* new ike_sa_entry could not be created */
482 this->logger
->log(this->logger
,ERROR
,"Fatal error: ike_sa_entry could not be created");
486 { /* next SPI could not be created */
487 this->logger
->log(this->logger
,ERROR
,"Fatal error: Next SPI could not be created");
491 else if (!initiator_spi_set
&& !responder_spi_set
)
493 /* creation of an IKE_SA from local site,
494 * we are the initiator!
496 u_int64_t initiator_spi
;
497 ike_sa_entry_t
*new_ike_sa_entry
;
499 retval
= this->get_next_spi(this, &initiator_spi
);
500 if (retval
== SUCCESS
)
502 /* we also set arguments SPI, so its still valid */
503 ike_sa_id
->set_initiator_spi(ike_sa_id
, initiator_spi
);
506 new_ike_sa_entry
= ike_sa_entry_create(ike_sa_id
);
507 if (new_ike_sa_entry
!= NULL
)
509 retval
= this->ike_sa_list
->insert_last(this->ike_sa_list
, new_ike_sa_entry
);
511 if (retval
== SUCCESS
)
513 /* check ike_sa out */
514 this->logger
->log(this->logger
,CONTROL
| MORE
,"New IKE_SA created and added to list of known IKE_SA's");
515 new_ike_sa_entry
->checked_out
= TRUE
;
516 *ike_sa
= new_ike_sa_entry
->ike_sa
;
518 /* DON'T use return, we must unlock the mutex! */
522 /* ike_sa_entry could not be added to list*/
523 this->logger
->log(this->logger
,ERROR
,"Fatal error: ike_sa_entry could not be added to list");
528 /* new ike_sa_entry could not be created */
529 this->logger
->log(this->logger
,ERROR
,"Fatal error: ike_sa_entry could not be created");
534 /* next SPI could not be created */
535 this->logger
->log(this->logger
,ERROR
,"Fatal error: Next SPI could not be created");
542 /* responder set, initiator not: here is something seriously wrong! */
543 this->logger
->log(this->logger
,ERROR
| MORE
,"Invalid IKE_SA SPI's");
544 /* DON'T use return, we must unlock the mutex! */
545 retval
= INVALID_ARG
;
548 pthread_mutex_unlock(&(this->mutex
));
549 /* OK, unlocked... */
554 * Implements ike_sa_manager_t-function checkin.
555 * @see ike_sa_manager_t.checkin.
557 static status_t
checkin(private_ike_sa_manager_t
*this, ike_sa_t
*ike_sa
)
559 /* to check the SA back in, we look for the pointer of the ike_sa
561 * We can't search by SPI's since the MAY have changed (e.g. on reception
562 * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary...
565 ike_sa_entry_t
*entry
;
567 pthread_mutex_lock(&(this->mutex
));
569 /* look for the entry */
570 if (this->get_entry_by_sa(this, ike_sa
, &entry
) == SUCCESS
)
572 /* ike_sa_id must be updated */
573 entry
->ike_sa_id
->replace_values(entry
->ike_sa_id
, ike_sa
->get_id(ike_sa
));
574 /* signal waiting threads */
575 entry
->checked_out
= FALSE
;
576 this->logger
->log(this->logger
,CONTROL
| MORE
,"Checkin of IKE_SA successful.");
577 pthread_cond_signal(&(entry
->condvar
));
582 this->logger
->log(this->logger
,ERROR
,"Fatal Error: Tried to checkin nonexisting IKE_SA");
583 /* this SA is no more, this REALLY should not happen */
586 pthread_mutex_unlock(&(this->mutex
));
592 * Implements ike_sa_manager_t-function checkin_and_delete.
593 * @see ike_sa_manager_t.checkin_and_delete.
595 static status_t
checkin_and_delete(private_ike_sa_manager_t
*this, ike_sa_t
*ike_sa
)
597 /* deletion is a bit complex, we must garant that no thread is waiting for
599 * We take this SA from the list, and start signaling while threads
600 * are in the condvar.
602 ike_sa_entry_t
*entry
;
605 pthread_mutex_lock(&(this->mutex
));
607 if (this->get_entry_by_sa(this, ike_sa
, &entry
) == SUCCESS
)
609 /* mark it, so now new threads can acquire this SA */
610 entry
->driveout_new_threads
= TRUE
;
611 /* additionaly, drive out waiting threads */
612 entry
->driveout_waiting_threads
= TRUE
;
614 /* wait until all workers have done their work */
615 while (entry
->waiting_threads
> 0)
617 /* let the other threads do some work*/
618 pthread_cond_signal(&(entry
->condvar
));
619 /* and the nice thing, they will wake us again when their work is done */
620 pthread_cond_wait(&(entry
->condvar
), &(this->mutex
));
622 /* ok, we are alone now, no threads waiting in the entry's condvar */
623 this->delete_entry(this, entry
);
624 this->logger
->log(this->logger
,CONTROL
| MORE
,"Checkin and delete of IKE_SA successful");
629 this->logger
->log(this->logger
,ERROR
,"Fatal Error: Tried to checkin and delete nonexisting IKE_SA");
633 pthread_mutex_unlock(&(this->mutex
));
638 * Implements ike_sa_manager_t-function delete.
639 * @see ike_sa_manager_t.delete.
641 static status_t
delete(private_ike_sa_manager_t
*this, ike_sa_id_t
*ike_sa_id
)
643 /* deletion is a bit complex, we must garant that no thread is waiting for
645 * We take this SA from the list, and start signaling while threads
646 * are in the condvar.
648 ike_sa_entry_t
*entry
;
651 pthread_mutex_lock(&(this->mutex
));
653 if (this->get_entry_by_id(this, ike_sa_id
, &entry
) == SUCCESS
)
655 /* mark it, so now new threads can acquire this SA */
656 entry
->driveout_new_threads
= TRUE
;
658 /* wait until all workers have done their work */
659 while (entry
->waiting_threads
)
662 pthread_cond_signal(&(entry
->condvar
));
663 /* and the nice thing, they will wake us again when their work is done */
664 pthread_cond_wait(&(entry
->condvar
), &(this->mutex
));
666 /* ok, we are alone now, no threads waiting in the entry's condvar */
667 this->delete_entry(this, entry
);
668 this->logger
->log(this->logger
,CONTROL
| MORE
,"Delete of IKE_SA successful");
673 this->logger
->log(this->logger
,ERROR
,"Fatal Error: Tried to delete nonexisting IKE_SA");
677 pthread_mutex_unlock(&(this->mutex
));
682 * Implements ike_sa_manager_t-function destroy.
683 * @see ike_sa_manager_t.destroy.
685 static status_t
destroy(private_ike_sa_manager_t
*this)
687 /* destroy all list entries */
688 linked_list_t
*list
= this->ike_sa_list
;
689 linked_list_iterator_t
*iterator
;
691 ike_sa_entry_t
*entry
;
693 pthread_mutex_lock(&(this->mutex
));
695 this->logger
->log(this->logger
,CONTROL
| MORE
,"Going to destroy IKE_SA manager and all managed IKE_SA's");
697 /* Step 1: drive out all waiting threads */
698 status
= list
->create_iterator(list
, &iterator
, TRUE
);
700 if (status
!= SUCCESS
)
702 this->logger
->log(this->logger
,ERROR
,"Fatal Error: Create of iterator while destroying IKE_SA-Manager failed");
706 this->logger
->log(this->logger
,CONTROL
| MOST
,"Set driveout flags for all stored IKE_SA's");
707 while (iterator
->has_next(iterator
))
709 iterator
->current(iterator
, (void**)&entry
);
710 /* do not accept new threads, drive out waiting threads */
711 entry
->driveout_new_threads
= TRUE
;
712 entry
->driveout_waiting_threads
= TRUE
;
715 this->logger
->log(this->logger
,CONTROL
| MOST
,"Wait for all threads to leave IKE_SA's");
716 /* Step 2: wait until all are gone */
717 iterator
->reset(iterator
);
718 while (iterator
->has_next(iterator
))
720 iterator
->current(iterator
, (void**)&entry
);
721 while (entry
->waiting_threads
)
724 pthread_cond_signal(&(entry
->condvar
));
725 /* go sleeping until they are gone */
726 pthread_cond_wait(&(entry
->condvar
), &(this->mutex
));
729 this->logger
->log(this->logger
,CONTROL
| MOST
,"Delete all IKE_SA's");
730 /* Step 3: delete all entries */
731 iterator
->destroy(iterator
);
733 while (list
->get_count(list
) > 0)
735 list
->get_first(list
, (void**)&entry
);
736 this->delete_entry(this, entry
);
739 this->logger
->log(this->logger
,CONTROL
| MOST
,"IKE_SA's deleted");
740 pthread_mutex_unlock(&(this->mutex
));
742 /* destroy logger at end */
743 global_logger_manager
->destroy_logger(global_logger_manager
,this->logger
);
745 allocator_free(this);
751 * Described in header
753 ike_sa_manager_t
*ike_sa_manager_create()
755 private_ike_sa_manager_t
*this = allocator_alloc_thing(private_ike_sa_manager_t
);
757 /* assign public functions */
758 this->public.destroy
= (status_t(*)(ike_sa_manager_t
*))destroy
;
759 this->public.checkout
= (status_t(*)(ike_sa_manager_t
*, ike_sa_id_t
*sa_id
, ike_sa_t
**sa
))checkout
;
760 this->public.checkin
= (status_t(*)(ike_sa_manager_t
*, ike_sa_t
*sa
))checkin
;
761 this->public.delete = (status_t(*)(ike_sa_manager_t
*, ike_sa_id_t
*sa_id
))delete;
762 this->public.checkin_and_delete
= (status_t(*)(ike_sa_manager_t
*, ike_sa_t
*ike_sa
))checkin_and_delete
;
764 /* initialize private functions */
765 this->get_next_spi
= get_next_spi
;
766 this->get_entry_by_sa
= get_entry_by_sa
;
767 this->get_entry_by_id
= get_entry_by_id
;
768 this->delete_entry
= delete_entry
;
770 /* initialize private variables */
771 this->logger
= global_logger_manager
->create_logger(global_logger_manager
,IKE_SA_MANAGER
,NULL
);
772 if (this->logger
== NULL
)
774 allocator_free(this);
778 this->ike_sa_list
= linked_list_create();
779 if (this->ike_sa_list
== NULL
)
781 this->logger
->log(this->logger
,ERROR
,"Fatal error: Failed to create list for managed IKE_SA");
782 global_logger_manager
->destroy_logger(global_logger_manager
,this->logger
);
783 allocator_free(this);
787 pthread_mutex_init(&(this->mutex
), NULL
);
791 return (ike_sa_manager_t
*)this;