Implement bus_t.listen() directly in controller_t (the only user).
[strongswan.git] / src / libcharon / control / controller.c
1 /*
2 * Copyright (C) 2011 Tobias Brunner
3 * Copyright (C) 2007-2011 Martin Willi
4 * Copyright (C) 2011 revosec AG
5 * Hochschule fuer Technik Rapperswil
6 *
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>.
11 *
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
15 * for more details.
16 */
17
18 #include "controller.h"
19
20 #include <sys/types.h>
21 #include <dirent.h>
22 #include <sys/stat.h>
23 #include <dlfcn.h>
24
25 #include <daemon.h>
26 #include <library.h>
27 #include <threading/mutex.h>
28 #include <threading/condvar.h>
29 #include <threading/thread.h>
30
31 typedef struct private_controller_t private_controller_t;
32 typedef struct interface_listener_t interface_listener_t;
33
34 /**
35 * Private data of an stroke_t object.
36 */
37 struct private_controller_t {
38
39 /**
40 * Public part of stroke_t object.
41 */
42 controller_t public;
43 };
44
45 /**
46 * helper struct to map listener callbacks to interface callbacks
47 */
48 struct interface_listener_t {
49
50 /**
51 * public bus listener interface
52 */
53 listener_t public;
54
55 /**
56 * status of the operation, return to method callers
57 */
58 status_t status;
59
60 /**
61 * interface callback (listener gets redirected to here)
62 */
63 controller_cb_t callback;
64
65 /**
66 * user parameter to pass to callback
67 */
68 void *param;
69
70 /**
71 * child configuration, used for initiate
72 */
73 child_cfg_t *child_cfg;
74
75 /**
76 * peer configuration, used for initiate
77 */
78 peer_cfg_t *peer_cfg;
79
80 /**
81 * IKE_SA to handle
82 */
83 ike_sa_t *ike_sa;
84
85 /**
86 * CHILD_SA to handle
87 */
88 child_sa_t *child_sa;
89
90 /**
91 * unique ID, used for various methods
92 */
93 u_int32_t id;
94
95 /**
96 * mutex to implement wait_for_listener()
97 */
98 mutex_t *mutex;
99
100 /**
101 * condvar to wake a thread waiting in wait_for_listener()
102 */
103 condvar_t *condvar;
104
105 /**
106 * flag to indicate whether a listener is done
107 */
108 bool done;
109 };
110
111
112 typedef struct interface_job_t interface_job_t;
113
114 /**
115 * job for asynchronous listen operations
116 */
117 struct interface_job_t {
118
119 /**
120 * job interface
121 */
122 job_t public;
123
124 /**
125 * associated listener
126 */
127 interface_listener_t listener;
128 };
129
130 /**
131 * This function properly unregisters a listener that is used
132 * with wait_for_listener()
133 */
134 static inline bool listener_done(interface_listener_t *listener)
135 {
136 if (listener->mutex)
137 {
138 listener->mutex->lock(listener->mutex);
139 listener->condvar->signal(listener->condvar);
140 listener->done = TRUE;
141 listener->mutex->unlock(listener->mutex);
142 }
143 return FALSE;
144 }
145
146 /**
147 * thread_cleanup_t handler to unregister and cleanup a listener
148 */
149 static void listener_cleanup(interface_listener_t *listener)
150 {
151 charon->bus->remove_listener(charon->bus, &listener->public);
152 listener->condvar->destroy(listener->condvar);
153 listener->mutex->destroy(listener->mutex);
154 }
155
156 /**
157 * Registers the listener, executes the job and then waits synchronously until
158 * the listener is done or the timeout occured.
159 *
160 * @note Use 'return listener_done(listener)' to properly unregister a listener
161 *
162 * @returns TRUE in case of a timeout
163 */
164 static bool wait_for_listener(interface_listener_t *listener, job_t *job,
165 u_int timeout)
166 {
167 bool old, timed_out = FALSE;
168 timeval_t tv, add;
169
170 if (timeout)
171 {
172 add.tv_sec = timeout / 1000;
173 add.tv_usec = (timeout - (add.tv_sec * 1000)) * 1000;
174 time_monotonic(&tv);
175 timeradd(&tv, &add, &tv);
176 }
177
178 listener->condvar = condvar_create(CONDVAR_TYPE_DEFAULT);
179 listener->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
180
181 charon->bus->add_listener(charon->bus, &listener->public);
182 lib->processor->queue_job(lib->processor, job);
183
184 listener->mutex->lock(listener->mutex);
185 thread_cleanup_push((thread_cleanup_t)listener_cleanup, listener);
186 thread_cleanup_push((thread_cleanup_t)listener->mutex->unlock,
187 listener->mutex);
188 old = thread_cancelability(TRUE);
189 while (!listener->done)
190 {
191 if (timeout)
192 {
193 if (listener->condvar->timed_wait_abs(listener->condvar,
194 listener->mutex, tv))
195 {
196
197 timed_out = TRUE;
198 break;
199 }
200 }
201 else
202 {
203 listener->condvar->wait(listener->condvar, listener->mutex);
204 }
205 }
206 thread_cancelability(old);
207 /* unlock mutex */
208 thread_cleanup_pop(TRUE);
209 thread_cleanup_pop(TRUE);
210 return timed_out;
211 }
212
213 METHOD(listener_t, listener_log, bool,
214 interface_listener_t *this, debug_t group, level_t level, int thread,
215 ike_sa_t *ike_sa, char* format, va_list args)
216 {
217 if (this->ike_sa == ike_sa)
218 {
219 if (!this->callback(this->param, group, level, ike_sa, format, args))
220 {
221 return listener_done(this);
222 }
223 }
224 return TRUE;
225 }
226
227 METHOD(job_t, get_priority_medium, job_priority_t,
228 job_t *this)
229 {
230 return JOB_PRIO_MEDIUM;
231 }
232
233 METHOD(listener_t, ike_state_change, bool,
234 interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
235 {
236 if (this->ike_sa == ike_sa)
237 {
238 switch (state)
239 {
240 #ifdef ME
241 case IKE_ESTABLISHED:
242 { /* mediation connections are complete without CHILD_SA */
243 peer_cfg_t *peer_cfg = ike_sa->get_peer_cfg(ike_sa);
244
245 if (peer_cfg->is_mediation(peer_cfg))
246 {
247 this->status = SUCCESS;
248 return listener_done(this);
249 }
250 break;
251 }
252 #endif /* ME */
253 case IKE_DESTROYING:
254 if (ike_sa->get_state(ike_sa) == IKE_DELETING)
255 { /* proper termination */
256 this->status = SUCCESS;
257 }
258 return listener_done(this);
259 default:
260 break;
261 }
262 }
263 return TRUE;
264 }
265
266 METHOD(listener_t, child_state_change, bool,
267 interface_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
268 child_sa_state_t state)
269 {
270 if (this->ike_sa == ike_sa)
271 {
272 switch (state)
273 {
274 case CHILD_INSTALLED:
275 this->status = SUCCESS;
276 return listener_done(this);
277 case CHILD_DESTROYING:
278 switch (child_sa->get_state(child_sa))
279 {
280 case CHILD_DELETING:
281 /* proper delete */
282 this->status = SUCCESS;
283 break;
284 default:
285 break;
286 }
287 return listener_done(this);
288 default:
289 break;
290 }
291 }
292 return TRUE;
293 }
294
295 METHOD(job_t, recheckin, void,
296 interface_job_t *job)
297 {
298 if (job->listener.ike_sa)
299 {
300 charon->ike_sa_manager->checkin(charon->ike_sa_manager,
301 job->listener.ike_sa);
302 }
303 }
304
305 METHOD(controller_t, create_ike_sa_enumerator, enumerator_t*,
306 private_controller_t *this, bool wait)
307 {
308 return charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager,
309 wait);
310 }
311
312 METHOD(job_t, initiate_execute, void,
313 interface_job_t *job)
314 {
315 ike_sa_t *ike_sa;
316 interface_listener_t *listener = &job->listener;
317 peer_cfg_t *peer_cfg = listener->peer_cfg;
318
319 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
320 peer_cfg);
321 if (!ike_sa)
322 {
323 listener->child_cfg->destroy(listener->child_cfg);
324 peer_cfg->destroy(peer_cfg);
325 /* trigger down event to release listener */
326 listener->ike_sa = charon->ike_sa_manager->checkout_new(
327 charon->ike_sa_manager, IKE_ANY, TRUE);
328 DESTROY_IF(listener->ike_sa);
329 listener->status = FAILED;
330 return;
331 }
332 listener->ike_sa = ike_sa;
333
334 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
335 {
336 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
337 }
338 peer_cfg->destroy(peer_cfg);
339
340 if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
341 {
342 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
343 listener->status = SUCCESS;
344 }
345 else
346 {
347 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
348 ike_sa);
349 listener->status = FAILED;
350 }
351 }
352
353 METHOD(controller_t, initiate, status_t,
354 private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
355 controller_cb_t callback, void *param, u_int timeout)
356 {
357 interface_job_t job = {
358 .listener = {
359 .public = {
360 .log = _listener_log,
361 .ike_state_change = _ike_state_change,
362 .child_state_change = _child_state_change,
363 },
364 .callback = callback,
365 .param = param,
366 .status = FAILED,
367 .child_cfg = child_cfg,
368 .peer_cfg = peer_cfg,
369 },
370 .public = {
371 .execute = _initiate_execute,
372 .get_priority = _get_priority_medium,
373 .destroy = _recheckin,
374 },
375 };
376 if (callback == NULL)
377 {
378 initiate_execute(&job);
379 }
380 else
381 {
382 if (wait_for_listener(&job.listener, &job.public, timeout))
383 {
384 job.listener.status = OUT_OF_RES;
385 }
386 }
387 return job.listener.status;
388 }
389
390 METHOD(job_t, terminate_ike_execute, void,
391 interface_job_t *job)
392 {
393 interface_listener_t *listener = &job->listener;
394 ike_sa_t *ike_sa = listener->ike_sa;
395
396 charon->bus->set_sa(charon->bus, ike_sa);
397
398 if (ike_sa->delete(ike_sa) != DESTROY_ME)
399 {
400 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
401 /* delete failed */
402 listener->status = FAILED;
403 }
404 else
405 {
406 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
407 ike_sa);
408 listener->status = SUCCESS;
409 }
410 }
411
412 METHOD(controller_t, terminate_ike, status_t,
413 controller_t *this, u_int32_t unique_id,
414 controller_cb_t callback, void *param, u_int timeout)
415 {
416 ike_sa_t *ike_sa;
417 interface_job_t job = {
418 .listener = {
419 .public = {
420 .log = _listener_log,
421 .ike_state_change = _ike_state_change,
422 .child_state_change = _child_state_change,
423 },
424 .callback = callback,
425 .param = param,
426 .status = FAILED,
427 .id = unique_id,
428 },
429 .public = {
430 .execute = _terminate_ike_execute,
431 .get_priority = _get_priority_medium,
432 .destroy = _recheckin,
433 },
434 };
435
436 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
437 unique_id, FALSE);
438 if (ike_sa == NULL)
439 {
440 DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
441 return NOT_FOUND;
442 }
443 job.listener.ike_sa = ike_sa;
444
445 if (callback == NULL)
446 {
447 terminate_ike_execute(&job);
448 }
449 else
450 {
451 if (wait_for_listener(&job.listener, &job.public, timeout))
452 {
453 job.listener.status = OUT_OF_RES;
454 }
455 /* checkin of the ike_sa happened in the thread that executed the job */
456 charon->bus->set_sa(charon->bus, NULL);
457 }
458 return job.listener.status;
459 }
460
461 METHOD(job_t, terminate_child_execute, void,
462 interface_job_t *job)
463 {
464 interface_listener_t *listener = &job->listener;
465 ike_sa_t *ike_sa = listener->ike_sa;
466 child_sa_t *child_sa = listener->child_sa;
467
468 charon->bus->set_sa(charon->bus, ike_sa);
469 if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
470 child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
471 {
472 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
473 listener->status = SUCCESS;
474 }
475 else
476 {
477 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
478 ike_sa);
479 listener->status = FAILED;
480 }
481 }
482
483 METHOD(controller_t, terminate_child, status_t,
484 controller_t *this, u_int32_t reqid,
485 controller_cb_t callback, void *param, u_int timeout)
486 {
487 ike_sa_t *ike_sa;
488 child_sa_t *child_sa;
489 enumerator_t *enumerator;
490 interface_job_t job = {
491 .listener = {
492 .public = {
493 .log = _listener_log,
494 .ike_state_change = _ike_state_change,
495 .child_state_change = _child_state_change,
496 },
497 .callback = callback,
498 .param = param,
499 .status = FAILED,
500 .id = reqid,
501 },
502 .public = {
503 .execute = _terminate_child_execute,
504 .get_priority = _get_priority_medium,
505 .destroy = _recheckin,
506 },
507 };
508
509 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
510 reqid, TRUE);
511 if (ike_sa == NULL)
512 {
513 DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
514 reqid);
515 return NOT_FOUND;
516 }
517 job.listener.ike_sa = ike_sa;
518
519 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
520 while (enumerator->enumerate(enumerator, (void**)&child_sa))
521 {
522 if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
523 child_sa->get_reqid(child_sa) == reqid)
524 {
525 break;
526 }
527 child_sa = NULL;
528 }
529 enumerator->destroy(enumerator);
530
531 if (child_sa == NULL)
532 {
533 DBG1(DBG_IKE, "unable to terminate, established "
534 "CHILD_SA with ID %d not found", reqid);
535 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
536 return NOT_FOUND;
537 }
538 job.listener.child_sa = child_sa;
539
540 if (callback == NULL)
541 {
542 terminate_child_execute(&job);
543 }
544 else
545 {
546 if (wait_for_listener(&job.listener, &job.public, timeout))
547 {
548 job.listener.status = OUT_OF_RES;
549 }
550 /* checkin of the ike_sa happened in the thread that executed the job */
551 charon->bus->set_sa(charon->bus, NULL);
552 }
553 return job.listener.status;
554 }
555
556 /**
557 * See header
558 */
559 bool controller_cb_empty(void *param, debug_t group, level_t level,
560 ike_sa_t *ike_sa, char *format, va_list args)
561 {
562 return TRUE;
563 }
564
565 METHOD(controller_t, destroy, void,
566 private_controller_t *this)
567 {
568 free(this);
569 }
570
571 /*
572 * Described in header-file
573 */
574 controller_t *controller_create(void)
575 {
576 private_controller_t *this;
577
578 INIT(this,
579 .public = {
580 .create_ike_sa_enumerator = _create_ike_sa_enumerator,
581 .initiate = _initiate,
582 .terminate_ike = _terminate_ike,
583 .terminate_child = _terminate_child,
584 .destroy = _destroy,
585 },
586 );
587
588 return &this->public;
589 }
590