controller: Use separate callbacks to track termination and initiation of SAs
[strongswan.git] / src / libcharon / control / controller.c
1 /*
2 * Copyright (C) 2011-2015 Tobias Brunner
3 * Copyright (C) 2007-2011 Martin Willi
4 * Copyright (C) 2011 revosec AG
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "controller.h"
19
20 #include <sys/types.h>
21 #include <dirent.h>
22 #include <sys/stat.h>
23
24 #include <daemon.h>
25 #include <library.h>
26 #include <threading/thread.h>
27 #include <threading/spinlock.h>
28 #include <threading/semaphore.h>
29
30 typedef struct private_controller_t private_controller_t;
31 typedef struct interface_listener_t interface_listener_t;
32 typedef struct interface_logger_t interface_logger_t;
33
34 /**
35 * Private data of an stroke_t object.
36 */
37 struct private_controller_t {
38
39 /**
40 * Public part of stroke_t object.
41 */
42 controller_t public;
43 };
44
45 /**
46 * helper struct for the logger interface
47 */
48 struct interface_logger_t {
49 /**
50 * public logger interface
51 */
52 logger_t public;
53
54 /**
55 * reference to the listener
56 */
57 interface_listener_t *listener;
58
59 /**
60 * interface callback (listener gets redirected to here)
61 */
62 controller_cb_t callback;
63
64 /**
65 * user parameter to pass to callback
66 */
67 void *param;
68 };
69
70 /**
71 * helper struct to map listener callbacks to interface callbacks
72 */
73 struct interface_listener_t {
74
75 /**
76 * public bus listener interface
77 */
78 listener_t public;
79
80 /**
81 * logger interface
82 */
83 interface_logger_t logger;
84
85 /**
86 * status of the operation, return to method callers
87 */
88 status_t status;
89
90 /**
91 * child configuration, used for initiate
92 */
93 child_cfg_t *child_cfg;
94
95 /**
96 * peer configuration, used for initiate
97 */
98 peer_cfg_t *peer_cfg;
99
100 /**
101 * IKE_SA to handle
102 */
103 ike_sa_t *ike_sa;
104
105 /**
106 * unique ID, used for various methods
107 */
108 uint32_t id;
109
110 /**
111 * semaphore to implement wait_for_listener()
112 */
113 semaphore_t *done;
114
115 /**
116 * spinlock to update the IKE_SA handle properly
117 */
118 spinlock_t *lock;
119
120 /**
121 * whether to check limits
122 */
123 bool limits;
124 };
125
126
127 typedef struct interface_job_t interface_job_t;
128
129 /**
130 * job for asynchronous listen operations
131 */
132 struct interface_job_t {
133
134 /**
135 * job interface
136 */
137 job_t public;
138
139 /**
140 * associated listener
141 */
142 interface_listener_t listener;
143
144 /**
145 * the job is reference counted as the thread executing a job as well as
146 * the thread waiting in wait_for_listener() require it but either of them
147 * could be done first
148 */
149 refcount_t refcount;
150 };
151
152 /**
153 * This function wakes a thread that is waiting in wait_for_listener(),
154 * either from a listener or from a job.
155 */
156 static inline bool listener_done(interface_listener_t *listener)
157 {
158 if (listener->done)
159 {
160 listener->done->post(listener->done);
161 }
162 return FALSE;
163 }
164
165 /**
166 * thread_cleanup_t handler to unregister a listener.
167 */
168 static void listener_unregister(interface_listener_t *listener)
169 {
170 charon->bus->remove_listener(charon->bus, &listener->public);
171 charon->bus->remove_logger(charon->bus, &listener->logger.public);
172 }
173
174 /**
175 * Registers the listener, executes the job and then waits synchronously until
176 * the listener is done or the timeout occurred.
177 *
178 * @note Use 'return listener_done(listener)' to properly unregister a listener
179 *
180 * @param listener listener to register
181 * @param job job to execute asynchronously when registered, or NULL
182 * @param timeout max timeout in ms to listen for events, 0 to disable
183 * @return TRUE if timed out
184 */
185 static bool wait_for_listener(interface_job_t *job, u_int timeout)
186 {
187 interface_listener_t *listener = &job->listener;
188 bool old, timed_out = FALSE;
189
190 /* avoid that the job is destroyed too early */
191 ref_get(&job->refcount);
192
193 listener->done = semaphore_create(0);
194
195 charon->bus->add_logger(charon->bus, &listener->logger.public);
196 charon->bus->add_listener(charon->bus, &listener->public);
197 lib->processor->queue_job(lib->processor, &job->public);
198
199 thread_cleanup_push((thread_cleanup_t)listener_unregister, listener);
200 old = thread_cancelability(TRUE);
201 if (timeout)
202 {
203 timed_out = listener->done->timed_wait(listener->done, timeout);
204 }
205 else
206 {
207 listener->done->wait(listener->done);
208 }
209 thread_cancelability(old);
210 thread_cleanup_pop(TRUE);
211 return timed_out;
212 }
213
214 METHOD(logger_t, listener_log, void,
215 interface_logger_t *this, debug_t group, level_t level, int thread,
216 ike_sa_t *ike_sa, const char *message)
217 {
218 ike_sa_t *target;
219
220 this->listener->lock->lock(this->listener->lock);
221 target = this->listener->ike_sa;
222 this->listener->lock->unlock(this->listener->lock);
223
224 if (target == ike_sa)
225 {
226 if (!this->callback(this->param, group, level, ike_sa, message))
227 {
228 this->listener->status = NEED_MORE;
229 listener_done(this->listener);
230 }
231 }
232 }
233
234 METHOD(logger_t, listener_get_level, level_t,
235 interface_logger_t *this, debug_t group)
236 {
237 /* in order to allow callback listeners to decide what they want to log
238 * we request any log message, but only if we actually want logging */
239 return this->callback == controller_cb_empty ? LEVEL_SILENT : LEVEL_PRIVATE;
240 }
241
242 METHOD(job_t, get_priority_medium, job_priority_t,
243 job_t *this)
244 {
245 return JOB_PRIO_MEDIUM;
246 }
247
248 METHOD(listener_t, ike_state_change, bool,
249 interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
250 {
251 ike_sa_t *target;
252
253 this->lock->lock(this->lock);
254 target = this->ike_sa;
255 this->lock->unlock(this->lock);
256
257 if (target == ike_sa)
258 {
259 switch (state)
260 {
261 #ifdef ME
262 case IKE_ESTABLISHED:
263 { /* mediation connections are complete without CHILD_SA */
264 peer_cfg_t *peer_cfg = ike_sa->get_peer_cfg(ike_sa);
265
266 if (peer_cfg->is_mediation(peer_cfg))
267 {
268 this->status = SUCCESS;
269 return listener_done(this);
270 }
271 break;
272 }
273 #endif /* ME */
274 case IKE_DESTROYING:
275 return listener_done(this);
276 default:
277 break;
278 }
279 }
280 return TRUE;
281 }
282
283 METHOD(listener_t, ike_state_change_terminate, bool,
284 interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
285 {
286 ike_sa_t *target;
287
288 this->lock->lock(this->lock);
289 target = this->ike_sa;
290 this->lock->unlock(this->lock);
291
292 if (target == ike_sa)
293 {
294 switch (state)
295 {
296 case IKE_DESTROYING:
297 if (ike_sa->get_state(ike_sa) == IKE_DELETING)
298 { /* proper termination */
299 this->status = SUCCESS;
300 }
301 return listener_done(this);
302 default:
303 break;
304 }
305 }
306 return TRUE;
307 }
308
309 METHOD(listener_t, child_state_change, bool,
310 interface_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
311 child_sa_state_t state)
312 {
313 ike_sa_t *target;
314
315 this->lock->lock(this->lock);
316 target = this->ike_sa;
317 this->lock->unlock(this->lock);
318
319 if (target == ike_sa)
320 {
321 switch (state)
322 {
323 case CHILD_INSTALLED:
324 this->status = SUCCESS;
325 return listener_done(this);
326 case CHILD_DESTROYING:
327 switch (child_sa->get_state(child_sa))
328 {
329 case CHILD_RETRYING:
330 /* retrying with a different DH group; survive another
331 * initiation round */
332 this->status = NEED_MORE;
333 return TRUE;
334 case CHILD_CREATED:
335 if (this->status == NEED_MORE)
336 {
337 this->status = FAILED;
338 return TRUE;
339 }
340 break;
341 default:
342 break;
343 }
344 return listener_done(this);
345 default:
346 break;
347 }
348 }
349 return TRUE;
350 }
351
352 METHOD(listener_t, child_state_change_terminate, bool,
353 interface_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
354 child_sa_state_t state)
355 {
356 ike_sa_t *target;
357
358 this->lock->lock(this->lock);
359 target = this->ike_sa;
360 this->lock->unlock(this->lock);
361
362 if (target == ike_sa)
363 {
364 switch (state)
365 {
366 case CHILD_DESTROYING:
367 switch (child_sa->get_state(child_sa))
368 {
369 case CHILD_DELETING:
370 /* proper delete */
371 this->status = SUCCESS;
372 break;
373 default:
374 break;
375 }
376 return listener_done(this);
377 default:
378 break;
379 }
380 }
381 return TRUE;
382 }
383
384 METHOD(job_t, destroy_job, void,
385 interface_job_t *this)
386 {
387 if (ref_put(&this->refcount))
388 {
389 this->listener.lock->destroy(this->listener.lock);
390 DESTROY_IF(this->listener.done);
391 free(this);
392 }
393 }
394
395 METHOD(controller_t, create_ike_sa_enumerator, enumerator_t*,
396 private_controller_t *this, bool wait)
397 {
398 return charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager,
399 wait);
400 }
401
402 METHOD(job_t, initiate_execute, job_requeue_t,
403 interface_job_t *job)
404 {
405 ike_sa_t *ike_sa;
406 interface_listener_t *listener = &job->listener;
407 peer_cfg_t *peer_cfg = listener->peer_cfg;
408
409 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
410 peer_cfg);
411 if (!ike_sa)
412 {
413 listener->child_cfg->destroy(listener->child_cfg);
414 peer_cfg->destroy(peer_cfg);
415 listener->status = FAILED;
416 listener_done(listener);
417 return JOB_REQUEUE_NONE;
418 }
419 listener->lock->lock(listener->lock);
420 listener->ike_sa = ike_sa;
421 listener->lock->unlock(listener->lock);
422
423 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
424 {
425 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
426 }
427 peer_cfg->destroy(peer_cfg);
428
429 if (listener->limits && ike_sa->get_state(ike_sa) == IKE_CREATED)
430 { /* only check if we are not reusing an IKE_SA */
431 u_int half_open, limit_half_open, limit_job_load;
432
433 half_open = charon->ike_sa_manager->get_half_open_count(
434 charon->ike_sa_manager, NULL, FALSE);
435 limit_half_open = lib->settings->get_int(lib->settings,
436 "%s.init_limit_half_open", 0, lib->ns);
437 limit_job_load = lib->settings->get_int(lib->settings,
438 "%s.init_limit_job_load", 0, lib->ns);
439 if (limit_half_open && half_open >= limit_half_open)
440 {
441 DBG1(DBG_IKE, "abort IKE_SA initiation, half open IKE_SA count of "
442 "%d exceeds limit of %d", half_open, limit_half_open);
443 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
444 ike_sa);
445 listener->child_cfg->destroy(listener->child_cfg);
446 listener->status = INVALID_STATE;
447 listener_done(listener);
448 return JOB_REQUEUE_NONE;
449 }
450 if (limit_job_load)
451 {
452 u_int jobs = 0, i;
453
454 for (i = 0; i < JOB_PRIO_MAX; i++)
455 {
456 jobs += lib->processor->get_job_load(lib->processor, i);
457 }
458 if (jobs > limit_job_load)
459 {
460 DBG1(DBG_IKE, "abort IKE_SA initiation, job load of %d exceeds "
461 "limit of %d", jobs, limit_job_load);
462 charon->ike_sa_manager->checkin_and_destroy(
463 charon->ike_sa_manager, ike_sa);
464 listener->child_cfg->destroy(listener->child_cfg);
465 listener->status = INVALID_STATE;
466 listener_done(listener);
467 return JOB_REQUEUE_NONE;
468 }
469 }
470 }
471
472 if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
473 {
474 if (!listener->logger.callback)
475 {
476 listener->status = SUCCESS;
477 }
478 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
479 }
480 else
481 {
482 listener->status = FAILED;
483 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
484 ike_sa);
485 }
486 return JOB_REQUEUE_NONE;
487 }
488
489 METHOD(controller_t, initiate, status_t,
490 private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
491 controller_cb_t callback, void *param, u_int timeout, bool limits)
492 {
493 interface_job_t *job;
494 status_t status;
495
496 INIT(job,
497 .listener = {
498 .public = {
499 .ike_state_change = _ike_state_change,
500 .child_state_change = _child_state_change,
501 },
502 .logger = {
503 .public = {
504 .log = _listener_log,
505 .get_level = _listener_get_level,
506 },
507 .callback = callback,
508 .param = param,
509 },
510 .status = FAILED,
511 .child_cfg = child_cfg,
512 .peer_cfg = peer_cfg,
513 .lock = spinlock_create(),
514 .limits = limits,
515 },
516 .public = {
517 .execute = _initiate_execute,
518 .get_priority = _get_priority_medium,
519 .destroy = _destroy_job,
520 },
521 .refcount = 1,
522 );
523 job->listener.logger.listener = &job->listener;
524 thread_cleanup_push((void*)destroy_job, job);
525
526 if (callback == NULL)
527 {
528 initiate_execute(job);
529 }
530 else
531 {
532 if (wait_for_listener(job, timeout))
533 {
534 job->listener.status = OUT_OF_RES;
535 }
536 }
537 status = job->listener.status;
538 thread_cleanup_pop(TRUE);
539 return status;
540 }
541
542 METHOD(job_t, terminate_ike_execute, job_requeue_t,
543 interface_job_t *job)
544 {
545 interface_listener_t *listener = &job->listener;
546 uint32_t unique_id = listener->id;
547 ike_sa_t *ike_sa;
548
549 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
550 unique_id);
551 if (!ike_sa)
552 {
553 DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
554 listener->status = NOT_FOUND;
555 /* release listener */
556 listener_done(listener);
557 return JOB_REQUEUE_NONE;
558 }
559 listener->lock->lock(listener->lock);
560 listener->ike_sa = ike_sa;
561 listener->lock->unlock(listener->lock);
562
563 if (ike_sa->delete(ike_sa) != DESTROY_ME)
564 { /* delete failed */
565 listener->status = FAILED;
566 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
567 }
568 else
569 {
570 if (!listener->logger.callback)
571 {
572 listener->status = SUCCESS;
573 }
574 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
575 ike_sa);
576 }
577 return JOB_REQUEUE_NONE;
578 }
579
580 METHOD(controller_t, terminate_ike, status_t,
581 controller_t *this, uint32_t unique_id,
582 controller_cb_t callback, void *param, u_int timeout)
583 {
584 interface_job_t *job;
585 status_t status;
586
587 INIT(job,
588 .listener = {
589 .public = {
590 .ike_state_change = _ike_state_change_terminate,
591 .child_state_change = _child_state_change_terminate,
592 },
593 .logger = {
594 .public = {
595 .log = _listener_log,
596 .get_level = _listener_get_level,
597 },
598 .callback = callback,
599 .param = param,
600 },
601 .status = FAILED,
602 .id = unique_id,
603 .lock = spinlock_create(),
604 },
605 .public = {
606 .execute = _terminate_ike_execute,
607 .get_priority = _get_priority_medium,
608 .destroy = _destroy_job,
609 },
610 .refcount = 1,
611 );
612 job->listener.logger.listener = &job->listener;
613 thread_cleanup_push((void*)destroy_job, job);
614
615 if (callback == NULL)
616 {
617 terminate_ike_execute(job);
618 }
619 else
620 {
621 if (wait_for_listener(job, timeout))
622 {
623 job->listener.status = OUT_OF_RES;
624 }
625 }
626 status = job->listener.status;
627 thread_cleanup_pop(TRUE);
628 return status;
629 }
630
631 METHOD(job_t, terminate_child_execute, job_requeue_t,
632 interface_job_t *job)
633 {
634 interface_listener_t *listener = &job->listener;
635 uint32_t id = listener->id;
636 child_sa_t *child_sa;
637 ike_sa_t *ike_sa;
638
639 ike_sa = charon->child_sa_manager->checkout_by_id(charon->child_sa_manager,
640 id, &child_sa);
641 if (!ike_sa)
642 {
643 DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found", id);
644 listener->status = NOT_FOUND;
645 /* release listener */
646 listener_done(listener);
647 return JOB_REQUEUE_NONE;
648 }
649 listener->lock->lock(listener->lock);
650 listener->ike_sa = ike_sa;
651 listener->lock->unlock(listener->lock);
652
653 if (child_sa->get_state(child_sa) == CHILD_ROUTED)
654 {
655 DBG1(DBG_IKE, "unable to terminate, established "
656 "CHILD_SA with ID %d not found", id);
657 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
658 listener->status = NOT_FOUND;
659 /* release listener */
660 listener_done(listener);
661 return JOB_REQUEUE_NONE;
662 }
663
664 if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
665 child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
666 {
667 if (!listener->logger.callback)
668 {
669 listener->status = SUCCESS;
670 }
671 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
672 }
673 else
674 {
675 listener->status = FAILED;
676 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
677 ike_sa);
678 }
679 return JOB_REQUEUE_NONE;
680 }
681
682 METHOD(controller_t, terminate_child, status_t,
683 controller_t *this, uint32_t unique_id,
684 controller_cb_t callback, void *param, u_int timeout)
685 {
686 interface_job_t *job;
687 status_t status;
688
689 INIT(job,
690 .listener = {
691 .public = {
692 .ike_state_change = _ike_state_change_terminate,
693 .child_state_change = _child_state_change_terminate,
694 },
695 .logger = {
696 .public = {
697 .log = _listener_log,
698 .get_level = _listener_get_level,
699 },
700 .callback = callback,
701 .param = param,
702 },
703 .status = FAILED,
704 .id = unique_id,
705 .lock = spinlock_create(),
706 },
707 .public = {
708 .execute = _terminate_child_execute,
709 .get_priority = _get_priority_medium,
710 .destroy = _destroy_job,
711 },
712 .refcount = 1,
713 );
714 job->listener.logger.listener = &job->listener;
715 thread_cleanup_push((void*)destroy_job, job);
716
717 if (callback == NULL)
718 {
719 terminate_child_execute(job);
720 }
721 else
722 {
723 if (wait_for_listener(job, timeout))
724 {
725 job->listener.status = OUT_OF_RES;
726 }
727 }
728 status = job->listener.status;
729 thread_cleanup_pop(TRUE);
730 return status;
731 }
732
733 /**
734 * See header
735 */
736 bool controller_cb_empty(void *param, debug_t group, level_t level,
737 ike_sa_t *ike_sa, const char *message)
738 {
739 return TRUE;
740 }
741
742 METHOD(controller_t, destroy, void,
743 private_controller_t *this)
744 {
745 free(this);
746 }
747
748 /*
749 * Described in header-file
750 */
751 controller_t *controller_create(void)
752 {
753 private_controller_t *this;
754
755 INIT(this,
756 .public = {
757 .create_ike_sa_enumerator = _create_ike_sa_enumerator,
758 .initiate = _initiate,
759 .terminate_ike = _terminate_ike,
760 .terminate_child = _terminate_child,
761 .destroy = _destroy,
762 },
763 );
764
765 return &this->public;
766 }