Provide get_priority() method in controller jobs
[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 METHOD(job_t, get_priority_medium, job_priority_t,
128 job_t *this)
129 {
130 return JOB_PRIO_MEDIUM;
131 }
132
133 /**
134 * Implementation of listener_t.ike_state_change
135 */
136 static bool listener_ike_state(interface_listener_t *this, ike_sa_t *ike_sa,
137 ike_sa_state_t state)
138 {
139 if (this->ike_sa == ike_sa)
140 {
141 switch (state)
142 {
143 #ifdef ME
144 case IKE_ESTABLISHED:
145 { /* mediation connections are complete without CHILD_SA */
146 peer_cfg_t *peer_cfg = ike_sa->get_peer_cfg(ike_sa);
147
148 if (peer_cfg->is_mediation(peer_cfg))
149 {
150 this->status = SUCCESS;
151 return FALSE;
152 }
153 break;
154 }
155 #endif /* ME */
156 case IKE_DESTROYING:
157 if (ike_sa->get_state(ike_sa) == IKE_DELETING)
158 { /* proper termination */
159 this->status = SUCCESS;
160 }
161 return FALSE;
162 default:
163 break;
164 }
165 }
166 return TRUE;
167 }
168
169 /**
170 * Implementation of listener_t.child_state_change
171 */
172 static bool listener_child_state(interface_listener_t *this, ike_sa_t *ike_sa,
173 child_sa_t *child_sa, child_sa_state_t state)
174 {
175 if (this->ike_sa == ike_sa)
176 {
177 switch (state)
178 {
179 case CHILD_INSTALLED:
180 this->status = SUCCESS;
181 return FALSE;
182 case CHILD_DESTROYING:
183 switch (child_sa->get_state(child_sa))
184 {
185 case CHILD_DELETING:
186 /* proper delete */
187 this->status = SUCCESS;
188 break;
189 default:
190 break;
191 }
192 return FALSE;
193 default:
194 break;
195 }
196 }
197 return TRUE;
198 }
199
200 /**
201 * cleanup job if job is never executed
202 */
203 static void recheckin(interface_job_t *job)
204 {
205 if (job->listener.ike_sa)
206 {
207 charon->ike_sa_manager->checkin(charon->ike_sa_manager,
208 job->listener.ike_sa);
209 }
210 }
211
212 /**
213 * Implementation of controller_t.create_ike_sa_iterator.
214 */
215 static enumerator_t* create_ike_sa_enumerator(controller_t *this, bool wait)
216 {
217 return charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager,
218 wait);
219 }
220
221 /**
222 * execute function for initiate
223 */
224 static status_t initiate_execute(interface_job_t *job)
225 {
226 ike_sa_t *ike_sa;
227 interface_listener_t *listener = &job->listener;
228 peer_cfg_t *peer_cfg = listener->peer_cfg;
229
230 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
231 peer_cfg);
232 listener->ike_sa = ike_sa;
233
234 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
235 {
236 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
237 }
238 peer_cfg->destroy(peer_cfg);
239
240 if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
241 {
242 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
243 return SUCCESS;
244 }
245 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
246 return FAILED;
247 }
248
249 /**
250 * Implementation of controller_t.initiate.
251 */
252 static status_t initiate(private_controller_t *this,
253 peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
254 controller_cb_t callback, void *param)
255 {
256 interface_job_t job = {
257 .listener = {
258 .public = {
259 .log = (void*)listener_log,
260 .ike_state_change = (void*)listener_ike_state,
261 .child_state_change = (void*)listener_child_state,
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 = (void*)initiate_execute,
271 .get_priority = _get_priority_medium,
272 .destroy = (void*)recheckin,
273 },
274 };
275 if (callback == NULL)
276 {
277 return initiate_execute(&job);
278 }
279 charon->bus->listen(charon->bus, &job.listener.public, (job_t*)&job);
280 return job.listener.status;
281 }
282
283 /**
284 * execute function for terminate_ike
285 */
286 static status_t terminate_ike_execute(interface_job_t *job)
287 {
288 interface_listener_t *listener = &job->listener;
289 ike_sa_t *ike_sa = listener->ike_sa;
290
291 charon->bus->set_sa(charon->bus, ike_sa);
292
293 if (ike_sa->delete(ike_sa) != DESTROY_ME)
294 {
295 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
296 /* delete failed */
297 return FAILED;
298 }
299 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
300 return SUCCESS;
301 }
302
303 /**
304 * Implementation of controller_t.terminate_ike.
305 */
306 static status_t terminate_ike(controller_t *this, u_int32_t unique_id,
307 controller_cb_t callback, void *param)
308 {
309 ike_sa_t *ike_sa;
310 interface_job_t job = {
311 .listener = {
312 .public = {
313 .log = (void*)listener_log,
314 .ike_state_change = (void*)listener_ike_state,
315 .child_state_change = (void*)listener_child_state,
316 },
317 .callback = callback,
318 .param = param,
319 .status = FAILED,
320 .id = unique_id,
321 },
322 .public = {
323 .execute = (void*)terminate_ike_execute,
324 .get_priority = _get_priority_medium,
325 .destroy = (void*)recheckin,
326 },
327 };
328
329 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
330 unique_id, FALSE);
331 if (ike_sa == NULL)
332 {
333 DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
334 return NOT_FOUND;
335 }
336 job.listener.ike_sa = ike_sa;
337
338 if (callback == NULL)
339 {
340 return terminate_ike_execute(&job);
341 }
342 charon->bus->listen(charon->bus, &job.listener.public, (job_t*)&job);
343 /* checkin of the ike_sa happend in the thread that executed the job */
344 charon->bus->set_sa(charon->bus, NULL);
345 return job.listener.status;
346 }
347
348 /**
349 * execute function for terminate_child
350 */
351 static status_t terminate_child_execute(interface_job_t *job)
352 {
353 interface_listener_t *listener = &job->listener;
354 ike_sa_t *ike_sa = listener->ike_sa;
355 child_sa_t *child_sa = listener->child_sa;
356
357 charon->bus->set_sa(charon->bus, ike_sa);
358 if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
359 child_sa->get_spi(child_sa, TRUE)) != DESTROY_ME)
360 {
361 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
362 return SUCCESS;
363 }
364 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
365 return FAILED;
366 }
367
368 /**
369 * Implementation of controller_t.terminate_child.
370 */
371 static status_t terminate_child(controller_t *this, u_int32_t reqid,
372 controller_cb_t callback, void *param)
373 {
374 ike_sa_t *ike_sa;
375 child_sa_t *child_sa;
376 iterator_t *iterator;
377 interface_job_t job = {
378 .listener = {
379 .public = {
380 .log = (void*)listener_log,
381 .ike_state_change = (void*)listener_ike_state,
382 .child_state_change = (void*)listener_child_state,
383 },
384 .callback = callback,
385 .param = param,
386 .status = FAILED,
387 .id = reqid,
388 },
389 .public = {
390 .execute = (void*)terminate_child_execute,
391 .get_priority = _get_priority_medium,
392 .destroy = (void*)recheckin,
393 },
394 };
395
396 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
397 reqid, TRUE);
398 if (ike_sa == NULL)
399 {
400 DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
401 reqid);
402 return NOT_FOUND;
403 }
404 job.listener.ike_sa = ike_sa;
405
406 iterator = ike_sa->create_child_sa_iterator(ike_sa);
407 while (iterator->iterate(iterator, (void**)&child_sa))
408 {
409 if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
410 child_sa->get_reqid(child_sa) == reqid)
411 {
412 break;
413 }
414 child_sa = NULL;
415 }
416 iterator->destroy(iterator);
417
418 if (child_sa == NULL)
419 {
420 DBG1(DBG_IKE, "unable to terminate, established "
421 "CHILD_SA with ID %d not found", reqid);
422 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
423 return NOT_FOUND;
424 }
425 job.listener.child_sa = child_sa;
426
427 if (callback == NULL)
428 {
429 return terminate_child_execute(&job);
430 }
431 charon->bus->listen(charon->bus, &job.listener.public, (job_t*)&job);
432 /* checkin of the ike_sa happend in the thread that executed the job */
433 charon->bus->set_sa(charon->bus, NULL);
434 return job.listener.status;
435 }
436
437 /**
438 * See header
439 */
440 bool controller_cb_empty(void *param, debug_t group, level_t level,
441 ike_sa_t *ike_sa, char *format, va_list args)
442 {
443 return TRUE;
444 }
445
446 /**
447 * Implementation of stroke_t.destroy.
448 */
449 static void destroy(private_controller_t *this)
450 {
451 free(this);
452 }
453
454 /*
455 * Described in header-file
456 */
457 controller_t *controller_create(void)
458 {
459 private_controller_t *this = malloc_thing(private_controller_t);
460
461 this->public.create_ike_sa_enumerator = (enumerator_t*(*)(controller_t*, bool))create_ike_sa_enumerator;
462 this->public.initiate = (status_t(*)(controller_t*,peer_cfg_t*,child_cfg_t*,controller_cb_t,void*))initiate;
463 this->public.terminate_ike = (status_t(*)(controller_t*,u_int32_t,controller_cb_t, void*))terminate_ike;
464 this->public.terminate_child = (status_t(*)(controller_t*,u_int32_t,controller_cb_t, void *param))terminate_child;
465 this->public.destroy = (void (*)(controller_t*))destroy;
466
467 return &this->public;
468 }
469