vici: Avoid recursive control log invocations
[strongswan.git] / src / libcharon / plugins / vici / vici_control.c
1 /*
2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 revosec AG
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 "vici_control.h"
17 #include "vici_builder.h"
18
19 #include <inttypes.h>
20
21 #include <daemon.h>
22 #include <collections/array.h>
23
24 typedef struct private_vici_control_t private_vici_control_t;
25
26 /**
27 * Private data of an vici_control_t object.
28 */
29 struct private_vici_control_t {
30
31 /**
32 * Public vici_control_t interface.
33 */
34 vici_control_t public;
35
36 /**
37 * Dispatcher
38 */
39 vici_dispatcher_t *dispatcher;
40 };
41
42 /**
43 * Log callback helper data
44 */
45 typedef struct {
46 /** dispatcher to send log messages over */
47 vici_dispatcher_t *dispatcher;
48 /** connection ID to send messages to */
49 u_int id;
50 /** loglevel */
51 level_t level;
52 /** prevent recursive log */
53 u_int recursive;
54 } log_info_t;
55
56 /**
57 * Log using vici event messages
58 */
59 static bool log_vici(log_info_t *info, debug_t group, level_t level,
60 ike_sa_t *ike_sa, char *text)
61 {
62 if (level <= info->level)
63 {
64 if (info->recursive++ == 0)
65 {
66 vici_message_t *message;
67 vici_builder_t *builder;
68
69 builder = vici_builder_create();
70 builder->add_kv(builder, "group", "%N", debug_names, group);
71 builder->add_kv(builder, "level", "%d", level);
72 if (ike_sa)
73 {
74 builder->add_kv(builder, "ikesa-name", "%s",
75 ike_sa->get_name(ike_sa));
76 builder->add_kv(builder, "ikesa-uniqueid", "%u",
77 ike_sa->get_unique_id(ike_sa));
78 }
79 builder->add_kv(builder, "msg", "%s", text);
80
81 message = builder->finalize(builder);
82 if (message)
83 {
84 info->dispatcher->raise_event(info->dispatcher, "control-log",
85 info->id, message);
86 }
87 }
88 info->recursive--;
89 }
90 return TRUE;
91 }
92
93 /**
94 * Send a (error) reply message
95 */
96 static vici_message_t* send_reply(private_vici_control_t *this, char *fmt, ...)
97 {
98 vici_builder_t *builder;
99 va_list args;
100
101 builder = vici_builder_create();
102 builder->add_kv(builder, "success", fmt ? "no" : "yes");
103 if (fmt)
104 {
105 va_start(args, fmt);
106 builder->vadd_kv(builder, "errmsg", fmt, args);
107 va_end(args);
108 }
109 return builder->finalize(builder);
110 }
111
112 /**
113 * Get the child_cfg having name from peer_cfg
114 */
115 static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
116 {
117 child_cfg_t *current, *found = NULL;
118 enumerator_t *enumerator;
119
120 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
121 while (enumerator->enumerate(enumerator, &current))
122 {
123 if (streq(current->get_name(current), name))
124 {
125 found = current;
126 found->get_ref(found);
127 break;
128 }
129 }
130 enumerator->destroy(enumerator);
131 return found;
132 }
133
134 CALLBACK(initiate, vici_message_t*,
135 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
136 {
137 child_cfg_t *child_cfg = NULL;
138 peer_cfg_t *peer_cfg;
139 enumerator_t *enumerator;
140 char *child;
141 u_int timeout;
142 log_info_t log = {
143 .dispatcher = this->dispatcher,
144 .id = id,
145 };
146
147 child = request->get_str(request, NULL, "child");
148 timeout = request->get_int(request, 0, "timeout");
149 log.level = request->get_int(request, 1, "loglevel");
150
151 if (!child)
152 {
153 return send_reply(this, "missing configuration name");
154 }
155 enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
156 NULL, NULL, NULL, NULL, IKE_ANY);
157 while (enumerator->enumerate(enumerator, &peer_cfg))
158 {
159 child_cfg = get_child_from_peer(peer_cfg, child);
160 if (child_cfg)
161 {
162 peer_cfg->get_ref(peer_cfg);
163 break;
164 }
165 }
166 enumerator->destroy(enumerator);
167
168 if (!child_cfg)
169 {
170 return send_reply(this, "CHILD_SA config '%s' not found", child);
171 }
172 switch (charon->controller->initiate(charon->controller,
173 peer_cfg, child_cfg, (controller_cb_t)log_vici, &log, timeout))
174 {
175 case SUCCESS:
176 return send_reply(this, NULL);
177 case OUT_OF_RES:
178 return send_reply(this, "CHILD_SA '%s' not established after %dms",
179 child, timeout);
180 case FAILED:
181 default:
182 return send_reply(this, "establishing CHILD_SA '%s' failed", child);
183 }
184 }
185
186 CALLBACK(terminate, vici_message_t*,
187 private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
188 {
189 enumerator_t *enumerator, *isas, *csas;
190 char *child, *ike;
191 u_int timeout, child_id, ike_id, current, *del, done = 0;
192 ike_sa_t *ike_sa;
193 child_sa_t *child_sa;
194 array_t *ids;
195 vici_message_t *reply;
196 log_info_t log = {
197 .dispatcher = this->dispatcher,
198 .id = id,
199 };
200
201 child = request->get_str(request, NULL, "child");
202 ike = request->get_str(request, NULL, "ike");
203 child_id = request->get_int(request, 0, "child-id");
204 ike_id = request->get_int(request, 0, "ike-id");
205 timeout = request->get_int(request, 0, "timeout");
206 log.level = request->get_int(request, 1, "loglevel");
207
208 if (!child && !ike && !ike_id && !child_id)
209 {
210 return send_reply(this, "missing terminate selector");
211 }
212
213 ids = array_create(sizeof(u_int), 0);
214
215 isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
216 while (isas->enumerate(isas, &ike_sa))
217 {
218 if (child || child_id)
219 {
220 if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
221 {
222 continue;
223 }
224 if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
225 {
226 continue;
227 }
228 csas = ike_sa->create_child_sa_enumerator(ike_sa);
229 while (csas->enumerate(csas, &child_sa))
230 {
231 if (child && !streq(child, child_sa->get_name(child_sa)))
232 {
233 continue;
234 }
235 if (child_id && child_sa->get_reqid(child_sa) != child_id)
236 {
237 continue;
238 }
239 current = child_sa->get_reqid(child_sa);
240 array_insert(ids, ARRAY_TAIL, &current);
241 }
242 csas->destroy(csas);
243 }
244 else if (ike && streq(ike, ike_sa->get_name(ike_sa)))
245 {
246 current = ike_sa->get_unique_id(ike_sa);
247 array_insert(ids, ARRAY_TAIL, &current);
248 }
249 else if (ike_id && ike_id == ike_sa->get_unique_id(ike_sa))
250 {
251 array_insert(ids, ARRAY_TAIL, &ike_id);
252 }
253 }
254 isas->destroy(isas);
255
256 enumerator = array_create_enumerator(ids);
257 while (enumerator->enumerate(enumerator, &del))
258 {
259 if (child || child_id)
260 {
261 if (charon->controller->terminate_child(charon->controller, *del,
262 (controller_cb_t)log_vici, &log, timeout) == SUCCESS)
263 {
264 done++;
265 }
266 }
267 else
268 {
269 if (charon->controller->terminate_ike(charon->controller, *del,
270 (controller_cb_t)log_vici, &log, timeout) == SUCCESS)
271 {
272 done++;
273 }
274 }
275 }
276 enumerator->destroy(enumerator);
277
278 if (array_count(ids) == 0)
279 {
280 reply = send_reply(this, "no matching SAs to terminate found");
281 }
282 else if (done < array_count(ids))
283 {
284 if (array_count(ids) == 1)
285 {
286 reply = send_reply(this, "terminating SA failed");
287 }
288 else
289 {
290 reply = send_reply(this, "terminated %u of %u SAs",
291 done, array_count(ids));
292 }
293 }
294 else
295 {
296 reply = send_reply(this, NULL);
297 }
298 array_destroy(ids);
299 return reply;
300 }
301
302 static void manage_command(private_vici_control_t *this,
303 char *name, vici_command_cb_t cb, bool reg)
304 {
305 this->dispatcher->manage_command(this->dispatcher, name,
306 reg ? cb : NULL, this);
307 }
308
309 /**
310 * (Un-)register dispatcher functions
311 */
312 static void manage_commands(private_vici_control_t *this, bool reg)
313 {
314 manage_command(this, "initiate", initiate, reg);
315 manage_command(this, "terminate", terminate, reg);
316 this->dispatcher->manage_event(this->dispatcher, "control-log", reg);
317 }
318
319 METHOD(vici_control_t, destroy, void,
320 private_vici_control_t *this)
321 {
322 manage_commands(this, FALSE);
323 free(this);
324 }
325
326 /**
327 * See header
328 */
329 vici_control_t *vici_control_create(vici_dispatcher_t *dispatcher)
330 {
331 private_vici_control_t *this;
332
333 INIT(this,
334 .public = {
335 .destroy = _destroy,
336 },
337 .dispatcher = dispatcher,
338 );
339
340 manage_commands(this, TRUE);
341
342 return &this->public;
343 }