stroke accepts "down conn1{*}/[*]" to delete all CHILD/IKE_SAs using a given config
[strongswan.git] / src / charon / 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
21 typedef struct private_stroke_control_t private_stroke_control_t;
22
23 /**
24 * private data of stroke_control
25 */
26 struct private_stroke_control_t {
27
28 /**
29 * public functions
30 */
31 stroke_control_t public;
32 };
33
34
35 typedef struct stroke_log_info_t stroke_log_info_t;
36
37 /**
38 * helper struct to say what and where to log when using controller callback
39 */
40 struct stroke_log_info_t {
41
42 /**
43 * level to log up to
44 */
45 level_t level;
46
47 /**
48 * where to write log
49 */
50 FILE* out;
51 };
52
53 /**
54 * logging to the stroke interface
55 */
56 static bool stroke_log(stroke_log_info_t *info, debug_t group, level_t level,
57 ike_sa_t *ike_sa, char *format, va_list args)
58 {
59 if (level <= info->level)
60 {
61 if (vfprintf(info->out, format, args) < 0 ||
62 fprintf(info->out, "\n") < 0 ||
63 fflush(info->out) != 0)
64 {
65 return FALSE;
66 }
67 }
68 return TRUE;
69 }
70
71 /**
72 * get the child_cfg with the same name as the peer cfg
73 */
74 static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
75 {
76 child_cfg_t *current, *found = NULL;
77 enumerator_t *enumerator;
78
79 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
80 while (enumerator->enumerate(enumerator, &current))
81 {
82 if (streq(current->get_name(current), name))
83 {
84 found = current;
85 found->get_ref(found);
86 break;
87 }
88 }
89 enumerator->destroy(enumerator);
90 return found;
91 }
92
93 /**
94 * Implementation of stroke_control_t.initiate.
95 */
96 static void initiate(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 * Implementation of stroke_control_t.terminate.
141 */
142 static void terminate(private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
143 {
144 char *string, *pos = NULL, *name = NULL;
145 u_int32_t id = 0;
146 bool child, all = FALSE;
147 int len;
148 ike_sa_t *ike_sa;
149 enumerator_t *enumerator;
150 linked_list_t *ike_list, *child_list;
151 stroke_log_info_t info;
152 uintptr_t del;
153
154 string = msg->terminate.name;
155
156 len = strlen(string);
157 if (len < 1)
158 {
159 DBG1(DBG_CFG, "error parsing string");
160 return;
161 }
162 switch (string[len-1])
163 {
164 case '}':
165 child = TRUE;
166 pos = strchr(string, '{');
167 break;
168 case ']':
169 child = FALSE;
170 pos = strchr(string, '[');
171 break;
172 default:
173 name = string;
174 child = FALSE;
175 break;
176 }
177
178 if (name)
179 {
180 /* is a single name */
181 }
182 else if (pos == string + len - 2)
183 { /* is name[] or name{} */
184 string[len-2] = '\0';
185 name = string;
186 }
187 else
188 {
189 if (*(pos + 1) == '*')
190 { /* is name[*] */
191 all = TRUE;
192 *pos = '\0';
193 name = string;
194 }
195 else
196 { /* is name[123] or name{23} */
197 id = atoi(pos + 1);
198 if (id == 0)
199 {
200 DBG1(DBG_CFG, "error parsing string");
201 return;
202 }
203 }
204 }
205
206 info.out = out;
207 info.level = msg->output_verbosity;
208
209 if (id)
210 {
211 if (child)
212 {
213 charon->controller->terminate_child(charon->controller, id,
214 (controller_cb_t)stroke_log, &info);
215 }
216 else
217 {
218 charon->controller->terminate_ike(charon->controller, id,
219 (controller_cb_t)stroke_log, &info);
220 }
221 return;
222 }
223
224 ike_list = linked_list_create();
225 child_list = linked_list_create();
226 enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
227 while (enumerator->enumerate(enumerator, &ike_sa))
228 {
229 child_sa_t *child_sa;
230 iterator_t *children;
231
232 if (child)
233 {
234 children = ike_sa->create_child_sa_iterator(ike_sa);
235 while (children->iterate(children, (void**)&child_sa))
236 {
237 if (streq(name, child_sa->get_name(child_sa)))
238 {
239 child_list->insert_last(child_list,
240 (void*)(uintptr_t)child_sa->get_reqid(child_sa));
241 if (!all)
242 {
243 break;
244 }
245 }
246 }
247 children->destroy(children);
248 if (child_list->get_count(child_list) && !all)
249 {
250 break;
251 }
252 }
253 else if (streq(name, ike_sa->get_name(ike_sa)))
254 {
255 ike_list->insert_last(ike_list,
256 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
257 if (!all)
258 {
259 break;
260 }
261 }
262 }
263 enumerator->destroy(enumerator);
264
265 enumerator = child_list->create_enumerator(child_list);
266 while (enumerator->enumerate(enumerator, &del))
267 {
268 charon->controller->terminate_child(charon->controller, del,
269 (controller_cb_t)stroke_log, &info);
270 }
271 enumerator->destroy(enumerator);
272
273 enumerator = ike_list->create_enumerator(ike_list);
274 while (enumerator->enumerate(enumerator, &del))
275 {
276 charon->controller->terminate_ike(charon->controller, del,
277 (controller_cb_t)stroke_log, &info);
278 }
279 enumerator->destroy(enumerator);
280
281 if (child_list->get_count(child_list) == 0 &&
282 ike_list->get_count(ike_list) == 0)
283 {
284 DBG1(DBG_CFG, "no %s_SA named '%s' found",
285 child ? "CHILD" : "IKE", name);
286 }
287 ike_list->destroy(ike_list);
288 child_list->destroy(child_list);
289 }
290
291 /**
292 * Implementation of stroke_control_t.terminate_srcip.
293 */
294 static void terminate_srcip(private_stroke_control_t *this,
295 stroke_msg_t *msg, FILE *out)
296 {
297 enumerator_t *enumerator;
298 ike_sa_t *ike_sa;
299 host_t *start = NULL, *end = NULL, *vip;
300 chunk_t chunk_start, chunk_end = chunk_empty, chunk_vip;
301
302 if (msg->terminate_srcip.start)
303 {
304 start = host_create_from_string(msg->terminate_srcip.start, 0);
305 }
306 if (!start)
307 {
308 DBG1(DBG_CFG, "invalid start address: %s", msg->terminate_srcip.start);
309 return;
310 }
311 chunk_start = start->get_address(start);
312 if (msg->terminate_srcip.end)
313 {
314 end = host_create_from_string(msg->terminate_srcip.end, 0);
315 if (!end)
316 {
317 DBG1(DBG_CFG, "invalid end address: %s", msg->terminate_srcip.end);
318 start->destroy(start);
319 return;
320 }
321 chunk_end = end->get_address(end);
322 }
323
324 enumerator = charon->controller->create_ike_sa_enumerator(charon->controller);
325 while (enumerator->enumerate(enumerator, &ike_sa))
326 {
327 vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
328 if (!vip)
329 {
330 continue;
331 }
332 if (!end)
333 {
334 if (!vip->ip_equals(vip, start))
335 {
336 continue;
337 }
338 }
339 else
340 {
341 chunk_vip = vip->get_address(vip);
342 if (chunk_vip.len != chunk_start.len ||
343 chunk_vip.len != chunk_end.len ||
344 memcmp(chunk_vip.ptr, chunk_start.ptr, chunk_vip.len) < 0 ||
345 memcmp(chunk_vip.ptr, chunk_end.ptr, chunk_vip.len) > 0)
346 {
347 continue;
348 }
349 }
350
351 /* schedule delete asynchronously */
352 charon->processor->queue_job(charon->processor, (job_t*)
353 delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE));
354 }
355 enumerator->destroy(enumerator);
356 start->destroy(start);
357 DESTROY_IF(end);
358 }
359
360 /**
361 * Implementation of stroke_control_t.route.
362 */
363 static void route(private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
364 {
365 peer_cfg_t *peer_cfg;
366 child_cfg_t *child_cfg;
367
368 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends,
369 msg->route.name);
370 if (peer_cfg == NULL)
371 {
372 fprintf(out, "no config named '%s'\n", msg->route.name);
373 return;
374 }
375 if (peer_cfg->get_ike_version(peer_cfg) != 2)
376 {
377 peer_cfg->destroy(peer_cfg);
378 return;
379 }
380
381 child_cfg = get_child_from_peer(peer_cfg, msg->route.name);
382 if (child_cfg == NULL)
383 {
384 fprintf(out, "no child config named '%s'\n", msg->route.name);
385 peer_cfg->destroy(peer_cfg);
386 return;
387 }
388
389 if (charon->traps->install(charon->traps, peer_cfg, child_cfg))
390 {
391 fprintf(out, "configuration '%s' routed\n", msg->route.name);
392 }
393 else
394 {
395 fprintf(out, "routing configuration '%s' failed\n", msg->route.name);
396 }
397 peer_cfg->destroy(peer_cfg);
398 child_cfg->destroy(child_cfg);
399 }
400
401 /**
402 * Implementation of stroke_control_t.unroute.
403 */
404 static void unroute(private_stroke_control_t *this, stroke_msg_t *msg, FILE *out)
405 {
406 child_sa_t *child_sa;
407 enumerator_t *enumerator;
408 u_int32_t id;
409
410 enumerator = charon->traps->create_enumerator(charon->traps);
411 while (enumerator->enumerate(enumerator, NULL, &child_sa))
412 {
413 if (streq(msg->unroute.name, child_sa->get_name(child_sa)))
414 {
415 id = child_sa->get_reqid(child_sa);
416 enumerator->destroy(enumerator);
417 charon->traps->uninstall(charon->traps, id);
418 fprintf(out, "configuration '%s' unrouted\n", msg->unroute.name);
419 return;
420 }
421 }
422 enumerator->destroy(enumerator);
423 fprintf(out, "configuration '%s' not found\n", msg->unroute.name);
424 }
425
426 /**
427 * Implementation of stroke_control_t.destroy
428 */
429 static void destroy(private_stroke_control_t *this)
430 {
431 free(this);
432 }
433
434 /*
435 * see header file
436 */
437 stroke_control_t *stroke_control_create()
438 {
439 private_stroke_control_t *this = malloc_thing(private_stroke_control_t);
440
441 this->public.initiate = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))initiate;
442 this->public.terminate = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))terminate;
443 this->public.terminate_srcip = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))terminate_srcip;
444 this->public.route = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))route;
445 this->public.unroute = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))unroute;
446 this->public.destroy = (void(*)(stroke_control_t*))destroy;
447
448 return &this->public;
449 }
450