2 * Copyright (C) 2011-2015 Tobias Brunner
3 * Copyright (C) 2007-2011 Martin Willi
4 * Copyright (C) 2011 revosec AG
5 * HSR 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
122 * whether to check limits during initiation
127 * whether to force termination
134 typedef struct interface_job_t interface_job_t
;
137 * job for asynchronous listen operations
139 struct interface_job_t
{
147 * associated listener
149 interface_listener_t listener
;
152 * the job is reference counted as the thread executing a job as well as
153 * the thread waiting in wait_for_listener() require it but either of them
154 * could be done first
160 * This function wakes a thread that is waiting in wait_for_listener(),
161 * either from a listener or from a job.
163 static inline bool listener_done(interface_listener_t
*listener
)
167 listener
->done
->post(listener
->done
);
173 * thread_cleanup_t handler to unregister a listener.
175 static void listener_unregister(interface_listener_t
*listener
)
177 charon
->bus
->remove_listener(charon
->bus
, &listener
->public);
178 charon
->bus
->remove_logger(charon
->bus
, &listener
->logger
.public);
182 * Registers the listener, executes the job and then waits synchronously until
183 * the listener is done or the timeout occurred.
185 * @note Use 'return listener_done(listener)' to properly unregister a listener
187 * @param listener listener to register
188 * @param job job to execute asynchronously when registered, or NULL
189 * @param timeout max timeout in ms to listen for events, 0 to disable
190 * @return TRUE if timed out
192 static bool wait_for_listener(interface_job_t
*job
, u_int timeout
)
194 interface_listener_t
*listener
= &job
->listener
;
195 bool old
, timed_out
= FALSE
;
197 /* avoid that the job is destroyed too early */
198 ref_get(&job
->refcount
);
200 listener
->done
= semaphore_create(0);
202 charon
->bus
->add_logger(charon
->bus
, &listener
->logger
.public);
203 charon
->bus
->add_listener(charon
->bus
, &listener
->public);
204 lib
->processor
->queue_job(lib
->processor
, &job
->public);
206 thread_cleanup_push((thread_cleanup_t
)listener_unregister
, listener
);
207 old
= thread_cancelability(TRUE
);
210 timed_out
= listener
->done
->timed_wait(listener
->done
, timeout
);
214 listener
->done
->wait(listener
->done
);
216 thread_cancelability(old
);
217 thread_cleanup_pop(TRUE
);
221 METHOD(logger_t
, listener_log
, void,
222 interface_logger_t
*this, debug_t group
, level_t level
, int thread
,
223 ike_sa_t
*ike_sa
, const char *message
)
227 this->listener
->lock
->lock(this->listener
->lock
);
228 target
= this->listener
->ike_sa
;
229 this->listener
->lock
->unlock(this->listener
->lock
);
231 if (target
== ike_sa
)
233 if (!this->callback(this->param
, group
, level
, ike_sa
, message
))
235 this->listener
->status
= NEED_MORE
;
236 listener_done(this->listener
);
241 METHOD(logger_t
, listener_get_level
, level_t
,
242 interface_logger_t
*this, debug_t group
)
244 /* in order to allow callback listeners to decide what they want to log
245 * we request any log message, but only if we actually want logging */
246 return this->callback
== controller_cb_empty ? LEVEL_SILENT
: LEVEL_PRIVATE
;
249 METHOD(job_t
, get_priority_medium
, job_priority_t
,
252 return JOB_PRIO_MEDIUM
;
255 METHOD(listener_t
, ike_state_change
, bool,
256 interface_listener_t
*this, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
260 this->lock
->lock(this->lock
);
261 target
= this->ike_sa
;
262 this->lock
->unlock(this->lock
);
264 if (target
== ike_sa
)
269 case IKE_ESTABLISHED
:
270 { /* mediation connections are complete without CHILD_SA */
271 peer_cfg_t
*peer_cfg
= ike_sa
->get_peer_cfg(ike_sa
);
273 if (peer_cfg
->is_mediation(peer_cfg
))
275 this->status
= SUCCESS
;
276 return listener_done(this);
282 return listener_done(this);
290 METHOD(listener_t
, ike_state_change_terminate
, bool,
291 interface_listener_t
*this, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
295 this->lock
->lock(this->lock
);
296 target
= this->ike_sa
;
297 this->lock
->unlock(this->lock
);
299 if (target
== ike_sa
)
304 this->status
= SUCCESS
;
305 return listener_done(this);
313 METHOD(listener_t
, child_state_change
, bool,
314 interface_listener_t
*this, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
315 child_sa_state_t state
)
319 this->lock
->lock(this->lock
);
320 target
= this->ike_sa
;
321 this->lock
->unlock(this->lock
);
323 if (target
== ike_sa
)
327 case CHILD_INSTALLED
:
328 this->status
= SUCCESS
;
329 return listener_done(this);
330 case CHILD_DESTROYING
:
331 switch (child_sa
->get_state(child_sa
))
334 /* retrying with a different DH group; survive another
335 * initiation round */
336 this->status
= NEED_MORE
;
339 if (this->status
== NEED_MORE
)
341 this->status
= FAILED
;
348 return listener_done(this);
356 METHOD(listener_t
, child_state_change_terminate
, bool,
357 interface_listener_t
*this, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
358 child_sa_state_t state
)
362 this->lock
->lock(this->lock
);
363 target
= this->ike_sa
;
364 this->lock
->unlock(this->lock
);
366 if (target
== ike_sa
)
370 case CHILD_DESTROYING
:
371 switch (child_sa
->get_state(child_sa
))
375 this->status
= SUCCESS
;
380 return listener_done(this);
388 METHOD(job_t
, destroy_job
, void,
389 interface_job_t
*this)
391 if (ref_put(&this->refcount
))
393 this->listener
.lock
->destroy(this->listener
.lock
);
394 DESTROY_IF(this->listener
.done
);
399 METHOD(controller_t
, create_ike_sa_enumerator
, enumerator_t
*,
400 private_controller_t
*this, bool wait
)
402 return charon
->ike_sa_manager
->create_enumerator(charon
->ike_sa_manager
,
406 METHOD(job_t
, initiate_execute
, job_requeue_t
,
407 interface_job_t
*job
)
410 interface_listener_t
*listener
= &job
->listener
;
411 peer_cfg_t
*peer_cfg
= listener
->peer_cfg
;
413 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(charon
->ike_sa_manager
,
417 listener
->child_cfg
->destroy(listener
->child_cfg
);
418 peer_cfg
->destroy(peer_cfg
);
419 listener
->status
= FAILED
;
420 listener_done(listener
);
421 return JOB_REQUEUE_NONE
;
423 listener
->lock
->lock(listener
->lock
);
424 listener
->ike_sa
= ike_sa
;
425 listener
->lock
->unlock(listener
->lock
);
427 if (ike_sa
->get_peer_cfg(ike_sa
) == NULL
)
429 ike_sa
->set_peer_cfg(ike_sa
, peer_cfg
);
431 peer_cfg
->destroy(peer_cfg
);
433 if (listener
->options
.limits
&& ike_sa
->get_state(ike_sa
) == IKE_CREATED
)
434 { /* only check if we are not reusing an IKE_SA */
435 u_int half_open
, limit_half_open
, limit_job_load
;
437 half_open
= charon
->ike_sa_manager
->get_half_open_count(
438 charon
->ike_sa_manager
, NULL
, FALSE
);
439 limit_half_open
= lib
->settings
->get_int(lib
->settings
,
440 "%s.init_limit_half_open", 0, lib
->ns
);
441 limit_job_load
= lib
->settings
->get_int(lib
->settings
,
442 "%s.init_limit_job_load", 0, lib
->ns
);
443 if (limit_half_open
&& half_open
>= limit_half_open
)
445 DBG1(DBG_IKE
, "abort IKE_SA initiation, half open IKE_SA count of "
446 "%d exceeds limit of %d", half_open
, limit_half_open
);
447 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
449 listener
->child_cfg
->destroy(listener
->child_cfg
);
450 listener
->status
= INVALID_STATE
;
451 listener_done(listener
);
452 return JOB_REQUEUE_NONE
;
458 for (i
= 0; i
< JOB_PRIO_MAX
; i
++)
460 jobs
+= lib
->processor
->get_job_load(lib
->processor
, i
);
462 if (jobs
> limit_job_load
)
464 DBG1(DBG_IKE
, "abort IKE_SA initiation, job load of %d exceeds "
465 "limit of %d", jobs
, limit_job_load
);
466 charon
->ike_sa_manager
->checkin_and_destroy(
467 charon
->ike_sa_manager
, ike_sa
);
468 listener
->child_cfg
->destroy(listener
->child_cfg
);
469 listener
->status
= INVALID_STATE
;
470 listener_done(listener
);
471 return JOB_REQUEUE_NONE
;
476 if (ike_sa
->initiate(ike_sa
, listener
->child_cfg
, 0, NULL
, NULL
) == SUCCESS
)
478 if (!listener
->logger
.callback
)
480 listener
->status
= SUCCESS
;
482 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
486 listener
->status
= FAILED
;
487 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
490 return JOB_REQUEUE_NONE
;
493 METHOD(controller_t
, initiate
, status_t
,
494 private_controller_t
*this, peer_cfg_t
*peer_cfg
, child_cfg_t
*child_cfg
,
495 controller_cb_t callback
, void *param
, u_int timeout
, bool limits
)
497 interface_job_t
*job
;
503 .ike_state_change
= _ike_state_change
,
504 .child_state_change
= _child_state_change
,
508 .log
= _listener_log
,
509 .get_level
= _listener_get_level
,
511 .callback
= callback
,
515 .child_cfg
= child_cfg
,
516 .peer_cfg
= peer_cfg
,
517 .lock
= spinlock_create(),
518 .options
.limits
= limits
,
521 .execute
= _initiate_execute
,
522 .get_priority
= _get_priority_medium
,
523 .destroy
= _destroy_job
,
527 job
->listener
.logger
.listener
= &job
->listener
;
528 thread_cleanup_push((void*)destroy_job
, job
);
530 if (callback
== NULL
)
532 initiate_execute(job
);
536 if (wait_for_listener(job
, timeout
))
538 job
->listener
.status
= OUT_OF_RES
;
541 status
= job
->listener
.status
;
542 thread_cleanup_pop(TRUE
);
546 METHOD(job_t
, terminate_ike_execute
, job_requeue_t
,
547 interface_job_t
*job
)
549 interface_listener_t
*listener
= &job
->listener
;
550 uint32_t unique_id
= listener
->id
;
553 ike_sa
= charon
->ike_sa_manager
->checkout_by_id(charon
->ike_sa_manager
,
557 DBG1(DBG_IKE
, "unable to terminate IKE_SA: ID %d not found", unique_id
);
558 listener
->status
= NOT_FOUND
;
559 /* release listener */
560 listener_done(listener
);
561 return JOB_REQUEUE_NONE
;
563 listener
->lock
->lock(listener
->lock
);
564 listener
->ike_sa
= ike_sa
;
565 listener
->lock
->unlock(listener
->lock
);
567 if (ike_sa
->delete(ike_sa
, listener
->options
.force
) != DESTROY_ME
)
568 { /* delete queued */
569 listener
->status
= FAILED
;
570 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
574 if (!listener
->logger
.callback
)
576 listener
->status
= SUCCESS
;
578 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
581 return JOB_REQUEUE_NONE
;
584 METHOD(controller_t
, terminate_ike
, status_t
,
585 controller_t
*this, uint32_t unique_id
, bool force
,
586 controller_cb_t callback
, void *param
, u_int timeout
)
588 interface_job_t
*job
;
594 .ike_state_change
= _ike_state_change_terminate
,
598 .log
= _listener_log
,
599 .get_level
= _listener_get_level
,
601 .callback
= callback
,
606 .lock
= spinlock_create(),
609 .execute
= _terminate_ike_execute
,
610 .get_priority
= _get_priority_medium
,
611 .destroy
= _destroy_job
,
615 job
->listener
.logger
.listener
= &job
->listener
;
616 thread_cleanup_push((void*)destroy_job
, job
);
618 if (callback
== NULL
)
620 job
->listener
.options
.force
= force
;
621 terminate_ike_execute(job
);
627 job
->listener
.options
.force
= force
;
629 if (wait_for_listener(job
, timeout
))
631 job
->listener
.status
= OUT_OF_RES
;
634 { /* force termination once timeout is reached */
635 job
->listener
.options
.force
= TRUE
;
636 terminate_ike_execute(job
);
640 status
= job
->listener
.status
;
641 thread_cleanup_pop(TRUE
);
645 METHOD(job_t
, terminate_child_execute
, job_requeue_t
,
646 interface_job_t
*job
)
648 interface_listener_t
*listener
= &job
->listener
;
649 uint32_t id
= listener
->id
;
650 child_sa_t
*child_sa
;
653 ike_sa
= charon
->child_sa_manager
->checkout_by_id(charon
->child_sa_manager
,
657 DBG1(DBG_IKE
, "unable to terminate, CHILD_SA with ID %d not found", id
);
658 listener
->status
= NOT_FOUND
;
659 /* release listener */
660 listener_done(listener
);
661 return JOB_REQUEUE_NONE
;
663 listener
->lock
->lock(listener
->lock
);
664 listener
->ike_sa
= ike_sa
;
665 listener
->lock
->unlock(listener
->lock
);
667 if (ike_sa
->delete_child_sa(ike_sa
, child_sa
->get_protocol(child_sa
),
668 child_sa
->get_spi(child_sa
, TRUE
), FALSE
) != DESTROY_ME
)
670 if (!listener
->logger
.callback
)
672 listener
->status
= SUCCESS
;
674 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
678 listener
->status
= FAILED
;
679 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
682 return JOB_REQUEUE_NONE
;
685 METHOD(controller_t
, terminate_child
, status_t
,
686 controller_t
*this, uint32_t unique_id
,
687 controller_cb_t callback
, void *param
, u_int timeout
)
689 interface_job_t
*job
;
695 .ike_state_change
= _ike_state_change_terminate
,
696 .child_state_change
= _child_state_change_terminate
,
700 .log
= _listener_log
,
701 .get_level
= _listener_get_level
,
703 .callback
= callback
,
708 .lock
= spinlock_create(),
711 .execute
= _terminate_child_execute
,
712 .get_priority
= _get_priority_medium
,
713 .destroy
= _destroy_job
,
717 job
->listener
.logger
.listener
= &job
->listener
;
718 thread_cleanup_push((void*)destroy_job
, job
);
720 if (callback
== NULL
)
722 terminate_child_execute(job
);
726 if (wait_for_listener(job
, timeout
))
728 job
->listener
.status
= OUT_OF_RES
;
731 status
= job
->listener
.status
;
732 thread_cleanup_pop(TRUE
);
739 bool controller_cb_empty(void *param
, debug_t group
, level_t level
,
740 ike_sa_t
*ike_sa
, const char *message
)
745 METHOD(controller_t
, destroy
, void,
746 private_controller_t
*this)
752 * Described in header-file
754 controller_t
*controller_create(void)
756 private_controller_t
*this;
760 .create_ike_sa_enumerator
= _create_ike_sa_enumerator
,
761 .initiate
= _initiate
,
762 .terminate_ike
= _terminate_ike
,
763 .terminate_child
= _terminate_child
,
768 return &this->public;