Added a non-blocking, skipping variant of IKE_SA enumerator
[strongswan.git] / src / libcharon / 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_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 /**
195 * cleanup job if job is never executed
196 */
197 static void recheckin(interface_job_t *job)
198 {
199 if (job->listener.ike_sa)
200 {
201 charon->ike_sa_manager->checkin(charon->ike_sa_manager,
202 job->listener.ike_sa);
203 }
204 }
205
206 /**
207 * Implementation of controller_t.create_ike_sa_iterator.
208 */
209 static enumerator_t* create_ike_sa_enumerator(controller_t *this, bool wait)
210 {
211 return charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager,
212 wait);
213 }
214
215 /**
216 * execute function for initiate
217 */
218 static status_t initiate_execute(interface_job_t *job)
219 {
220 ike_sa_t *ike_sa;
221 interface_listener_t *listener = &job->listener;
222 peer_cfg_t *peer_cfg = listener->peer_cfg;
223
224 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
225 peer_cfg);
226 listener->ike_sa = ike_sa;
227
228 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
229 {
230 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
231 }
232 peer_cfg->destroy(peer_cfg);
233
234 if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
235 {
236 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
237 return SUCCESS;
238 }
239 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
240 return FAILED;
241 }
242
243 /**
244 * Implementation of controller_t.initiate.
245 */
246 static status_t initiate(private_controller_t *this,
247 peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
248 controller_cb_t callback, void *param)
249 {
250 interface_job_t job = {
251 .listener = {
252 .public = {
253 .log = (void*)listener_log,
254 .ike_state_change = (void*)listener_ike_state,
255 .child_state_change = (void*)listener_child_state,
256 },
257 .callback = callback,
258 .param = param,
259 .status = FAILED,
260 .child_cfg = child_cfg,
261 .peer_cfg = peer_cfg,
262 },
263 .public = {
264 .execute = (void*)initiate_execute,
265 .destroy = (void*)recheckin,
266 },
267 };
268 if (callback == NULL)
269 {
270 return initiate_execute(&job);
271 }
272 charon->bus->listen(charon->bus, &job.listener.public, (job_t*)&job);
273 return job.listener.status;
274 }
275
276 /**
277 * execute function for terminate_ike
278 */
279 static status_t terminate_ike_execute(interface_job_t *job)
280 {
281 interface_listener_t *listener = &job->listener;
282 ike_sa_t *ike_sa = listener->ike_sa;
283
284 charon->bus->set_sa(charon->bus, ike_sa);
285
286 if (ike_sa->delete(ike_sa) != DESTROY_ME)
287 {
288 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
289 /* delete failed */
290 return FAILED;
291 }
292 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
293 return SUCCESS;
294 }
295
296 /**
297 * Implementation of controller_t.terminate_ike.
298 */
299 static status_t terminate_ike(controller_t *this, u_int32_t unique_id,
300 controller_cb_t callback, void *param)
301 {
302 ike_sa_t *ike_sa;
303 interface_job_t job = {
304 .listener = {
305 .public = {
306 .log = (void*)listener_log,
307 .ike_state_change = (void*)listener_ike_state,
308 .child_state_change = (void*)listener_child_state,
309 },
310 .callback = callback,
311 .param = param,
312 .status = FAILED,
313 .id = unique_id,
314 },
315 .public = {
316 .execute = (void*)terminate_ike_execute,
317 .destroy = (void*)recheckin,
318 },
319 };
320
321 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
322 unique_id, FALSE);
323 if (ike_sa == NULL)
324 {
325 DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
326 return NOT_FOUND;
327 }
328 job.listener.ike_sa = ike_sa;
329
330 if (callback == NULL)
331 {
332 return terminate_ike_execute(&job);
333 }
334 charon->bus->listen(charon->bus, &job.listener.public, (job_t*)&job);
335 /* checkin of the ike_sa happend in the thread that executed the job */
336 charon->bus->set_sa(charon->bus, NULL);
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 /* checkin of the ike_sa happend in the thread that executed the job */
424 charon->bus->set_sa(charon->bus, NULL);
425 return job.listener.status;
426 }
427
428 /**
429 * See header
430 */
431 bool controller_cb_empty(void *param, debug_t group, level_t level,
432 ike_sa_t *ike_sa, char *format, va_list args)
433 {
434 return TRUE;
435 }
436
437 /**
438 * Implementation of stroke_t.destroy.
439 */
440 static void destroy(private_controller_t *this)
441 {
442 free(this);
443 }
444
445 /*
446 * Described in header-file
447 */
448 controller_t *controller_create(void)
449 {
450 private_controller_t *this = malloc_thing(private_controller_t);
451
452 this->public.create_ike_sa_enumerator = (enumerator_t*(*)(controller_t*, bool))create_ike_sa_enumerator;
453 this->public.initiate = (status_t(*)(controller_t*,peer_cfg_t*,child_cfg_t*,controller_cb_t,void*))initiate;
454 this->public.terminate_ike = (status_t(*)(controller_t*,u_int32_t,controller_cb_t, void*))terminate_ike;
455 this->public.terminate_child = (status_t(*)(controller_t*,u_int32_t,controller_cb_t, void *param))terminate_child;
456 this->public.destroy = (void (*)(controller_t*))destroy;
457
458 return &this->public;
459 }
460