- logger in utils
[strongswan.git] / Source / charon / ike_sa_manager.c
1 /**
2 * @file ike_sa_manager.c
3 *
4 * @brief Central point for managing IKE-SAs (creation, locking, deleting...)
5 *
6 */
7
8 /*
9 * Copyright (C) 2005 Jan Hutter, Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
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>.
16 *
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
20 * for more details.
21 */
22
23 #include <pthread.h>
24 #include <string.h>
25
26 #include "ike_sa_manager.h"
27
28 #include "ike_sa_id.h"
29 #include "utils/allocator.h"
30 #include "utils/linked_list.h"
31
32 /**
33 * @brief An entry in the linked list, contains IKE_SA, locking and lookup data.
34 */
35 typedef struct ike_sa_entry_s ike_sa_entry_t;
36 struct ike_sa_entry_s {
37 /**
38 * destructor, also destroys ike_sa
39 */
40 status_t (*destroy) (ike_sa_entry_t *this);
41 /**
42 * Number of threads waiting for this ike_sa
43 */
44 int waiting_threads;
45 /**
46 * condvar where threads can wait until it's free again
47 */
48 pthread_cond_t condvar;
49 /**
50 * is this ike_sa currently checked out?
51 */
52 bool checked_out;
53 /**
54 * does this SA let new treads in?
55 */
56 bool driveout_new_threads;
57 /**
58 * does this SA drives out new threads?
59 */
60 bool driveout_waiting_threads;;
61 /**
62 * identifiaction of ike_sa (SPIs)
63 */
64 ike_sa_id_t *ike_sa_id;
65 /**
66 * the contained ike_sa
67 */
68 ike_sa_t *ike_sa;
69 };
70
71 /**
72 * @see ike_sa_entry_t.destroy
73 */
74 static status_t ike_sa_entry_destroy(ike_sa_entry_t *this)
75 {
76 this->ike_sa->destroy(this->ike_sa);
77 this->ike_sa_id->destroy(this->ike_sa_id);
78 allocator_free(this);
79 return SUCCESS;
80 }
81
82
83 /**
84 * @brief creates a new entry for the ike_sa list
85 *
86 * This constructor additionaly creates a new and empty SA
87 *
88 * @param ike_sa_id the associated ike_sa_id_t, will be cloned
89 * @return created entry, with ike_sa and ike_sa_id
90 */
91 static ike_sa_entry_t *ike_sa_entry_create(ike_sa_id_t *ike_sa_id)
92 {
93 ike_sa_entry_t *this = allocator_alloc_thing(ike_sa_entry_t);
94
95 this->destroy = ike_sa_entry_destroy;
96 this->waiting_threads = 0;
97 pthread_cond_init(&(this->condvar), NULL);
98 /* we set checkout flag when we really give it out */
99 this->checked_out = FALSE;
100 this->driveout_new_threads = FALSE;
101 this->driveout_waiting_threads = FALSE;
102 ike_sa_id->clone(ike_sa_id, &(this->ike_sa_id));
103 this->ike_sa = ike_sa_create(ike_sa_id);
104 return this;
105 }
106
107 /**
108 * Additional private members to ike_sa_manager_t
109 */
110 typedef struct private_ike_sa_manager_s private_ike_sa_manager_t;
111 struct private_ike_sa_manager_s {
112 /**
113 * Public members
114 */
115 ike_sa_manager_t public;
116
117 /**
118 * @brief get next spi
119 *
120 * we give out SPIs incremental
121 *
122 * @param this the ike_sa_manager
123 * @param spi[out] spi will be written here
124 * @return SUCCESS or,
125 * OUT_OF_RES when we already served 2^64 SPIs ;-)
126 */
127 status_t (*get_next_spi) (private_ike_sa_manager_t *this, spi_t *spi);
128 /**
129 * @brief find the ike_sa_entry in the list by SPIs
130 *
131 * This function simply iterates over the linked list. A hash-table
132 * would be more efficient when storing a lot of IKE_SAs...
133 *
134 * @param this the ike_sa_manager containing the list
135 * @param ike_sa_id id of the ike_sa, containing SPIs
136 * @param entry[out] pointer to set to the found entry
137 * @return SUCCESS when found,
138 * NOT_FOUND when no such ike_sa_id in list
139 */
140 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 /**
142 * @brief find the ike_sa_entry in the list by pointer to SA.
143 *
144 * This function simply iterates over the linked list. A hash-table
145 * would be more efficient when storing a lot of IKE_SAs...
146 *
147 * @param this the ike_sa_manager containing the list
148 * @param ike_sa pointer to the ike_sa
149 * @param entry[out] pointer to set to the found entry
150 * @return SUCCESS when found,
151 * NOT_FOUND when no such ike_sa_id in list
152 */
153 status_t (*get_entry_by_sa) (private_ike_sa_manager_t *this, ike_sa_t *ike_sa, ike_sa_entry_t **entry);
154 /**
155 * @brief delete an entry from the linked list
156 *
157 * @param this the ike_sa_manager containing the list
158 * @param entry entry to delete
159 * @return SUCCESS when found,
160 * NOT_FOUND when no such ike_sa_id in list
161 */
162 status_t (*delete_entry) (private_ike_sa_manager_t *this, ike_sa_entry_t *entry);
163 /**
164 * lock for exclusivly accessing the manager
165 */
166 pthread_mutex_t mutex;
167
168 /**
169 * Linked list with entries for the ike_sa
170 */
171 linked_list_t *list;
172 /**
173 * Next SPI, needed for incremental creation of SPIs
174 */
175 spi_t next_spi;
176 };
177
178
179 /**
180 * @see private_ike_sa_manager_t.get_entry_by_id
181 */
182 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 {
184 linked_list_t *list = this->list;
185 linked_list_iterator_t *iterator;
186 list->create_iterator(list, &iterator, TRUE);
187 while (iterator->has_next(iterator))
188 {
189 ike_sa_entry_t *current;
190 bool are_equal = FALSE;
191 iterator->current(iterator, (void**)&current);
192 current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id, &are_equal);
193 if (are_equal)
194 {
195 *entry = current;
196 iterator->destroy(iterator);
197 return SUCCESS;
198 }
199 }
200 iterator->destroy(iterator);
201 return NOT_FOUND;
202 }
203
204 /**
205 * @see private_ike_sa_manager_t.get_entry_by_sa
206 */
207 static status_t get_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa, ike_sa_entry_t **entry)
208 {
209 linked_list_t *list = this->list;
210 linked_list_iterator_t *iterator;
211 list->create_iterator(list, &iterator, TRUE);
212 while (iterator->has_next(iterator))
213 {
214 ike_sa_entry_t *current;
215 iterator->current(iterator, (void**)&current);
216 if (current->ike_sa == ike_sa)
217 {
218 *entry = current;
219 iterator->destroy(iterator);
220 return SUCCESS;
221 }
222 }
223 iterator->destroy(iterator);
224 return NOT_FOUND;
225 }
226
227 /**
228 * @see private_ike_sa_manager_t.delete_entry
229 */
230 static status_t delete_entry(private_ike_sa_manager_t *this, ike_sa_entry_t *entry)
231 {
232 linked_list_t *list = this->list;
233 linked_list_iterator_t *iterator;
234 list->create_iterator(list, &iterator, TRUE);
235 while (iterator->has_next(iterator))
236 {
237 ike_sa_entry_t *current;
238 iterator->current(iterator, (void**)&current);
239 if (current == entry)
240 {
241 list->remove(list, iterator);
242 entry->destroy(entry);
243 iterator->destroy(iterator);
244 return SUCCESS;
245 }
246 }
247 iterator->destroy(iterator);
248 return NOT_FOUND;
249 }
250
251
252 /**
253 * @see private_ike_sa_manager_t.get_next_spi
254 */
255 static status_t get_next_spi(private_ike_sa_manager_t *this, spi_t *spi)
256 {
257 this->next_spi.low ++;
258 if (this->next_spi.low == 0) {
259 /* overflow of lower int in spi */
260 this->next_spi.high ++;
261 if (this->next_spi.high == 0) {
262 /* our software ran so incredible stable, we have no more
263 * SPIs to give away :-/. */
264 return OUT_OF_RES;
265 }
266 }
267 *spi = this->next_spi;
268 return SUCCESS;
269 }
270
271
272 /**
273 * @see ike_sa_manager_s.checkout_ike_sa
274 */
275 static status_t checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_t **ike_sa)
276 {
277 bool responder_spi_set;
278 bool initiator_spi_set;
279 status_t retval;
280
281 pthread_mutex_lock(&(this->mutex));
282
283 responder_spi_set = ike_sa_id->responder_spi_is_set(ike_sa_id);
284 initiator_spi_set = ike_sa_id->initiator_spi_is_set(ike_sa_id);
285
286 if (initiator_spi_set && responder_spi_set)
287 {
288 /* we SHOULD have an IKE_SA for these SPIs in the list,
289 * if not, we cant handle the request...
290 */
291 ike_sa_entry_t *entry;
292 /* look for the entry */
293 if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
294 {
295 /* can we give this out to new requesters? */
296 if (entry->driveout_new_threads)
297 {
298 retval = NOT_FOUND;
299 }
300 else
301 {
302 /* is this IKE_SA already checked out ??
303 * are we welcome to get this SA ? */
304 while (entry->checked_out && !entry->driveout_waiting_threads)
305 {
306 /* so wait until we can get it for us.
307 * we register us as waiting.
308 */
309 entry->waiting_threads++;
310 pthread_cond_wait(&(entry->condvar), &(this->mutex));
311 entry->waiting_threads--;
312 }
313 /* hm, a deletion request forbids us to get this SA, go home */
314 if (entry->driveout_waiting_threads)
315 {
316 /* we must signal here, others are interested that we leave */
317 pthread_cond_signal(&(entry->condvar));
318 retval = NOT_FOUND;
319 }
320 else
321 {
322 /* ok, this IKE_SA is finally ours */
323 entry->checked_out = TRUE;
324 *ike_sa = entry->ike_sa;
325 /* DON'T use return, we must unlock the mutex! */
326 retval = SUCCESS;
327 }
328 }
329 }
330 else
331 {
332 /* looks like there is no such IKE_SA, better luck next time... */
333 /* DON'T use return, we must unlock the mutex! */
334 retval = NOT_FOUND;
335 }
336 }
337 else if (initiator_spi_set && !responder_spi_set)
338 {
339 /* an IKE_SA_INIT from an another endpoint,
340 * he is the initiator.
341 * For simplicity, we do NOT check for retransmitted
342 * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT-
343 * Request (even a retransmitted one) will result in a
344 * IKE_SA. This could be improved...
345 */
346 spi_t responder_spi;
347 ike_sa_entry_t *new_ike_sa_entry;
348
349 /* set SPIs, we are the responder */
350 this->get_next_spi(this, &responder_spi);
351 /* we also set arguments spi, so its still valid */
352 ike_sa_id->set_responder_spi(ike_sa_id, responder_spi);
353
354 /* create entry */
355 new_ike_sa_entry = ike_sa_entry_create(ike_sa_id);
356 this->list->insert_last(this->list, new_ike_sa_entry);
357
358 /* check ike_sa out */
359 new_ike_sa_entry->checked_out = TRUE;
360 *ike_sa = new_ike_sa_entry->ike_sa;
361
362 /* DON'T use return, we must unlock the mutex! */
363 retval = SUCCESS;
364 }
365 else if (!initiator_spi_set && !responder_spi_set)
366 {
367 /* creation of an IKE_SA from local site,
368 * we are the initiator!
369 */
370 spi_t initiator_spi;
371 ike_sa_entry_t *new_ike_sa_entry;
372
373 this->get_next_spi(this, &initiator_spi);
374
375 /* we also set arguments SPI, so its still valid */
376 ike_sa_id->set_initiator_spi(ike_sa_id, initiator_spi);
377
378 /* create entry */
379 new_ike_sa_entry = ike_sa_entry_create(ike_sa_id);
380 this->list->insert_last(this->list, new_ike_sa_entry);
381
382 /* check ike_sa out */
383 new_ike_sa_entry->checked_out = TRUE;
384 *ike_sa = new_ike_sa_entry->ike_sa;
385
386 /* DON'T use return, we must unlock the mutex! */
387 retval = SUCCESS;
388 }
389 else
390 {
391 /* responder set, initiator not: here is something seriously wrong! */
392
393 /* DON'T use return, we must unlock the mutex! */
394 retval = INVALID_ARG;
395 }
396
397 pthread_mutex_unlock(&(this->mutex));
398 /* OK, unlocked... */
399 return retval;
400 }
401
402 static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
403 {
404 /* to check the SA back in, we look for the pointer of the ike_sa
405 * in all entries.
406 * We can't search by SPI's since the MAY have changed (e.g. on reception
407 * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary...
408 */
409 status_t retval;
410 ike_sa_entry_t *entry;
411
412 pthread_mutex_lock(&(this->mutex));
413
414 /* look for the entry */
415 if (this->get_entry_by_sa(this, ike_sa, &entry) == SUCCESS)
416 {
417 /* ike_sa_id must be updated */
418 entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa));
419 /* signal waiting threads */
420 entry->checked_out = FALSE;
421 pthread_cond_signal(&(entry->condvar));
422 retval = SUCCESS;
423 }
424 else
425 {
426 /* this SA is no more, this REALLY should not happen */
427 retval = NOT_FOUND;
428 }
429 pthread_mutex_unlock(&(this->mutex));
430 return retval;
431 }
432
433
434
435 static status_t checkin_and_delete(private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
436 {
437 /* deletion is a bit complex, we must garant that no thread is waiting for
438 * this SA.
439 * We take this SA from the list, and start signaling while threads
440 * are in the condvar.
441 */
442 ike_sa_entry_t *entry;
443 status_t retval;
444
445 pthread_mutex_lock(&(this->mutex));
446
447 if (this->get_entry_by_sa(this, ike_sa, &entry) == SUCCESS)
448 {
449 /* mark it, so now new threads can acquire this SA */
450 entry->driveout_new_threads = TRUE;
451 /* additionaly, drive out waiting threads */
452 entry->driveout_waiting_threads = TRUE;
453
454 /* wait until all workers have done their work */
455 while (entry->waiting_threads)
456 {
457 /* let the other threads do some work*/
458 pthread_cond_signal(&(entry->condvar));
459 /* and the nice thing, they will wake us again when their work is done */
460 pthread_cond_wait(&(entry->condvar), &(this->mutex));
461 }
462 /* ok, we are alone now, no threads waiting in the entry's condvar */
463 this->delete_entry(this, entry);
464 retval = SUCCESS;
465 }
466 else
467 {
468 retval = NOT_FOUND;
469 }
470
471 pthread_mutex_unlock(&(this->mutex));
472 return retval;
473 }
474
475 static status_t delete(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id)
476 {
477 /* deletion is a bit complex, we must garant that no thread is waiting for
478 * this SA.
479 * We take this SA from the list, and start signaling while threads
480 * are in the condvar.
481 */
482 ike_sa_entry_t *entry;
483 status_t retval;
484
485 pthread_mutex_lock(&(this->mutex));
486
487 if (this->get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
488 {
489 /* mark it, so now new threads can acquire this SA */
490 entry->driveout_new_threads = TRUE;
491
492 /* wait until all workers have done their work */
493 while (entry->waiting_threads)
494 {
495 /* wake up all */
496 pthread_cond_signal(&(entry->condvar));
497 /* and the nice thing, they will wake us again when their work is done */
498 pthread_cond_wait(&(entry->condvar), &(this->mutex));
499 }
500 /* ok, we are alone now, no threads waiting in the entry's condvar */
501 this->delete_entry(this, entry);
502 retval = SUCCESS;
503 }
504 else
505 {
506 retval = NOT_FOUND;
507 }
508
509 pthread_mutex_unlock(&(this->mutex));
510 return retval;
511 }
512
513 static status_t destroy(private_ike_sa_manager_t *this)
514 {
515 /* destroy all list entries */
516 linked_list_t *list = this->list;
517 linked_list_iterator_t *iterator;
518
519 pthread_mutex_lock(&(this->mutex));
520
521 /* Step 1: drive out all waiting threads */
522 list->create_iterator(list, &iterator, TRUE);
523 while (iterator->has_next(iterator))
524 {
525 ike_sa_entry_t *entry;
526 iterator->current(iterator, (void**)&entry);
527 /* do not accept new threads, drive out waiting threads */
528 entry->driveout_new_threads = TRUE;
529 entry->driveout_waiting_threads = TRUE;
530 }
531 /* Step 2: wait until all are gone */
532 iterator->reset(iterator);
533 while (iterator->has_next(iterator))
534 {
535 ike_sa_entry_t *entry;
536 iterator->current(iterator, (void**)&entry);
537 while (entry->waiting_threads)
538 {
539 /* wake up all */
540 pthread_cond_signal(&(entry->condvar));
541 /* go sleeping until they are gone */
542 pthread_cond_wait(&(entry->condvar), &(this->mutex));
543 }
544 }
545 /* Step 3: delete all entries */
546 iterator->reset(iterator);
547 while (iterator->has_next(iterator))
548 {
549 ike_sa_entry_t *entry;
550 iterator->current(iterator, (void**)&entry);
551 this->delete_entry(this, entry);
552 }
553 iterator->destroy(iterator);
554
555 list->destroy(list);
556
557 pthread_mutex_unlock(&(this->mutex));
558
559 allocator_free(this);
560
561 return SUCCESS;
562 }
563
564
565 ike_sa_manager_t *ike_sa_manager_create()
566 {
567 private_ike_sa_manager_t *this = allocator_alloc_thing(private_ike_sa_manager_t);
568
569 /* assign public functions */
570 this->public.destroy = (status_t(*)(ike_sa_manager_t*))destroy;
571 this->public.checkout = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t *sa_id, ike_sa_t **sa))checkout;
572 this->public.checkin = (status_t(*)(ike_sa_manager_t*, ike_sa_t *sa))checkin;
573 this->public.delete = (status_t(*)(ike_sa_manager_t*, ike_sa_id_t *sa_id))delete;
574 this->public.checkin_and_delete = (status_t(*)(ike_sa_manager_t*, ike_sa_t *ike_sa))checkin_and_delete;
575
576 /* initialize private data */
577 this->get_next_spi = get_next_spi;
578 this->get_entry_by_sa = get_entry_by_sa;
579 this->get_entry_by_id = get_entry_by_id;
580 this->delete_entry = delete_entry;
581
582 this->list = linked_list_create();
583 if (this->list == NULL)
584 {
585 allocator_free(this);
586 return NULL;
587 }
588
589 pthread_mutex_init(&(this->mutex), NULL);
590
591 this->next_spi.low = 1;
592 this->next_spi.high = 0;
593
594 return (ike_sa_manager_t*)this;
595 }