replaced ipsec up %startall command by start_action job
[strongswan.git] / src / libcharon / plugins / stroke / stroke_control.c
1 /*
2 * Copyright (C) 2008 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 "stroke_control.h"
17
18 #include <daemon.h>
19 #include <processing/jobs/delete_ike_sa_job.h>
20 #include <processing/jobs/rekey_ike_sa_job.h>
21 #include <processing/jobs/rekey_child_sa_job.h>
22
23 typedef struct private_stroke_control_t private_stroke_control_t;
24
25 /**
26 * private data of stroke_control
27 */
28 struct private_stroke_control_t {
29
30 /**
31 * public functions
32 */
33 stroke_control_t public;
34 };
35
36
37 typedef struct stroke_log_info_t stroke_log_info_t;
38
39 /**
40 * helper struct to say what and where to log when using controller callback
41 */
42 struct stroke_log_info_t {
43
44 /**
45 * level to log up to
46 */
47 level_t level;
48
49 /**
50 * where to write log
51 */
52 FILE* out;
53 };
54
55 /**
56 * logging to the stroke interface
57 */
58 static bool stroke_log(stroke_log_info_t *info, debug_t group, level_t level,
59 ike_sa_t *ike_sa, char *format, va_list args)
60 {
61 if (level <= info->level)
62 {
63 if (vfprintf(info->out, format, args) < 0 ||
64 fprintf(info->out, "\n") < 0 ||
65 fflush(info->out) != 0)
66 {
67 return FALSE;
68 }
69 }
70 return TRUE;
71 }
72
73 /**
74 * get the child_cfg with the same name as the peer cfg
75 */
76 static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
77 {
78 child_cfg_t *current, *found = NULL;
79 enumerator_t *enumerator;
80
81 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
82 while (enumerator->enumerate(enumerator, &current))
83 {
84 if (streq(current->get_name(current), name))
85 {
86 found = current;
87 found->get_ref(found);
88 break;
89 }
90 }
91 enumerator->destroy(enumerator);
92 return found;
93 }
94
95 METHOD(stroke_control_t, initiate, void,
96 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
97 {
98 peer_cfg_t *peer_cfg;
99 child_cfg_t *child_cfg;
100 stroke_log_info_t info;
101
102 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
103 msg->initiate.name);
104 if (peer_cfg == NULL)
105 {
106 DBG1(DBG_CFG, "no config named '%s'\n", msg->initiate.name);
107 return;
108 }
109 if (peer_cfg->get_ike_version(peer_cfg) != 2)
110 {
111 DBG1(DBG_CFG, "ignoring initiation request for IKEv%d config",
112 peer_cfg->get_ike_version(peer_cfg));
113 peer_cfg->destroy(peer_cfg);
114 return;
115 }
116
117 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
118 if (child_cfg == NULL)
119 {
120 DBG1(DBG_CFG, "no child config named '%s'\n", msg->initiate.name);
121 peer_cfg->destroy(peer_cfg);
122 return;
123 }
124
125 if (msg->output_verbosity < 0)
126 {
127 charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
128 NULL, NULL);
129 }
130 else
131 {
132 info.out = out;
133 info.level = msg->output_verbosity;
134 charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
135 (controller_cb_t)stroke_log, &info);
136 }
137 }
138
139 /**
140 * Parse a terminate/rekey specifier
141 */
142 static bool parse_specifier(char *string, u_int32_t *id,
143 char **name, bool *child, bool *all)
144 {
145 int len;
146 char *pos = NULL;
147
148 *id = 0;
149 *name = NULL;
150 *all = FALSE;
151
152 len = strlen(string);
153 if (len < 1)
154 {
155 return FALSE;
156 }
157 switch (string[len-1])
158 {
159 case '}':
160 *child = TRUE;
161 pos = strchr(string, '{');
162 break;
163 case ']':
164 *child = FALSE;
165 pos = strchr(string, '[');
166 break;
167 default:
168 *name = string;
169 *child = FALSE;
170 break;
171 }
172
173 if (*name)
174 {
175 /* is a single name */
176 }
177 else if (pos == string + len - 2)
178 { /* is name[] or name{} */
179 string[len-2] = '\0';
180 *name = string;
181 }
182 else
183 {
184 if (!pos)
185 {
186 return FALSE;
187 }
188 if (*(pos + 1) == '*')
189 { /* is name[*] */
190 *all = TRUE;
191 *pos = '\0';
192 *name = string;
193 }
194 else
195 { /* is name[123] or name{23} */
196 *id = atoi(pos + 1);
197 if (*id == 0)
198 {
199 return FALSE;
200 }
201 }
202 }
203 return TRUE;
204 }
205
206 METHOD(stroke_control_t, terminate, void,
207 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
208 {
209 char *name;
210 u_int32_t id;
211 bool child, all;
212 ike_sa_t *ike_sa;
213 enumerator_t *enumerator;
214 linked_list_t *ike_list, *child_list;
215 stroke_log_info_t info;
216 uintptr_t del;
217
218 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
219 {
220 DBG1(DBG_CFG, "error parsing specifier string");
221 return;
222 }
223
224 info.out = out;
225 info.level = msg->output_verbosity;
226
227 if (id)
228 {
229 if (child)
230 {
231 charon->controller->terminate_child(charon->controller, id,
232 (controller_cb_t)stroke_log, &info);
233 }
234 else
235 {
236 charon->controller->terminate_ike(charon->controller, id,
237 (controller_cb_t)stroke_log, &info);
238 }
239 return;
240 }
241
242 ike_list = linked_list_create();
243 child_list = linked_list_create();
244 enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
245 while (enumerator->enumerate(enumerator, &ike_sa))
246 {
247 child_sa_t *child_sa;
248 iterator_t *children;
249
250 if (child)
251 {
252 children = ike_sa->create_child_sa_iterator(ike_sa);
253 while (children->iterate(children, (void**)&child_sa))
254 {
255 if (streq(name, child_sa->get_name(child_sa)))
256 {
257 child_list->insert_last(child_list,
258 (void*)(uintptr_t)child_sa->get_reqid(child_sa));
259 if (!all)
260 {
261 break;
262 }
263 }
264 }
265 children->destroy(children);
266 if (child_list->get_count(child_list) && !all)
267 {
268 break;
269 }
270 }
271 else if (streq(name, ike_sa->get_name(ike_sa)))
272 {
273 ike_list->insert_last(ike_list,
274 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
275 if (!all)
276 {
277 break;
278 }
279 }
280 }
281 enumerator->destroy(enumerator);
282
283 enumerator = child_list->create_enumerator(child_list);
284 while (enumerator->enumerate(enumerator, &del))
285 {
286 charon->controller->terminate_child(charon->controller, del,
287 (controller_cb_t)stroke_log, &info);
288 }
289 enumerator->destroy(enumerator);
290
291 enumerator = ike_list->create_enumerator(ike_list);
292 while (enumerator->enumerate(enumerator, &del))
293 {
294 charon->controller->terminate_ike(charon->controller, del,
295 (controller_cb_t)stroke_log, &info);
296 }
297 enumerator->destroy(enumerator);
298
299 if (child_list->get_count(child_list) == 0 &&
300 ike_list->get_count(ike_list) == 0)
301 {
302 DBG1(DBG_CFG, "no %s_SA named '%s' found",
303 child ? "CHILD" : "IKE", name);
304 }
305 ike_list->destroy(ike_list);
306 child_list->destroy(child_list);
307 }
308
309 METHOD(stroke_control_t, rekey, void,
310 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
311 {
312 char *name;
313 u_int32_t id;
314 bool child, all, finished = FALSE;
315 ike_sa_t *ike_sa;
316 enumerator_t *enumerator;
317
318 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
319 {
320 DBG1(DBG_CFG, "error parsing specifier string");
321 return;
322 }
323 enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
324 while (enumerator->enumerate(enumerator, &ike_sa))
325 {
326 child_sa_t *child_sa;
327 iterator_t *children;
328
329 if (child)
330 {
331 children = ike_sa->create_child_sa_iterator(ike_sa);
332 while (children->iterate(children, (void**)&child_sa))
333 {
334 if ((name && streq(name, child_sa->get_name(child_sa))) ||
335 (id && id == child_sa->get_reqid(child_sa)))
336 {
337 lib->processor->queue_job(lib->processor,
338 (job_t*)rekey_child_sa_job_create(
339 child_sa->get_reqid(child_sa),
340 child_sa->get_protocol(child_sa),
341 child_sa->get_spi(child_sa, TRUE)));
342 if (!all)
343 {
344 finished = TRUE;
345 break;
346 }
347 }
348 }
349 children->destroy(children);
350 }
351 else if ((name && streq(name, ike_sa->get_name(ike_sa))) ||
352 (id && id == ike_sa->get_unique_id(ike_sa)))
353 {
354 lib->processor->queue_job(lib->processor,
355 (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE));
356 if (!all)
357 {
358 finished = TRUE;
359 }
360 }
361 if (finished)
362 {
363 break;
364 }
365 }
366 enumerator->destroy(enumerator);
367 }
368
369 METHOD(stroke_control_t, terminate_srcip, void,
370 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
371 {
372 enumerator_t *enumerator;
373 ike_sa_t *ike_sa;
374 host_t *start = NULL, *end = NULL, *vip;
375 chunk_t chunk_start, chunk_end = chunk_empty, chunk_vip;
376
377 if (msg->terminate_srcip.start)
378 {
379 start = host_create_from_string(msg->terminate_srcip.start, 0);
380 }
381 if (!start)
382 {
383 DBG1(DBG_CFG, "invalid start address: %s", msg->terminate_srcip.start);
384 return;
385 }
386 chunk_start = start->get_address(start);
387 if (msg->terminate_srcip.end)
388 {
389 end = host_create_from_string(msg->terminate_srcip.end, 0);
390 if (!end)
391 {
392 DBG1(DBG_CFG, "invalid end address: %s", msg->terminate_srcip.end);
393 start->destroy(start);
394 return;
395 }
396 chunk_end = end->get_address(end);
397 }
398
399 enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
400 while (enumerator->enumerate(enumerator, &ike_sa))
401 {
402 vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
403 if (!vip)
404 {
405 continue;
406 }
407 if (!end)
408 {
409 if (!vip->ip_equals(vip, start))
410 {
411 continue;
412 }
413 }
414 else
415 {
416 chunk_vip = vip->get_address(vip);
417 if (chunk_vip.len != chunk_start.len ||
418 chunk_vip.len != chunk_end.len ||
419 memcmp(chunk_vip.ptr, chunk_start.ptr, chunk_vip.len) < 0 ||
420 memcmp(chunk_vip.ptr, chunk_end.ptr, chunk_vip.len) > 0)
421 {
422 continue;
423 }
424 }
425
426 /* schedule delete asynchronously */
427 lib->processor->queue_job(lib->processor, (job_t*)
428 delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE));
429 }
430 enumerator->destroy(enumerator);
431 start->destroy(start);
432 DESTROY_IF(end);
433 }
434
435 METHOD(stroke_control_t, purge_ike, void,
436 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
437 {
438 enumerator_t *enumerator;
439 iterator_t *iterator;
440 ike_sa_t *ike_sa;
441 child_sa_t *child_sa;
442 linked_list_t *list;
443 uintptr_t del;
444 stroke_log_info_t info;
445
446 info.out = out;
447 info.level = msg->output_verbosity;
448
449 list = linked_list_create();
450 enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
451 while (enumerator->enumerate(enumerator, &ike_sa))
452 {
453 iterator = ike_sa->create_child_sa_iterator(ike_sa);
454 if (!iterator->iterate(iterator, (void**)&child_sa))
455 {
456 list->insert_last(list,
457 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
458 }
459 iterator->destroy(iterator);
460 }
461 enumerator->destroy(enumerator);
462
463 enumerator = list->create_enumerator(list);
464 while (enumerator->enumerate(enumerator, &del))
465 {
466 charon->controller->terminate_ike(charon->controller, del,
467 (controller_cb_t)stroke_log, &info);
468 }
469 enumerator->destroy(enumerator);
470 list->destroy(list);
471 }
472
473 METHOD(stroke_control_t, route, void,
474 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
475 {
476 peer_cfg_t *peer_cfg;
477 child_cfg_t *child_cfg;
478
479 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
480 msg->route.name);
481 if (peer_cfg == NULL)
482 {
483 fprintf(out, "no config named '%s'\n", msg->route.name);
484 return;
485 }
486 if (peer_cfg->get_ike_version(peer_cfg) != 2)
487 {
488 peer_cfg->destroy(peer_cfg);
489 return;
490 }
491
492 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
493 if (child_cfg == NULL)
494 {
495 fprintf(out, "no child config named '%s'\n", msg->route.name);
496 peer_cfg->destroy(peer_cfg);
497 return;
498 }
499
500 if (charon->traps->install(charon->traps, peer_cfg, child_cfg))
501 {
502 fprintf(out, "configuration '%s' routed\n", msg->route.name);
503 }
504 else
505 {
506 fprintf(out, "routing configuration '%s' failed\n", msg->route.name);
507 }
508 peer_cfg->destroy(peer_cfg);
509 child_cfg->destroy(child_cfg);
510 }
511
512 METHOD(stroke_control_t, unroute, void,
513 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
514 {
515 child_sa_t *child_sa;
516 enumerator_t *enumerator;
517 u_int32_t id;
518
519 enumerator = charon->traps->create_enumerator(charon->traps);
520 while (enumerator->enumerate(enumerator, NULL, &child_sa))
521 {
522 if (streq(msg->unroute.name, child_sa->get_name(child_sa)))
523 {
524 id = child_sa->get_reqid(child_sa);
525 enumerator->destroy(enumerator);
526 charon->traps->uninstall(charon->traps, id);
527 fprintf(out, "configuration '%s' unrouted\n", msg->unroute.name);
528 return;
529 }
530 }
531 enumerator->destroy(enumerator);
532 fprintf(out, "configuration '%s' not found\n", msg->unroute.name);
533 }
534
535 METHOD(stroke_control_t, destroy, void,
536 private_stroke_control_t *this)
537 {
538 free(this);
539 }
540
541 /*
542 * see header file
543 */
544 stroke_control_t *stroke_control_create()
545 {
546 private_stroke_control_t *this;
547
548 INIT(this,
549 .public = {
550 .initiate = _initiate,
551 .terminate = _terminate,
552 .terminate_srcip = _terminate_srcip,
553 .rekey = _rekey,
554 .purge_ike = _purge_ike,
555 .route = _route,
556 .unroute = _unroute,
557 .destroy = _destroy,
558 },
559 );
560
561 return &this->public;
562 }
563