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