]>
Commit | Line | Data |
---|---|---|
472217f1 MW |
1 | /** |
2 | * @file ike_sa_manager.c | |
79538669 | 3 | * |
df3c59d0 | 4 | * @brief Implementation of ike_sa_mananger_t. |
79538669 | 5 | * |
472217f1 MW |
6 | */ |
7 | ||
8 | /* | |
c71d53ba MW |
9 | * Copyright (C) 2005-2006 Martin Willi |
10 | * Copyright (C) 2005 Jan Hutter | |
472217f1 MW |
11 | * Hochschule fuer Technik Rapperswil |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify it | |
14 | * under the terms of the GNU General Public License as published by the | |
15 | * Free Software Foundation; either version 2 of the License, or (at your | |
16 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
17 | * | |
18 | * This program is distributed in the hope that it will be useful, but | |
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
20 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
21 | * for more details. | |
22 | */ | |
23 | ||
24 | #include <pthread.h> | |
25 | #include <string.h> | |
26 | ||
27 | #include "ike_sa_manager.h" | |
88878242 | 28 | |
0e96f7d8 | 29 | #include <daemon.h> |
96f79ff1 | 30 | #include <sa/ike_sa_id.h> |
60356f33 | 31 | #include <bus/bus.h> |
021c2322 | 32 | #include <utils/linked_list.h> |
472217f1 | 33 | |
60356f33 | 34 | typedef struct entry_t entry_t; |
5796aa16 | 35 | |
472217f1 | 36 | /** |
df3c59d0 | 37 | * An entry in the linked list, contains IKE_SA, locking and lookup data. |
472217f1 | 38 | */ |
60356f33 | 39 | struct entry_t { |
39b2903f | 40 | |
472217f1 | 41 | /** |
39b2903f | 42 | * Number of threads waiting for this ike_sa_t object. |
472217f1 MW |
43 | */ |
44 | int waiting_threads; | |
39b2903f | 45 | |
472217f1 | 46 | /** |
39b2903f | 47 | * Condvar where threads can wait until ike_sa_t object is free for use again. |
472217f1 MW |
48 | */ |
49 | pthread_cond_t condvar; | |
39b2903f | 50 | |
472217f1 | 51 | /** |
39b2903f | 52 | * Is this ike_sa currently checked out? |
472217f1 MW |
53 | */ |
54 | bool checked_out; | |
39b2903f | 55 | |
d4425c1b | 56 | /** |
f0ddd8ae | 57 | * Does this SA drives out new threads? |
d4425c1b MW |
58 | */ |
59 | bool driveout_new_threads; | |
39b2903f | 60 | |
d4425c1b | 61 | /** |
f0ddd8ae | 62 | * Does this SA drives out waiting threads? |
d4425c1b | 63 | */ |
f0ddd8ae | 64 | bool driveout_waiting_threads; |
39b2903f | 65 | |
472217f1 | 66 | /** |
39b2903f | 67 | * Identifiaction of an IKE_SA (SPIs). |
472217f1 | 68 | */ |
79538669 | 69 | ike_sa_id_t *ike_sa_id; |
39b2903f | 70 | |
472217f1 | 71 | /** |
39b2903f | 72 | * The contained ike_sa_t object. |
472217f1 MW |
73 | */ |
74 | ike_sa_t *ike_sa; | |
b9e363f8 MW |
75 | |
76 | /** | |
77 | * hash of the IKE_SA_INIT message, used to detect retransmissions | |
78 | */ | |
79 | chunk_t init_hash; | |
80 | ||
81 | /** | |
82 | * message ID currently processing, if any | |
83 | */ | |
84 | u_int32_t message_id; | |
472217f1 MW |
85 | }; |
86 | ||
d4425c1b | 87 | /** |
60356f33 | 88 | * Implementation of entry_t.destroy. |
d4425c1b | 89 | */ |
60356f33 | 90 | static status_t entry_destroy(entry_t *this) |
472217f1 | 91 | { |
697aa8f5 | 92 | /* also destroy IKE SA */ |
472217f1 MW |
93 | this->ike_sa->destroy(this->ike_sa); |
94 | this->ike_sa_id->destroy(this->ike_sa_id); | |
b9e363f8 | 95 | chunk_free(&this->init_hash); |
5113680f | 96 | free(this); |
472217f1 | 97 | return SUCCESS; |
472217f1 MW |
98 | } |
99 | ||
472217f1 | 100 | /** |
60356f33 | 101 | * Creates a new entry for the ike_sa_t list. |
472217f1 | 102 | */ |
60356f33 | 103 | static entry_t *entry_create(ike_sa_id_t *ike_sa_id) |
472217f1 | 104 | { |
60356f33 | 105 | entry_t *this = malloc_thing(entry_t); |
f0ddd8ae | 106 | |
472217f1 | 107 | this->waiting_threads = 0; |
b9e363f8 | 108 | pthread_cond_init(&this->condvar, NULL); |
f0ddd8ae | 109 | |
472217f1 | 110 | /* we set checkout flag when we really give it out */ |
79538669 | 111 | this->checked_out = FALSE; |
d4425c1b MW |
112 | this->driveout_new_threads = FALSE; |
113 | this->driveout_waiting_threads = FALSE; | |
b9e363f8 MW |
114 | this->message_id = -1; |
115 | this->init_hash = chunk_empty; | |
f0ddd8ae JH |
116 | |
117 | /* ike_sa_id is always cloned */ | |
d048df5c MW |
118 | this->ike_sa_id = ike_sa_id->clone(ike_sa_id); |
119 | ||
697aa8f5 | 120 | /* create new ike_sa */ |
472217f1 | 121 | this->ike_sa = ike_sa_create(ike_sa_id); |
d048df5c | 122 | |
472217f1 MW |
123 | return this; |
124 | } | |
125 | ||
39b2903f | 126 | |
5796aa16 MW |
127 | typedef struct private_ike_sa_manager_t private_ike_sa_manager_t; |
128 | ||
472217f1 | 129 | /** |
39b2903f | 130 | * Additional private members of ike_sa_manager_t. |
472217f1 | 131 | */ |
5796aa16 | 132 | struct private_ike_sa_manager_t { |
472217f1 | 133 | /** |
39b2903f | 134 | * Public interface of ike_sa_manager_t. |
472217f1 MW |
135 | */ |
136 | ike_sa_manager_t public; | |
a095243f | 137 | |
472217f1 | 138 | /** |
39b2903f | 139 | * Lock for exclusivly accessing the manager. |
472217f1 MW |
140 | */ |
141 | pthread_mutex_t mutex; | |
79538669 | 142 | |
472217f1 | 143 | /** |
39b2903f | 144 | * Linked list with entries for the ike_sa_t objects. |
472217f1 | 145 | */ |
697aa8f5 JH |
146 | linked_list_t *ike_sa_list; |
147 | ||
472217f1 | 148 | /** |
2192375b | 149 | * A randomizer, to get random SPIs for our side |
472217f1 | 150 | */ |
2192375b | 151 | randomizer_t *randomizer; |
b9e363f8 MW |
152 | |
153 | /** | |
154 | * SHA1 hasher for IKE_SA_INIT retransmit detection | |
155 | */ | |
156 | hasher_t *hasher; | |
472217f1 MW |
157 | }; |
158 | ||
472217f1 | 159 | /** |
39b2903f | 160 | * Implementation of private_ike_sa_manager_t.get_entry_by_id. |
472217f1 | 161 | */ |
60356f33 | 162 | static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, entry_t **entry) |
472217f1 | 163 | { |
697aa8f5 | 164 | linked_list_t *list = this->ike_sa_list; |
bdb141cb | 165 | iterator_t *iterator; |
60356f33 | 166 | entry_t *current; |
f0ddd8ae | 167 | status_t status; |
697aa8f5 JH |
168 | |
169 | /* create iterator over list of ike_sa's */ | |
a0753941 | 170 | iterator = list->create_iterator(list, TRUE); |
d048df5c | 171 | |
f0ddd8ae JH |
172 | /* default status */ |
173 | status = NOT_FOUND; | |
174 | ||
60356f33 | 175 | while (iterator->iterate(iterator, (void**)¤t)) |
472217f1 | 176 | { |
60356f33 MW |
177 | if (current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id)) |
178 | { | |
b83806d8 | 179 | DBG2(DBG_MGR, "found entry by both SPIs"); |
60356f33 MW |
180 | *entry = current; |
181 | status = SUCCESS; | |
182 | break; | |
183 | } | |
184 | if (ike_sa_id->get_responder_spi(ike_sa_id) == 0 || | |
185 | current->ike_sa_id->get_responder_spi(current->ike_sa_id) == 0) | |
94b0f906 | 186 | { |
697aa8f5 | 187 | /* seems to be a half ready ike_sa */ |
3dd3c5f3 MW |
188 | if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) == |
189 | ike_sa_id->get_initiator_spi(ike_sa_id)) && | |
60356f33 MW |
190 | (current->ike_sa_id->is_initiator(ike_sa_id) == |
191 | ike_sa_id->is_initiator(current->ike_sa_id))) | |
daa1c00e | 192 | { |
b83806d8 | 193 | DBG2(DBG_MGR, "found entry by initiator SPI"); |
daa1c00e MW |
194 | *entry = current; |
195 | status = SUCCESS; | |
196 | break; | |
197 | } | |
198 | } | |
472217f1 | 199 | } |
f0ddd8ae | 200 | |
472217f1 | 201 | iterator->destroy(iterator); |
f0ddd8ae | 202 | return status; |
472217f1 MW |
203 | } |
204 | ||
205 | /** | |
39b2903f | 206 | * Implementation of private_ike_sa_manager_t.get_entry_by_sa. |
472217f1 | 207 | */ |
60356f33 | 208 | static status_t get_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa, entry_t **entry) |
472217f1 | 209 | { |
697aa8f5 | 210 | linked_list_t *list = this->ike_sa_list; |
bdb141cb | 211 | iterator_t *iterator; |
191a26a6 | 212 | entry_t *current; |
f0ddd8ae | 213 | status_t status; |
697aa8f5 | 214 | |
a0753941 | 215 | iterator = list->create_iterator(list, TRUE); |
f0ddd8ae JH |
216 | |
217 | /* default status */ | |
218 | status = NOT_FOUND; | |
219 | ||
191a26a6 | 220 | while (iterator->iterate(iterator, (void**)¤t)) |
472217f1 | 221 | { |
f0ddd8ae | 222 | /* only pointers are compared */ |
79538669 | 223 | if (current->ike_sa == ike_sa) |
472217f1 | 224 | { |
b83806d8 | 225 | DBG2(DBG_MGR, "found entry by pointer"); |
472217f1 | 226 | *entry = current; |
f0ddd8ae JH |
227 | status = SUCCESS; |
228 | break; | |
472217f1 MW |
229 | } |
230 | } | |
231 | iterator->destroy(iterator); | |
d048df5c | 232 | |
f0ddd8ae | 233 | return status; |
472217f1 MW |
234 | } |
235 | ||
d4425c1b | 236 | /** |
39b2903f | 237 | * Implementation of private_ike_sa_manager_s.delete_entry. |
d4425c1b | 238 | */ |
60356f33 | 239 | static status_t delete_entry(private_ike_sa_manager_t *this, entry_t *entry) |
d4425c1b | 240 | { |
697aa8f5 | 241 | linked_list_t *list = this->ike_sa_list; |
bdb141cb | 242 | iterator_t *iterator; |
191a26a6 | 243 | entry_t *current; |
697aa8f5 JH |
244 | status_t status; |
245 | ||
a0753941 | 246 | iterator = list->create_iterator(list, TRUE); |
697aa8f5 | 247 | |
4a5bba25 | 248 | status = NOT_FOUND; |
697aa8f5 | 249 | |
191a26a6 | 250 | while (iterator->iterate(iterator, (void**)¤t)) |
d4425c1b | 251 | { |
191a26a6 | 252 | if (current == entry) |
d4425c1b | 253 | { |
a095243f MW |
254 | /* mark it, so now new threads can get this entry */ |
255 | entry->driveout_new_threads = TRUE; | |
256 | /* wait until all workers have done their work */ | |
257 | while (entry->waiting_threads) | |
258 | { | |
259 | /* wake up all */ | |
260 | pthread_cond_broadcast(&(entry->condvar)); | |
261 | /* they will wake us again when their work is done */ | |
262 | pthread_cond_wait(&(entry->condvar), &(this->mutex)); | |
263 | } | |
264 | ||
b83806d8 | 265 | DBG2(DBG_MGR, "found entry by pointer, deleting it"); |
12c3e4c8 | 266 | iterator->remove(iterator); |
60356f33 | 267 | entry_destroy(entry); |
697aa8f5 JH |
268 | status = SUCCESS; |
269 | break; | |
d4425c1b MW |
270 | } |
271 | } | |
272 | iterator->destroy(iterator); | |
60356f33 | 273 | return status; |
d4425c1b MW |
274 | } |
275 | ||
c0593835 MW |
276 | /** |
277 | * Wait until no other thread is using an IKE_SA, return FALSE if entry not | |
278 | * acquireable | |
279 | */ | |
60356f33 | 280 | static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry) |
c0593835 MW |
281 | { |
282 | if (entry->driveout_new_threads) | |
283 | { | |
284 | /* we are not allowed to get this */ | |
285 | return FALSE; | |
286 | } | |
287 | while (entry->checked_out && !entry->driveout_waiting_threads) | |
288 | { | |
289 | /* so wait until we can get it for us. | |
b9e363f8 | 290 | * we register us as waiting. */ |
c0593835 MW |
291 | entry->waiting_threads++; |
292 | pthread_cond_wait(&(entry->condvar), &(this->mutex)); | |
293 | entry->waiting_threads--; | |
294 | } | |
295 | /* hm, a deletion request forbids us to get this SA, get next one */ | |
296 | if (entry->driveout_waiting_threads) | |
297 | { | |
298 | /* we must signal here, others may be waiting on it, too */ | |
299 | pthread_cond_signal(&(entry->condvar)); | |
300 | return FALSE; | |
301 | } | |
302 | return TRUE; | |
303 | } | |
472217f1 MW |
304 | |
305 | /** | |
39b2903f | 306 | * Implementation of private_ike_sa_manager_t.get_next_spi. |
472217f1 | 307 | */ |
d048df5c | 308 | static u_int64_t get_next_spi(private_ike_sa_manager_t *this) |
472217f1 | 309 | { |
2192375b MW |
310 | u_int64_t spi; |
311 | ||
b9e363f8 MW |
312 | this->randomizer->get_pseudo_random_bytes(this->randomizer, sizeof(spi), |
313 | (u_int8_t*)&spi); | |
2192375b | 314 | return spi; |
472217f1 MW |
315 | } |
316 | ||
c60c7694 MW |
317 | /** |
318 | * Implementation of of ike_sa_manager.checkout. | |
319 | */ | |
320 | static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id) | |
321 | { | |
c60c7694 | 322 | ike_sa_t *ike_sa = NULL; |
b9e363f8 | 323 | entry_t *entry; |
c60c7694 | 324 | |
b9e363f8 MW |
325 | DBG2(DBG_MGR, "checkout IKE_SA: %J, %d IKE_SAs in manager", |
326 | ike_sa_id, this->ike_sa_list->get_count(this->ike_sa_list)); | |
c60c7694 | 327 | |
c60c7694 | 328 | pthread_mutex_lock(&(this->mutex)); |
b9e363f8 MW |
329 | if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) |
330 | { | |
331 | if (wait_for_entry(this, entry)) | |
332 | { | |
333 | DBG2(DBG_MGR, "IKE_SA successfully checked out"); | |
334 | entry->checked_out = TRUE; | |
335 | ike_sa = entry->ike_sa; | |
336 | } | |
337 | } | |
338 | pthread_mutex_unlock(&this->mutex); | |
339 | charon->bus->set_sa(charon->bus, ike_sa); | |
340 | return ike_sa; | |
341 | } | |
342 | ||
343 | /** | |
344 | * Implementation of of ike_sa_manager.checkout_new. | |
345 | */ | |
346 | static ike_sa_t *checkout_new(private_ike_sa_manager_t* this, bool initiator) | |
347 | { | |
348 | entry_t *entry; | |
349 | ike_sa_id_t *id; | |
c60c7694 | 350 | |
b9e363f8 MW |
351 | if (initiator) |
352 | { | |
353 | id = ike_sa_id_create(get_next_spi(this), 0, TRUE); | |
354 | } | |
355 | else | |
356 | { | |
357 | id = ike_sa_id_create(0, get_next_spi(this), FALSE); | |
358 | } | |
359 | entry = entry_create(id); | |
360 | pthread_mutex_lock(&this->mutex); | |
361 | this->ike_sa_list->insert_last(this->ike_sa_list, entry); | |
362 | entry->checked_out = TRUE; | |
363 | pthread_mutex_unlock(&this->mutex); | |
364 | DBG2(DBG_MGR, "created IKE_SA: %J, %d IKE_SAs in manager", | |
365 | id, this->ike_sa_list->get_count(this->ike_sa_list)); | |
e0fe7651 | 366 | id->destroy(id); |
b9e363f8 MW |
367 | return entry->ike_sa; |
368 | } | |
369 | ||
370 | /** | |
371 | * Implementation of of ike_sa_manager.checkout_by_id. | |
372 | */ | |
373 | static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, | |
374 | message_t *message) | |
375 | { | |
376 | entry_t *entry; | |
377 | ike_sa_t *ike_sa = NULL; | |
378 | ike_sa_id_t *id = message->get_ike_sa_id(message); | |
379 | id = id->clone(id); | |
380 | id->switch_initiator(id); | |
381 | ||
382 | DBG2(DBG_MGR, "checkout IKE_SA: %J by message, %d IKE_SAs in manager", | |
383 | id, this->ike_sa_list->get_count(this->ike_sa_list)); | |
c60c7694 | 384 | |
b9e363f8 MW |
385 | if (message->get_request(message) && |
386 | message->get_exchange_type(message) == IKE_SA_INIT) | |
c60c7694 | 387 | { |
b9e363f8 MW |
388 | /* IKE_SA_INIT request. Check for an IKE_SA with such a message hash. */ |
389 | iterator_t *iterator; | |
390 | chunk_t data, hash; | |
b9e363f8 MW |
391 | |
392 | data = message->get_packet_data(message); | |
393 | this->hasher->allocate_hash(this->hasher, data, &hash); | |
394 | chunk_free(&data); | |
395 | ||
396 | pthread_mutex_lock(&this->mutex); | |
397 | iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); | |
398 | while (iterator->iterate(iterator, (void**)&entry)) | |
c60c7694 | 399 | { |
b9e363f8 | 400 | if (chunk_equals(hash, entry->init_hash)) |
c60c7694 | 401 | { |
b9e363f8 | 402 | if (entry->message_id == 0) |
c60c7694 | 403 | { |
80b71625 MW |
404 | iterator->destroy(iterator); |
405 | pthread_mutex_unlock(&this->mutex); | |
406 | chunk_free(&hash); | |
407 | id->destroy(id); | |
408 | DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing"); | |
409 | return NULL; | |
c60c7694 | 410 | } |
b9e363f8 MW |
411 | else if (wait_for_entry(this, entry)) |
412 | { | |
413 | DBG2(DBG_MGR, "IKE_SA checked out by hash"); | |
414 | entry->checked_out = TRUE; | |
415 | entry->message_id = message->get_message_id(message); | |
416 | ike_sa = entry->ike_sa; | |
417 | } | |
418 | break; | |
c60c7694 MW |
419 | } |
420 | } | |
b9e363f8 MW |
421 | iterator->destroy(iterator); |
422 | pthread_mutex_unlock(&this->mutex); | |
80b71625 | 423 | |
b9e363f8 | 424 | if (ike_sa == NULL) |
c60c7694 | 425 | { |
80b71625 MW |
426 | if (id->get_responder_spi(id) == 0 && |
427 | message->get_exchange_type(message) == IKE_SA_INIT) | |
428 | { | |
429 | /* no IKE_SA found, create a new one */ | |
430 | id->set_responder_spi(id, get_next_spi(this)); | |
431 | entry = entry_create(id); | |
432 | ||
433 | pthread_mutex_lock(&this->mutex); | |
434 | this->ike_sa_list->insert_last(this->ike_sa_list, entry); | |
435 | entry->checked_out = TRUE; | |
436 | entry->message_id = message->get_message_id(message); | |
437 | pthread_mutex_unlock(&this->mutex); | |
438 | entry->init_hash = hash; | |
439 | ike_sa = entry->ike_sa; | |
440 | } | |
441 | else | |
442 | { | |
443 | DBG1(DBG_MGR, "ignoring message for %J, no such IKE_SA", id); | |
444 | } | |
c60c7694 MW |
445 | } |
446 | else | |
447 | { | |
b9e363f8 | 448 | chunk_free(&hash); |
c60c7694 | 449 | } |
b9e363f8 MW |
450 | id->destroy(id); |
451 | charon->bus->set_sa(charon->bus, ike_sa); | |
452 | return ike_sa; | |
c60c7694 | 453 | } |
b9e363f8 MW |
454 | |
455 | pthread_mutex_lock(&(this->mutex)); | |
456 | if (get_entry_by_id(this, id, &entry) == SUCCESS) | |
c60c7694 | 457 | { |
b9e363f8 MW |
458 | /* only check out if we are not processing this request */ |
459 | if (message->get_request(message) && | |
460 | message->get_message_id(message) == entry->message_id) | |
461 | { | |
80b71625 MW |
462 | DBG1(DBG_MGR, "ignoring request with ID %d, already processing", |
463 | entry->message_id); | |
b9e363f8 MW |
464 | } |
465 | else if (wait_for_entry(this, entry)) | |
466 | { | |
467 | ike_sa_id_t *ike_id = entry->ike_sa->get_id(entry->ike_sa); | |
468 | DBG2(DBG_MGR, "IKE_SA successfully checked out"); | |
469 | entry->checked_out = TRUE; | |
470 | entry->message_id = message->get_message_id(message); | |
471 | if (ike_id->get_responder_spi(ike_id) == 0) | |
472 | { | |
473 | ike_id->set_responder_spi(ike_id, id->get_responder_spi(id)); | |
474 | } | |
475 | ike_sa = entry->ike_sa; | |
476 | } | |
c60c7694 | 477 | } |
b9e363f8 MW |
478 | pthread_mutex_unlock(&this->mutex); |
479 | id->destroy(id); | |
c60c7694 MW |
480 | charon->bus->set_sa(charon->bus, ike_sa); |
481 | return ike_sa; | |
482 | } | |
483 | ||
472217f1 | 484 | /** |
a655f5c0 | 485 | * Implementation of of ike_sa_manager.checkout_by_id. |
207dd9b5 | 486 | */ |
c60c7694 MW |
487 | static ike_sa_t* checkout_by_peer(private_ike_sa_manager_t *this, |
488 | host_t *my_host, host_t *other_host, | |
489 | identification_t *my_id, | |
490 | identification_t *other_id) | |
207dd9b5 | 491 | { |
c0593835 | 492 | iterator_t *iterator; |
191a26a6 | 493 | entry_t *entry; |
c0593835 MW |
494 | ike_sa_t *ike_sa = NULL; |
495 | ||
d048df5c | 496 | pthread_mutex_lock(&(this->mutex)); |
207dd9b5 | 497 | |
c0593835 | 498 | iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); |
191a26a6 | 499 | while (iterator->iterate(iterator, (void**)&entry)) |
c0593835 | 500 | { |
c0593835 | 501 | identification_t *found_my_id, *found_other_id; |
a655f5c0 | 502 | host_t *found_my_host, *found_other_host; |
c0593835 MW |
503 | int wc; |
504 | ||
c0593835 MW |
505 | if (!wait_for_entry(this, entry)) |
506 | { | |
507 | continue; | |
508 | } | |
509 | ||
6fe03b0a MW |
510 | if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING) |
511 | { | |
512 | /* skip IKE_SA which are not useable */ | |
513 | continue; | |
514 | } | |
515 | ||
c0593835 MW |
516 | found_my_id = entry->ike_sa->get_my_id(entry->ike_sa); |
517 | found_other_id = entry->ike_sa->get_other_id(entry->ike_sa); | |
a655f5c0 MW |
518 | found_my_host = entry->ike_sa->get_my_host(entry->ike_sa); |
519 | found_other_host = entry->ike_sa->get_other_host(entry->ike_sa); | |
c0593835 | 520 | |
fe04e93a MW |
521 | if (found_my_id->get_type(found_my_id) == ID_ANY && |
522 | found_other_id->get_type(found_other_id) == ID_ANY) | |
c0593835 MW |
523 | { |
524 | /* IKE_SA has no IDs yet, so we can't use it */ | |
525 | continue; | |
526 | } | |
527 | ||
a655f5c0 MW |
528 | /* compare ID and hosts. Supplied ID may contain wildcards, and IP |
529 | * may be %any. */ | |
530 | if ((found_my_host->is_anyaddr(found_my_host) || | |
531 | my_host->ip_equals(my_host, found_my_host)) && | |
532 | (found_other_host->is_anyaddr(found_other_host) || | |
533 | other_host->ip_equals(other_host, found_other_host)) && | |
534 | found_my_id->matches(found_my_id, my_id, &wc) && | |
c0593835 MW |
535 | found_other_id->matches(found_other_id, other_id, &wc)) |
536 | { | |
537 | /* looks good, we take this one */ | |
b83806d8 | 538 | DBG2(DBG_MGR, "found an existing IKE_SA for %H[%D]...%H[%D]", |
60356f33 | 539 | my_host, other_host, my_id, other_id); |
c0593835 MW |
540 | entry->checked_out = TRUE; |
541 | ike_sa = entry->ike_sa; | |
542 | } | |
543 | } | |
544 | iterator->destroy(iterator); | |
545 | ||
546 | if (!ike_sa) | |
547 | { | |
548 | u_int64_t initiator_spi; | |
60356f33 | 549 | entry_t *new_entry; |
c0593835 MW |
550 | ike_sa_id_t *new_ike_sa_id; |
551 | ||
a095243f | 552 | initiator_spi = get_next_spi(this); |
c0593835 MW |
553 | new_ike_sa_id = ike_sa_id_create(0, 0, TRUE); |
554 | new_ike_sa_id->set_initiator_spi(new_ike_sa_id, initiator_spi); | |
555 | ||
556 | /* create entry */ | |
60356f33 | 557 | new_entry = entry_create(new_ike_sa_id); |
b83806d8 | 558 | DBG2(DBG_MGR, "created IKE_SA: %J", new_ike_sa_id); |
c0593835 MW |
559 | new_ike_sa_id->destroy(new_ike_sa_id); |
560 | ||
60356f33 | 561 | this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); |
c0593835 MW |
562 | |
563 | /* check ike_sa out */ | |
b83806d8 | 564 | DBG2(DBG_MGR, "new IKE_SA created for IDs [%D]...[%D]", my_id, other_id); |
60356f33 MW |
565 | new_entry->checked_out = TRUE; |
566 | ike_sa = new_entry->ike_sa; | |
c0593835 | 567 | } |
d048df5c | 568 | pthread_mutex_unlock(&(this->mutex)); |
60356f33 | 569 | charon->bus->set_sa(charon->bus, ike_sa); |
c0593835 | 570 | return ike_sa; |
207dd9b5 JH |
571 | } |
572 | ||
573 | /** | |
c60c7694 | 574 | * Implementation of of ike_sa_manager.checkout_by_id. |
472217f1 | 575 | */ |
c60c7694 MW |
576 | static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, |
577 | bool child) | |
472217f1 | 578 | { |
c60c7694 MW |
579 | iterator_t *iterator, *children; |
580 | entry_t *entry; | |
c0593835 | 581 | ike_sa_t *ike_sa = NULL; |
c60c7694 | 582 | child_sa_t *child_sa; |
d048df5c | 583 | |
472217f1 | 584 | pthread_mutex_lock(&(this->mutex)); |
d048df5c | 585 | |
c60c7694 MW |
586 | iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); |
587 | while (iterator->iterate(iterator, (void**)&entry)) | |
472217f1 | 588 | { |
c60c7694 | 589 | if (wait_for_entry(this, entry)) |
32b6500f | 590 | { |
c60c7694 MW |
591 | /* look for a child with such a reqid ... */ |
592 | if (child) | |
32b6500f | 593 | { |
c60c7694 MW |
594 | children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); |
595 | while (children->iterate(children, (void**)&child_sa)) | |
596 | { | |
597 | if (child_sa->get_reqid(child_sa) == id) | |
598 | { | |
599 | ike_sa = entry->ike_sa; | |
600 | break; | |
601 | } | |
602 | } | |
603 | children->destroy(children); | |
32b6500f | 604 | } |
c60c7694 | 605 | else /* ... or for a IKE_SA with such a unique id */ |
32b6500f | 606 | { |
c60c7694 MW |
607 | if (entry->ike_sa->get_unique_id(entry->ike_sa) == id) |
608 | { | |
609 | ike_sa = entry->ike_sa; | |
610 | } | |
611 | } | |
612 | /* got one, return */ | |
613 | if (ike_sa) | |
614 | { | |
615 | entry->checked_out = TRUE; | |
616 | break; | |
d048df5c | 617 | } |
d048df5c | 618 | } |
472217f1 | 619 | } |
c60c7694 | 620 | iterator->destroy(iterator); |
472217f1 | 621 | pthread_mutex_unlock(&(this->mutex)); |
47f50278 | 622 | |
60356f33 | 623 | charon->bus->set_sa(charon->bus, ike_sa); |
c0593835 | 624 | return ike_sa; |
472217f1 MW |
625 | } |
626 | ||
f2ee13a7 | 627 | /** |
c60c7694 | 628 | * Implementation of of ike_sa_manager.checkout_by_name. |
f2ee13a7 | 629 | */ |
c60c7694 MW |
630 | static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, |
631 | bool child) | |
f2ee13a7 | 632 | { |
c60c7694 | 633 | iterator_t *iterator, *children; |
191a26a6 | 634 | entry_t *entry; |
45f76a7d | 635 | ike_sa_t *ike_sa = NULL; |
c60c7694 | 636 | child_sa_t *child_sa; |
f2ee13a7 MW |
637 | |
638 | pthread_mutex_lock(&(this->mutex)); | |
639 | ||
640 | iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); | |
191a26a6 | 641 | while (iterator->iterate(iterator, (void**)&entry)) |
f2ee13a7 | 642 | { |
c0593835 | 643 | if (wait_for_entry(this, entry)) |
f2ee13a7 | 644 | { |
c60c7694 MW |
645 | /* look for a child with such a policy name ... */ |
646 | if (child) | |
647 | { | |
648 | children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa); | |
649 | while (children->iterate(children, (void**)&child_sa)) | |
650 | { | |
651 | if (streq(child_sa->get_name(child_sa), name)) | |
652 | { | |
653 | ike_sa = entry->ike_sa; | |
654 | break; | |
655 | } | |
656 | } | |
657 | children->destroy(children); | |
658 | } | |
659 | else /* ... or for a IKE_SA with such a connection name */ | |
660 | { | |
661 | if (streq(entry->ike_sa->get_name(entry->ike_sa), name)) | |
662 | { | |
663 | ike_sa = entry->ike_sa; | |
664 | } | |
665 | } | |
666 | /* got one, return */ | |
667 | if (ike_sa) | |
c0593835 | 668 | { |
c0593835 | 669 | entry->checked_out = TRUE; |
c0593835 MW |
670 | break; |
671 | } | |
f2ee13a7 MW |
672 | } |
673 | } | |
674 | iterator->destroy(iterator); | |
675 | pthread_mutex_unlock(&(this->mutex)); | |
676 | ||
60356f33 | 677 | charon->bus->set_sa(charon->bus, ike_sa); |
45f76a7d | 678 | return ike_sa; |
f2ee13a7 MW |
679 | } |
680 | ||
681 | /** | |
191a26a6 | 682 | * Iterator hook for iterate, gets ike_sas instead of entries |
f2ee13a7 | 683 | */ |
c60c7694 MW |
684 | static bool iterator_hook(private_ike_sa_manager_t* this, entry_t *in, |
685 | ike_sa_t **out) | |
f2ee13a7 | 686 | { |
c60c7694 MW |
687 | /* check out entry */ |
688 | if (wait_for_entry(this, in)) | |
689 | { | |
690 | *out = in->ike_sa; | |
691 | return TRUE; | |
692 | } | |
693 | return FALSE; | |
f2ee13a7 MW |
694 | } |
695 | ||
e168ee17 | 696 | /** |
60356f33 | 697 | * Implementation of ike_sa_manager_t.create_iterator. |
e168ee17 | 698 | */ |
60356f33 | 699 | static iterator_t *create_iterator(private_ike_sa_manager_t* this) |
e168ee17 | 700 | { |
60356f33 | 701 | iterator_t *iterator = this->ike_sa_list->create_iterator_locked( |
c60c7694 | 702 | this->ike_sa_list, &this->mutex); |
191a26a6 | 703 | /* register hook to iterator over ike_sas, not entries */ |
c60c7694 | 704 | iterator->set_iterator_hook(iterator, (iterator_hook_t*)iterator_hook, this); |
60356f33 | 705 | return iterator; |
e168ee17 MW |
706 | } |
707 | ||
f0ddd8ae | 708 | /** |
39b2903f | 709 | * Implementation of ike_sa_manager_t.checkin. |
f0ddd8ae | 710 | */ |
d4425c1b | 711 | static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) |
79538669 JH |
712 | { |
713 | /* to check the SA back in, we look for the pointer of the ike_sa | |
472217f1 MW |
714 | * in all entries. |
715 | * We can't search by SPI's since the MAY have changed (e.g. on reception | |
716 | * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary... | |
717 | */ | |
718 | status_t retval; | |
60356f33 | 719 | entry_t *entry; |
3dd3c5f3 MW |
720 | ike_sa_id_t *ike_sa_id; |
721 | ||
722 | ike_sa_id = ike_sa->get_id(ike_sa); | |
723 | ||
b83806d8 | 724 | DBG2(DBG_MGR, "checkin IKE_SA: %J", ike_sa_id); |
d4425c1b | 725 | |
472217f1 MW |
726 | pthread_mutex_lock(&(this->mutex)); |
727 | ||
472217f1 | 728 | /* look for the entry */ |
a095243f | 729 | if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) |
472217f1 MW |
730 | { |
731 | /* ike_sa_id must be updated */ | |
732 | entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa)); | |
733 | /* signal waiting threads */ | |
734 | entry->checked_out = FALSE; | |
b9e363f8 | 735 | entry->message_id = -1; |
b83806d8 | 736 | DBG2(DBG_MGR, "check-in of IKE_SA successful."); |
472217f1 | 737 | pthread_cond_signal(&(entry->condvar)); |
79538669 | 738 | retval = SUCCESS; |
472217f1 MW |
739 | } |
740 | else | |
741 | { | |
b83806d8 | 742 | DBG2(DBG_MGR, "tried to check in nonexisting IKE_SA"); |
f2ee13a7 | 743 | /* this SA is no more, this REALLY should not happen */ |
472217f1 MW |
744 | retval = NOT_FOUND; |
745 | } | |
3dd3c5f3 | 746 | |
b83806d8 | 747 | DBG2(DBG_MGR, "%d IKE_SAs in manager now", |
60356f33 | 748 | this->ike_sa_list->get_count(this->ike_sa_list)); |
472217f1 | 749 | pthread_mutex_unlock(&(this->mutex)); |
47f50278 | 750 | |
60356f33 | 751 | charon->bus->set_sa(charon->bus, NULL); |
472217f1 MW |
752 | return retval; |
753 | } | |
754 | ||
755 | ||
f0ddd8ae | 756 | /** |
4a5bba25 | 757 | * Implementation of ike_sa_manager_t.checkin_and_destroy. |
f0ddd8ae | 758 | */ |
4a5bba25 | 759 | static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) |
79538669 JH |
760 | { |
761 | /* deletion is a bit complex, we must garant that no thread is waiting for | |
762 | * this SA. | |
472217f1 | 763 | * We take this SA from the list, and start signaling while threads |
79538669 | 764 | * are in the condvar. |
472217f1 | 765 | */ |
60356f33 | 766 | entry_t *entry; |
472217f1 | 767 | status_t retval; |
3dd3c5f3 MW |
768 | ike_sa_id_t *ike_sa_id; |
769 | ||
770 | ike_sa_id = ike_sa->get_id(ike_sa); | |
b83806d8 | 771 | DBG2(DBG_MGR, "checkin and destroy IKE_SA: %J", ike_sa_id); |
79538669 | 772 | |
472217f1 | 773 | pthread_mutex_lock(&(this->mutex)); |
79538669 | 774 | |
a095243f | 775 | if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) |
472217f1 | 776 | { |
a095243f | 777 | /* drive out waiting threads, as we are in hurry */ |
d4425c1b | 778 | entry->driveout_waiting_threads = TRUE; |
a095243f MW |
779 | |
780 | delete_entry(this, entry); | |
781 | ||
b83806d8 | 782 | DBG2(DBG_MGR, "check-in and destroy of IKE_SA successful"); |
472217f1 MW |
783 | retval = SUCCESS; |
784 | } | |
785 | else | |
786 | { | |
b83806d8 | 787 | DBG2(DBG_MGR, "tried to check-in and delete nonexisting IKE_SA"); |
472217f1 MW |
788 | retval = NOT_FOUND; |
789 | } | |
d048df5c | 790 | |
472217f1 | 791 | pthread_mutex_unlock(&(this->mutex)); |
60356f33 | 792 | charon->bus->set_sa(charon->bus, ike_sa); |
472217f1 MW |
793 | return retval; |
794 | } | |
795 | ||
4deb8948 MW |
796 | /** |
797 | * Implementation of ike_sa_manager_t.get_half_open_count. | |
798 | */ | |
799 | static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip) | |
800 | { | |
801 | iterator_t *iterator; | |
802 | entry_t *entry; | |
803 | int count = 0; | |
804 | ||
805 | pthread_mutex_lock(&(this->mutex)); | |
806 | iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); | |
807 | while (iterator->iterate(iterator, (void**)&entry)) | |
808 | { | |
809 | /* we check if we have a responder CONNECTING IKE_SA without checkout */ | |
810 | if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) && | |
811 | entry->ike_sa->get_state(entry->ike_sa) == IKE_CONNECTING) | |
812 | { | |
813 | /* if we have a host, we have wait until no other uses the IKE_SA */ | |
814 | if (ip) | |
815 | { | |
816 | if (wait_for_entry(this, entry) && ip->ip_equals(ip, | |
817 | entry->ike_sa->get_other_host(entry->ike_sa))) | |
818 | { | |
819 | count++; | |
820 | } | |
821 | } | |
822 | else | |
823 | { | |
824 | count++; | |
825 | } | |
826 | } | |
827 | } | |
828 | iterator->destroy(iterator); | |
829 | ||
830 | pthread_mutex_unlock(&(this->mutex)); | |
831 | return count; | |
832 | } | |
833 | ||
f0ddd8ae | 834 | /** |
39b2903f | 835 | * Implementation of ike_sa_manager_t.destroy. |
f0ddd8ae | 836 | */ |
d048df5c | 837 | static void destroy(private_ike_sa_manager_t *this) |
472217f1 MW |
838 | { |
839 | /* destroy all list entries */ | |
697aa8f5 | 840 | linked_list_t *list = this->ike_sa_list; |
bdb141cb | 841 | iterator_t *iterator; |
60356f33 | 842 | entry_t *entry; |
d4425c1b MW |
843 | |
844 | pthread_mutex_lock(&(this->mutex)); | |
b83806d8 | 845 | DBG2(DBG_MGR, "going to destroy IKE_SA manager and all managed IKE_SA's"); |
d4425c1b | 846 | /* Step 1: drive out all waiting threads */ |
b83806d8 | 847 | DBG2(DBG_MGR, "set driveout flags for all stored IKE_SA's"); |
a0753941 | 848 | iterator = list->create_iterator(list, TRUE); |
191a26a6 | 849 | while (iterator->iterate(iterator, (void**)&entry)) |
472217f1 | 850 | { |
d4425c1b MW |
851 | /* do not accept new threads, drive out waiting threads */ |
852 | entry->driveout_new_threads = TRUE; | |
853 | entry->driveout_waiting_threads = TRUE; | |
854 | } | |
b83806d8 | 855 | DBG2(DBG_MGR, "wait for all threads to leave IKE_SA's"); |
d4425c1b MW |
856 | /* Step 2: wait until all are gone */ |
857 | iterator->reset(iterator); | |
191a26a6 | 858 | while (iterator->iterate(iterator, (void**)&entry)) |
d4425c1b | 859 | { |
d4425c1b MW |
860 | while (entry->waiting_threads) |
861 | { | |
862 | /* wake up all */ | |
4a5bba25 | 863 | pthread_cond_broadcast(&(entry->condvar)); |
d4425c1b | 864 | /* go sleeping until they are gone */ |
4a5bba25 | 865 | pthread_cond_wait(&(entry->condvar), &(this->mutex)); |
d4425c1b MW |
866 | } |
867 | } | |
b83806d8 | 868 | DBG2(DBG_MGR, "delete all IKE_SA's"); |
b82908b8 MW |
869 | /* Step 3: initiate deletion of all IKE_SAs */ |
870 | iterator->reset(iterator); | |
191a26a6 | 871 | while (iterator->iterate(iterator, (void**)&entry)) |
b82908b8 | 872 | { |
b82908b8 MW |
873 | entry->ike_sa->delete(entry->ike_sa); |
874 | } | |
83cad34b | 875 | iterator->destroy(iterator); |
b82908b8 | 876 | |
b83806d8 | 877 | DBG2(DBG_MGR, "destroy all entries"); |
b82908b8 | 878 | /* Step 4: destroy all entries */ |
55bbff11 | 879 | list->destroy_function(list, (void*)entry_destroy); |
d4425c1b | 880 | pthread_mutex_unlock(&(this->mutex)); |
2192375b MW |
881 | |
882 | this->randomizer->destroy(this->randomizer); | |
b9e363f8 | 883 | this->hasher->destroy(this->hasher); |
2192375b | 884 | |
5113680f | 885 | free(this); |
472217f1 MW |
886 | } |
887 | ||
f0ddd8ae | 888 | /* |
39b2903f | 889 | * Described in header. |
f0ddd8ae | 890 | */ |
472217f1 MW |
891 | ike_sa_manager_t *ike_sa_manager_create() |
892 | { | |
5113680f | 893 | private_ike_sa_manager_t *this = malloc_thing(private_ike_sa_manager_t); |
79538669 | 894 | |
472217f1 | 895 | /* assign public functions */ |
d048df5c | 896 | this->public.destroy = (void(*)(ike_sa_manager_t*))destroy; |
c0593835 | 897 | this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout; |
b9e363f8 MW |
898 | this->public.checkout_new = (ike_sa_t*(*)(ike_sa_manager_t*,bool))checkout_new; |
899 | this->public.checkout_by_message = (ike_sa_t*(*)(ike_sa_manager_t*,message_t*))checkout_by_message; | |
c60c7694 MW |
900 | this->public.checkout_by_peer = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_peer; |
901 | this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id; | |
902 | this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name; | |
60356f33 | 903 | this->public.create_iterator = (iterator_t*(*)(ike_sa_manager_t*))create_iterator; |
f2ee13a7 | 904 | this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin; |
4a5bba25 | 905 | this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy; |
4deb8948 | 906 | this->public.get_half_open_count = (int(*)(ike_sa_manager_t*,host_t*))get_half_open_count; |
f0ddd8ae | 907 | |
60356f33 | 908 | /* initialize private variables */ |
697aa8f5 | 909 | this->ike_sa_list = linked_list_create(); |
c60c7694 | 910 | pthread_mutex_init(&this->mutex, NULL); |
2192375b | 911 | this->randomizer = randomizer_create(); |
b9e363f8 | 912 | this->hasher = hasher_create(HASH_SHA1); |
60356f33 | 913 | |
c60c7694 | 914 | return &this->public; |
472217f1 | 915 | } |