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