11f40388ffa830a3a8bb23689d7f1e53b19deb3a
[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 if (!ike_sa)
221 {
222 listener->child_cfg->destroy(listener->child_cfg);
223 peer_cfg->destroy(peer_cfg);
224 /* trigger down event to release listener */
225 listener->ike_sa = charon->ike_sa_manager->checkout_new(
226 charon->ike_sa_manager, IKE_ANY, TRUE);
227 DESTROY_IF(listener->ike_sa);
228 listener->status = FAILED;
229 return;
230 }
231 listener->ike_sa = ike_sa;
232
233 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
234 {
235 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
236 }
237 peer_cfg->destroy(peer_cfg);
238
239 if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
240 {
241 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
242 listener->status = SUCCESS;
243 }
244 else
245 {
246 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
247 ike_sa);
248 listener->status = FAILED;
249 }
250 }
251
252 METHOD(controller_t, initiate, status_t,
253 private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
254 controller_cb_t callback, void *param, u_int timeout)
255 {
256 interface_job_t job = {
257 .listener = {
258 .public = {
259 .log = _listener_log,
260 .ike_state_change = _ike_state_change,
261 .child_state_change = _child_state_change,
262 },
263 .callback = callback,
264 .param = param,
265 .status = FAILED,
266 .child_cfg = child_cfg,
267 .peer_cfg = peer_cfg,
268 },
269 .public = {
270 .execute = _initiate_execute,
271 .get_priority = _get_priority_medium,
272 .destroy = _recheckin,
273 },
274 };
275 if (callback == NULL)
276 {
277 initiate_execute(&job);
278 }
279 else
280 {
281 if (charon->bus->listen(charon->bus, &job.listener.public, &job.public,
282 timeout))
283 {
284 job.listener.status = OUT_OF_RES;
285 }
286 }
287 return job.listener.status;
288 }
289
290 METHOD(job_t, terminate_ike_execute, void,
291 interface_job_t *job)
292 {
293 interface_listener_t *listener = &job->listener;
294 ike_sa_t *ike_sa = listener->ike_sa;
295
296 charon->bus->set_sa(charon->bus, ike_sa);
297
298 if (ike_sa->delete(ike_sa) != DESTROY_ME)
299 {
300 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
301 /* delete failed */
302 listener->status = FAILED;
303 }
304 else
305 {
306 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
307 ike_sa);
308 listener->status = SUCCESS;
309 }
310 }
311
312 METHOD(controller_t, terminate_ike, status_t,
313 controller_t *this, u_int32_t unique_id,
314 controller_cb_t callback, void *param, u_int timeout)
315 {
316 ike_sa_t *ike_sa;
317 interface_job_t job = {
318 .listener = {
319 .public = {
320 .log = _listener_log,
321 .ike_state_change = _ike_state_change,
322 .child_state_change = _child_state_change,
323 },
324 .callback = callback,
325 .param = param,
326 .status = FAILED,
327 .id = unique_id,
328 },
329 .public = {
330 .execute = _terminate_ike_execute,
331 .get_priority = _get_priority_medium,
332 .destroy = _recheckin,
333 },
334 };
335
336 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
337 unique_id, FALSE);
338 if (ike_sa == NULL)
339 {
340 DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
341 return NOT_FOUND;
342 }
343 job.listener.ike_sa = ike_sa;
344
345 if (callback == NULL)
346 {
347 terminate_ike_execute(&job);
348 }
349 else
350 {
351 if (charon->bus->listen(charon->bus, &job.listener.public, &job.public,
352 timeout))
353 {
354 job.listener.status = OUT_OF_RES;
355 }
356 /* checkin of the ike_sa happened in the thread that executed the job */
357 charon->bus->set_sa(charon->bus, NULL);
358 }
359 return job.listener.status;
360 }
361
362 METHOD(job_t, terminate_child_execute, void,
363 interface_job_t *job)
364 {
365 interface_listener_t *listener = &job->listener;
366 ike_sa_t *ike_sa = listener->ike_sa;
367 child_sa_t *child_sa = listener->child_sa;
368
369 charon->bus->set_sa(charon->bus, ike_sa);
370 if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
371 child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
372 {
373 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
374 listener->status = SUCCESS;
375 }
376 else
377 {
378 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
379 ike_sa);
380 listener->status = FAILED;
381 }
382 }
383
384 METHOD(controller_t, terminate_child, status_t,
385 controller_t *this, u_int32_t reqid,
386 controller_cb_t callback, void *param, u_int timeout)
387 {
388 ike_sa_t *ike_sa;
389 child_sa_t *child_sa;
390 enumerator_t *enumerator;
391 interface_job_t job = {
392 .listener = {
393 .public = {
394 .log = _listener_log,
395 .ike_state_change = _ike_state_change,
396 .child_state_change = _child_state_change,
397 },
398 .callback = callback,
399 .param = param,
400 .status = FAILED,
401 .id = reqid,
402 },
403 .public = {
404 .execute = _terminate_child_execute,
405 .get_priority = _get_priority_medium,
406 .destroy = _recheckin,
407 },
408 };
409
410 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
411 reqid, TRUE);
412 if (ike_sa == NULL)
413 {
414 DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
415 reqid);
416 return NOT_FOUND;
417 }
418 job.listener.ike_sa = ike_sa;
419
420 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
421 while (enumerator->enumerate(enumerator, (void**)&child_sa))
422 {
423 if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
424 child_sa->get_reqid(child_sa) == reqid)
425 {
426 break;
427 }
428 child_sa = NULL;
429 }
430 enumerator->destroy(enumerator);
431
432 if (child_sa == NULL)
433 {
434 DBG1(DBG_IKE, "unable to terminate, established "
435 "CHILD_SA with ID %d not found", reqid);
436 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
437 return NOT_FOUND;
438 }
439 job.listener.child_sa = child_sa;
440
441 if (callback == NULL)
442 {
443 terminate_child_execute(&job);
444 }
445 else
446 {
447 if (charon->bus->listen(charon->bus, &job.listener.public, &job.public,
448 timeout))
449 {
450 job.listener.status = OUT_OF_RES;
451 }
452 /* checkin of the ike_sa happened in the thread that executed the job */
453 charon->bus->set_sa(charon->bus, NULL);
454 }
455 return job.listener.status;
456 }
457
458 /**
459 * See header
460 */
461 bool controller_cb_empty(void *param, debug_t group, level_t level,
462 ike_sa_t *ike_sa, char *format, va_list args)
463 {
464 return TRUE;
465 }
466
467 METHOD(controller_t, destroy, void,
468 private_controller_t *this)
469 {
470 free(this);
471 }
472
473 /*
474 * Described in header-file
475 */
476 controller_t *controller_create(void)
477 {
478 private_controller_t *this;
479
480 INIT(this,
481 .public = {
482 .create_ike_sa_enumerator = _create_ike_sa_enumerator,
483 .initiate = _initiate,
484 .terminate_ike = _terminate_ike,
485 .terminate_child = _terminate_child,
486 .destroy = _destroy,
487 },
488 );
489
490 return &this->public;
491 }
492