Migrated controller_t to INIT/METHOD macros
[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)
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 charon->bus->listen(charon->bus, &job.listener.public, &job.public);
271 }
272 return job.listener.status;
273 }
274
275 METHOD(job_t, terminate_ike_execute, void,
276 interface_job_t *job)
277 {
278 interface_listener_t *listener = &job->listener;
279 ike_sa_t *ike_sa = listener->ike_sa;
280
281 charon->bus->set_sa(charon->bus, ike_sa);
282
283 if (ike_sa->delete(ike_sa) != DESTROY_ME)
284 {
285 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
286 /* delete failed */
287 listener->status = FAILED;
288 }
289 else
290 {
291 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
292 ike_sa);
293 listener->status = SUCCESS;
294 }
295 }
296
297 METHOD(controller_t, terminate_ike, status_t,
298 controller_t *this, u_int32_t unique_id,
299 controller_cb_t callback, void *param)
300 {
301 ike_sa_t *ike_sa;
302 interface_job_t job = {
303 .listener = {
304 .public = {
305 .log = _listener_log,
306 .ike_state_change = _ike_state_change,
307 .child_state_change = _child_state_change,
308 },
309 .callback = callback,
310 .param = param,
311 .status = FAILED,
312 .id = unique_id,
313 },
314 .public = {
315 .execute = _terminate_ike_execute,
316 .get_priority = _get_priority_medium,
317 .destroy = _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 terminate_ike_execute(&job);
333 }
334 else
335 {
336 charon->bus->listen(charon->bus, &job.listener.public, &job.public);
337 /* checkin of the ike_sa happend in the thread that executed the job */
338 charon->bus->set_sa(charon->bus, NULL);
339 }
340 return job.listener.status;
341 }
342
343 METHOD(job_t, terminate_child_execute, void,
344 interface_job_t *job)
345 {
346 interface_listener_t *listener = &job->listener;
347 ike_sa_t *ike_sa = listener->ike_sa;
348 child_sa_t *child_sa = listener->child_sa;
349
350 charon->bus->set_sa(charon->bus, ike_sa);
351 if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
352 child_sa->get_spi(child_sa, TRUE)) != DESTROY_ME)
353 {
354 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
355 listener->status = SUCCESS;
356 }
357 else
358 {
359 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
360 ike_sa);
361 listener->status = FAILED;
362 }
363 }
364
365 METHOD(controller_t, terminate_child, status_t,
366 controller_t *this, u_int32_t reqid, controller_cb_t callback, void *param)
367 {
368 ike_sa_t *ike_sa;
369 child_sa_t *child_sa;
370 iterator_t *iterator;
371 interface_job_t job = {
372 .listener = {
373 .public = {
374 .log = _listener_log,
375 .ike_state_change = _ike_state_change,
376 .child_state_change = _child_state_change,
377 },
378 .callback = callback,
379 .param = param,
380 .status = FAILED,
381 .id = reqid,
382 },
383 .public = {
384 .execute = _terminate_child_execute,
385 .get_priority = _get_priority_medium,
386 .destroy = _recheckin,
387 },
388 };
389
390 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
391 reqid, TRUE);
392 if (ike_sa == NULL)
393 {
394 DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
395 reqid);
396 return NOT_FOUND;
397 }
398 job.listener.ike_sa = ike_sa;
399
400 iterator = ike_sa->create_child_sa_iterator(ike_sa);
401 while (iterator->iterate(iterator, (void**)&child_sa))
402 {
403 if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
404 child_sa->get_reqid(child_sa) == reqid)
405 {
406 break;
407 }
408 child_sa = NULL;
409 }
410 iterator->destroy(iterator);
411
412 if (child_sa == NULL)
413 {
414 DBG1(DBG_IKE, "unable to terminate, established "
415 "CHILD_SA with ID %d not found", reqid);
416 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
417 return NOT_FOUND;
418 }
419 job.listener.child_sa = child_sa;
420
421 if (callback == NULL)
422 {
423 terminate_child_execute(&job);
424 }
425 else
426 {
427 charon->bus->listen(charon->bus, &job.listener.public, &job.public);
428 /* checkin of the ike_sa happend in the thread that executed the job */
429 charon->bus->set_sa(charon->bus, NULL);
430 }
431 return job.listener.status;
432 }
433
434 /**
435 * See header
436 */
437 bool controller_cb_empty(void *param, debug_t group, level_t level,
438 ike_sa_t *ike_sa, char *format, va_list args)
439 {
440 return TRUE;
441 }
442
443 METHOD(controller_t, destroy, void,
444 private_controller_t *this)
445 {
446 free(this);
447 }
448
449 /*
450 * Described in header-file
451 */
452 controller_t *controller_create(void)
453 {
454 private_controller_t *this;
455
456 INIT(this,
457 .public = {
458 .create_ike_sa_enumerator = _create_ike_sa_enumerator,
459 .initiate = _initiate,
460 .terminate_ike = _terminate_ike,
461 .terminate_child = _terminate_child,
462 .destroy = _destroy,
463 },
464 );
465
466 return &this->public;
467 }
468