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
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>.
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
18 #include "controller.h"
20 #include <sys/types.h>
26 #include <threading/thread.h>
27 #include <threading/spinlock.h>
28 #include <threading/semaphore.h>
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
;
35 * Private data of an stroke_t object.
37 struct private_controller_t
{
40 * Public part of stroke_t object.
46 * helper struct for the logger interface
48 struct interface_logger_t
{
50 * public logger interface
55 * reference to the listener
57 interface_listener_t
*listener
;
60 * interface callback (listener gets redirected to here)
62 controller_cb_t callback
;
65 * user parameter to pass to callback
71 * helper struct to map listener callbacks to interface callbacks
73 struct interface_listener_t
{
76 * public bus listener interface
83 interface_logger_t logger
;
86 * status of the operation, return to method callers
91 * child configuration, used for initiate
93 child_cfg_t
*child_cfg
;
96 * peer configuration, used for initiate
106 * unique ID, used for various methods
111 * semaphore to implement wait_for_listener()
116 * spinlock to update the IKE_SA handle properly
121 * whether to check limits
127 typedef struct interface_job_t interface_job_t
;
130 * job for asynchronous listen operations
132 struct interface_job_t
{
140 * associated listener
142 interface_listener_t listener
;
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
153 * This function wakes a thread that is waiting in wait_for_listener(),
154 * either from a listener or from a job.
156 static inline bool listener_done(interface_listener_t
*listener
)
160 listener
->done
->post(listener
->done
);
166 * thread_cleanup_t handler to unregister a listener.
168 static void listener_unregister(interface_listener_t
*listener
)
170 charon
->bus
->remove_listener(charon
->bus
, &listener
->public);
171 charon
->bus
->remove_logger(charon
->bus
, &listener
->logger
.public);
175 * Registers the listener, executes the job and then waits synchronously until
176 * the listener is done or the timeout occurred.
178 * @note Use 'return listener_done(listener)' to properly unregister a listener
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
185 static bool wait_for_listener(interface_job_t
*job
, u_int timeout
)
187 interface_listener_t
*listener
= &job
->listener
;
188 bool old
, timed_out
= FALSE
;
190 /* avoid that the job is destroyed too early */
191 ref_get(&job
->refcount
);
193 listener
->done
= semaphore_create(0);
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);
199 thread_cleanup_push((thread_cleanup_t
)listener_unregister
, listener
);
200 old
= thread_cancelability(TRUE
);
203 timed_out
= listener
->done
->timed_wait(listener
->done
, timeout
);
207 listener
->done
->wait(listener
->done
);
209 thread_cancelability(old
);
210 thread_cleanup_pop(TRUE
);
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
)
220 this->listener
->lock
->lock(this->listener
->lock
);
221 target
= this->listener
->ike_sa
;
222 this->listener
->lock
->unlock(this->listener
->lock
);
224 if (target
== ike_sa
)
226 if (!this->callback(this->param
, group
, level
, ike_sa
, message
))
228 this->listener
->status
= NEED_MORE
;
229 listener_done(this->listener
);
234 METHOD(logger_t
, listener_get_level
, level_t
,
235 interface_logger_t
*this, debug_t group
)
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
;
242 METHOD(job_t
, get_priority_medium
, job_priority_t
,
245 return JOB_PRIO_MEDIUM
;
248 METHOD(listener_t
, ike_state_change
, bool,
249 interface_listener_t
*this, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
253 this->lock
->lock(this->lock
);
254 target
= this->ike_sa
;
255 this->lock
->unlock(this->lock
);
257 if (target
== ike_sa
)
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
);
266 if (peer_cfg
->is_mediation(peer_cfg
))
268 this->status
= SUCCESS
;
269 return listener_done(this);
275 return listener_done(this);
283 METHOD(listener_t
, ike_state_change_terminate
, bool,
284 interface_listener_t
*this, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
288 this->lock
->lock(this->lock
);
289 target
= this->ike_sa
;
290 this->lock
->unlock(this->lock
);
292 if (target
== ike_sa
)
297 if (ike_sa
->get_state(ike_sa
) == IKE_DELETING
)
298 { /* proper termination */
299 this->status
= SUCCESS
;
301 return listener_done(this);
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
)
315 this->lock
->lock(this->lock
);
316 target
= this->ike_sa
;
317 this->lock
->unlock(this->lock
);
319 if (target
== ike_sa
)
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
))
330 /* retrying with a different DH group; survive another
331 * initiation round */
332 this->status
= NEED_MORE
;
335 if (this->status
== NEED_MORE
)
337 this->status
= FAILED
;
344 return listener_done(this);
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
)
358 this->lock
->lock(this->lock
);
359 target
= this->ike_sa
;
360 this->lock
->unlock(this->lock
);
362 if (target
== ike_sa
)
366 case CHILD_DESTROYING
:
367 switch (child_sa
->get_state(child_sa
))
371 this->status
= SUCCESS
;
376 return listener_done(this);
384 METHOD(job_t
, destroy_job
, void,
385 interface_job_t
*this)
387 if (ref_put(&this->refcount
))
389 this->listener
.lock
->destroy(this->listener
.lock
);
390 DESTROY_IF(this->listener
.done
);
395 METHOD(controller_t
, create_ike_sa_enumerator
, enumerator_t
*,
396 private_controller_t
*this, bool wait
)
398 return charon
->ike_sa_manager
->create_enumerator(charon
->ike_sa_manager
,
402 METHOD(job_t
, initiate_execute
, job_requeue_t
,
403 interface_job_t
*job
)
406 interface_listener_t
*listener
= &job
->listener
;
407 peer_cfg_t
*peer_cfg
= listener
->peer_cfg
;
409 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(charon
->ike_sa_manager
,
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
;
419 listener
->lock
->lock(listener
->lock
);
420 listener
->ike_sa
= ike_sa
;
421 listener
->lock
->unlock(listener
->lock
);
423 if (ike_sa
->get_peer_cfg(ike_sa
) == NULL
)
425 ike_sa
->set_peer_cfg(ike_sa
, peer_cfg
);
427 peer_cfg
->destroy(peer_cfg
);
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
;
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
)
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
,
445 listener
->child_cfg
->destroy(listener
->child_cfg
);
446 listener
->status
= INVALID_STATE
;
447 listener_done(listener
);
448 return JOB_REQUEUE_NONE
;
454 for (i
= 0; i
< JOB_PRIO_MAX
; i
++)
456 jobs
+= lib
->processor
->get_job_load(lib
->processor
, i
);
458 if (jobs
> limit_job_load
)
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
;
472 if (ike_sa
->initiate(ike_sa
, listener
->child_cfg
, 0, NULL
, NULL
) == SUCCESS
)
474 if (!listener
->logger
.callback
)
476 listener
->status
= SUCCESS
;
478 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
482 listener
->status
= FAILED
;
483 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
486 return JOB_REQUEUE_NONE
;
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
)
493 interface_job_t
*job
;
499 .ike_state_change
= _ike_state_change
,
500 .child_state_change
= _child_state_change
,
504 .log
= _listener_log
,
505 .get_level
= _listener_get_level
,
507 .callback
= callback
,
511 .child_cfg
= child_cfg
,
512 .peer_cfg
= peer_cfg
,
513 .lock
= spinlock_create(),
517 .execute
= _initiate_execute
,
518 .get_priority
= _get_priority_medium
,
519 .destroy
= _destroy_job
,
523 job
->listener
.logger
.listener
= &job
->listener
;
524 thread_cleanup_push((void*)destroy_job
, job
);
526 if (callback
== NULL
)
528 initiate_execute(job
);
532 if (wait_for_listener(job
, timeout
))
534 job
->listener
.status
= OUT_OF_RES
;
537 status
= job
->listener
.status
;
538 thread_cleanup_pop(TRUE
);
542 METHOD(job_t
, terminate_ike_execute
, job_requeue_t
,
543 interface_job_t
*job
)
545 interface_listener_t
*listener
= &job
->listener
;
546 uint32_t unique_id
= listener
->id
;
549 ike_sa
= charon
->ike_sa_manager
->checkout_by_id(charon
->ike_sa_manager
,
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
;
559 listener
->lock
->lock(listener
->lock
);
560 listener
->ike_sa
= ike_sa
;
561 listener
->lock
->unlock(listener
->lock
);
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
);
570 if (!listener
->logger
.callback
)
572 listener
->status
= SUCCESS
;
574 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
577 return JOB_REQUEUE_NONE
;
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
)
584 interface_job_t
*job
;
590 .ike_state_change
= _ike_state_change_terminate
,
591 .child_state_change
= _child_state_change_terminate
,
595 .log
= _listener_log
,
596 .get_level
= _listener_get_level
,
598 .callback
= callback
,
603 .lock
= spinlock_create(),
606 .execute
= _terminate_ike_execute
,
607 .get_priority
= _get_priority_medium
,
608 .destroy
= _destroy_job
,
612 job
->listener
.logger
.listener
= &job
->listener
;
613 thread_cleanup_push((void*)destroy_job
, job
);
615 if (callback
== NULL
)
617 terminate_ike_execute(job
);
621 if (wait_for_listener(job
, timeout
))
623 job
->listener
.status
= OUT_OF_RES
;
626 status
= job
->listener
.status
;
627 thread_cleanup_pop(TRUE
);
631 METHOD(job_t
, terminate_child_execute
, job_requeue_t
,
632 interface_job_t
*job
)
634 interface_listener_t
*listener
= &job
->listener
;
635 uint32_t id
= listener
->id
;
636 child_sa_t
*child_sa
;
639 ike_sa
= charon
->child_sa_manager
->checkout_by_id(charon
->child_sa_manager
,
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
;
649 listener
->lock
->lock(listener
->lock
);
650 listener
->ike_sa
= ike_sa
;
651 listener
->lock
->unlock(listener
->lock
);
653 if (child_sa
->get_state(child_sa
) == CHILD_ROUTED
)
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
;
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
)
667 if (!listener
->logger
.callback
)
669 listener
->status
= SUCCESS
;
671 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
675 listener
->status
= FAILED
;
676 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
679 return JOB_REQUEUE_NONE
;
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
)
686 interface_job_t
*job
;
692 .ike_state_change
= _ike_state_change_terminate
,
693 .child_state_change
= _child_state_change_terminate
,
697 .log
= _listener_log
,
698 .get_level
= _listener_get_level
,
700 .callback
= callback
,
705 .lock
= spinlock_create(),
708 .execute
= _terminate_child_execute
,
709 .get_priority
= _get_priority_medium
,
710 .destroy
= _destroy_job
,
714 job
->listener
.logger
.listener
= &job
->listener
;
715 thread_cleanup_push((void*)destroy_job
, job
);
717 if (callback
== NULL
)
719 terminate_child_execute(job
);
723 if (wait_for_listener(job
, timeout
))
725 job
->listener
.status
= OUT_OF_RES
;
728 status
= job
->listener
.status
;
729 thread_cleanup_pop(TRUE
);
736 bool controller_cb_empty(void *param
, debug_t group
, level_t level
,
737 ike_sa_t
*ike_sa
, const char *message
)
742 METHOD(controller_t
, destroy
, void,
743 private_controller_t
*this)
749 * Described in header-file
751 controller_t
*controller_create(void)
753 private_controller_t
*this;
757 .create_ike_sa_enumerator
= _create_ike_sa_enumerator
,
758 .initiate
= _initiate
,
759 .terminate_ike
= _terminate_ike
,
760 .terminate_child
= _terminate_child
,
765 return &this->public;