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