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