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