Merge branch 'radius-ext'
[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
416 if (callback == NULL)
417 {
418 initiate_execute(job);
419 }
420 else
421 {
422 if (wait_for_listener(job, timeout))
423 {
424 job->listener.status = OUT_OF_RES;
425 }
426 }
427 status = job->listener.status;
428 destroy_job(job);
429 return status;
430 }
431
432 METHOD(job_t, terminate_ike_execute, job_requeue_t,
433 interface_job_t *job)
434 {
435 interface_listener_t *listener = &job->listener;
436 u_int32_t unique_id = listener->id;
437 ike_sa_t *ike_sa;
438
439 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
440 unique_id, FALSE);
441 if (!ike_sa)
442 {
443 DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
444 listener->status = NOT_FOUND;
445 /* release listener */
446 listener_done(listener);
447 return JOB_REQUEUE_NONE;
448 }
449 listener->lock->lock(listener->lock);
450 listener->ike_sa = ike_sa;
451 listener->lock->unlock(listener->lock);
452
453 if (ike_sa->delete(ike_sa) != DESTROY_ME)
454 { /* delete failed */
455 listener->status = FAILED;
456 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
457 }
458 else
459 {
460 if (!listener->logger.callback)
461 {
462 listener->status = SUCCESS;
463 }
464 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
465 ike_sa);
466 }
467 return JOB_REQUEUE_NONE;
468 }
469
470 METHOD(controller_t, terminate_ike, status_t,
471 controller_t *this, u_int32_t unique_id,
472 controller_cb_t callback, void *param, u_int timeout)
473 {
474 interface_job_t *job;
475 status_t status;
476
477 INIT(job,
478 .listener = {
479 .public = {
480 .ike_state_change = _ike_state_change,
481 .child_state_change = _child_state_change,
482 },
483 .logger = {
484 .public = {
485 .log = _listener_log,
486 .get_level = _listener_get_level,
487 },
488 .callback = callback,
489 .param = param,
490 },
491 .status = FAILED,
492 .id = unique_id,
493 .lock = spinlock_create(),
494 },
495 .public = {
496 .execute = _terminate_ike_execute,
497 .get_priority = _get_priority_medium,
498 .destroy = _destroy_job,
499 },
500 .refcount = 1,
501 );
502 job->listener.logger.listener = &job->listener;
503
504 if (callback == NULL)
505 {
506 terminate_ike_execute(job);
507 }
508 else
509 {
510 if (wait_for_listener(job, timeout))
511 {
512 job->listener.status = OUT_OF_RES;
513 }
514 }
515 status = job->listener.status;
516 destroy_job(job);
517 return status;
518 }
519
520 METHOD(job_t, terminate_child_execute, job_requeue_t,
521 interface_job_t *job)
522 {
523 interface_listener_t *listener = &job->listener;
524 u_int32_t reqid = listener->id;
525 enumerator_t *enumerator;
526 child_sa_t *child_sa;
527 ike_sa_t *ike_sa;
528
529 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
530 reqid, TRUE);
531 if (!ike_sa)
532 {
533 DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
534 reqid);
535 listener->status = NOT_FOUND;
536 /* release listener */
537 listener_done(listener);
538 return JOB_REQUEUE_NONE;
539 }
540 listener->lock->lock(listener->lock);
541 listener->ike_sa = ike_sa;
542 listener->lock->unlock(listener->lock);
543
544 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
545 while (enumerator->enumerate(enumerator, (void**)&child_sa))
546 {
547 if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
548 child_sa->get_reqid(child_sa) == reqid)
549 {
550 break;
551 }
552 child_sa = NULL;
553 }
554 enumerator->destroy(enumerator);
555
556 if (!child_sa)
557 {
558 DBG1(DBG_IKE, "unable to terminate, established "
559 "CHILD_SA with ID %d not found", reqid);
560 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
561 listener->status = NOT_FOUND;
562 /* release listener */
563 listener_done(listener);
564 return JOB_REQUEUE_NONE;
565 }
566
567 if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
568 child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
569 {
570 if (!listener->logger.callback)
571 {
572 listener->status = SUCCESS;
573 }
574 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
575 }
576 else
577 {
578 listener->status = FAILED;
579 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
580 ike_sa);
581 }
582 return JOB_REQUEUE_NONE;
583 }
584
585 METHOD(controller_t, terminate_child, status_t,
586 controller_t *this, u_int32_t reqid,
587 controller_cb_t callback, void *param, u_int timeout)
588 {
589 interface_job_t *job;
590 status_t status;
591
592 INIT(job,
593 .listener = {
594 .public = {
595 .ike_state_change = _ike_state_change,
596 .child_state_change = _child_state_change,
597 },
598 .logger = {
599 .public = {
600 .log = _listener_log,
601 .get_level = _listener_get_level,
602 },
603 .callback = callback,
604 .param = param,
605 },
606 .status = FAILED,
607 .id = reqid,
608 .lock = spinlock_create(),
609 },
610 .public = {
611 .execute = _terminate_child_execute,
612 .get_priority = _get_priority_medium,
613 .destroy = _destroy_job,
614 },
615 .refcount = 1,
616 );
617 job->listener.logger.listener = &job->listener;
618
619 if (callback == NULL)
620 {
621 terminate_child_execute(job);
622 }
623 else
624 {
625 if (wait_for_listener(job, timeout))
626 {
627 job->listener.status = OUT_OF_RES;
628 }
629 }
630 status = job->listener.status;
631 destroy_job(job);
632 return status;
633 }
634
635 /**
636 * See header
637 */
638 bool controller_cb_empty(void *param, debug_t group, level_t level,
639 ike_sa_t *ike_sa, const char *message)
640 {
641 return TRUE;
642 }
643
644 METHOD(controller_t, destroy, void,
645 private_controller_t *this)
646 {
647 free(this);
648 }
649
650 /*
651 * Described in header-file
652 */
653 controller_t *controller_create(void)
654 {
655 private_controller_t *this;
656
657 INIT(this,
658 .public = {
659 .create_ike_sa_enumerator = _create_ike_sa_enumerator,
660 .initiate = _initiate,
661 .terminate_ike = _terminate_ike,
662 .terminate_child = _terminate_child,
663 .destroy = _destroy,
664 },
665 );
666
667 return &this->public;
668 }