added .gitignore files to NM applet
[strongswan.git] / src / charon / sa / connect_manager.c
1 /*
2 * Copyright (C) 2007-2008 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "connect_manager.h"
17
18 #include <math.h>
19
20 #include <daemon.h>
21 #include <utils/mutex.h>
22 #include <utils/linked_list.h>
23 #include <crypto/hashers/hasher.h>
24
25 #include <processing/jobs/callback_job.h>
26 #include <processing/jobs/initiate_mediation_job.h>
27 #include <encoding/payloads/endpoint_notify.h>
28
29 /* base timeout
30 * the check interval is ME_INTERVAL */
31 #define ME_INTERVAL 25 /* ms */
32 /* retransmission timeout is first ME_INTERVAL for ME_BOOST retransmissions
33 * then gets reduced to ME_INTERVAL * ME_RETRANS_BASE ^ (sent retransmissions - ME_BOOST). */
34 /* number of initial retransmissions sent in short interval */
35 #define ME_BOOST 2
36 /* base for retransmissions */
37 #define ME_RETRANS_BASE 1.8
38 /* max number of retransmissions */
39 #define ME_MAX_RETRANS 13
40
41 /* time to wait before the initiator finishes the connectivity checks after
42 * the first check has succeeded */
43 #define ME_WAIT_TO_FINISH 1000 /* ms */
44
45
46 typedef struct private_connect_manager_t private_connect_manager_t;
47
48 /**
49 * Additional private members of connect_manager_t.
50 */
51 struct private_connect_manager_t {
52 /**
53 * Public interface of connect_manager_t.
54 */
55 connect_manager_t public;
56
57 /**
58 * Lock for exclusivly accessing the manager.
59 */
60 mutex_t *mutex;
61
62 /**
63 * Hasher to generate signatures
64 */
65 hasher_t *hasher;
66
67 /**
68 * Linked list with initiated mediated connections
69 */
70 linked_list_t *initiated;
71
72 /**
73 * Linked list with checklists (hash table with connect ID as key would be better).
74 */
75 linked_list_t *checklists;
76 };
77
78 typedef enum check_state_t check_state_t;
79
80 enum check_state_t {
81 CHECK_NONE,
82 CHECK_WAITING,
83 CHECK_IN_PROGRESS,
84 CHECK_SUCCEEDED,
85 CHECK_FAILED
86 };
87
88 typedef struct endpoint_pair_t endpoint_pair_t;
89
90 /**
91 * An entry in the check list.
92 */
93 struct endpoint_pair_t {
94 /** pair id */
95 u_int32_t id;
96
97 /** priority */
98 u_int64_t priority;
99
100 /** local endpoint */
101 host_t *local;
102
103 /** remote endpoint */
104 host_t *remote;
105
106 /** state */
107 check_state_t state;
108
109 /** number of retransmissions */
110 u_int32_t retransmitted;
111
112 /** the generated packet */
113 packet_t *packet;
114 };
115
116 /**
117 * Destroys an endpoint pair
118 */
119 static void endpoint_pair_destroy(endpoint_pair_t *this)
120 {
121 DESTROY_IF(this->local);
122 DESTROY_IF(this->remote);
123 DESTROY_IF(this->packet);
124 free(this);
125 }
126
127 /**
128 * Creates a new entry for the list.
129 */
130 static endpoint_pair_t *endpoint_pair_create(endpoint_notify_t *initiator,
131 endpoint_notify_t *responder, bool initiator_is_local)
132 {
133 endpoint_pair_t *this = malloc_thing(endpoint_pair_t);
134
135 this->id = 0;
136
137 u_int32_t pi = initiator->get_priority(initiator);
138 u_int32_t pr = responder->get_priority(responder);
139 this->priority = pow(2, 32) * min(pi, pr) + 2 * max(pi, pr) + (pi > pr ? 1 : 0);
140
141 this->local = initiator_is_local ? initiator->get_base(initiator) : responder->get_base(responder);
142 this->local = this->local->clone(this->local);
143 this->remote = initiator_is_local ? responder->get_host(responder) : initiator->get_host(initiator);
144 this->remote = this->remote->clone(this->remote);
145
146 this->state = CHECK_WAITING;
147 this->retransmitted = 0;
148 this->packet = NULL;
149
150 return this;
151 }
152
153
154 typedef struct check_list_t check_list_t;
155
156 /**
157 * An entry in the linked list.
158 */
159 struct check_list_t {
160
161 struct {
162 /** initiator's id */
163 identification_t *id;
164
165 /** initiator's key */
166 chunk_t key;
167
168 /** initiator's endpoints */
169 linked_list_t *endpoints;
170 } initiator;
171
172 struct {
173 /** responder's id */
174 identification_t *id;
175
176 /** responder's key */
177 chunk_t key;
178
179 /** responder's endpoints */
180 linked_list_t *endpoints;
181 } responder;
182
183 /** connect id */
184 chunk_t connect_id;
185
186 /** list of endpoint pairs */
187 linked_list_t *pairs;
188
189 /** pairs queued for triggered checks */
190 linked_list_t *triggered;
191
192 /** state */
193 check_state_t state;
194
195 /** TRUE if this is the initiator */
196 bool is_initiator;
197
198 /** TRUE if the initiator is finishing the checks */
199 bool is_finishing;
200
201 /** the current sender job */
202 job_t *sender;
203
204 };
205
206 /**
207 * Destroys a checklist
208 */
209 static void check_list_destroy(check_list_t *this)
210 {
211 DESTROY_IF(this->initiator.id);
212 DESTROY_IF(this->responder.id);
213
214 chunk_free(&this->connect_id);
215 chunk_free(&this->initiator.key);
216 chunk_free(&this->responder.key);
217
218 DESTROY_OFFSET_IF(this->initiator.endpoints, offsetof(endpoint_notify_t, destroy));
219 DESTROY_OFFSET_IF(this->responder.endpoints, offsetof(endpoint_notify_t, destroy));
220
221 DESTROY_FUNCTION_IF(this->pairs, (void*)endpoint_pair_destroy);
222 /* this list contains some of the same elements as contained in this->pairs */
223 DESTROY_IF(this->triggered);
224
225 free(this);
226 }
227
228 /**
229 * Creates a new checklist
230 */
231 static check_list_t *check_list_create(identification_t *initiator, identification_t *responder,
232 chunk_t connect_id, chunk_t initiator_key, linked_list_t *initiator_endpoints,
233 bool is_initiator)
234 {
235 check_list_t *this = malloc_thing(check_list_t);
236
237 this->connect_id = chunk_clone(connect_id);
238
239 this->initiator.id = initiator->clone(initiator);
240 this->initiator.key = chunk_clone(initiator_key);
241 this->initiator.endpoints = initiator_endpoints->clone_offset(initiator_endpoints, offsetof(endpoint_notify_t, clone));
242
243 this->responder.id = responder->clone(responder);
244 this->responder.key = chunk_empty;
245 this->responder.endpoints = NULL;
246
247 this->pairs = linked_list_create();
248 this->triggered = linked_list_create();
249 this->state = CHECK_NONE;
250 this->is_initiator = is_initiator;
251 this->is_finishing = FALSE;
252
253 return this;
254 }
255
256 typedef struct initiated_t initiated_t;
257
258 /**
259 * For an initiator, the data stored about initiated mediation connections
260 */
261 struct initiated_t {
262 /** my id */
263 identification_t *id;
264
265 /** peer id */
266 identification_t *peer_id;
267
268 /** list of mediated sas */
269 linked_list_t *mediated;
270 };
271
272 /**
273 * Destroys a queued initiation
274 */
275 static void initiated_destroy(initiated_t *this)
276 {
277 DESTROY_IF(this->id);
278 DESTROY_IF(this->peer_id);
279 this->mediated->destroy_offset(this->mediated, offsetof(ike_sa_id_t, destroy));
280 free(this);
281 }
282
283 /**
284 * Creates a queued initiation
285 */
286 static initiated_t *initiated_create(identification_t *id, identification_t *peer_id)
287 {
288 initiated_t *this = malloc_thing(initiated_t);
289
290 this->id = id->clone(id);
291 this->peer_id = peer_id->clone(peer_id);
292 this->mediated = linked_list_create();
293
294 return this;
295 }
296
297
298 typedef struct check_t check_t;
299
300 /**
301 * Data exchanged in a connectivity check
302 */
303 struct check_t {
304 /** message id */
305 u_int32_t mid;
306
307 /** source of the connectivity check */
308 host_t *src;
309
310 /** destination of the connectivity check */
311 host_t *dst;
312
313 /** connect id */
314 chunk_t connect_id;
315
316 /** endpoint */
317 endpoint_notify_t *endpoint;
318
319 /** raw endpoint payload (to verify the signature) */
320 chunk_t endpoint_raw;
321
322 /** connect auth */
323 chunk_t auth;
324 };
325
326 /**
327 * Destroys a connectivity check
328 */
329 static void check_destroy(check_t *this)
330 {
331 chunk_free(&this->connect_id);
332 chunk_free(&this->endpoint_raw);
333 chunk_free(&this->auth);
334 DESTROY_IF(this->src);
335 DESTROY_IF(this->dst);
336 DESTROY_IF(this->endpoint);
337 free(this);
338 }
339
340 /**
341 * Creates a new connectivity check
342 */
343 static check_t *check_create()
344 {
345 check_t *this = malloc_thing(check_t);
346
347 this->connect_id = chunk_empty;
348 this->auth = chunk_empty;
349 this->endpoint_raw = chunk_empty;
350 this->src = NULL;
351 this->dst = NULL;
352 this->endpoint = NULL;
353
354 this->mid = 0;
355
356 return this;
357 }
358
359 typedef struct callback_data_t callback_data_t;
360
361 /**
362 * Data required by several callback jobs used in this file
363 */
364 struct callback_data_t {
365 /** connect manager */
366 private_connect_manager_t *connect_manager;
367
368 /** connect id */
369 chunk_t connect_id;
370
371 /** message (pair) id */
372 u_int32_t mid;
373 };
374
375 /**
376 * Destroys a callback data object
377 */
378 static void callback_data_destroy(callback_data_t *this)
379 {
380 chunk_free(&this->connect_id);
381 free(this);
382 }
383
384 /**
385 * Creates a new callback data object
386 */
387 static callback_data_t *callback_data_create(private_connect_manager_t *connect_manager,
388 chunk_t connect_id)
389 {
390 callback_data_t *this = malloc_thing(callback_data_t);
391 this->connect_manager = connect_manager;
392 this->connect_id = chunk_clone(connect_id);
393 this->mid = 0;
394 return this;
395 }
396
397 /**
398 * Creates a new retransmission data object
399 */
400 static callback_data_t *retransmit_data_create(private_connect_manager_t *connect_manager,
401 chunk_t connect_id, u_int32_t mid)
402 {
403 callback_data_t *this = callback_data_create(connect_manager, connect_id);
404 this->mid = mid;
405 return this;
406 }
407
408 typedef struct initiate_data_t initiate_data_t;
409
410 /**
411 * Data required by the initiate mediated
412 */
413 struct initiate_data_t {
414 /** checklist */
415 check_list_t *checklist;
416
417 /** waiting mediated connections */
418 initiated_t *initiated;
419 };
420
421 /**
422 * Destroys a initiate data object
423 */
424 static void initiate_data_destroy(initiate_data_t *this)
425 {
426 check_list_destroy(this->checklist);
427 initiated_destroy(this->initiated);
428 free(this);
429 }
430
431 /**
432 * Creates a new initiate data object
433 */
434 static initiate_data_t *initiate_data_create(check_list_t *checklist, initiated_t *initiated)
435 {
436 initiate_data_t *this = malloc_thing(initiate_data_t);
437
438 this->checklist = checklist;
439 this->initiated = initiated;
440
441 return this;
442 }
443
444 /**
445 * Find an initiated connection by the peers' ids
446 */
447 static bool match_initiated_by_ids(initiated_t *current, identification_t *id,
448 identification_t *peer_id)
449 {
450 return id->equals(id, current->id) && peer_id->equals(peer_id, current->peer_id);
451 }
452
453 static status_t get_initiated_by_ids(private_connect_manager_t *this,
454 identification_t *id, identification_t *peer_id, initiated_t **initiated)
455 {
456 return this->initiated->find_first(this->initiated,
457 (linked_list_match_t)match_initiated_by_ids,
458 (void**)initiated, id, peer_id);
459 }
460
461 /**
462 * Removes data about initiated connections
463 */
464 static void remove_initiated(private_connect_manager_t *this, initiated_t *initiated)
465 {
466 iterator_t *iterator;
467 initiated_t *current;
468
469 iterator = this->initiated->create_iterator(this->initiated, TRUE);
470 while (iterator->iterate(iterator, (void**)&current))
471 {
472 if (current == initiated)
473 {
474 iterator->remove(iterator);
475 break;
476 }
477 }
478 iterator->destroy(iterator);
479 }
480
481 /**
482 * Find the checklist with a specific connect ID
483 */
484 static bool match_checklist_by_id(check_list_t *current, chunk_t *connect_id)
485 {
486 return chunk_equals(*connect_id, current->connect_id);
487 }
488
489 static status_t get_checklist_by_id(private_connect_manager_t *this,
490 chunk_t connect_id, check_list_t **check_list)
491 {
492 return this->checklists->find_first(this->checklists,
493 (linked_list_match_t)match_checklist_by_id,
494 (void**)check_list, &connect_id);
495 }
496
497 /**
498 * Removes a checklist
499 */
500 static void remove_checklist(private_connect_manager_t *this, check_list_t *checklist)
501 {
502 iterator_t *iterator;
503 check_list_t *current;
504
505 iterator = this->checklists->create_iterator(this->checklists, TRUE);
506 while (iterator->iterate(iterator, (void**)&current))
507 {
508 if (current == checklist)
509 {
510 iterator->remove(iterator);
511 break;
512 }
513 }
514 iterator->destroy(iterator);
515 }
516
517 /**
518 * Checks if a list of endpoint_notify_t contains a certain host_t
519 */
520 static bool match_endpoint_by_host(endpoint_notify_t *current, host_t *host)
521 {
522 return host->equals(host, current->get_host(current));
523 }
524
525 static status_t endpoints_contain(linked_list_t *endpoints, host_t *host, endpoint_notify_t **endpoint)
526 {
527 return endpoints->find_first(endpoints,
528 (linked_list_match_t)match_endpoint_by_host,
529 (void**)endpoint, host);
530 }
531
532 /**
533 * Inserts an endpoint pair into the list of pairs ordered by priority (high to low)
534 */
535 static void insert_pair_by_priority(linked_list_t *pairs, endpoint_pair_t *pair)
536 {
537 iterator_t *iterator;
538 endpoint_pair_t *current;
539 bool inserted = FALSE;
540
541 iterator = pairs->create_iterator(pairs, TRUE);
542 while (iterator->iterate(iterator, (void**)&current))
543 {
544 if (current->priority < pair->priority)
545 {
546 iterator->insert_before(iterator, pair);
547 inserted = TRUE;
548 break;
549 }
550 }
551 iterator->destroy(iterator);
552
553 if (!inserted)
554 {
555 pairs->insert_last(pairs, pair);
556 }
557 }
558
559 /**
560 * Searches a list of endpoint_pair_t for a pair with specific host_ts
561 */
562 static bool match_pair_by_hosts(endpoint_pair_t *current, host_t *local, host_t *remote)
563 {
564 return local->equals(local, current->local) && remote->equals(remote, current->remote);
565 }
566
567 static status_t get_pair_by_hosts(linked_list_t *pairs, host_t *local, host_t *remote, endpoint_pair_t **pair)
568 {
569 return pairs->find_first(pairs,
570 (linked_list_match_t)match_pair_by_hosts,
571 (void**)pair, local, remote);
572 }
573
574 static bool match_pair_by_id(endpoint_pair_t *current, u_int32_t *id)
575 {
576 return current->id == *id;
577 }
578
579 /**
580 * Searches for a pair with a specific id
581 */
582 static status_t get_pair_by_id(check_list_t *checklist, u_int32_t id, endpoint_pair_t **pair)
583 {
584 return checklist->pairs->find_first(checklist->pairs,
585 (linked_list_match_t)match_pair_by_id,
586 (void**)pair, &id);
587 }
588
589 static bool match_succeeded_pair(endpoint_pair_t *current)
590 {
591 return current->state == CHECK_SUCCEEDED;
592 }
593
594 /**
595 * Returns the best pair of state CHECK_SUCCEEDED from a checklist.
596 */
597 static status_t get_best_valid_pair(check_list_t *checklist, endpoint_pair_t **pair)
598 {
599 return checklist->pairs->find_first(checklist->pairs,
600 (linked_list_match_t)match_succeeded_pair,
601 (void**)pair);
602 }
603
604 static bool match_waiting_pair(endpoint_pair_t *current)
605 {
606 return current->state == CHECK_WAITING;
607 }
608
609 /**
610 * Returns and *removes* the first triggered pair in state CHECK_WAITING.
611 */
612 static status_t get_triggered_pair(check_list_t *checklist, endpoint_pair_t **pair)
613 {
614 iterator_t *iterator;
615 endpoint_pair_t *current;
616 status_t status = NOT_FOUND;
617
618 iterator = checklist->triggered->create_iterator(checklist->triggered, TRUE);
619 while (iterator->iterate(iterator, (void**)&current))
620 {
621 iterator->remove(iterator);
622
623 if (current->state == CHECK_WAITING)
624 {
625 if (pair)
626 {
627 *pair = current;
628 }
629 status = SUCCESS;
630 break;
631 }
632 }
633 iterator->destroy(iterator);
634
635 return status;
636 }
637
638 /**
639 * Prints all the pairs on a checklist
640 */
641 static void print_checklist(check_list_t *checklist)
642 {
643 iterator_t *iterator;
644 endpoint_pair_t *current;
645
646 DBG1(DBG_IKE, "pairs on checklist %#B:", &checklist->connect_id);
647 iterator = checklist->pairs->create_iterator(checklist->pairs, TRUE);
648 while (iterator->iterate(iterator, (void**)&current))
649 {
650 DBG1(DBG_IKE, " * %#H - %#H (%d)", current->local, current->remote,
651 current->priority);
652 }
653 iterator->destroy(iterator);
654 }
655
656 /**
657 * Prunes identical pairs with lower priority from the list
658 * Note: this function also numbers the remaining pairs serially
659 */
660 static void prune_pairs(linked_list_t *pairs)
661 {
662 iterator_t *iterator, *search;
663 endpoint_pair_t *current, *other;
664 u_int32_t id = 0;
665
666 iterator = pairs->create_iterator(pairs, TRUE);
667 search = pairs->create_iterator(pairs, TRUE);
668 while (iterator->iterate(iterator, (void**)&current))
669 {
670 current->id = ++id;
671
672 while (search->iterate(search, (void**)&other))
673 {
674 if (current == other)
675 {
676 continue;
677 }
678
679 if (current->local->equals(current->local, other->local) &&
680 current->remote->equals(current->remote, other->remote))
681 {
682 /* since the list of pairs is sorted by priority in descending
683 * order, and we iterate the list from the beginning, we are
684 * sure that the priority of 'other' is lower than that of
685 * 'current', remove it */
686 DBG1(DBG_IKE, "pruning endpoint pair %#H - %#H with priority %d",
687 other->local, other->remote, other->priority);
688 search->remove(search);
689 endpoint_pair_destroy(other);
690 }
691 }
692 search->reset(search);
693 }
694 search->destroy(search);
695 iterator->destroy(iterator);
696 }
697
698 /**
699 * Builds a list of endpoint pairs
700 */
701 static void build_pairs(check_list_t *checklist)
702 {
703 /* FIXME: limit endpoints and pairs */
704 iterator_t *iterator_i, *iterator_r;
705 endpoint_notify_t *initiator, *responder;
706
707 iterator_i = checklist->initiator.endpoints->create_iterator(checklist->initiator.endpoints, TRUE);
708 while (iterator_i->iterate(iterator_i, (void**)&initiator))
709 {
710 iterator_r = checklist->responder.endpoints->create_iterator(checklist->responder.endpoints, TRUE);
711 while (iterator_r->iterate(iterator_r, (void**)&responder))
712 {
713 if (initiator->get_family(initiator) != responder->get_family(responder))
714 {
715 continue;
716 }
717
718 insert_pair_by_priority(checklist->pairs,
719 endpoint_pair_create(initiator, responder, checklist->is_initiator));
720 }
721 iterator_r->destroy(iterator_r);
722 }
723 iterator_i->destroy(iterator_i);
724
725 print_checklist(checklist);
726
727 prune_pairs(checklist->pairs);
728 }
729
730 /**
731 * Processes the payloads of a connectivity check and returns the extracted data
732 */
733 static status_t process_payloads(message_t *message, check_t *check)
734 {
735 enumerator_t *enumerator;
736 payload_t *payload;
737
738 enumerator = message->create_payload_enumerator(message);
739 while (enumerator->enumerate(enumerator, &payload))
740 {
741 if (payload->get_type(payload) != NOTIFY)
742 {
743 DBG1(DBG_IKE, "ignoring payload of type '%N' while processing "
744 "connectivity check", payload_type_names, payload->get_type(payload));
745 continue;
746 }
747
748 notify_payload_t *notify = (notify_payload_t*)payload;
749
750 switch (notify->get_notify_type(notify))
751 {
752 case ME_ENDPOINT:
753 {
754 if (check->endpoint)
755 {
756 DBG1(DBG_IKE, "connectivity check contains multiple ME_ENDPOINT notifies");
757 break;
758 }
759
760 endpoint_notify_t *endpoint = endpoint_notify_create_from_payload(notify);
761 if (!endpoint)
762 {
763 DBG1(DBG_IKE, "received invalid ME_ENDPOINT notify");
764 break;
765 }
766 check->endpoint = endpoint;
767 check->endpoint_raw = chunk_clone(notify->get_notification_data(notify));
768 DBG2(DBG_IKE, "received ME_ENDPOINT notify");
769 break;
770 }
771 case ME_CONNECTID:
772 {
773 if (check->connect_id.ptr)
774 {
775 DBG1(DBG_IKE, "connectivity check contains multiple ME_CONNECTID notifies");
776 break;
777 }
778 check->connect_id = chunk_clone(notify->get_notification_data(notify));
779 DBG2(DBG_IKE, "received ME_CONNECTID %#B", &check->connect_id);
780 break;
781 }
782 case ME_CONNECTAUTH:
783 {
784 if (check->auth.ptr)
785 {
786 DBG1(DBG_IKE, "connectivity check contains multiple ME_CONNECTAUTH notifies");
787 break;
788 }
789 check->auth = chunk_clone(notify->get_notification_data(notify));
790 DBG2(DBG_IKE, "received ME_CONNECTAUTH %#B", &check->auth);
791 break;
792 }
793 default:
794 break;
795 }
796 }
797 enumerator->destroy(enumerator);
798
799 if (!check->connect_id.ptr || !check->endpoint || !check->auth.ptr)
800 {
801 DBG1(DBG_IKE, "at least one payload was missing from the connectivity check");
802 return FAILED;
803 }
804
805 return SUCCESS;
806 }
807
808 /**
809 * Builds the signature for a connectivity check
810 */
811 static chunk_t build_signature(private_connect_manager_t *this,
812 check_list_t *checklist, check_t *check, bool outbound)
813 {
814 u_int32_t mid;
815 chunk_t mid_chunk, key_chunk, sig_chunk;
816 chunk_t sig_hash;
817
818 mid = htonl(check->mid);
819 mid_chunk = chunk_from_thing(mid);
820
821 key_chunk = (checklist->is_initiator && outbound) || (!checklist->is_initiator && !outbound)
822 ? checklist->initiator.key : checklist->responder.key;
823
824 /* signature = SHA1( MID | ME_CONNECTID | ME_ENDPOINT | ME_CONNECTKEY ) */
825 sig_chunk = chunk_cat("cccc", mid_chunk, check->connect_id, check->endpoint_raw, key_chunk);
826 this->hasher->allocate_hash(this->hasher, sig_chunk, &sig_hash);
827 DBG3(DBG_IKE, "sig_chunk %#B", &sig_chunk);
828 DBG3(DBG_IKE, "sig_hash %#B", &sig_hash);
829
830 chunk_free(&sig_chunk);
831 return sig_hash;
832 }
833
834 static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair);
835 static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, u_int32_t time);
836 static void finish_checks(private_connect_manager_t *this, check_list_t *checklist);
837
838 /**
839 * After one of the initiator's pairs has succeeded we finish the checks without
840 * waiting for all the timeouts
841 */
842 static job_requeue_t initiator_finish(callback_data_t *data)
843 {
844 private_connect_manager_t *this = data->connect_manager;
845
846 this->mutex->lock(this->mutex);
847
848 check_list_t *checklist;
849 if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS)
850 {
851 DBG1(DBG_IKE, "checklist with id '%#B' not found, can't finish connectivity checks",
852 &data->connect_id);
853 this->mutex->unlock(this->mutex);
854 return JOB_REQUEUE_NONE;
855 }
856
857 finish_checks(this, checklist);
858
859 this->mutex->unlock(this->mutex);
860
861 return JOB_REQUEUE_NONE;
862 }
863
864 /**
865 * Updates the state of the whole checklist
866 */
867 static void update_checklist_state(private_connect_manager_t *this, check_list_t *checklist)
868 {
869 iterator_t *iterator;
870 endpoint_pair_t *current;
871 bool in_progress = FALSE, succeeded = FALSE;
872
873 iterator = checklist->pairs->create_iterator(checklist->pairs, TRUE);
874 while (iterator->iterate(iterator, (void**)&current))
875 {
876 switch(current->state)
877 {
878 case CHECK_WAITING:
879 /* at least one is still waiting -> checklist remains
880 * in waiting state */
881 iterator->destroy(iterator);
882 return;
883 case CHECK_IN_PROGRESS:
884 in_progress = TRUE;
885 break;
886 case CHECK_SUCCEEDED:
887 succeeded = TRUE;
888 break;
889 default:
890 break;
891 }
892 }
893 iterator->destroy(iterator);
894
895 if (checklist->is_initiator && succeeded && !checklist->is_finishing)
896 {
897 /* instead of waiting until all checks have finished (i.e. all
898 * retransmissions have failed) the initiator finishes the checks
899 * right after the first check has succeeded. to allow a probably
900 * better pair to succeed, we still wait a certain time */
901 DBG2(DBG_IKE, "fast finishing checks for checklist '%#B'", &checklist->connect_id);
902
903 callback_data_t *data = callback_data_create(this, checklist->connect_id);
904 job_t *job = (job_t*)callback_job_create((callback_job_cb_t)initiator_finish, data, (callback_job_cleanup_t)callback_data_destroy, NULL);
905 charon->scheduler->schedule_job_ms(charon->scheduler, job, ME_WAIT_TO_FINISH);
906 checklist->is_finishing = TRUE;
907 }
908
909 if (in_progress)
910 {
911 checklist->state = CHECK_IN_PROGRESS;
912 }
913 else if (succeeded)
914 {
915 checklist->state = CHECK_SUCCEEDED;
916 }
917 else
918 {
919 checklist->state = CHECK_FAILED;
920 }
921 }
922
923 /**
924 * This function is triggered for each sent check after a specific timeout
925 */
926 static job_requeue_t retransmit(callback_data_t *data)
927 {
928 private_connect_manager_t *this = data->connect_manager;
929
930 this->mutex->lock(this->mutex);
931
932 check_list_t *checklist;
933 if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS)
934 {
935 DBG1(DBG_IKE, "checklist with id '%#B' not found, can't retransmit connectivity check",
936 &data->connect_id);
937 this->mutex->unlock(this->mutex);
938 return JOB_REQUEUE_NONE;
939 }
940
941 endpoint_pair_t *pair;
942 if (get_pair_by_id(checklist, data->mid, &pair) != SUCCESS)
943 {
944 DBG1(DBG_IKE, "pair with id '%d' not found, can't retransmit connectivity check",
945 data->mid);
946 goto retransmit_end;
947 }
948
949 if (pair->state != CHECK_IN_PROGRESS)
950 {
951 DBG2(DBG_IKE, "pair with id '%d' is in wrong state [%d], don't retransmit the connectivity check",
952 data->mid, pair->state);
953 goto retransmit_end;
954 }
955
956 if (++pair->retransmitted > ME_MAX_RETRANS)
957 {
958 DBG2(DBG_IKE, "pair with id '%d' failed after %d retransmissions",
959 data->mid, ME_MAX_RETRANS);
960 pair->state = CHECK_FAILED;
961 goto retransmit_end;
962 }
963
964 charon->sender->send(charon->sender, pair->packet->clone(pair->packet));
965
966 queue_retransmission(this, checklist, pair);
967
968 retransmit_end:
969 update_checklist_state(this, checklist);
970
971 switch(checklist->state)
972 {
973 case CHECK_SUCCEEDED:
974 case CHECK_FAILED:
975 finish_checks(this, checklist);
976 break;
977 default:
978 break;
979 }
980
981 this->mutex->unlock(this->mutex);
982
983 /* we reschedule it manually */
984 return JOB_REQUEUE_NONE;
985 }
986
987 /**
988 * Queues a retransmission job
989 */
990 static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair)
991 {
992 callback_data_t *data = retransmit_data_create(this, checklist->connect_id, pair->id);
993 job_t *job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data, (callback_job_cleanup_t)callback_data_destroy, NULL);
994
995 u_int32_t retransmission = pair->retransmitted + 1;
996 u_int32_t rto = ME_INTERVAL;
997 if (retransmission > ME_BOOST)
998 {
999 rto = (u_int32_t)(ME_INTERVAL * pow(ME_RETRANS_BASE, retransmission - ME_BOOST));
1000 }
1001 DBG2(DBG_IKE, "scheduling retransmission %d of pair '%d' in %dms", retransmission, pair->id, rto);
1002
1003 charon->scheduler->schedule_job_ms(charon->scheduler, (job_t*)job, rto);
1004 }
1005
1006 /**
1007 * Sends a check
1008 */
1009 static void send_check(private_connect_manager_t *this, check_list_t *checklist,
1010 check_t *check, endpoint_pair_t *pair, bool request)
1011 {
1012 message_t *message = message_create();
1013 message->set_message_id(message, check->mid);
1014 message->set_exchange_type(message, INFORMATIONAL);
1015 message->set_request(message, request);
1016 message->set_destination(message, check->dst->clone(check->dst));
1017 message->set_source(message, check->src->clone(check->src));
1018
1019 ike_sa_id_t *ike_sa_id = ike_sa_id_create(0, 0, request);
1020 message->set_ike_sa_id(message, ike_sa_id);
1021 ike_sa_id->destroy(ike_sa_id);
1022
1023 message->add_notify(message, FALSE, ME_CONNECTID, check->connect_id);
1024 DBG2(DBG_IKE, "send ME_CONNECTID %#B", &check->connect_id);
1025
1026 notify_payload_t *endpoint = check->endpoint->build_notify(check->endpoint);
1027 check->endpoint_raw = chunk_clone(endpoint->get_notification_data(endpoint));
1028 message->add_payload(message, (payload_t*)endpoint);
1029 DBG2(DBG_IKE, "send ME_ENDPOINT notify");
1030
1031 check->auth = build_signature(this, checklist, check, TRUE);
1032 message->add_notify(message, FALSE, ME_CONNECTAUTH, check->auth);
1033 DBG2(DBG_IKE, "send ME_CONNECTAUTH %#B", &check->auth);
1034
1035 packet_t *packet;
1036 if (message->generate(message, NULL, NULL, &packet) == SUCCESS)
1037 {
1038 charon->sender->send(charon->sender, packet->clone(packet));
1039
1040 if (request)
1041 {
1042 DESTROY_IF(pair->packet);
1043 pair->packet = packet;
1044 pair->retransmitted = 0;
1045 queue_retransmission(this, checklist, pair);
1046 }
1047 else
1048 {
1049 packet->destroy(packet);
1050 }
1051 }
1052 message->destroy(message);
1053 }
1054
1055 /**
1056 * Queues a triggered check
1057 */
1058 static void queue_triggered_check(private_connect_manager_t *this,
1059 check_list_t *checklist, endpoint_pair_t *pair)
1060 {
1061 DBG2(DBG_IKE, "queueing triggered check for pair '%d'", pair->id);
1062 pair->state = CHECK_WAITING;
1063 checklist->triggered->insert_last(checklist->triggered, pair);
1064
1065 if (!checklist->sender)
1066 {
1067 /* if the sender is not running we restart it */
1068 schedule_checks(this, checklist, ME_INTERVAL);
1069 }
1070 }
1071
1072 /**
1073 * This function is triggered for each checklist at a specific interval
1074 */
1075 static job_requeue_t sender(callback_data_t *data)
1076 {
1077 private_connect_manager_t *this = data->connect_manager;
1078
1079 this->mutex->lock(this->mutex);
1080
1081 check_list_t *checklist;
1082 if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS)
1083 {
1084 DBG1(DBG_IKE, "checklist with id '%#B' not found, can't send connectivity check",
1085 &data->connect_id);
1086 this->mutex->unlock(this->mutex);
1087 return JOB_REQUEUE_NONE;
1088 }
1089
1090 /* reset the sender */
1091 checklist->sender = NULL;
1092
1093 endpoint_pair_t *pair;
1094 if (get_triggered_pair(checklist, &pair) != SUCCESS)
1095 {
1096 DBG1(DBG_IKE, "no triggered check queued, sending an ordinary check");
1097
1098 if (checklist->pairs->find_first(checklist->pairs,
1099 (linked_list_match_t)match_waiting_pair, (void**)&pair) != SUCCESS)
1100 {
1101 this->mutex->unlock(this->mutex);
1102 DBG1(DBG_IKE, "no pairs in waiting state, aborting");
1103 return JOB_REQUEUE_NONE;
1104 }
1105 }
1106 else
1107 {
1108 DBG1(DBG_IKE, "triggered check found");
1109 }
1110
1111 check_t *check = check_create();
1112 check->mid = pair->id;
1113 check->src = pair->local->clone(pair->local);
1114 check->dst = pair->remote->clone(pair->remote);
1115 check->connect_id = chunk_clone(checklist->connect_id);
1116 check->endpoint = endpoint_notify_create();
1117
1118 pair->state = CHECK_IN_PROGRESS;
1119
1120 send_check(this, checklist, check, pair, TRUE);
1121
1122 check_destroy(check);
1123
1124 /* schedule this job again */
1125 schedule_checks(this, checklist, ME_INTERVAL);
1126
1127 this->mutex->unlock(this->mutex);
1128
1129 /* we reschedule it manually */
1130 return JOB_REQUEUE_NONE;
1131 }
1132
1133 /**
1134 * Schedules checks for a checklist (time in ms)
1135 */
1136 static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, u_int32_t time)
1137 {
1138 callback_data_t *data = callback_data_create(this, checklist->connect_id);
1139 checklist->sender = (job_t*)callback_job_create((callback_job_cb_t)sender, data, (callback_job_cleanup_t)callback_data_destroy, NULL);
1140 charon->scheduler->schedule_job_ms(charon->scheduler, checklist->sender, time);
1141 }
1142
1143 /**
1144 * Initiates waiting mediated connections
1145 */
1146 static job_requeue_t initiate_mediated(initiate_data_t *data)
1147 {
1148 check_list_t *checklist = data->checklist;
1149 initiated_t *initiated = data->initiated;
1150
1151 endpoint_pair_t *pair;
1152 if (get_best_valid_pair(checklist, &pair) == SUCCESS)
1153 {
1154 ike_sa_id_t *waiting_sa;
1155 iterator_t *iterator = initiated->mediated->create_iterator(initiated->mediated, TRUE);
1156 while (iterator->iterate(iterator, (void**)&waiting_sa))
1157 {
1158 ike_sa_t *sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, waiting_sa);
1159 if (sa->initiate_mediated(sa, pair->local, pair->remote, checklist->connect_id) != SUCCESS)
1160 {
1161 DBG1(DBG_IKE, "establishing mediated connection failed");
1162 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa);
1163 }
1164 charon->ike_sa_manager->checkin(charon->ike_sa_manager, sa);
1165 }
1166 iterator->destroy(iterator);
1167 }
1168 else
1169 {
1170 /* this should (can?) not happen */
1171 }
1172
1173 return JOB_REQUEUE_NONE;
1174 }
1175
1176 /**
1177 * Finishes checks for a checklist
1178 */
1179 static void finish_checks(private_connect_manager_t *this, check_list_t *checklist)
1180 {
1181 if (checklist->is_initiator)
1182 {
1183 initiated_t *initiated;
1184 if (get_initiated_by_ids(this, checklist->initiator.id,
1185 checklist->responder.id, &initiated) == SUCCESS)
1186 {
1187 remove_checklist(this, checklist);
1188 remove_initiated(this, initiated);
1189
1190 initiate_data_t *data = initiate_data_create(checklist, initiated);
1191 job_t *job = (job_t*)callback_job_create((callback_job_cb_t)initiate_mediated, data, (callback_job_cleanup_t)initiate_data_destroy, NULL);
1192 charon->processor->queue_job(charon->processor, job);
1193 return;
1194 }
1195 else
1196 {
1197 DBG1(DBG_IKE, "there is no mediated connection waiting between '%Y' "
1198 "and '%Y'", checklist->initiator.id, checklist->responder.id);
1199 }
1200 }
1201 }
1202
1203 /**
1204 * Process the response to one of our requests
1205 */
1206 static void process_response(private_connect_manager_t *this, check_t *check,
1207 check_list_t *checklist)
1208 {
1209 endpoint_pair_t *pair;
1210 if (get_pair_by_id(checklist, check->mid, &pair) == SUCCESS)
1211 {
1212 if (pair->local->equals(pair->local, check->dst) &&
1213 pair->remote->equals(pair->remote, check->src))
1214 {
1215 DBG1(DBG_IKE, "endpoint pair '%d' is valid: '%#H' - '%#H'", pair->id,
1216 pair->local, pair->remote);
1217 pair->state = CHECK_SUCCEEDED;
1218 }
1219
1220 linked_list_t *local_endpoints = checklist->is_initiator ?
1221 checklist->initiator.endpoints : checklist->responder.endpoints;
1222
1223 endpoint_notify_t *local_endpoint;
1224 if (endpoints_contain(local_endpoints,
1225 check->endpoint->get_host(check->endpoint), &local_endpoint) != SUCCESS)
1226 {
1227 local_endpoint = endpoint_notify_create_from_host(PEER_REFLEXIVE,
1228 check->endpoint->get_host(check->endpoint), pair->local);
1229 local_endpoint->set_priority(local_endpoint, check->endpoint->get_priority(check->endpoint));
1230 local_endpoints->insert_last(local_endpoints, local_endpoint);
1231 }
1232
1233 update_checklist_state(this, checklist);
1234
1235 switch(checklist->state)
1236 {
1237 case CHECK_SUCCEEDED:
1238 case CHECK_FAILED:
1239 finish_checks(this, checklist);
1240 break;
1241 default:
1242 break;
1243 }
1244 }
1245 else
1246 {
1247 DBG1(DBG_IKE, "pair with id '%d' not found", check->mid);
1248 }
1249 }
1250
1251 static void process_request(private_connect_manager_t *this, check_t *check,
1252 check_list_t *checklist)
1253 {
1254 linked_list_t *remote_endpoints = checklist->is_initiator ?
1255 checklist->responder.endpoints : checklist->initiator.endpoints;
1256
1257 endpoint_notify_t *peer_reflexive, *remote_endpoint;
1258 peer_reflexive = endpoint_notify_create_from_host(PEER_REFLEXIVE, check->src, NULL);
1259 peer_reflexive->set_priority(peer_reflexive, check->endpoint->get_priority(check->endpoint));
1260
1261 if (endpoints_contain(remote_endpoints, check->src, &remote_endpoint) != SUCCESS)
1262 {
1263 remote_endpoint = peer_reflexive->clone(peer_reflexive);
1264 remote_endpoints->insert_last(remote_endpoints, remote_endpoint);
1265 }
1266
1267 endpoint_pair_t *pair;
1268 if (get_pair_by_hosts(checklist->pairs, check->dst, check->src, &pair) == SUCCESS)
1269 {
1270 switch(pair->state)
1271 {
1272 case CHECK_IN_PROGRESS:
1273 /* prevent retransmissions */
1274 pair->retransmitted = ME_MAX_RETRANS;
1275 /* FIXME: we should wait to the next rto to send the triggered check
1276 * fall-through */
1277 case CHECK_WAITING:
1278 case CHECK_FAILED:
1279 queue_triggered_check(this, checklist, pair);
1280 break;
1281 case CHECK_SUCCEEDED:
1282 default:
1283 break;
1284 }
1285 }
1286 else
1287 {
1288 endpoint_notify_t *local_endpoint = endpoint_notify_create_from_host(HOST, check->dst, NULL);
1289
1290 endpoint_notify_t *initiator = checklist->is_initiator ? local_endpoint : remote_endpoint;
1291 endpoint_notify_t *responder = checklist->is_initiator ? remote_endpoint : local_endpoint;
1292
1293 pair = endpoint_pair_create(initiator, responder, checklist->is_initiator);
1294 pair->id = checklist->pairs->get_count(checklist->pairs) + 1;
1295
1296 insert_pair_by_priority(checklist->pairs, pair);
1297
1298 queue_triggered_check(this, checklist, pair);
1299
1300 local_endpoint->destroy(local_endpoint);
1301 }
1302
1303
1304 check_t *response = check_create();
1305
1306 response->mid = check->mid;
1307 response->src = check->dst->clone(check->dst);
1308 response->dst = check->src->clone(check->src);
1309 response->connect_id = chunk_clone(check->connect_id);
1310 response->endpoint = peer_reflexive;
1311
1312 send_check(this, checklist, response, pair, FALSE);
1313
1314 check_destroy(response);
1315 }
1316
1317 /**
1318 * Implementation of connect_manager_t.process_check.
1319 */
1320 static void process_check(private_connect_manager_t *this, message_t *message)
1321 {
1322 if (message->parse_body(message, NULL, NULL) != SUCCESS)
1323 {
1324 DBG1(DBG_IKE, "%N %s with message ID %d processing failed",
1325 exchange_type_names, message->get_exchange_type(message),
1326 message->get_request(message) ? "request" : "response",
1327 message->get_message_id(message));
1328 return;
1329 }
1330
1331 check_t *check = check_create();
1332 check->mid = message->get_message_id(message);
1333 check->src = message->get_source(message);
1334 check->src = check->src->clone(check->src);
1335 check->dst = message->get_destination(message);
1336 check->dst = check->dst->clone(check->dst);
1337
1338 if (process_payloads(message, check) != SUCCESS)
1339 {
1340 DBG1(DBG_IKE, "invalid connectivity check %s received",
1341 message->get_request(message) ? "request" : "response");
1342 check_destroy(check);
1343 return;
1344 }
1345
1346 this->mutex->lock(this->mutex);
1347
1348 check_list_t *checklist;
1349 if (get_checklist_by_id(this, check->connect_id, &checklist) != SUCCESS)
1350 {
1351 DBG1(DBG_IKE, "checklist with id '%#B' not found",
1352 &check->connect_id);
1353 check_destroy(check);
1354 this->mutex->unlock(this->mutex);
1355 return;
1356 }
1357
1358 chunk_t sig = build_signature(this, checklist, check, FALSE);
1359 if (!chunk_equals(sig, check->auth))
1360 {
1361 DBG1(DBG_IKE, "connectivity check verification failed");
1362 check_destroy(check);
1363 chunk_free(&sig);
1364 this->mutex->unlock(this->mutex);
1365 return;
1366 }
1367 chunk_free(&sig);
1368
1369 if (message->get_request(message))
1370 {
1371 process_request(this, check, checklist);
1372 }
1373 else
1374 {
1375 process_response(this, check, checklist);
1376 }
1377
1378 this->mutex->unlock(this->mutex);
1379
1380 check_destroy(check);
1381 }
1382
1383 /**
1384 * Implementation of connect_manager_t.check_and_register.
1385 */
1386 static bool check_and_register(private_connect_manager_t *this,
1387 identification_t *id, identification_t *peer_id,
1388 ike_sa_id_t *mediated_sa)
1389 {
1390 initiated_t *initiated;
1391 bool already_there = TRUE;
1392
1393 this->mutex->lock(this->mutex);
1394
1395 if (get_initiated_by_ids(this, id, peer_id, &initiated) != SUCCESS)
1396 {
1397 DBG2(DBG_IKE, "registered waiting mediated connection with '%Y'", peer_id);
1398 initiated = initiated_create(id, peer_id);
1399 this->initiated->insert_last(this->initiated, initiated);
1400 already_there = FALSE;
1401 }
1402
1403 if (initiated->mediated->find_first(initiated->mediated,
1404 (linked_list_match_t)mediated_sa->equals, NULL, mediated_sa) != SUCCESS)
1405 {
1406 initiated->mediated->insert_last(initiated->mediated, mediated_sa->clone(mediated_sa));
1407 }
1408
1409 this->mutex->unlock(this->mutex);
1410
1411 return already_there;
1412 }
1413
1414 /**
1415 * Implementation of connect_manager_t.check_and_initiate.
1416 */
1417 static void check_and_initiate(private_connect_manager_t *this, ike_sa_id_t *mediation_sa,
1418 identification_t *id, identification_t *peer_id)
1419 {
1420 initiated_t *initiated;
1421
1422 this->mutex->lock(this->mutex);
1423
1424 if (get_initiated_by_ids(this, id, peer_id, &initiated) != SUCCESS)
1425 {
1426 DBG2(DBG_IKE, "no waiting mediated connections with '%Y'", peer_id);
1427 this->mutex->unlock(this->mutex);
1428 return;
1429 }
1430
1431 ike_sa_id_t *waiting_sa;
1432 iterator_t *iterator = initiated->mediated->create_iterator(initiated->mediated, TRUE);
1433 while (iterator->iterate(iterator, (void**)&waiting_sa))
1434 {
1435 job_t *job = (job_t*)reinitiate_mediation_job_create(mediation_sa, waiting_sa);
1436 charon->processor->queue_job(charon->processor, job);
1437 }
1438 iterator->destroy(iterator);
1439
1440 this->mutex->unlock(this->mutex);
1441 }
1442
1443 /**
1444 * Implementation of connect_manager_t.set_initiator_data.
1445 */
1446 static status_t set_initiator_data(private_connect_manager_t *this,
1447 identification_t *initiator, identification_t *responder,
1448 chunk_t connect_id, chunk_t key, linked_list_t *endpoints, bool is_initiator)
1449 {
1450 check_list_t *checklist;
1451
1452 this->mutex->lock(this->mutex);
1453
1454 if (get_checklist_by_id(this, connect_id, NULL) == SUCCESS)
1455 {
1456 DBG1(DBG_IKE, "checklist with id '%#B' already exists, aborting",
1457 &connect_id);
1458 this->mutex->unlock(this->mutex);
1459 return FAILED;
1460 }
1461
1462 checklist = check_list_create(initiator, responder, connect_id, key, endpoints, is_initiator);
1463 this->checklists->insert_last(this->checklists, checklist);
1464
1465 this->mutex->unlock(this->mutex);
1466
1467 return SUCCESS;
1468 }
1469
1470 /**
1471 * Implementation of connect_manager_t.set_responder_data.
1472 */
1473 static status_t set_responder_data(private_connect_manager_t *this,
1474 chunk_t connect_id, chunk_t key, linked_list_t *endpoints)
1475 {
1476 check_list_t *checklist;
1477
1478 this->mutex->lock(this->mutex);
1479
1480 if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS)
1481 {
1482 DBG1(DBG_IKE, "checklist with id '%#B' not found",
1483 &connect_id);
1484 this->mutex->unlock(this->mutex);
1485 return NOT_FOUND;
1486 }
1487
1488 checklist->responder.key = chunk_clone(key);
1489 checklist->responder.endpoints = endpoints->clone_offset(endpoints, offsetof(endpoint_notify_t, clone));
1490 checklist->state = CHECK_WAITING;
1491
1492 build_pairs(checklist);
1493
1494 /* send the first check immediately */
1495 schedule_checks(this, checklist, 0);
1496
1497 this->mutex->unlock(this->mutex);
1498
1499 return SUCCESS;
1500 }
1501
1502 /**
1503 * Implementation of connect_manager_t.stop_checks.
1504 */
1505 static status_t stop_checks(private_connect_manager_t *this, chunk_t connect_id)
1506 {
1507 check_list_t *checklist;
1508
1509 this->mutex->lock(this->mutex);
1510
1511 if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS)
1512 {
1513 DBG1(DBG_IKE, "checklist with id '%#B' not found",
1514 &connect_id);
1515 this->mutex->unlock(this->mutex);
1516 return NOT_FOUND;
1517 }
1518
1519 DBG1(DBG_IKE, "removing checklist with id '%#B'", &connect_id);
1520
1521 remove_checklist(this, checklist);
1522 check_list_destroy(checklist);
1523
1524 this->mutex->unlock(this->mutex);
1525
1526 return SUCCESS;
1527 }
1528
1529 /**
1530 * Implementation of connect_manager_t.destroy.
1531 */
1532 static void destroy(private_connect_manager_t *this)
1533 {
1534 this->mutex->lock(this->mutex);
1535
1536 this->hasher->destroy(this->hasher);
1537 this->checklists->destroy_function(this->checklists, (void*)check_list_destroy);
1538 this->initiated->destroy_function(this->initiated, (void*)initiated_destroy);
1539
1540 this->mutex->unlock(this->mutex);
1541 this->mutex->destroy(this->mutex);
1542 free(this);
1543 }
1544
1545 /*
1546 * Described in header.
1547 */
1548 connect_manager_t *connect_manager_create()
1549 {
1550 private_connect_manager_t *this = malloc_thing(private_connect_manager_t);
1551
1552 this->public.destroy = (void(*)(connect_manager_t*))destroy;
1553 this->public.check_and_register = (bool(*)(connect_manager_t*,identification_t*,identification_t*,ike_sa_id_t*))check_and_register;
1554 this->public.check_and_initiate = (void(*)(connect_manager_t*,ike_sa_id_t*,identification_t*,identification_t*))check_and_initiate;
1555 this->public.set_initiator_data = (status_t(*)(connect_manager_t*,identification_t*,identification_t*,chunk_t,chunk_t,linked_list_t*,bool))set_initiator_data;
1556 this->public.set_responder_data = (status_t(*)(connect_manager_t*,chunk_t,chunk_t,linked_list_t*))set_responder_data;
1557 this->public.process_check = (void(*)(connect_manager_t*,message_t*))process_check;
1558 this->public.stop_checks = (status_t(*)(connect_manager_t*,chunk_t))stop_checks;
1559
1560 this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
1561 if (this->hasher == NULL)
1562 {
1563 DBG1(DBG_IKE, "unable to create connect manager, SHA1 not supported");
1564 free(this);
1565 return NULL;
1566 }
1567
1568 this->checklists = linked_list_create();
1569 this->initiated = linked_list_create();
1570
1571 this->mutex = mutex_create(MUTEX_DEFAULT);
1572
1573 return (connect_manager_t*)this;
1574 }