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 "allocator.h"
27 #include "ike_sa_manager.h"
28 #include "linked_list.h"
29 #include "ike_sa_id.h"
32 * @brief An entry in the linked list, contains IKE_SA, locking and lookup data.
34 typedef struct ike_sa_entry_s ike_sa_entry_t
;
35 struct ike_sa_entry_s
{
37 * destructor, also destroys ike_sa
39 status_t (*destroy
) (ike_sa_entry_t
*this);
41 * Number of threads waiting for this ike_sa
45 * is this SA flagged for deleting ?
47 pthread_cond_t condvar
;
49 * is this ike_sa currently checked out?
53 * does this SA let new treads in?
55 bool driveout_new_threads
;
57 * does this SA drives out new threads?
59 bool driveout_waiting_threads
;;
61 * identifiaction of ike_sa (SPIs)
63 ike_sa_id_t
*ike_sa_id
;
65 * the contained ike_sa
71 * @see ike_sa_entry_t.destroy
73 static status_t
ike_sa_entry_destroy(ike_sa_entry_t
*this)
75 this->ike_sa
->destroy(this->ike_sa
);
76 this->ike_sa_id
->destroy(this->ike_sa_id
);
83 * @brief creates a new entry for the ike_sa list
85 * This constructor additionaly creates a new and empty SA
87 * @param ike_sa_id the associated ike_sa_id_t, cloned
88 * @return created entry, with ike_sa and ike_sa_id
90 static ike_sa_entry_t
*ike_sa_entry_create(ike_sa_id_t
*ike_sa_id
)
92 ike_sa_entry_t
*this = allocator_alloc_thing(ike_sa_entry_t
);
94 this->destroy
= ike_sa_entry_destroy
;
95 this->waiting_threads
= 0;
96 pthread_cond_init(&(this->condvar
), NULL
);
97 /* we set checkout flag when we really give it out */
98 this->checked_out
= FALSE
;
99 this->driveout_new_threads
= FALSE
;
100 this->driveout_waiting_threads
= FALSE
;
101 this->ike_sa_id
= ike_sa_id
;
102 this->ike_sa
= ike_sa_create(ike_sa_id
);
107 * Additional private members to ike_sa_manager_t
109 typedef struct private_ike_sa_manager_s private_ike_sa_manager_t
;
110 struct private_ike_sa_manager_s
{
114 ike_sa_manager_t
public;
117 * @brief get next spi
119 * we give out SPIs incremental
121 * @param this the ike_sa_manager
122 * @param spi[out] spi will be written here
123 * @return SUCCESS or,
124 * OUT_OF_RES when we already served 2^64 SPIs ;-)
126 status_t (*get_next_spi
) (private_ike_sa_manager_t
*this, spi_t
*spi
);
128 * @brief find the ike_sa_entry in the list by SPIs
130 * This function simply iterates over the linked list. A hash-table
131 * would be more efficient when storing a lot of IKE_SAs...
133 * @param this the ike_sa_manager containing the list
134 * @param ike_sa_id id of the ike_sa, containing SPIs
135 * @param entry[out] pointer to set to the found entry
136 * @return SUCCESS when found,
137 * NOT_FOUND when no such ike_sa_id in list
139 status_t (*get_entry_by_id
) (private_ike_sa_manager_t
*this, ike_sa_id_t
*ike_sa_id
, ike_sa_entry_t
**entry
);
141 * @brief find the ike_sa_entry in the list by pointer to SA.
143 * This function simply iterates over the linked list. A hash-table
144 * would be more efficient when storing a lot of IKE_SAs...
146 * @param this the ike_sa_manager containing the list
147 * @param ike_sa pointer to the ike_sa
148 * @param entry[out] pointer to set to the found entry
149 * @return SUCCESS when found,
150 * NOT_FOUND when no such ike_sa_id in list
152 status_t (*get_entry_by_sa
) (private_ike_sa_manager_t
*this, ike_sa_t
*ike_sa
, ike_sa_entry_t
**entry
);
154 * @brief delete an entry from the linked list
156 * @param this the ike_sa_manager containing the list
157 * @param entry entry to delete
158 * @return SUCCESS when found,
159 * NOT_FOUND when no such ike_sa_id in list
161 status_t (*delete_entry
) (private_ike_sa_manager_t
*this, ike_sa_entry_t
*entry
);
163 * lock for exclusivly accessing the manager
165 pthread_mutex_t mutex
;
168 * Linked list with entries for the ike_sa
172 * Next SPI, needed for incremental creation of SPIs
179 * @see private_ike_sa_manager_t.get_entry_by_id
181 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
)
183 linked_list_t
*list
= this->list
;
184 linked_list_iterator_t
*iterator
;
185 list
->create_iterator(list
, &iterator
, TRUE
);
186 while (iterator
->has_next(iterator
))
188 ike_sa_entry_t
*current
;
189 bool are_equal
= FALSE
;
190 iterator
->current(iterator
, (void**)¤t
);
191 current
->ike_sa_id
->equals(current
->ike_sa_id
, ike_sa_id
, &are_equal
);
195 iterator
->destroy(iterator
);
199 iterator
->destroy(iterator
);
204 * @see private_ike_sa_manager_t.get_entry_by_sa
206 static status_t
get_entry_by_sa(private_ike_sa_manager_t
*this, ike_sa_t
*ike_sa
, ike_sa_entry_t
**entry
)
208 linked_list_t
*list
= this->list
;
209 linked_list_iterator_t
*iterator
;
210 list
->create_iterator(list
, &iterator
, TRUE
);
211 while (iterator
->has_next(iterator
))
213 ike_sa_entry_t
*current
;
214 iterator
->current(iterator
, (void**)¤t
);
215 if (current
->ike_sa
== ike_sa
)
218 iterator
->destroy(iterator
);
222 iterator
->destroy(iterator
);
227 * @see private_ike_sa_manager_t.delete_entry
229 static status_t
delete_entry(private_ike_sa_manager_t
*this, ike_sa_entry_t
*entry
)
231 linked_list_t
*list
= this->list
;
232 linked_list_iterator_t
*iterator
;
233 list
->create_iterator(list
, &iterator
, TRUE
);
234 while (iterator
->has_next(iterator
))
236 ike_sa_entry_t
*current
;
237 iterator
->current(iterator
, (void**)¤t
);
238 if (current
== entry
)
240 list
->remove(list
, iterator
);
241 entry
->destroy(entry
);
242 iterator
->destroy(iterator
);
246 iterator
->destroy(iterator
);
252 * @see private_ike_sa_manager_t.get_next_spi
254 static status_t
get_next_spi(private_ike_sa_manager_t
*this, spi_t
*spi
)
256 this->next_spi
.low
++;
257 if (this->next_spi
.low
== 0) {
258 /* overflow of lower int in spi */
259 this->next_spi
.high
++;
260 if (this->next_spi
.high
== 0) {
261 /* our software ran so incredible stable, we have no more
262 * SPIs to give away :-/. */
266 *spi
= this->next_spi
;
272 * @see ike_sa_manager_s.checkout_ike_sa
274 static status_t
checkout(private_ike_sa_manager_t
*this, ike_sa_id_t
*ike_sa_id
, ike_sa_t
**ike_sa
)
276 bool responder_spi_set
;
277 bool initiator_spi_set
;
280 pthread_mutex_lock(&(this->mutex
));
282 responder_spi_set
= ike_sa_id
->responder_spi_is_set(ike_sa_id
);
283 initiator_spi_set
= ike_sa_id
->initiator_spi_is_set(ike_sa_id
);
285 if (initiator_spi_set
&& responder_spi_set
)
287 /* we SHOULD have an IKE_SA for these SPIs in the list,
288 * if not, we cant handle the request...
290 ike_sa_entry_t
*entry
;
291 /* look for the entry */
292 if (this->get_entry_by_id(this, ike_sa_id
, &entry
) == SUCCESS
)
294 /* can we give this out to new requesters? */
295 if (entry
->driveout_new_threads
)
301 /* is this IKE_SA already checked out ??
302 * are we welcome to get this SA ? */
303 while (entry
->checked_out
&& !entry
->driveout_waiting_threads
)
305 /* so wait until we can get it for us.
306 * we register us as waiting.
308 entry
->waiting_threads
++;
309 pthread_cond_wait(&(entry
->condvar
), &(this->mutex
));
310 entry
->waiting_threads
--;
312 /* hm, a deletion request forbids us to get this SA, go home */
313 if (entry
->driveout_waiting_threads
)
315 /* we must signal here, others are interested that we leave */
316 pthread_cond_signal(&(entry
->condvar
));
321 /* ok, this IKE_SA is finally ours */
322 entry
->checked_out
= TRUE
;
323 *ike_sa
= entry
->ike_sa
;
324 /* DON'T use return, we must unlock the mutex! */
331 /* looks like there is no such IKE_SA, better luck next time... */
332 /* DON'T use return, we must unlock the mutex! */
336 else if (initiator_spi_set
&& !responder_spi_set
)
338 /* an IKE_SA_INIT from an another endpoint,
339 * he is the initiator.
340 * For simplicity, we do NOT check for retransmitted
341 * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT-
342 * Request (even a retransmitted one) will result in a
343 * IKE_SA. This could be improved...
346 ike_sa_id_t
*new_ike_sa_id
;
347 ike_sa_entry_t
*new_ike_sa_entry
;
349 /* set SPIs, we are the responder */
350 ike_sa_id
->clone(ike_sa_id
, &new_ike_sa_id
);
351 this->get_next_spi(this, &responder_spi
);
352 new_ike_sa_id
->set_responder_spi(new_ike_sa_id
, responder_spi
);
353 /* we also set arguments spi, so its still valid */
354 ike_sa_id
->set_responder_spi(ike_sa_id
, responder_spi
);
357 new_ike_sa_entry
= ike_sa_entry_create(new_ike_sa_id
);
358 this->list
->insert_last(this->list
, new_ike_sa_entry
);
360 /* check ike_sa out */
361 new_ike_sa_entry
->checked_out
= TRUE
;
362 *ike_sa
= new_ike_sa_entry
->ike_sa
;
364 /* DON'T use return, we must unlock the mutex! */
367 else if (!initiator_spi_set
&& !responder_spi_set
)
369 /* creation of an IKE_SA from local site,
370 * we are the initiator!
372 spi_t initiator_spi
, responder_spi
;
373 ike_sa_id_t
*new_ike_sa_id
;
374 ike_sa_entry_t
*new_ike_sa_entry
;
377 memset(&responder_spi
, 0, sizeof(spi_t
));
379 this->get_next_spi(this, &initiator_spi
);
381 /* we also set arguments SPI, so its still valid */
382 ike_sa_id
->set_initiator_spi(ike_sa_id
, initiator_spi
);
385 new_ike_sa_id
= ike_sa_id_create(initiator_spi
, responder_spi
, INITIATOR
);
386 new_ike_sa_entry
= ike_sa_entry_create(new_ike_sa_id
);
387 this->list
->insert_last(this->list
, new_ike_sa_entry
);
389 /* check ike_sa out */
390 new_ike_sa_entry
->checked_out
= TRUE
;
391 *ike_sa
= new_ike_sa_entry
->ike_sa
;
393 /* DON'T use return, we must unlock the mutex! */
398 /* responder set, initiator not: here is something seriously wrong! */
400 /* DON'T use return, we must unlock the mutex! */
401 retval
= INVALID_ARG
;
404 pthread_mutex_unlock(&(this->mutex
));
405 /* OK, unlocked... */
409 static status_t
checkin(private_ike_sa_manager_t
*this, ike_sa_t
*ike_sa
)
411 /* to check the SA back in, we look for the pointer of the ike_sa
413 * We can't search by SPI's since the MAY have changed (e.g. on reception
414 * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary...
417 ike_sa_entry_t
*entry
;
419 pthread_mutex_lock(&(this->mutex
));
421 /* look for the entry */
422 if (this->get_entry_by_sa(this, ike_sa
, &entry
) == SUCCESS
)
424 /* ike_sa_id must be updated */
425 entry
->ike_sa_id
->replace_values(entry
->ike_sa_id
, ike_sa
->get_id(ike_sa
));
426 /* signal waiting threads */
427 entry
->checked_out
= FALSE
;
428 pthread_cond_signal(&(entry
->condvar
));
433 /* this SA is no more, this REALLY should not happen */
436 pthread_mutex_unlock(&(this->mutex
));
442 static status_t
checkin_and_delete(private_ike_sa_manager_t
*this, ike_sa_t
*ike_sa
)
444 /* deletion is a bit complex, we must garant that no thread is waiting for
446 * We take this SA from the list, and start signaling while threads
447 * are in the condvar.
449 ike_sa_entry_t
*entry
;
452 pthread_mutex_lock(&(this->mutex
));
454 if (this->get_entry_by_sa(this, ike_sa
, &entry
) == SUCCESS
)
456 /* mark it, so now new threads can acquire this SA */
457 entry
->driveout_new_threads
= TRUE
;
458 /* additionaly, drive out waiting threads */
459 entry
->driveout_waiting_threads
= TRUE
;
461 /* wait until all workers have done their work */
462 while (entry
->waiting_threads
)
464 /* let the other threads do some work*/
465 pthread_cond_signal(&(entry
->condvar
));
466 /* and the nice thing, they will wake us again when their work is done */
467 pthread_cond_wait(&(entry
->condvar
), &(this->mutex
));
469 /* ok, we are alone now, no threads waiting in the entry's condvar */
470 this->delete_entry(this, entry
);
478 pthread_mutex_unlock(&(this->mutex
));
482 static status_t
delete(private_ike_sa_manager_t
*this, ike_sa_id_t
*ike_sa_id
)
484 /* deletion is a bit complex, we must garant that no thread is waiting for
486 * We take this SA from the list, and start signaling while threads
487 * are in the condvar.
489 ike_sa_entry_t
*entry
;
492 pthread_mutex_lock(&(this->mutex
));
494 if (this->get_entry_by_id(this, ike_sa_id
, &entry
) == SUCCESS
)
496 /* mark it, so now new threads can acquire this SA */
497 entry
->driveout_new_threads
= TRUE
;
499 /* wait until all workers have done their work */
500 while (entry
->waiting_threads
)
503 pthread_cond_signal(&(entry
->condvar
));
504 /* and the nice thing, they will wake us again when their work is done */
505 pthread_cond_wait(&(entry
->condvar
), &(this->mutex
));
507 /* ok, we are alone now, no threads waiting in the entry's condvar */
508 this->delete_entry(this, entry
);
516 pthread_mutex_unlock(&(this->mutex
));
520 static status_t
destroy(private_ike_sa_manager_t
*this)
522 /* destroy all list entries */
523 linked_list_t
*list
= this->list
;
524 linked_list_iterator_t
*iterator
;
526 pthread_mutex_lock(&(this->mutex
));
528 /* Step 1: drive out all waiting threads */
529 list
->create_iterator(list
, &iterator
, TRUE
);
530 while (iterator
->has_next(iterator
))
532 ike_sa_entry_t
*entry
;
533 iterator
->current(iterator
, (void**)&entry
);
534 /* do not accept new threads, drive out waiting threads */
535 entry
->driveout_new_threads
= TRUE
;
536 entry
->driveout_waiting_threads
= TRUE
;
538 /* Step 2: wait until all are gone */
539 iterator
->reset(iterator
);
540 while (iterator
->has_next(iterator
))
542 ike_sa_entry_t
*entry
;
543 iterator
->current(iterator
, (void**)&entry
);
544 while (entry
->waiting_threads
)
547 pthread_cond_signal(&(entry
->condvar
));
548 /* go sleeping until they are gone */
549 pthread_cond_wait(&(entry
->condvar
), &(this->mutex
));
552 /* Step 3: delete all entries */
553 iterator
->reset(iterator
);
554 while (iterator
->has_next(iterator
))
556 ike_sa_entry_t
*entry
;
557 iterator
->current(iterator
, (void**)&entry
);
558 this->delete_entry(this, entry
);
560 iterator
->destroy(iterator
);
564 pthread_mutex_unlock(&(this->mutex
));
566 allocator_free(this);
572 ike_sa_manager_t
*ike_sa_manager_create()
574 private_ike_sa_manager_t
*this = allocator_alloc_thing(private_ike_sa_manager_t
);
576 /* assign public functions */
577 this->public.destroy
= (status_t(*)(ike_sa_manager_t
*))destroy
;
578 this->public.checkout
= (status_t(*)(ike_sa_manager_t
*, ike_sa_id_t
*sa_id
, ike_sa_t
**sa
))checkout
;
579 this->public.checkin
= (status_t(*)(ike_sa_manager_t
*, ike_sa_t
*sa
))checkin
;
580 this->public.delete = (status_t(*)(ike_sa_manager_t
*, ike_sa_id_t
*sa_id
))delete;
581 this->public.checkin_and_delete
= (status_t(*)(ike_sa_manager_t
*, ike_sa_t
*ike_sa
))checkin_and_delete
;
583 /* initialize private data */
584 this->get_next_spi
= get_next_spi
;
585 this->get_entry_by_sa
= get_entry_by_sa
;
586 this->get_entry_by_id
= get_entry_by_id
;
587 this->delete_entry
= delete_entry
;
589 this->list
= linked_list_create();
590 if (this->list
== NULL
)
592 allocator_free(this);
596 pthread_mutex_init(&(this->mutex
), NULL
);
598 this->next_spi
.low
= 1;
599 this->next_spi
.high
= 0;
601 return (ike_sa_manager_t
*)this;