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