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