vici: Add a list-policy command to query trap and shunt policies
[strongswan.git] / src / libcharon / plugins / vici / vici_query.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_query.h"
17 #include "vici_builder.h"
18
19 #include <inttypes.h>
20 #include <sys/utsname.h>
21
22 #include <daemon.h>
23
24 typedef struct private_vici_query_t private_vici_query_t;
25
26 /**
27 * Private data of an vici_query_t object.
28 */
29 struct private_vici_query_t {
30
31 /**
32 * Public vici_query_t interface.
33 */
34 vici_query_t public;
35
36 /**
37 * Dispatcher
38 */
39 vici_dispatcher_t *dispatcher;
40 };
41
42 /**
43 * List details of a CHILD_SA
44 */
45 static void list_child(private_vici_query_t *this, vici_builder_t *b,
46 child_sa_t *child, time_t now)
47 {
48 time_t t;
49 u_int64_t bytes, packets;
50 u_int16_t alg, ks;
51 proposal_t *proposal;
52 enumerator_t *enumerator;
53 traffic_selector_t *ts;
54
55 b->add_kv(b, "reqid", "%u", child->get_reqid(child));
56 b->add_kv(b, "state", "%N", child_sa_state_names, child->get_state(child));
57 b->add_kv(b, "mode", "%N", ipsec_mode_names, child->get_mode(child));
58 if (child->get_state(child) == CHILD_INSTALLED ||
59 child->get_state(child) == CHILD_REKEYING)
60 {
61 b->add_kv(b, "protocol", "%N", protocol_id_names,
62 child->get_protocol(child));
63 if (child->has_encap(child))
64 {
65 b->add_kv(b, "encap", "yes");
66 }
67 b->add_kv(b, "spi-in", "%.8x", ntohl(child->get_spi(child, TRUE)));
68 b->add_kv(b, "spi-out", "%.8x", ntohl(child->get_spi(child, FALSE)));
69
70 if (child->get_ipcomp(child) != IPCOMP_NONE)
71 {
72 b->add_kv(b, "cpi-in", "%.4x", ntohs(child->get_cpi(child, TRUE)));
73 b->add_kv(b, "cpi-out", "%.4x", ntohs(child->get_cpi(child, FALSE)));
74 }
75 proposal = child->get_proposal(child);
76 if (proposal)
77 {
78 if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM,
79 &alg, &ks) && alg != ENCR_UNDEFINED)
80 {
81 b->add_kv(b, "encr-alg", "%N", encryption_algorithm_names, alg);
82 if (ks)
83 {
84 b->add_kv(b, "encr-keysize", "%u", ks);
85 }
86 }
87 if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
88 &alg, &ks) && alg != ENCR_UNDEFINED)
89 {
90 b->add_kv(b, "integ-alg", "%N", integrity_algorithm_names, alg);
91 if (ks)
92 {
93 b->add_kv(b, "integ-keysize", "%u", ks);
94 }
95 }
96 if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION,
97 &alg, NULL))
98 {
99 b->add_kv(b, "prf-alg", "%N", pseudo_random_function_names, alg);
100 }
101 if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP,
102 &alg, NULL))
103 {
104 b->add_kv(b, "dh-group", "%N", diffie_hellman_group_names, alg);
105 }
106 if (proposal->get_algorithm(proposal, EXTENDED_SEQUENCE_NUMBERS,
107 &alg, NULL) && alg == EXT_SEQ_NUMBERS)
108 {
109 b->add_kv(b, "esn", "1");
110 }
111 }
112
113 child->get_usestats(child, TRUE, &t, &bytes, &packets);
114 b->add_kv(b, "bytes-in", "%" PRIu64, bytes);
115 b->add_kv(b, "packets-in", "%" PRIu64, packets);
116 if (t)
117 {
118 b->add_kv(b, "use-in", "%"PRIu64, (u_int64_t)(now - t));
119 }
120
121 child->get_usestats(child, FALSE, &t, &bytes, &packets);
122 b->add_kv(b, "bytes-out", "%"PRIu64, bytes);
123 b->add_kv(b, "packets-out", "%"PRIu64, packets);
124 if (t)
125 {
126 b->add_kv(b, "use-out", "%"PRIu64, (u_int64_t)(now - t));
127 }
128
129 t = child->get_lifetime(child, FALSE);
130 if (t)
131 {
132 b->add_kv(b, "rekey-time", "%"PRId64, (int64_t)(t - now));
133 }
134 t = child->get_lifetime(child, TRUE);
135 if (t)
136 {
137 b->add_kv(b, "life-time", "%"PRId64, (int64_t)(t - now));
138 }
139 t = child->get_installtime(child);
140 b->add_kv(b, "install-time", "%"PRId64, (int64_t)(now - t));
141 }
142
143 b->begin_list(b, "local-ts");
144 enumerator = child->create_ts_enumerator(child, TRUE);
145 while (enumerator->enumerate(enumerator, &ts))
146 {
147 b->add_li(b, "%R", ts);
148 }
149 enumerator->destroy(enumerator);
150 b->end_list(b /* local-ts */);
151
152 b->begin_list(b, "remote-ts");
153 enumerator = child->create_ts_enumerator(child, FALSE);
154 while (enumerator->enumerate(enumerator, &ts))
155 {
156 b->add_li(b, "%R", ts);
157 }
158 enumerator->destroy(enumerator);
159 b->end_list(b /* remote-ts */);
160 }
161
162 /**
163 * List tasks in a specific queue
164 */
165 static void list_task_queue(private_vici_query_t *this, vici_builder_t *b,
166 ike_sa_t *ike_sa, task_queue_t q, char *name)
167 {
168 enumerator_t *enumerator;
169 bool has = FALSE;
170 task_t *task;
171
172 enumerator = ike_sa->create_task_enumerator(ike_sa, q);
173 while (enumerator->enumerate(enumerator, &task))
174 {
175 if (!has)
176 {
177 b->begin_list(b, name);
178 has = TRUE;
179 }
180 b->add_li(b, "%N", task_type_names, task->get_type(task));
181 }
182 enumerator->destroy(enumerator);
183 if (has)
184 {
185 b->end_list(b);
186 }
187 }
188
189 /**
190 * List details of an IKE_SA
191 */
192 static void list_ike(private_vici_query_t *this, vici_builder_t *b,
193 ike_sa_t *ike_sa, time_t now)
194 {
195 time_t t;
196 ike_sa_id_t *id;
197 identification_t *eap;
198 proposal_t *proposal;
199 u_int16_t alg, ks;
200
201 b->add_kv(b, "uniqueid", "%u", ike_sa->get_unique_id(ike_sa));
202 b->add_kv(b, "version", "%u", ike_sa->get_version(ike_sa));
203 b->add_kv(b, "state", "%N", ike_sa_state_names, ike_sa->get_state(ike_sa));
204
205 b->add_kv(b, "local-host", "%H", ike_sa->get_my_host(ike_sa));
206 b->add_kv(b, "local-id", "%Y", ike_sa->get_my_id(ike_sa));
207
208 b->add_kv(b, "remote-host", "%H", ike_sa->get_other_host(ike_sa));
209 b->add_kv(b, "remote-id", "%Y", ike_sa->get_other_id(ike_sa));
210
211 eap = ike_sa->get_other_eap_id(ike_sa);
212
213 if (!eap->equals(eap, ike_sa->get_other_id(ike_sa)))
214 {
215 if (ike_sa->get_version(ike_sa) == IKEV1)
216 {
217 b->add_kv(b, "remote-xauth-id", "%Y", eap);
218 }
219 else
220 {
221 b->add_kv(b, "remote-eap-id", "%Y", eap);
222 }
223 }
224
225 id = ike_sa->get_id(ike_sa);
226 if (id->is_initiator(id))
227 {
228 b->add_kv(b, "initiator", "yes");
229 }
230 b->add_kv(b, "initiator-spi", "%.16"PRIx64, id->get_initiator_spi(id));
231 b->add_kv(b, "responder-spi", "%.16"PRIx64, id->get_responder_spi(id));
232
233 proposal = ike_sa->get_proposal(ike_sa);
234 if (proposal)
235 {
236 if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &ks))
237 {
238 b->add_kv(b, "encr-alg", "%N", encryption_algorithm_names, alg);
239 if (ks)
240 {
241 b->add_kv(b, "encr-keysize", "%u", ks);
242 }
243 }
244 if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, &ks))
245 {
246 b->add_kv(b, "integ-alg", "%N", integrity_algorithm_names, alg);
247 if (ks)
248 {
249 b->add_kv(b, "integ-keysize", "%u", ks);
250 }
251 }
252 if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
253 {
254 b->add_kv(b, "prf-alg", "%N", pseudo_random_function_names, alg);
255 }
256 if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &alg, NULL))
257 {
258 b->add_kv(b, "dh-group", "%N", diffie_hellman_group_names, alg);
259 }
260 }
261
262 if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
263 {
264 t = ike_sa->get_statistic(ike_sa, STAT_ESTABLISHED);
265 b->add_kv(b, "established", "%"PRId64, (int64_t)(now - t));
266 t = ike_sa->get_statistic(ike_sa, STAT_REKEY);
267 if (t)
268 {
269 b->add_kv(b, "rekey-time", "%"PRId64, (int64_t)(t - now));
270 }
271 t = ike_sa->get_statistic(ike_sa, STAT_REAUTH);
272 if (t)
273 {
274 b->add_kv(b, "reauth-time", "%"PRId64, (int64_t)(t - now));
275 }
276 }
277
278 list_task_queue(this, b, ike_sa, TASK_QUEUE_QUEUED, "tasks-queued");
279 list_task_queue(this, b, ike_sa, TASK_QUEUE_ACTIVE, "tasks-active");
280 list_task_queue(this, b, ike_sa, TASK_QUEUE_PASSIVE, "tasks-passive");
281 }
282
283 CALLBACK(list_sas, vici_message_t*,
284 private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
285 {
286 vici_builder_t *b;
287 enumerator_t *isas, *csas;
288 ike_sa_t *ike_sa;
289 child_sa_t *child_sa;
290 time_t now;
291 char *ike;
292 u_int ike_id;
293 bool bl;
294
295 bl = request->get_str(request, NULL, "noblock") == NULL;
296 ike = request->get_str(request, NULL, "ike");
297 ike_id = request->get_int(request, 0, "ike-id");
298
299 isas = charon->controller->create_ike_sa_enumerator(charon->controller, bl);
300 while (isas->enumerate(isas, &ike_sa))
301 {
302 if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
303 {
304 continue;
305 }
306 if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
307 {
308 continue;
309 }
310
311 now = time_monotonic(NULL);
312
313 b = vici_builder_create();
314 b->begin_section(b, ike_sa->get_name(ike_sa));
315
316 list_ike(this, b, ike_sa, now);
317
318 b->begin_section(b, "child-sas");
319 csas = ike_sa->create_child_sa_enumerator(ike_sa);
320 while (csas->enumerate(csas, &child_sa))
321 {
322 b->begin_section(b, child_sa->get_name(child_sa));
323 list_child(this, b, child_sa, now);
324 b->end_section(b);
325 }
326 csas->destroy(csas);
327 b->end_section(b /* child-sas */ );
328
329 b->end_section(b);
330
331 this->dispatcher->raise_event(this->dispatcher, "list-sa", id,
332 b->finalize(b));
333 }
334 isas->destroy(isas);
335
336 b = vici_builder_create();
337 return b->finalize(b);
338 }
339
340 /**
341 * Raise a list-policy event for given CHILD_SA
342 */
343 static void raise_policy(private_vici_query_t *this, u_int id, child_sa_t *child)
344 {
345 enumerator_t *enumerator;
346 traffic_selector_t *ts;
347 vici_builder_t *b;
348
349 b = vici_builder_create();
350 b->begin_section(b, child->get_name(child));
351
352 b->add_kv(b, "mode", "%N", ipsec_mode_names, child->get_mode(child));
353
354 b->begin_list(b, "local-ts");
355 enumerator = child->create_ts_enumerator(child, TRUE);
356 while (enumerator->enumerate(enumerator, &ts))
357 {
358 b->add_li(b, "%R", ts);
359 }
360 enumerator->destroy(enumerator);
361 b->end_list(b /* local-ts */);
362
363 b->begin_list(b, "remote-ts");
364 enumerator = child->create_ts_enumerator(child, FALSE);
365 while (enumerator->enumerate(enumerator, &ts))
366 {
367 b->add_li(b, "%R", ts);
368 }
369 enumerator->destroy(enumerator);
370 b->end_list(b /* remote-ts */);
371
372 b->end_section(b);
373
374 this->dispatcher->raise_event(this->dispatcher, "list-policy", id,
375 b->finalize(b));
376 }
377
378 /**
379 * Raise a list-policy event for given CHILD_SA config
380 */
381 static void raise_policy_cfg(private_vici_query_t *this, u_int id,
382 child_cfg_t *cfg)
383 {
384 enumerator_t *enumerator;
385 linked_list_t *list;
386 traffic_selector_t *ts;
387 vici_builder_t *b;
388
389 b = vici_builder_create();
390 b->begin_section(b, cfg->get_name(cfg));
391
392 b->add_kv(b, "mode", "%N", ipsec_mode_names, cfg->get_mode(cfg));
393
394 b->begin_list(b, "local-ts");
395 list = cfg->get_traffic_selectors(cfg, TRUE, NULL, NULL);
396 enumerator = list->create_enumerator(list);
397 while (enumerator->enumerate(enumerator, &ts))
398 {
399 b->add_li(b, "%R", ts);
400 }
401 enumerator->destroy(enumerator);
402 list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
403 b->end_list(b /* local-ts */);
404
405 b->begin_list(b, "remote-ts");
406 list = cfg->get_traffic_selectors(cfg, FALSE, NULL, NULL);
407 enumerator = list->create_enumerator(list);
408 while (enumerator->enumerate(enumerator, &ts))
409 {
410 b->add_li(b, "%R", ts);
411 }
412 enumerator->destroy(enumerator);
413 list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
414 b->end_list(b /* remote-ts */);
415
416 b->end_section(b);
417
418 this->dispatcher->raise_event(this->dispatcher, "list-policy", id,
419 b->finalize(b));
420 }
421
422 CALLBACK(list_policies, vici_message_t*,
423 private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
424 {
425 enumerator_t *enumerator;
426 vici_builder_t *b;
427 child_sa_t *child_sa;
428 child_cfg_t *child_cfg;
429 bool drop, pass, trap;
430 char *child;
431
432 drop = request->get_str(request, NULL, "drop") != NULL;
433 pass = request->get_str(request, NULL, "pass") != NULL;
434 trap = request->get_str(request, NULL, "trap") != NULL;
435 child = request->get_str(request, NULL, "child");
436
437 if (trap)
438 {
439 enumerator = charon->traps->create_enumerator(charon->traps);
440 while (enumerator->enumerate(enumerator, NULL, &child_sa))
441 {
442 if (child && !streq(child, child_sa->get_name(child_sa)))
443 {
444 continue;
445 }
446 raise_policy(this, id, child_sa);
447 }
448 enumerator->destroy(enumerator);
449 }
450
451 if (drop || pass)
452 {
453 enumerator = charon->shunts->create_enumerator(charon->shunts);
454 while (enumerator->enumerate(enumerator, &child_cfg))
455 {
456 if (child && !streq(child, child_cfg->get_name(child_cfg)))
457 {
458 continue;
459 }
460 switch (child_cfg->get_mode(child_cfg))
461 {
462 case MODE_DROP:
463 if (drop)
464 {
465 raise_policy_cfg(this, id, child_cfg);
466 }
467 break;
468 case MODE_PASS:
469 if (pass)
470 {
471 raise_policy_cfg(this, id, child_cfg);
472 }
473 break;
474 default:
475 break;
476 }
477 }
478 enumerator->destroy(enumerator);
479 }
480
481 b = vici_builder_create();
482 return b->finalize(b);
483 }
484
485 CALLBACK(version, vici_message_t*,
486 private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
487 {
488 struct utsname utsname;
489 vici_builder_t *b;
490
491 b = vici_builder_create();
492
493 b->add_kv(b, "daemon", "%s", lib->ns);
494 b->add_kv(b, "version", "%s", VERSION);
495
496 if (uname(&utsname) == 0)
497 {
498 b->add_kv(b, "sysname", "%s", utsname.sysname);
499 b->add_kv(b, "release", "%s", utsname.release);
500 b->add_kv(b, "machine", "%s", utsname.machine);
501 }
502
503 return b->finalize(b);
504 }
505
506 static void manage_command(private_vici_query_t *this,
507 char *name, vici_command_cb_t cb, bool reg)
508 {
509 this->dispatcher->manage_command(this->dispatcher, name,
510 reg ? cb : NULL, this);
511 }
512
513 /**
514 * (Un-)register dispatcher functions
515 */
516 static void manage_commands(private_vici_query_t *this, bool reg)
517 {
518 this->dispatcher->manage_event(this->dispatcher, "list-sa", reg);
519 this->dispatcher->manage_event(this->dispatcher, "list-policy", reg);
520 manage_command(this, "list-sas", list_sas, reg);
521 manage_command(this, "list-policies", list_policies, reg);
522 manage_command(this, "version", version, reg);
523 }
524
525 METHOD(vici_query_t, destroy, void,
526 private_vici_query_t *this)
527 {
528 manage_commands(this, FALSE);
529 free(this);
530 }
531
532 /**
533 * See header
534 */
535 vici_query_t *vici_query_create(vici_dispatcher_t *dispatcher)
536 {
537 private_vici_query_t *this;
538
539 INIT(this,
540 .public = {
541 .destroy = _destroy,
542 },
543 .dispatcher = dispatcher,
544 );
545
546 manage_commands(this, TRUE);
547
548 return &this->public;
549 }