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/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()
117 typedef struct interface_job_t interface_job_t
;
120 * job for asynchronous listen operations
122 struct interface_job_t
{
130 * associated listener
132 interface_listener_t listener
;
135 * the job is reference counted as the thread executing a job as well as
136 * the thread waiting in wait_for_listener() require it but either of them
137 * could be done first
143 * This function wakes a thread that is waiting in wait_for_listener(),
144 * either from a listener or from a job.
146 static inline bool listener_done(interface_listener_t
*listener
)
150 listener
->done
->post(listener
->done
);
156 * thread_cleanup_t handler to unregister a listener.
158 static void listener_unregister(interface_listener_t
*listener
)
160 charon
->bus
->remove_listener(charon
->bus
, &listener
->public);
161 charon
->bus
->remove_logger(charon
->bus
, &listener
->logger
.public);
165 * Registers the listener, executes the job and then waits synchronously until
166 * the listener is done or the timeout occurred.
168 * @note Use 'return listener_done(listener)' to properly unregister a listener
170 * @param listener listener to register
171 * @param job job to execute asynchronously when registered, or NULL
172 * @param timeout max timeout in ms to listen for events, 0 to disable
173 * @return TRUE if timed out
175 static bool wait_for_listener(interface_job_t
*job
, u_int timeout
)
177 interface_listener_t
*listener
= &job
->listener
;
178 bool old
, timed_out
= FALSE
;
180 /* avoid that the job is destroyed too early */
181 ref_get(&job
->refcount
);
183 listener
->done
= semaphore_create(0);
185 charon
->bus
->add_logger(charon
->bus
, &listener
->logger
.public);
186 charon
->bus
->add_listener(charon
->bus
, &listener
->public);
187 lib
->processor
->queue_job(lib
->processor
, &job
->public);
189 thread_cleanup_push((thread_cleanup_t
)listener_unregister
, listener
);
190 old
= thread_cancelability(TRUE
);
193 timed_out
= listener
->done
->timed_wait(listener
->done
, timeout
);
197 listener
->done
->wait(listener
->done
);
199 thread_cancelability(old
);
200 thread_cleanup_pop(TRUE
);
204 METHOD(logger_t
, listener_log
, void,
205 interface_logger_t
*this, debug_t group
, level_t level
, int thread
,
206 ike_sa_t
*ike_sa
, char* message
)
208 if (this->listener
->ike_sa
== ike_sa
)
210 if (!this->callback(this->param
, group
, level
, ike_sa
, message
))
212 this->listener
->status
= NEED_MORE
;
213 listener_done(this->listener
);
218 METHOD(logger_t
, listener_get_level
, level_t
,
219 interface_logger_t
*this, debug_t group
)
221 /* in order to allow callback listeners to decide what they want to log
222 * we request any log message, but only if we actually want logging */
223 return this->callback
== controller_cb_empty ? LEVEL_SILENT
: LEVEL_PRIVATE
;
226 METHOD(job_t
, get_priority_medium
, job_priority_t
,
229 return JOB_PRIO_MEDIUM
;
232 METHOD(listener_t
, ike_state_change
, bool,
233 interface_listener_t
*this, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
235 if (this->ike_sa
== ike_sa
)
240 case IKE_ESTABLISHED
:
241 { /* mediation connections are complete without CHILD_SA */
242 peer_cfg_t
*peer_cfg
= ike_sa
->get_peer_cfg(ike_sa
);
244 if (peer_cfg
->is_mediation(peer_cfg
))
246 this->status
= SUCCESS
;
247 return listener_done(this);
253 if (ike_sa
->get_state(ike_sa
) == IKE_DELETING
)
254 { /* proper termination */
255 this->status
= SUCCESS
;
257 return listener_done(this);
265 METHOD(listener_t
, child_state_change
, bool,
266 interface_listener_t
*this, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
267 child_sa_state_t state
)
269 if (this->ike_sa
== ike_sa
)
273 case CHILD_INSTALLED
:
274 this->status
= SUCCESS
;
275 return listener_done(this);
276 case CHILD_DESTROYING
:
277 switch (child_sa
->get_state(child_sa
))
281 this->status
= SUCCESS
;
286 return listener_done(this);
294 METHOD(job_t
, destroy_job
, void,
295 interface_job_t
*this)
297 if (ref_put(&this->refcount
))
299 DESTROY_IF(this->listener
.done
);
304 METHOD(controller_t
, create_ike_sa_enumerator
, enumerator_t
*,
305 private_controller_t
*this, bool wait
)
307 return charon
->ike_sa_manager
->create_enumerator(charon
->ike_sa_manager
,
311 METHOD(job_t
, initiate_execute
, job_requeue_t
,
312 interface_job_t
*job
)
315 interface_listener_t
*listener
= &job
->listener
;
316 peer_cfg_t
*peer_cfg
= listener
->peer_cfg
;
318 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(charon
->ike_sa_manager
,
322 listener
->child_cfg
->destroy(listener
->child_cfg
);
323 peer_cfg
->destroy(peer_cfg
);
324 listener
->status
= FAILED
;
325 /* release listener */
326 listener_done(listener
);
327 return JOB_REQUEUE_NONE
;
329 listener
->ike_sa
= ike_sa
;
331 if (ike_sa
->get_peer_cfg(ike_sa
) == NULL
)
333 ike_sa
->set_peer_cfg(ike_sa
, peer_cfg
);
335 peer_cfg
->destroy(peer_cfg
);
337 if (ike_sa
->initiate(ike_sa
, listener
->child_cfg
, 0, NULL
, NULL
) == SUCCESS
)
339 listener
->status
= SUCCESS
;
340 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
344 listener
->status
= FAILED
;
345 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
348 return JOB_REQUEUE_NONE
;
351 METHOD(controller_t
, initiate
, status_t
,
352 private_controller_t
*this, peer_cfg_t
*peer_cfg
, child_cfg_t
*child_cfg
,
353 controller_cb_t callback
, void *param
, u_int timeout
)
355 interface_job_t
*job
;
361 .ike_state_change
= _ike_state_change
,
362 .child_state_change
= _child_state_change
,
366 .log
= _listener_log
,
367 .get_level
= _listener_get_level
,
369 .callback
= callback
,
373 .child_cfg
= child_cfg
,
374 .peer_cfg
= peer_cfg
,
377 .execute
= _initiate_execute
,
378 .get_priority
= _get_priority_medium
,
379 .destroy
= _destroy_job
,
383 job
->listener
.logger
.listener
= &job
->listener
;
385 if (callback
== NULL
)
387 initiate_execute(job
);
391 if (wait_for_listener(job
, timeout
))
393 job
->listener
.status
= OUT_OF_RES
;
396 status
= job
->listener
.status
;
401 METHOD(job_t
, terminate_ike_execute
, job_requeue_t
,
402 interface_job_t
*job
)
404 interface_listener_t
*listener
= &job
->listener
;
405 u_int32_t unique_id
= listener
->id
;
408 ike_sa
= charon
->ike_sa_manager
->checkout_by_id(charon
->ike_sa_manager
,
412 DBG1(DBG_IKE
, "unable to terminate IKE_SA: ID %d not found", unique_id
);
413 listener
->status
= NOT_FOUND
;
414 /* release listener */
415 listener_done(listener
);
416 return JOB_REQUEUE_NONE
;
418 listener
->ike_sa
= ike_sa
;
420 if (ike_sa
->delete(ike_sa
) != DESTROY_ME
)
421 { /* delete failed */
422 listener
->status
= FAILED
;
423 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
427 listener
->status
= SUCCESS
;
428 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
431 return JOB_REQUEUE_NONE
;
434 METHOD(controller_t
, terminate_ike
, status_t
,
435 controller_t
*this, u_int32_t unique_id
,
436 controller_cb_t callback
, void *param
, u_int timeout
)
438 interface_job_t
*job
;
444 .ike_state_change
= _ike_state_change
,
445 .child_state_change
= _child_state_change
,
449 .log
= _listener_log
,
450 .get_level
= _listener_get_level
,
452 .callback
= callback
,
459 .execute
= _terminate_ike_execute
,
460 .get_priority
= _get_priority_medium
,
461 .destroy
= _destroy_job
,
465 job
->listener
.logger
.listener
= &job
->listener
;
467 if (callback
== NULL
)
469 terminate_ike_execute(job
);
473 if (wait_for_listener(job
, timeout
))
475 job
->listener
.status
= OUT_OF_RES
;
478 status
= job
->listener
.status
;
483 METHOD(job_t
, terminate_child_execute
, job_requeue_t
,
484 interface_job_t
*job
)
486 interface_listener_t
*listener
= &job
->listener
;
487 u_int32_t reqid
= listener
->id
;
488 enumerator_t
*enumerator
;
489 child_sa_t
*child_sa
;
492 ike_sa
= charon
->ike_sa_manager
->checkout_by_id(charon
->ike_sa_manager
,
496 DBG1(DBG_IKE
, "unable to terminate, CHILD_SA with ID %d not found",
498 listener
->status
= NOT_FOUND
;
499 /* release listener */
500 listener_done(listener
);
501 return JOB_REQUEUE_NONE
;
503 job
->listener
.ike_sa
= ike_sa
;
505 enumerator
= ike_sa
->create_child_sa_enumerator(ike_sa
);
506 while (enumerator
->enumerate(enumerator
, (void**)&child_sa
))
508 if (child_sa
->get_state(child_sa
) != CHILD_ROUTED
&&
509 child_sa
->get_reqid(child_sa
) == reqid
)
515 enumerator
->destroy(enumerator
);
519 DBG1(DBG_IKE
, "unable to terminate, established "
520 "CHILD_SA with ID %d not found", reqid
);
521 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
522 listener
->status
= NOT_FOUND
;
523 /* release listener */
524 listener_done(listener
);
525 return JOB_REQUEUE_NONE
;
528 if (ike_sa
->delete_child_sa(ike_sa
, child_sa
->get_protocol(child_sa
),
529 child_sa
->get_spi(child_sa
, TRUE
), FALSE
) != DESTROY_ME
)
531 listener
->status
= SUCCESS
;
532 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
536 listener
->status
= FAILED
;
537 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
540 return JOB_REQUEUE_NONE
;
543 METHOD(controller_t
, terminate_child
, status_t
,
544 controller_t
*this, u_int32_t reqid
,
545 controller_cb_t callback
, void *param
, u_int timeout
)
547 interface_job_t
*job
;
553 .ike_state_change
= _ike_state_change
,
554 .child_state_change
= _child_state_change
,
558 .log
= _listener_log
,
559 .get_level
= _listener_get_level
,
561 .callback
= callback
,
568 .execute
= _terminate_child_execute
,
569 .get_priority
= _get_priority_medium
,
570 .destroy
= _destroy_job
,
574 job
->listener
.logger
.listener
= &job
->listener
;
576 if (callback
== NULL
)
578 terminate_child_execute(job
);
582 if (wait_for_listener(job
, timeout
))
584 job
->listener
.status
= OUT_OF_RES
;
587 status
= job
->listener
.status
;
595 bool controller_cb_empty(void *param
, debug_t group
, level_t level
,
596 ike_sa_t
*ike_sa
, char *message
)
601 METHOD(controller_t
, destroy
, void,
602 private_controller_t
*this)
608 * Described in header-file
610 controller_t
*controller_create(void)
612 private_controller_t
*this;
616 .create_ike_sa_enumerator
= _create_ike_sa_enumerator
,
617 .initiate
= _initiate
,
618 .terminate_ike
= _terminate_ike
,
619 .terminate_child
= _terminate_child
,
624 return &this->public;