Added a non-blocking, skipping variant of IKE_SA enumerator
[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 /**
96 * call the charon controller to initiate the connection
97 */
98 static void charon_initiate(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
99 stroke_msg_t *msg, FILE *out)
100 {
101 if (msg->output_verbosity < 0)
102 {
103 charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
104 NULL, NULL);
105 }
106 else
107 {
108 stroke_log_info_t info = { msg->output_verbosity, out };
109
110 charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
111 (controller_cb_t)stroke_log, &info);
112 }
113 }
114
115 METHOD(stroke_control_t, initiate, void,
116 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
117 {
118 child_cfg_t *child_cfg = NULL;
119 peer_cfg_t *peer_cfg;
120 enumerator_t *enumerator;
121 bool empty = TRUE;
122
123 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
124 msg->initiate.name);
125 if (peer_cfg)
126 {
127 if (peer_cfg->get_ike_version(peer_cfg) != 2)
128 {
129 DBG1(DBG_CFG, "ignoring initiation request for IKEv%d config",
130 peer_cfg->get_ike_version(peer_cfg));
131 peer_cfg->destroy(peer_cfg);
132 return;
133 }
134
135 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
136 if (child_cfg == NULL)
137 {
138 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
139 while (enumerator->enumerate(enumerator, &child_cfg))
140 {
141 empty = FALSE;
142 charon_initiate(peer_cfg->get_ref(peer_cfg),
143 child_cfg->get_ref(child_cfg), msg, out);
144 }
145 enumerator->destroy(enumerator);
146
147 if (empty)
148 {
149 DBG1(DBG_CFG, "no child config named '%s'", msg->initiate.name);
150 fprintf(out, "no child config named '%s'\n", msg->initiate.name);
151 }
152 peer_cfg->destroy(peer_cfg);
153 return;
154 }
155 }
156 else
157 {
158 enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
159 NULL, NULL, NULL, NULL);
160 while (enumerator->enumerate(enumerator, &peer_cfg))
161 {
162 if (peer_cfg->get_ike_version(peer_cfg) != 2)
163 {
164 continue;
165 }
166 child_cfg = get_child_from_peer(peer_cfg, msg->initiate.name);
167 if (child_cfg)
168 {
169 peer_cfg->get_ref(peer_cfg);
170 break;
171 }
172 }
173 enumerator->destroy(enumerator);
174
175 if (child_cfg == NULL)
176 {
177 DBG1(DBG_CFG, "no config named '%s'", msg->initiate.name);
178 fprintf(out, "no config named '%s'\n", msg->initiate.name);
179 return;
180 }
181 }
182 charon_initiate(peer_cfg, child_cfg, msg, out);
183 }
184
185 /**
186 * Parse a terminate/rekey specifier
187 */
188 static bool parse_specifier(char *string, u_int32_t *id,
189 char **name, bool *child, bool *all)
190 {
191 int len;
192 char *pos = NULL;
193
194 *id = 0;
195 *name = NULL;
196 *all = FALSE;
197
198 len = strlen(string);
199 if (len < 1)
200 {
201 return FALSE;
202 }
203 switch (string[len-1])
204 {
205 case '}':
206 *child = TRUE;
207 pos = strchr(string, '{');
208 break;
209 case ']':
210 *child = FALSE;
211 pos = strchr(string, '[');
212 break;
213 default:
214 *name = string;
215 *child = FALSE;
216 break;
217 }
218
219 if (*name)
220 {
221 /* is a single name */
222 }
223 else if (pos == string + len - 2)
224 { /* is name[] or name{} */
225 string[len-2] = '\0';
226 *name = string;
227 }
228 else
229 {
230 if (!pos)
231 {
232 return FALSE;
233 }
234 if (*(pos + 1) == '*')
235 { /* is name[*] */
236 *all = TRUE;
237 *pos = '\0';
238 *name = string;
239 }
240 else
241 { /* is name[123] or name{23} */
242 *id = atoi(pos + 1);
243 if (*id == 0)
244 {
245 return FALSE;
246 }
247 }
248 }
249 return TRUE;
250 }
251
252 METHOD(stroke_control_t, terminate, void,
253 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
254 {
255 char *name;
256 u_int32_t id;
257 bool child, all;
258 ike_sa_t *ike_sa;
259 enumerator_t *enumerator;
260 linked_list_t *ike_list, *child_list;
261 stroke_log_info_t info;
262 uintptr_t del;
263
264 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
265 {
266 DBG1(DBG_CFG, "error parsing specifier string");
267 return;
268 }
269
270 info.out = out;
271 info.level = msg->output_verbosity;
272
273 if (id)
274 {
275 if (child)
276 {
277 charon->controller->terminate_child(charon->controller, id,
278 (controller_cb_t)stroke_log, &info);
279 }
280 else
281 {
282 charon->controller->terminate_ike(charon->controller, id,
283 (controller_cb_t)stroke_log, &info);
284 }
285 return;
286 }
287
288 ike_list = linked_list_create();
289 child_list = linked_list_create();
290 enumerator = charon->controller->create_ike_sa_enumerator(
291 charon->controller, TRUE);
292 while (enumerator->enumerate(enumerator, &ike_sa))
293 {
294 child_sa_t *child_sa;
295 iterator_t *children;
296
297 if (child)
298 {
299 children = ike_sa->create_child_sa_iterator(ike_sa);
300 while (children->iterate(children, (void**)&child_sa))
301 {
302 if (streq(name, child_sa->get_name(child_sa)))
303 {
304 child_list->insert_last(child_list,
305 (void*)(uintptr_t)child_sa->get_reqid(child_sa));
306 if (!all)
307 {
308 break;
309 }
310 }
311 }
312 children->destroy(children);
313 if (child_list->get_count(child_list) && !all)
314 {
315 break;
316 }
317 }
318 else if (streq(name, ike_sa->get_name(ike_sa)))
319 {
320 ike_list->insert_last(ike_list,
321 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
322 if (!all)
323 {
324 break;
325 }
326 }
327 }
328 enumerator->destroy(enumerator);
329
330 enumerator = child_list->create_enumerator(child_list);
331 while (enumerator->enumerate(enumerator, &del))
332 {
333 charon->controller->terminate_child(charon->controller, del,
334 (controller_cb_t)stroke_log, &info);
335 }
336 enumerator->destroy(enumerator);
337
338 enumerator = ike_list->create_enumerator(ike_list);
339 while (enumerator->enumerate(enumerator, &del))
340 {
341 charon->controller->terminate_ike(charon->controller, del,
342 (controller_cb_t)stroke_log, &info);
343 }
344 enumerator->destroy(enumerator);
345
346 if (child_list->get_count(child_list) == 0 &&
347 ike_list->get_count(ike_list) == 0)
348 {
349 DBG1(DBG_CFG, "no %s_SA named '%s' found",
350 child ? "CHILD" : "IKE", name);
351 }
352 ike_list->destroy(ike_list);
353 child_list->destroy(child_list);
354 }
355
356 METHOD(stroke_control_t, rekey, void,
357 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
358 {
359 char *name;
360 u_int32_t id;
361 bool child, all, finished = FALSE;
362 ike_sa_t *ike_sa;
363 enumerator_t *enumerator;
364
365 if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all))
366 {
367 DBG1(DBG_CFG, "error parsing specifier string");
368 return;
369 }
370 enumerator = charon->controller->create_ike_sa_enumerator(
371 charon->controller, TRUE);
372 while (enumerator->enumerate(enumerator, &ike_sa))
373 {
374 child_sa_t *child_sa;
375 iterator_t *children;
376
377 if (child)
378 {
379 children = ike_sa->create_child_sa_iterator(ike_sa);
380 while (children->iterate(children, (void**)&child_sa))
381 {
382 if ((name && streq(name, child_sa->get_name(child_sa))) ||
383 (id && id == child_sa->get_reqid(child_sa)))
384 {
385 lib->processor->queue_job(lib->processor,
386 (job_t*)rekey_child_sa_job_create(
387 child_sa->get_reqid(child_sa),
388 child_sa->get_protocol(child_sa),
389 child_sa->get_spi(child_sa, TRUE)));
390 if (!all)
391 {
392 finished = TRUE;
393 break;
394 }
395 }
396 }
397 children->destroy(children);
398 }
399 else if ((name && streq(name, ike_sa->get_name(ike_sa))) ||
400 (id && id == ike_sa->get_unique_id(ike_sa)))
401 {
402 lib->processor->queue_job(lib->processor,
403 (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE));
404 if (!all)
405 {
406 finished = TRUE;
407 }
408 }
409 if (finished)
410 {
411 break;
412 }
413 }
414 enumerator->destroy(enumerator);
415 }
416
417 METHOD(stroke_control_t, terminate_srcip, void,
418 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
419 {
420 enumerator_t *enumerator;
421 ike_sa_t *ike_sa;
422 host_t *start = NULL, *end = NULL, *vip;
423 chunk_t chunk_start, chunk_end = chunk_empty, chunk_vip;
424
425 if (msg->terminate_srcip.start)
426 {
427 start = host_create_from_string(msg->terminate_srcip.start, 0);
428 }
429 if (!start)
430 {
431 DBG1(DBG_CFG, "invalid start address: %s", msg->terminate_srcip.start);
432 return;
433 }
434 chunk_start = start->get_address(start);
435 if (msg->terminate_srcip.end)
436 {
437 end = host_create_from_string(msg->terminate_srcip.end, 0);
438 if (!end)
439 {
440 DBG1(DBG_CFG, "invalid end address: %s", msg->terminate_srcip.end);
441 start->destroy(start);
442 return;
443 }
444 chunk_end = end->get_address(end);
445 }
446
447 enumerator = charon->controller->create_ike_sa_enumerator(
448 charon->controller, TRUE);
449 while (enumerator->enumerate(enumerator, &ike_sa))
450 {
451 vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
452 if (!vip)
453 {
454 continue;
455 }
456 if (!end)
457 {
458 if (!vip->ip_equals(vip, start))
459 {
460 continue;
461 }
462 }
463 else
464 {
465 chunk_vip = vip->get_address(vip);
466 if (chunk_vip.len != chunk_start.len ||
467 chunk_vip.len != chunk_end.len ||
468 memcmp(chunk_vip.ptr, chunk_start.ptr, chunk_vip.len) < 0 ||
469 memcmp(chunk_vip.ptr, chunk_end.ptr, chunk_vip.len) > 0)
470 {
471 continue;
472 }
473 }
474
475 /* schedule delete asynchronously */
476 lib->processor->queue_job(lib->processor, (job_t*)
477 delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE));
478 }
479 enumerator->destroy(enumerator);
480 start->destroy(start);
481 DESTROY_IF(end);
482 }
483
484 METHOD(stroke_control_t, purge_ike, void,
485 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
486 {
487 enumerator_t *enumerator;
488 iterator_t *iterator;
489 ike_sa_t *ike_sa;
490 child_sa_t *child_sa;
491 linked_list_t *list;
492 uintptr_t del;
493 stroke_log_info_t info;
494
495 info.out = out;
496 info.level = msg->output_verbosity;
497
498 list = linked_list_create();
499 enumerator = charon->controller->create_ike_sa_enumerator(
500 charon->controller, TRUE);
501 while (enumerator->enumerate(enumerator, &ike_sa))
502 {
503 iterator = ike_sa->create_child_sa_iterator(ike_sa);
504 if (!iterator->iterate(iterator, (void**)&child_sa))
505 {
506 list->insert_last(list,
507 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
508 }
509 iterator->destroy(iterator);
510 }
511 enumerator->destroy(enumerator);
512
513 enumerator = list->create_enumerator(list);
514 while (enumerator->enumerate(enumerator, &del))
515 {
516 charon->controller->terminate_ike(charon->controller, del,
517 (controller_cb_t)stroke_log, &info);
518 }
519 enumerator->destroy(enumerator);
520 list->destroy(list);
521 }
522
523 /**
524 * call charon to install a trap
525 */
526 static void charon_route(peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
527 char *name, FILE *out)
528 {
529 if (charon->traps->install(charon->traps, peer_cfg, child_cfg))
530 {
531 fprintf(out, "'%s' routed\n", name);
532 }
533 else
534 {
535 fprintf(out, "routing '%s' failed\n", name);
536 }
537 }
538
539 METHOD(stroke_control_t, route, void,
540 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
541 {
542 child_cfg_t *child_cfg = NULL;
543 peer_cfg_t *peer_cfg;
544 enumerator_t *enumerator;
545 bool empty = TRUE;
546
547 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
548 msg->route.name);
549 if (peer_cfg)
550 {
551 if (peer_cfg->get_ike_version(peer_cfg) != 2)
552 {
553 DBG1(DBG_CFG, "ignoring initiation request for IKEv%d config",
554 peer_cfg->get_ike_version(peer_cfg));
555 peer_cfg->destroy(peer_cfg);
556 return;
557 }
558
559 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
560 if (child_cfg == NULL)
561 {
562 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
563 while (enumerator->enumerate(enumerator, &child_cfg))
564 {
565 empty = FALSE;
566 charon_route(peer_cfg, child_cfg, child_cfg->get_name(child_cfg),
567 out);
568 }
569 enumerator->destroy(enumerator);
570
571 if (empty)
572 {
573 DBG1(DBG_CFG, "no child config named '%s'", msg->route.name);
574 fprintf(out, "no child config named '%s'\n", msg->route.name);
575 }
576 peer_cfg->destroy(peer_cfg);
577 return;
578 }
579 }
580 else
581 {
582 enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
583 NULL, NULL, NULL, NULL);
584 while (enumerator->enumerate(enumerator, &peer_cfg))
585 {
586 if (peer_cfg->get_ike_version(peer_cfg) != 2)
587 {
588 continue;
589 }
590 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
591 if (child_cfg)
592 {
593 peer_cfg->get_ref(peer_cfg);
594 break;
595 }
596 }
597 enumerator->destroy(enumerator);
598
599 if (child_cfg == NULL)
600 {
601 DBG1(DBG_CFG, "no config named '%s'", msg->route.name);
602 fprintf(out, "no config named '%s'\n", msg->route.name);
603 return;
604 }
605 }
606 charon_route(peer_cfg, child_cfg, msg->route.name, out);
607 peer_cfg->destroy(peer_cfg);
608 child_cfg->destroy(child_cfg);
609 }
610
611 METHOD(stroke_control_t, unroute, void,
612 private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
613 {
614 child_sa_t *child_sa;
615 enumerator_t *enumerator;
616 u_int32_t id;
617
618 enumerator = charon->traps->create_enumerator(charon->traps);
619 while (enumerator->enumerate(enumerator, NULL, &child_sa))
620 {
621 if (streq(msg->unroute.name, child_sa->get_name(child_sa)))
622 {
623 id = child_sa->get_reqid(child_sa);
624 enumerator->destroy(enumerator);
625 charon->traps->uninstall(charon->traps, id);
626 fprintf(out, "configuration '%s' unrouted\n", msg->unroute.name);
627 return;
628 }
629 }
630 enumerator->destroy(enumerator);
631 fprintf(out, "configuration '%s' not found\n", msg->unroute.name);
632 }
633
634 METHOD(stroke_control_t, destroy, void,
635 private_stroke_control_t *this)
636 {
637 free(this);
638 }
639
640 /*
641 * see header file
642 */
643 stroke_control_t *stroke_control_create()
644 {
645 private_stroke_control_t *this;
646
647 INIT(this,
648 .public = {
649 .initiate = _initiate,
650 .terminate = _terminate,
651 .terminate_srcip = _terminate_srcip,
652 .rekey = _rekey,
653 .purge_ike = _purge_ike,
654 .route = _route,
655 .unroute = _unroute,
656 .destroy = _destroy,
657 },
658 );
659
660 return &this->public;
661 }
662