Merge branch 'charon-cmd'
[strongswan.git] / src / libcharon / control / controller.c
1 /*
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
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/thread.h>
28 #include <threading/spinlock.h>
29 #include <threading/semaphore.h>
30
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;
34
35 /**
36 * Private data of an stroke_t object.
37 */
38 struct private_controller_t {
39
40 /**
41 * Public part of stroke_t object.
42 */
43 controller_t public;
44 };
45
46 /**
47 * helper struct for the logger interface
48 */
49 struct interface_logger_t {
50 /**
51 * public logger interface
52 */
53 logger_t public;
54
55 /**
56 * reference to the listener
57 */
58 interface_listener_t *listener;
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 /**
72 * helper struct to map listener callbacks to interface callbacks
73 */
74 struct interface_listener_t {
75
76 /**
77 * public bus listener interface
78 */
79 listener_t public;
80
81 /**
82 * logger interface
83 */
84 interface_logger_t logger;
85
86 /**
87 * status of the operation, return to method callers
88 */
89 status_t status;
90
91 /**
92 * child configuration, used for initiate
93 */
94 child_cfg_t *child_cfg;
95
96 /**
97 * peer configuration, used for initiate
98 */
99 peer_cfg_t *peer_cfg;
100
101 /**
102 * IKE_SA to handle
103 */
104 ike_sa_t *ike_sa;
105
106 /**
107 * unique ID, used for various methods
108 */
109 u_int32_t id;
110
111 /**
112 * semaphore to implement wait_for_listener()
113 */
114 semaphore_t *done;
115
116 /**
117 * spinlock to update the IKE_SA handle properly
118 */
119 spinlock_t *lock;
120 };
121
122
123 typedef struct interface_job_t interface_job_t;
124
125 /**
126 * job for asynchronous listen operations
127 */
128 struct interface_job_t {
129
130 /**
131 * job interface
132 */
133 job_t public;
134
135 /**
136 * associated listener
137 */
138 interface_listener_t listener;
139
140 /**
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
144 */
145 refcount_t refcount;
146 };
147
148 /**
149 * This function wakes a thread that is waiting in wait_for_listener(),
150 * either from a listener or from a job.
151 */
152 static inline bool listener_done(interface_listener_t *listener)
153 {
154 if (listener->done)
155 {
156 listener->done->post(listener->done);
157 }
158 return FALSE;
159 }
160
161 /**
162 * thread_cleanup_t handler to unregister a listener.
163 */
164 static void listener_unregister(interface_listener_t *listener)
165 {
166 charon->bus->remove_listener(charon->bus, &listener->public);
167 charon->bus->remove_logger(charon->bus, &listener->logger.public);
168 }
169
170 /**
171 * Registers the listener, executes the job and then waits synchronously until
172 * the listener is done or the timeout occurred.
173 *
174 * @note Use 'return listener_done(listener)' to properly unregister a listener
175 *
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
180 */
181 static bool wait_for_listener(interface_job_t *job, u_int timeout)
182 {
183 interface_listener_t *listener = &job->listener;
184 bool old, timed_out = FALSE;
185
186 /* avoid that the job is destroyed too early */
187 ref_get(&job->refcount);
188
189 listener->done = semaphore_create(0);
190
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);
194
195 thread_cleanup_push((thread_cleanup_t)listener_unregister, listener);
196 old = thread_cancelability(TRUE);
197 if (timeout)
198 {
199 timed_out = listener->done->timed_wait(listener->done, timeout);
200 }
201 else
202 {
203 listener->done->wait(listener->done);
204 }
205 thread_cancelability(old);
206 thread_cleanup_pop(TRUE);
207 return timed_out;
208 }
209
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)
213 {
214 ike_sa_t *target;
215
216 this->listener->lock->lock(this->listener->lock);
217 target = this->listener->ike_sa;
218 this->listener->lock->unlock(this->listener->lock);
219
220 if (target == ike_sa)
221 {
222 if (!this->callback(this->param, group, level, ike_sa, message))
223 {
224 this->listener->status = NEED_MORE;
225 listener_done(this->listener);
226 }
227 }
228 }
229
230 METHOD(logger_t, listener_get_level, level_t,
231 interface_logger_t *this, debug_t group)
232 {
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;
236 }
237
238 METHOD(job_t, get_priority_medium, job_priority_t,
239 job_t *this)
240 {
241 return JOB_PRIO_MEDIUM;
242 }
243
244 METHOD(listener_t, ike_state_change, bool,
245 interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
246 {
247 ike_sa_t *target;
248
249 this->lock->lock(this->lock);
250 target = this->ike_sa;
251 this->lock->unlock(this->lock);
252
253 if (target == ike_sa)
254 {
255 switch (state)
256 {
257 #ifdef ME
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);
261
262 if (peer_cfg->is_mediation(peer_cfg))
263 {
264 this->status = SUCCESS;
265 return listener_done(this);
266 }
267 break;
268 }
269 #endif /* ME */
270 case IKE_DESTROYING:
271 if (ike_sa->get_state(ike_sa) == IKE_DELETING)
272 { /* proper termination */
273 this->status = SUCCESS;
274 }
275 return listener_done(this);
276 default:
277 break;
278 }
279 }
280 return TRUE;
281 }
282
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)
286 {
287 ike_sa_t *target;
288
289 this->lock->lock(this->lock);
290 target = this->ike_sa;
291 this->lock->unlock(this->lock);
292
293 if (target == ike_sa)
294 {
295 switch (state)
296 {
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))
302 {
303 case CHILD_DELETING:
304 /* proper delete */
305 this->status = SUCCESS;
306 break;
307 default:
308 break;
309 }
310 return listener_done(this);
311 default:
312 break;
313 }
314 }
315 return TRUE;
316 }
317
318 METHOD(job_t, destroy_job, void,
319 interface_job_t *this)
320 {
321 if (ref_put(&this->refcount))
322 {
323 this->listener.lock->destroy(this->listener.lock);
324 DESTROY_IF(this->listener.done);
325 free(this);
326 }
327 }
328
329 METHOD(controller_t, create_ike_sa_enumerator, enumerator_t*,
330 private_controller_t *this, bool wait)
331 {
332 return charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager,
333 wait);
334 }
335
336 METHOD(job_t, initiate_execute, job_requeue_t,
337 interface_job_t *job)
338 {
339 ike_sa_t *ike_sa;
340 interface_listener_t *listener = &job->listener;
341 peer_cfg_t *peer_cfg = listener->peer_cfg;
342
343 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
344 peer_cfg);
345 if (!ike_sa)
346 {
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;
353 }
354 listener->lock->lock(listener->lock);
355 listener->ike_sa = ike_sa;
356 listener->lock->unlock(listener->lock);
357
358 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
359 {
360 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
361 }
362 peer_cfg->destroy(peer_cfg);
363
364 if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
365 {
366 if (!listener->logger.callback)
367 {
368 listener->status = SUCCESS;
369 }
370 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
371 }
372 else
373 {
374 listener->status = FAILED;
375 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
376 ike_sa);
377 }
378 return JOB_REQUEUE_NONE;
379 }
380
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)
384 {
385 interface_job_t *job;
386 status_t status;
387
388 INIT(job,
389 .listener = {
390 .public = {
391 .ike_state_change = _ike_state_change,
392 .child_state_change = _child_state_change,
393 },
394 .logger = {
395 .public = {
396 .log = _listener_log,
397 .get_level = _listener_get_level,
398 },
399 .callback = callback,
400 .param = param,
401 },
402 .status = FAILED,
403 .child_cfg = child_cfg,
404 .peer_cfg = peer_cfg,
405 .lock = spinlock_create(),
406 },
407 .public = {
408 .execute = _initiate_execute,
409 .get_priority = _get_priority_medium,
410 .destroy = _destroy_job,
411 },
412 .refcount = 1,
413 );
414 job->listener.logger.listener = &job->listener;
415 thread_cleanup_push((void*)destroy_job, job);
416
417 if (callback == NULL)
418 {
419 initiate_execute(job);
420 }
421 else
422 {
423 if (wait_for_listener(job, timeout))
424 {
425 job->listener.status = OUT_OF_RES;
426 }
427 }
428 status = job->listener.status;
429 thread_cleanup_pop(TRUE);
430 return status;
431 }
432
433 METHOD(job_t, terminate_ike_execute, job_requeue_t,
434 interface_job_t *job)
435 {
436 interface_listener_t *listener = &job->listener;
437 u_int32_t unique_id = listener->id;
438 ike_sa_t *ike_sa;
439
440 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
441 unique_id, FALSE);
442 if (!ike_sa)
443 {
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;
449 }
450 listener->lock->lock(listener->lock);
451 listener->ike_sa = ike_sa;
452 listener->lock->unlock(listener->lock);
453
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);
458 }
459 else
460 {
461 if (!listener->logger.callback)
462 {
463 listener->status = SUCCESS;
464 }
465 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
466 ike_sa);
467 }
468 return JOB_REQUEUE_NONE;
469 }
470
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)
474 {
475 interface_job_t *job;
476 status_t status;
477
478 INIT(job,
479 .listener = {
480 .public = {
481 .ike_state_change = _ike_state_change,
482 .child_state_change = _child_state_change,
483 },
484 .logger = {
485 .public = {
486 .log = _listener_log,
487 .get_level = _listener_get_level,
488 },
489 .callback = callback,
490 .param = param,
491 },
492 .status = FAILED,
493 .id = unique_id,
494 .lock = spinlock_create(),
495 },
496 .public = {
497 .execute = _terminate_ike_execute,
498 .get_priority = _get_priority_medium,
499 .destroy = _destroy_job,
500 },
501 .refcount = 1,
502 );
503 job->listener.logger.listener = &job->listener;
504 thread_cleanup_push((void*)destroy_job, job);
505
506 if (callback == NULL)
507 {
508 terminate_ike_execute(job);
509 }
510 else
511 {
512 if (wait_for_listener(job, timeout))
513 {
514 job->listener.status = OUT_OF_RES;
515 }
516 }
517 status = job->listener.status;
518 thread_cleanup_pop(TRUE);
519 return status;
520 }
521
522 METHOD(job_t, terminate_child_execute, job_requeue_t,
523 interface_job_t *job)
524 {
525 interface_listener_t *listener = &job->listener;
526 u_int32_t reqid = listener->id;
527 enumerator_t *enumerator;
528 child_sa_t *child_sa;
529 ike_sa_t *ike_sa;
530
531 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
532 reqid, TRUE);
533 if (!ike_sa)
534 {
535 DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
536 reqid);
537 listener->status = NOT_FOUND;
538 /* release listener */
539 listener_done(listener);
540 return JOB_REQUEUE_NONE;
541 }
542 listener->lock->lock(listener->lock);
543 listener->ike_sa = ike_sa;
544 listener->lock->unlock(listener->lock);
545
546 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
547 while (enumerator->enumerate(enumerator, (void**)&child_sa))
548 {
549 if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
550 child_sa->get_reqid(child_sa) == reqid)
551 {
552 break;
553 }
554 child_sa = NULL;
555 }
556 enumerator->destroy(enumerator);
557
558 if (!child_sa)
559 {
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;
567 }
568
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)
571 {
572 if (!listener->logger.callback)
573 {
574 listener->status = SUCCESS;
575 }
576 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
577 }
578 else
579 {
580 listener->status = FAILED;
581 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
582 ike_sa);
583 }
584 return JOB_REQUEUE_NONE;
585 }
586
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)
590 {
591 interface_job_t *job;
592 status_t status;
593
594 INIT(job,
595 .listener = {
596 .public = {
597 .ike_state_change = _ike_state_change,
598 .child_state_change = _child_state_change,
599 },
600 .logger = {
601 .public = {
602 .log = _listener_log,
603 .get_level = _listener_get_level,
604 },
605 .callback = callback,
606 .param = param,
607 },
608 .status = FAILED,
609 .id = reqid,
610 .lock = spinlock_create(),
611 },
612 .public = {
613 .execute = _terminate_child_execute,
614 .get_priority = _get_priority_medium,
615 .destroy = _destroy_job,
616 },
617 .refcount = 1,
618 );
619 job->listener.logger.listener = &job->listener;
620 thread_cleanup_push((void*)destroy_job, job);
621
622 if (callback == NULL)
623 {
624 terminate_child_execute(job);
625 }
626 else
627 {
628 if (wait_for_listener(job, timeout))
629 {
630 job->listener.status = OUT_OF_RES;
631 }
632 }
633 status = job->listener.status;
634 thread_cleanup_pop(TRUE);
635 return status;
636 }
637
638 /**
639 * See header
640 */
641 bool controller_cb_empty(void *param, debug_t group, level_t level,
642 ike_sa_t *ike_sa, const char *message)
643 {
644 return TRUE;
645 }
646
647 METHOD(controller_t, destroy, void,
648 private_controller_t *this)
649 {
650 free(this);
651 }
652
653 /*
654 * Described in header-file
655 */
656 controller_t *controller_create(void)
657 {
658 private_controller_t *this;
659
660 INIT(this,
661 .public = {
662 .create_ike_sa_enumerator = _create_ike_sa_enumerator,
663 .initiate = _initiate,
664 .terminate_ike = _terminate_ike,
665 .terminate_child = _terminate_child,
666 .destroy = _destroy,
667 },
668 );
669
670 return &this->public;
671 }