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