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