2 * Copyright (C) 2011-2012 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>
27 #include <threading/thread.h>
28 #include <threading/spinlock.h>
29 #include <threading/semaphore.h>
31 typedef struct private_controller_t private_controller_t
;
32 typedef struct interface_listener_t interface_listener_t
;
33 typedef struct interface_logger_t interface_logger_t
;
36 * Private data of an stroke_t object.
38 struct private_controller_t
{
41 * Public part of stroke_t object.
47 * helper struct for the logger interface
49 struct interface_logger_t
{
51 * public logger interface
56 * reference to the listener
58 interface_listener_t
*listener
;
61 * interface callback (listener gets redirected to here)
63 controller_cb_t callback
;
66 * user parameter to pass to callback
72 * helper struct to map listener callbacks to interface callbacks
74 struct interface_listener_t
{
77 * public bus listener interface
84 interface_logger_t logger
;
87 * status of the operation, return to method callers
92 * child configuration, used for initiate
94 child_cfg_t
*child_cfg
;
97 * peer configuration, used for initiate
107 * unique ID, used for various methods
112 * semaphore to implement wait_for_listener()
117 * spinlock to update the IKE_SA handle properly
123 typedef struct interface_job_t interface_job_t
;
126 * job for asynchronous listen operations
128 struct interface_job_t
{
136 * associated listener
138 interface_listener_t listener
;
141 * the job is reference counted as the thread executing a job as well as
142 * the thread waiting in wait_for_listener() require it but either of them
143 * could be done first
149 * This function wakes a thread that is waiting in wait_for_listener(),
150 * either from a listener or from a job.
152 static inline bool listener_done(interface_listener_t
*listener
)
156 listener
->done
->post(listener
->done
);
162 * thread_cleanup_t handler to unregister a listener.
164 static void listener_unregister(interface_listener_t
*listener
)
166 charon
->bus
->remove_listener(charon
->bus
, &listener
->public);
167 charon
->bus
->remove_logger(charon
->bus
, &listener
->logger
.public);
171 * Registers the listener, executes the job and then waits synchronously until
172 * the listener is done or the timeout occurred.
174 * @note Use 'return listener_done(listener)' to properly unregister a listener
176 * @param listener listener to register
177 * @param job job to execute asynchronously when registered, or NULL
178 * @param timeout max timeout in ms to listen for events, 0 to disable
179 * @return TRUE if timed out
181 static bool wait_for_listener(interface_job_t
*job
, u_int timeout
)
183 interface_listener_t
*listener
= &job
->listener
;
184 bool old
, timed_out
= FALSE
;
186 /* avoid that the job is destroyed too early */
187 ref_get(&job
->refcount
);
189 listener
->done
= semaphore_create(0);
191 charon
->bus
->add_logger(charon
->bus
, &listener
->logger
.public);
192 charon
->bus
->add_listener(charon
->bus
, &listener
->public);
193 lib
->processor
->queue_job(lib
->processor
, &job
->public);
195 thread_cleanup_push((thread_cleanup_t
)listener_unregister
, listener
);
196 old
= thread_cancelability(TRUE
);
199 timed_out
= listener
->done
->timed_wait(listener
->done
, timeout
);
203 listener
->done
->wait(listener
->done
);
205 thread_cancelability(old
);
206 thread_cleanup_pop(TRUE
);
210 METHOD(logger_t
, listener_log
, void,
211 interface_logger_t
*this, debug_t group
, level_t level
, int thread
,
212 ike_sa_t
*ike_sa
, const char *message
)
216 this->listener
->lock
->lock(this->listener
->lock
);
217 target
= this->listener
->ike_sa
;
218 this->listener
->lock
->unlock(this->listener
->lock
);
220 if (target
== ike_sa
)
222 if (!this->callback(this->param
, group
, level
, ike_sa
, message
))
224 this->listener
->status
= NEED_MORE
;
225 listener_done(this->listener
);
230 METHOD(logger_t
, listener_get_level
, level_t
,
231 interface_logger_t
*this, debug_t group
)
233 /* in order to allow callback listeners to decide what they want to log
234 * we request any log message, but only if we actually want logging */
235 return this->callback
== controller_cb_empty ? LEVEL_SILENT
: LEVEL_PRIVATE
;
238 METHOD(job_t
, get_priority_medium
, job_priority_t
,
241 return JOB_PRIO_MEDIUM
;
244 METHOD(listener_t
, ike_state_change
, bool,
245 interface_listener_t
*this, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
249 this->lock
->lock(this->lock
);
250 target
= this->ike_sa
;
251 this->lock
->unlock(this->lock
);
253 if (target
== ike_sa
)
258 case IKE_ESTABLISHED
:
259 { /* mediation connections are complete without CHILD_SA */
260 peer_cfg_t
*peer_cfg
= ike_sa
->get_peer_cfg(ike_sa
);
262 if (peer_cfg
->is_mediation(peer_cfg
))
264 this->status
= SUCCESS
;
265 return listener_done(this);
271 if (ike_sa
->get_state(ike_sa
) == IKE_DELETING
)
272 { /* proper termination */
273 this->status
= SUCCESS
;
275 return listener_done(this);
283 METHOD(listener_t
, child_state_change
, bool,
284 interface_listener_t
*this, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
285 child_sa_state_t state
)
289 this->lock
->lock(this->lock
);
290 target
= this->ike_sa
;
291 this->lock
->unlock(this->lock
);
293 if (target
== ike_sa
)
297 case CHILD_INSTALLED
:
298 this->status
= SUCCESS
;
299 return listener_done(this);
300 case CHILD_DESTROYING
:
301 switch (child_sa
->get_state(child_sa
))
305 this->status
= SUCCESS
;
310 return listener_done(this);
318 METHOD(job_t
, destroy_job
, void,
319 interface_job_t
*this)
321 if (ref_put(&this->refcount
))
323 this->listener
.lock
->destroy(this->listener
.lock
);
324 DESTROY_IF(this->listener
.done
);
329 METHOD(controller_t
, create_ike_sa_enumerator
, enumerator_t
*,
330 private_controller_t
*this, bool wait
)
332 return charon
->ike_sa_manager
->create_enumerator(charon
->ike_sa_manager
,
336 METHOD(job_t
, initiate_execute
, job_requeue_t
,
337 interface_job_t
*job
)
340 interface_listener_t
*listener
= &job
->listener
;
341 peer_cfg_t
*peer_cfg
= listener
->peer_cfg
;
343 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(charon
->ike_sa_manager
,
347 listener
->child_cfg
->destroy(listener
->child_cfg
);
348 peer_cfg
->destroy(peer_cfg
);
349 listener
->status
= FAILED
;
350 /* release listener */
351 listener_done(listener
);
352 return JOB_REQUEUE_NONE
;
354 listener
->lock
->lock(listener
->lock
);
355 listener
->ike_sa
= ike_sa
;
356 listener
->lock
->unlock(listener
->lock
);
358 if (ike_sa
->get_peer_cfg(ike_sa
) == NULL
)
360 ike_sa
->set_peer_cfg(ike_sa
, peer_cfg
);
362 peer_cfg
->destroy(peer_cfg
);
364 if (ike_sa
->initiate(ike_sa
, listener
->child_cfg
, 0, NULL
, NULL
) == SUCCESS
)
366 if (!listener
->logger
.callback
)
368 listener
->status
= SUCCESS
;
370 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
374 listener
->status
= FAILED
;
375 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
378 return JOB_REQUEUE_NONE
;
381 METHOD(controller_t
, initiate
, status_t
,
382 private_controller_t
*this, peer_cfg_t
*peer_cfg
, child_cfg_t
*child_cfg
,
383 controller_cb_t callback
, void *param
, u_int timeout
)
385 interface_job_t
*job
;
391 .ike_state_change
= _ike_state_change
,
392 .child_state_change
= _child_state_change
,
396 .log
= _listener_log
,
397 .get_level
= _listener_get_level
,
399 .callback
= callback
,
403 .child_cfg
= child_cfg
,
404 .peer_cfg
= peer_cfg
,
405 .lock
= spinlock_create(),
408 .execute
= _initiate_execute
,
409 .get_priority
= _get_priority_medium
,
410 .destroy
= _destroy_job
,
414 job
->listener
.logger
.listener
= &job
->listener
;
415 thread_cleanup_push((void*)destroy_job
, job
);
417 if (callback
== NULL
)
419 initiate_execute(job
);
423 if (wait_for_listener(job
, timeout
))
425 job
->listener
.status
= OUT_OF_RES
;
428 status
= job
->listener
.status
;
429 thread_cleanup_pop(TRUE
);
433 METHOD(job_t
, terminate_ike_execute
, job_requeue_t
,
434 interface_job_t
*job
)
436 interface_listener_t
*listener
= &job
->listener
;
437 u_int32_t unique_id
= listener
->id
;
440 ike_sa
= charon
->ike_sa_manager
->checkout_by_id(charon
->ike_sa_manager
,
444 DBG1(DBG_IKE
, "unable to terminate IKE_SA: ID %d not found", unique_id
);
445 listener
->status
= NOT_FOUND
;
446 /* release listener */
447 listener_done(listener
);
448 return JOB_REQUEUE_NONE
;
450 listener
->lock
->lock(listener
->lock
);
451 listener
->ike_sa
= ike_sa
;
452 listener
->lock
->unlock(listener
->lock
);
454 if (ike_sa
->delete(ike_sa
) != DESTROY_ME
)
455 { /* delete failed */
456 listener
->status
= FAILED
;
457 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
461 if (!listener
->logger
.callback
)
463 listener
->status
= SUCCESS
;
465 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
468 return JOB_REQUEUE_NONE
;
471 METHOD(controller_t
, terminate_ike
, status_t
,
472 controller_t
*this, u_int32_t unique_id
,
473 controller_cb_t callback
, void *param
, u_int timeout
)
475 interface_job_t
*job
;
481 .ike_state_change
= _ike_state_change
,
482 .child_state_change
= _child_state_change
,
486 .log
= _listener_log
,
487 .get_level
= _listener_get_level
,
489 .callback
= callback
,
494 .lock
= spinlock_create(),
497 .execute
= _terminate_ike_execute
,
498 .get_priority
= _get_priority_medium
,
499 .destroy
= _destroy_job
,
503 job
->listener
.logger
.listener
= &job
->listener
;
504 thread_cleanup_push((void*)destroy_job
, job
);
506 if (callback
== NULL
)
508 terminate_ike_execute(job
);
512 if (wait_for_listener(job
, timeout
))
514 job
->listener
.status
= OUT_OF_RES
;
517 status
= job
->listener
.status
;
518 thread_cleanup_pop(TRUE
);
522 METHOD(job_t
, terminate_child_execute
, job_requeue_t
,
523 interface_job_t
*job
)
525 interface_listener_t
*listener
= &job
->listener
;
526 u_int32_t reqid
= listener
->id
;
527 enumerator_t
*enumerator
;
528 child_sa_t
*child_sa
;
531 ike_sa
= charon
->ike_sa_manager
->checkout_by_id(charon
->ike_sa_manager
,
535 DBG1(DBG_IKE
, "unable to terminate, CHILD_SA with ID %d not found",
537 listener
->status
= NOT_FOUND
;
538 /* release listener */
539 listener_done(listener
);
540 return JOB_REQUEUE_NONE
;
542 listener
->lock
->lock(listener
->lock
);
543 listener
->ike_sa
= ike_sa
;
544 listener
->lock
->unlock(listener
->lock
);
546 enumerator
= ike_sa
->create_child_sa_enumerator(ike_sa
);
547 while (enumerator
->enumerate(enumerator
, (void**)&child_sa
))
549 if (child_sa
->get_state(child_sa
) != CHILD_ROUTED
&&
550 child_sa
->get_reqid(child_sa
) == reqid
)
556 enumerator
->destroy(enumerator
);
560 DBG1(DBG_IKE
, "unable to terminate, established "
561 "CHILD_SA with ID %d not found", reqid
);
562 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
563 listener
->status
= NOT_FOUND
;
564 /* release listener */
565 listener_done(listener
);
566 return JOB_REQUEUE_NONE
;
569 if (ike_sa
->delete_child_sa(ike_sa
, child_sa
->get_protocol(child_sa
),
570 child_sa
->get_spi(child_sa
, TRUE
), FALSE
) != DESTROY_ME
)
572 if (!listener
->logger
.callback
)
574 listener
->status
= SUCCESS
;
576 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
580 listener
->status
= FAILED
;
581 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
584 return JOB_REQUEUE_NONE
;
587 METHOD(controller_t
, terminate_child
, status_t
,
588 controller_t
*this, u_int32_t reqid
,
589 controller_cb_t callback
, void *param
, u_int timeout
)
591 interface_job_t
*job
;
597 .ike_state_change
= _ike_state_change
,
598 .child_state_change
= _child_state_change
,
602 .log
= _listener_log
,
603 .get_level
= _listener_get_level
,
605 .callback
= callback
,
610 .lock
= spinlock_create(),
613 .execute
= _terminate_child_execute
,
614 .get_priority
= _get_priority_medium
,
615 .destroy
= _destroy_job
,
619 job
->listener
.logger
.listener
= &job
->listener
;
620 thread_cleanup_push((void*)destroy_job
, job
);
622 if (callback
== NULL
)
624 terminate_child_execute(job
);
628 if (wait_for_listener(job
, timeout
))
630 job
->listener
.status
= OUT_OF_RES
;
633 status
= job
->listener
.status
;
634 thread_cleanup_pop(TRUE
);
641 bool controller_cb_empty(void *param
, debug_t group
, level_t level
,
642 ike_sa_t
*ike_sa
, const char *message
)
647 METHOD(controller_t
, destroy
, void,
648 private_controller_t
*this)
654 * Described in header-file
656 controller_t
*controller_create(void)
658 private_controller_t
*this;
662 .create_ike_sa_enumerator
= _create_ike_sa_enumerator
,
663 .initiate
= _initiate
,
664 .terminate_ike
= _terminate_ike
,
665 .terminate_child
= _terminate_child
,
670 return &this->public;