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
108 child_sa_t
*child_sa
;
111 * unique ID, used for various methods
116 * semaphore to implement wait_for_listener()
122 typedef struct interface_job_t interface_job_t
;
125 * job for asynchronous listen operations
127 struct interface_job_t
{
135 * associated listener
137 interface_listener_t listener
;
141 * This function properly unregisters a listener that is used
142 * with wait_for_listener()
144 static inline bool listener_done(interface_listener_t
*listener
)
148 listener
->done
->post(listener
->done
);
154 * thread_cleanup_t handler to unregister and cleanup a listener
156 static void listener_cleanup(interface_listener_t
*listener
)
158 charon
->bus
->remove_listener(charon
->bus
, &listener
->public);
159 charon
->bus
->remove_logger(charon
->bus
, &listener
->logger
.public);
160 listener
->done
->destroy(listener
->done
);
164 * Registers the listener, executes the job and then waits synchronously until
165 * the listener is done or the timeout occured.
167 * @note Use 'return listener_done(listener)' to properly unregister a listener
169 * @param listener listener to register
170 * @param job job to execute asynchronously when registered, or NULL
171 * @param timeout max timeout in ms to listen for events, 0 to disable
172 * @return TRUE if timed out
174 static bool wait_for_listener(interface_listener_t
*listener
, job_t
*job
,
177 bool old
, timed_out
= FALSE
;
179 listener
->done
= semaphore_create(0);
181 charon
->bus
->add_logger(charon
->bus
, &listener
->logger
.public);
182 charon
->bus
->add_listener(charon
->bus
, &listener
->public);
183 lib
->processor
->queue_job(lib
->processor
, job
);
185 thread_cleanup_push((thread_cleanup_t
)listener_cleanup
, listener
);
186 old
= thread_cancelability(TRUE
);
189 timed_out
= listener
->done
->timed_wait(listener
->done
, timeout
);
193 listener
->done
->wait(listener
->done
);
195 thread_cancelability(old
);
196 thread_cleanup_pop(TRUE
);
200 METHOD(logger_t
, listener_log
, void,
201 interface_logger_t
*this, debug_t group
, level_t level
, int thread
,
202 ike_sa_t
*ike_sa
, char* message
)
204 if (this->listener
->ike_sa
== ike_sa
)
206 if (!this->callback(this->param
, group
, level
, ike_sa
, message
))
208 this->listener
->status
= NEED_MORE
;
209 listener_done(this->listener
);
214 METHOD(logger_t
, listener_get_level
, level_t
,
215 interface_logger_t
*this, debug_t group
)
217 /* in order to allow callback listeners to decide what they want to log
218 * we request any log message, but only if we actually want logging */
219 return this->callback
== controller_cb_empty ? LEVEL_SILENT
: LEVEL_PRIVATE
;
222 METHOD(job_t
, get_priority_medium
, job_priority_t
,
225 return JOB_PRIO_MEDIUM
;
228 METHOD(listener_t
, ike_state_change
, bool,
229 interface_listener_t
*this, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
231 if (this->ike_sa
== ike_sa
)
236 case IKE_ESTABLISHED
:
237 { /* mediation connections are complete without CHILD_SA */
238 peer_cfg_t
*peer_cfg
= ike_sa
->get_peer_cfg(ike_sa
);
240 if (peer_cfg
->is_mediation(peer_cfg
))
242 this->status
= SUCCESS
;
243 return listener_done(this);
249 if (ike_sa
->get_state(ike_sa
) == IKE_DELETING
)
250 { /* proper termination */
251 this->status
= SUCCESS
;
253 return listener_done(this);
261 METHOD(listener_t
, child_state_change
, bool,
262 interface_listener_t
*this, ike_sa_t
*ike_sa
, child_sa_t
*child_sa
,
263 child_sa_state_t state
)
265 if (this->ike_sa
== ike_sa
)
269 case CHILD_INSTALLED
:
270 this->status
= SUCCESS
;
271 return listener_done(this);
272 case CHILD_DESTROYING
:
273 switch (child_sa
->get_state(child_sa
))
277 this->status
= SUCCESS
;
282 return listener_done(this);
290 METHOD(job_t
, recheckin
, void,
291 interface_job_t
*job
)
293 if (job
->listener
.ike_sa
)
295 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
,
296 job
->listener
.ike_sa
);
300 METHOD(controller_t
, create_ike_sa_enumerator
, enumerator_t
*,
301 private_controller_t
*this, bool wait
)
303 return charon
->ike_sa_manager
->create_enumerator(charon
->ike_sa_manager
,
307 METHOD(job_t
, initiate_execute
, void,
308 interface_job_t
*job
)
311 interface_listener_t
*listener
= &job
->listener
;
312 peer_cfg_t
*peer_cfg
= listener
->peer_cfg
;
314 ike_sa
= charon
->ike_sa_manager
->checkout_by_config(charon
->ike_sa_manager
,
318 listener
->child_cfg
->destroy(listener
->child_cfg
);
319 peer_cfg
->destroy(peer_cfg
);
320 /* trigger down event to release listener */
321 listener
->ike_sa
= charon
->ike_sa_manager
->checkout_new(
322 charon
->ike_sa_manager
, IKE_ANY
, TRUE
);
323 DESTROY_IF(listener
->ike_sa
);
324 listener
->status
= FAILED
;
327 listener
->ike_sa
= ike_sa
;
329 if (ike_sa
->get_peer_cfg(ike_sa
) == NULL
)
331 ike_sa
->set_peer_cfg(ike_sa
, peer_cfg
);
333 peer_cfg
->destroy(peer_cfg
);
335 if (ike_sa
->initiate(ike_sa
, listener
->child_cfg
, 0, NULL
, NULL
) == SUCCESS
)
337 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
338 listener
->status
= SUCCESS
;
342 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
344 listener
->status
= FAILED
;
348 METHOD(controller_t
, initiate
, status_t
,
349 private_controller_t
*this, peer_cfg_t
*peer_cfg
, child_cfg_t
*child_cfg
,
350 controller_cb_t callback
, void *param
, u_int timeout
)
352 interface_job_t job
= {
355 .ike_state_change
= _ike_state_change
,
356 .child_state_change
= _child_state_change
,
360 .log
= _listener_log
,
361 .get_level
= _listener_get_level
,
363 .callback
= callback
,
367 .child_cfg
= child_cfg
,
368 .peer_cfg
= peer_cfg
,
371 .execute
= _initiate_execute
,
372 .get_priority
= _get_priority_medium
,
373 .destroy
= _recheckin
,
376 job
.listener
.logger
.listener
= &job
.listener
;
378 if (callback
== NULL
)
380 initiate_execute(&job
);
384 if (wait_for_listener(&job
.listener
, &job
.public, timeout
))
386 job
.listener
.status
= OUT_OF_RES
;
389 return job
.listener
.status
;
392 METHOD(job_t
, terminate_ike_execute
, void,
393 interface_job_t
*job
)
395 interface_listener_t
*listener
= &job
->listener
;
396 ike_sa_t
*ike_sa
= listener
->ike_sa
;
398 charon
->bus
->set_sa(charon
->bus
, ike_sa
);
400 if (ike_sa
->delete(ike_sa
) != DESTROY_ME
)
402 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
404 listener
->status
= FAILED
;
408 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
410 listener
->status
= SUCCESS
;
414 METHOD(controller_t
, terminate_ike
, status_t
,
415 controller_t
*this, u_int32_t unique_id
,
416 controller_cb_t callback
, void *param
, u_int timeout
)
419 interface_job_t job
= {
422 .ike_state_change
= _ike_state_change
,
423 .child_state_change
= _child_state_change
,
427 .log
= _listener_log
,
428 .get_level
= _listener_get_level
,
430 .callback
= callback
,
437 .execute
= _terminate_ike_execute
,
438 .get_priority
= _get_priority_medium
,
439 .destroy
= _recheckin
,
442 job
.listener
.logger
.listener
= &job
.listener
;
444 ike_sa
= charon
->ike_sa_manager
->checkout_by_id(charon
->ike_sa_manager
,
448 DBG1(DBG_IKE
, "unable to terminate IKE_SA: ID %d not found", unique_id
);
451 job
.listener
.ike_sa
= ike_sa
;
453 if (callback
== NULL
)
455 terminate_ike_execute(&job
);
459 if (wait_for_listener(&job
.listener
, &job
.public, timeout
))
461 job
.listener
.status
= OUT_OF_RES
;
463 /* checkin of the ike_sa happened in the thread that executed the job */
464 charon
->bus
->set_sa(charon
->bus
, NULL
);
466 return job
.listener
.status
;
469 METHOD(job_t
, terminate_child_execute
, void,
470 interface_job_t
*job
)
472 interface_listener_t
*listener
= &job
->listener
;
473 ike_sa_t
*ike_sa
= listener
->ike_sa
;
474 child_sa_t
*child_sa
= listener
->child_sa
;
476 charon
->bus
->set_sa(charon
->bus
, ike_sa
);
477 if (ike_sa
->delete_child_sa(ike_sa
, child_sa
->get_protocol(child_sa
),
478 child_sa
->get_spi(child_sa
, TRUE
), FALSE
) != DESTROY_ME
)
480 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
481 listener
->status
= SUCCESS
;
485 charon
->ike_sa_manager
->checkin_and_destroy(charon
->ike_sa_manager
,
487 listener
->status
= FAILED
;
491 METHOD(controller_t
, terminate_child
, status_t
,
492 controller_t
*this, u_int32_t reqid
,
493 controller_cb_t callback
, void *param
, u_int timeout
)
496 child_sa_t
*child_sa
;
497 enumerator_t
*enumerator
;
498 interface_job_t job
= {
501 .ike_state_change
= _ike_state_change
,
502 .child_state_change
= _child_state_change
,
506 .log
= _listener_log
,
507 .get_level
= _listener_get_level
,
509 .callback
= callback
,
516 .execute
= _terminate_child_execute
,
517 .get_priority
= _get_priority_medium
,
518 .destroy
= _recheckin
,
521 job
.listener
.logger
.listener
= &job
.listener
;
523 ike_sa
= charon
->ike_sa_manager
->checkout_by_id(charon
->ike_sa_manager
,
527 DBG1(DBG_IKE
, "unable to terminate, CHILD_SA with ID %d not found",
531 job
.listener
.ike_sa
= ike_sa
;
533 enumerator
= ike_sa
->create_child_sa_enumerator(ike_sa
);
534 while (enumerator
->enumerate(enumerator
, (void**)&child_sa
))
536 if (child_sa
->get_state(child_sa
) != CHILD_ROUTED
&&
537 child_sa
->get_reqid(child_sa
) == reqid
)
543 enumerator
->destroy(enumerator
);
545 if (child_sa
== NULL
)
547 DBG1(DBG_IKE
, "unable to terminate, established "
548 "CHILD_SA with ID %d not found", reqid
);
549 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
552 job
.listener
.child_sa
= child_sa
;
554 if (callback
== NULL
)
556 terminate_child_execute(&job
);
560 if (wait_for_listener(&job
.listener
, &job
.public, timeout
))
562 job
.listener
.status
= OUT_OF_RES
;
564 /* checkin of the ike_sa happened in the thread that executed the job */
565 charon
->bus
->set_sa(charon
->bus
, NULL
);
567 return job
.listener
.status
;
573 bool controller_cb_empty(void *param
, debug_t group
, level_t level
,
574 ike_sa_t
*ike_sa
, char *message
)
579 METHOD(controller_t
, destroy
, void,
580 private_controller_t
*this)
586 * Described in header-file
588 controller_t
*controller_create(void)
590 private_controller_t
*this;
594 .create_ike_sa_enumerator
= _create_ike_sa_enumerator
,
595 .initiate
= _initiate
,
596 .terminate_ike
= _terminate_ike
,
597 .terminate_child
= _terminate_child
,
602 return &this->public;