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