vici: Return local and remote virtual IPs when listing SAs
[strongswan.git] / src / swanctl / commands / list_sas.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 /*
17 * Copyright (C) 2014 Timo Teräs <timo.teras@iki.fi>
18 *
19 * Permission is hereby granted, free of charge, to any person obtaining a copy
20 * of this software and associated documentation files (the "Software"), to deal
21 * in the Software without restriction, including without limitation the rights
22 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 * copies of the Software, and to permit persons to whom the Software is
24 * furnished to do so, subject to the following conditions:
25 *
26 * The above copyright notice and this permission notice shall be included in
27 * all copies or substantial portions of the Software.
28 *
29 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 * THE SOFTWARE.
36 */
37
38 #define _GNU_SOURCE
39 #include <stdio.h>
40 #include <errno.h>
41
42 #include "command.h"
43
44 #include <collections/hashtable.h>
45
46 /**
47 * Free hashtable with contained strings
48 */
49 static void free_hashtable(hashtable_t *hashtable)
50 {
51 enumerator_t *enumerator;
52 char *str;
53
54 enumerator = hashtable->create_enumerator(hashtable);
55 while (enumerator->enumerate(enumerator, NULL, &str))
56 {
57 free(str);
58 }
59 enumerator->destroy(enumerator);
60
61 hashtable->destroy(hashtable);
62 }
63
64 CALLBACK(sa_values, int,
65 hashtable_t *sa, vici_res_t *res, char *name, void *value, int len)
66 {
67 chunk_t chunk;
68 char *str;
69
70 chunk = chunk_create(value, len);
71 if (chunk_printable(chunk, NULL, ' '))
72 {
73 if (asprintf(&str, "%.*s", len, value) >= 0)
74 {
75 free(sa->put(sa, name, str));
76 }
77 }
78 return 0;
79 }
80
81
82 CALLBACK(sa_list, int,
83 hashtable_t *sa, vici_res_t *res, char *name, void *value, int len)
84 {
85 chunk_t chunk;
86 char *str;
87
88 chunk = chunk_create(value, len);
89 if (chunk_printable(chunk, NULL, ' '))
90 {
91 str = sa->get(sa, name);
92 if (asprintf(&str, "%s%s%.*s",
93 str ?: "", str ? " " : "", len, value) >= 0)
94 {
95 free(sa->put(sa, name, str));
96 }
97 }
98 return 0;
99 }
100
101 CALLBACK(child_sas, int,
102 hashtable_t *ike, vici_res_t *res, char *name)
103 {
104 hashtable_t *child;
105 int ret;
106
107 child = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1);
108 ret = vici_parse_cb(res, NULL, sa_values, sa_list, child);
109 if (ret == 0)
110 {
111 printf(" %s: #%s, reqid %s, %s, %s%s, %s:",
112 name, child->get(child, "uniqueid"), child->get(child, "reqid"),
113 child->get(child, "state"), child->get(child, "mode"),
114 child->get(child, "encap") ? "-in-UDP" : "",
115 child->get(child, "protocol"));
116
117 if (child->get(child, "encr-alg"))
118 {
119 printf("%s", child->get(child, "encr-alg"));
120 if (child->get(child, "encr-keysize"))
121 {
122 printf("-%s", child->get(child, "encr-keysize"));
123 }
124 }
125 if (child->get(child, "integ-alg"))
126 {
127 if (child->get(child, "encr-alg"))
128 {
129 printf("/");
130 }
131 printf("%s", child->get(child, "integ-alg"));
132 if (child->get(child, "integ-keysize"))
133 {
134 printf("-%s", child->get(child, "integ-keysize"));
135 }
136 }
137 if (child->get(child, "prf-alg"))
138 {
139 printf("/%s", child->get(child, "prf-alg"));
140 }
141 if (child->get(child, "dh-group"))
142 {
143 printf("/%s", child->get(child, "dh-group"));
144 }
145 if (child->get(child, "esn"))
146 {
147 printf("/ESN");
148 }
149 printf("\n");
150
151 printf(" installed %ss ago", child->get(child, "install-time"));
152 if (child->get(child, "rekey-time"))
153 {
154 printf(", rekeying in %ss", child->get(child, "rekey-time"));
155 }
156 if (child->get(child, "life-time"))
157 {
158 printf(", expires in %ss", child->get(child, "life-time"));
159 }
160 printf("\n");
161
162 printf(" in %s%s%s", child->get(child, "spi-in"),
163 child->get(child, "cpi-in") ? "/" : "",
164 child->get(child, "cpi-in") ?: "");
165 printf(", %6s bytes, %5s packets",
166 child->get(child, "bytes-in"), child->get(child, "packets-in"));
167 if (child->get(child, "use-in"))
168 {
169 printf(", %5ss ago", child->get(child, "use-in"));
170 }
171 printf("\n");
172
173 printf(" out %s%s%s", child->get(child, "spi-out"),
174 child->get(child, "cpi-out") ? "/" : "",
175 child->get(child, "cpi-out") ?: "");
176 printf(", %6s bytes, %5s packets",
177 child->get(child, "bytes-out"), child->get(child, "packets-out"));
178 if (child->get(child, "use-out"))
179 {
180 printf(", %5ss ago", child->get(child, "use-out"));
181 }
182 printf("\n");
183
184 printf(" local %s\n", child->get(child, "local-ts"));
185 printf(" remote %s\n", child->get(child, "remote-ts"));
186 }
187 free_hashtable(child);
188 return ret;
189 }
190
191 CALLBACK(ike_sa, int,
192 hashtable_t *ike, vici_res_t *res, char *name)
193 {
194 if (streq(name, "child-sas"))
195 {
196 printf("%s: #%s, %s, IKEv%s, %s:%s\n",
197 ike->get(ike, "name"), ike->get(ike, "uniqueid"),
198 ike->get(ike, "state"), ike->get(ike, "version"),
199 ike->get(ike, "initiator-spi"), ike->get(ike, "responder-spi"));
200
201 printf(" local '%s' @ %s\n",
202 ike->get(ike, "local-id"), ike->get(ike, "local-host"));
203 printf(" remote '%s' @ %s",
204 ike->get(ike, "remote-id"), ike->get(ike, "remote-host"));
205 if (ike->get(ike, "remote-eap-id"))
206 {
207 printf(" EAP: '%s'", ike->get(ike, "remote-eap-id"));
208 }
209 if (ike->get(ike, "remote-xauth-id"))
210 {
211 printf(" XAuth: '%s'", ike->get(ike, "remote-xauth-id"));
212 }
213 printf("\n");
214
215 if (ike->get(ike, "encr-alg"))
216 {
217 printf(" %s", ike->get(ike, "encr-alg"));
218 if (ike->get(ike, "encr-keysize"))
219 {
220 printf("-%s", ike->get(ike, "encr-keysize"));
221 }
222 if (ike->get(ike, "integ-alg"))
223 {
224 printf("/%s", ike->get(ike, "integ-alg"));
225 }
226 if (ike->get(ike, "integ-keysize"))
227 {
228 printf("-%s", ike->get(ike, "integ-keysize"));
229 }
230 printf("/%s", ike->get(ike, "prf-alg"));
231 printf("/%s", ike->get(ike, "dh-group"));
232 printf("\n");
233 }
234
235 if (ike->get(ike, "established"))
236 {
237 printf(" established %ss ago", ike->get(ike, "established"));
238 if (ike->get(ike, "rekey-time"))
239 {
240 printf(", rekeying in %ss", ike->get(ike, "rekey-time"));
241 }
242 if (ike->get(ike, "reauth-time"))
243 {
244 printf(", reauth in %ss", ike->get(ike, "reauth-time"));
245 }
246 if (ike->get(ike, "life-time"))
247 {
248 printf(", expires in %ss", ike->get(ike, "life-time"));
249 }
250 printf("\n");
251 }
252
253 if (ike->get(ike, "tasks-queued"))
254 {
255 printf(" queued: %s\n", ike->get(ike, "tasks-queued"));
256 }
257 if (ike->get(ike, "tasks-active"))
258 {
259 printf(" active: %s\n", ike->get(ike, "tasks-active"));
260 }
261 if (ike->get(ike, "tasks-passive"))
262 {
263 printf(" passive: %s\n", ike->get(ike, "tasks-passive"));
264 }
265
266 return vici_parse_cb(res, child_sas, NULL, NULL, ike);
267 }
268 return 0;
269 }
270
271 CALLBACK(ike_sas, int,
272 void *null, vici_res_t *res, char *name)
273 {
274 hashtable_t *ike;
275 int ret;
276
277 ike = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1);
278 ike->put(ike, "name", strdup(name));
279 ret = vici_parse_cb(res, ike_sa, sa_values, sa_list, ike);
280 free_hashtable(ike);
281 return ret;
282 }
283
284 CALLBACK(list_cb, void,
285 command_format_options_t *format, char *name, vici_res_t *res)
286 {
287 char buf[256];
288
289 if (*format & COMMAND_FORMAT_RAW)
290 {
291 snprintf(buf, sizeof(buf), "%s event", name);
292 vici_dump(res, buf, *format & COMMAND_FORMAT_PRETTY,
293 stdout);
294 }
295 else
296 {
297 if (vici_parse_cb(res, ike_sas, NULL, NULL, NULL) != 0)
298 {
299 fprintf(stderr, "parsing SA event failed: %s\n", strerror(errno));
300 }
301 }
302 }
303
304 static int list_sas(vici_conn_t *conn)
305 {
306 vici_req_t *req;
307 vici_res_t *res;
308 bool noblock = FALSE;
309 command_format_options_t format = COMMAND_FORMAT_NONE;
310 char *arg, *ike = NULL;
311 int ike_id = 0, ret;
312
313 while (TRUE)
314 {
315 switch (command_getopt(&arg))
316 {
317 case 'h':
318 return command_usage(NULL);
319 case 'i':
320 ike = arg;
321 continue;
322 case 'I':
323 ike_id = atoi(arg);
324 continue;
325 case 'n':
326 noblock = TRUE;
327 continue;
328 case 'P':
329 format |= COMMAND_FORMAT_PRETTY;
330 /* fall through to raw */
331 case 'r':
332 format |= COMMAND_FORMAT_RAW;
333 continue;
334 case EOF:
335 break;
336 default:
337 return command_usage("invalid --list-sas option");
338 }
339 break;
340 }
341 if (vici_register(conn, "list-sa", list_cb, &format) != 0)
342 {
343 ret = errno;
344 fprintf(stderr, "registering for SAs failed: %s\n", strerror(errno));
345 return ret;
346 }
347 req = vici_begin("list-sas");
348 if (ike)
349 {
350 vici_add_key_valuef(req, "ike", "%s", ike);
351 }
352 if (ike_id)
353 {
354 vici_add_key_valuef(req, "ike-id", "%d", ike_id);
355 }
356 if (noblock)
357 {
358 vici_add_key_valuef(req, "noblock", "yes");
359 }
360 res = vici_submit(req, conn);
361 if (!res)
362 {
363 ret = errno;
364 fprintf(stderr, "list-sas request failed: %s\n", strerror(errno));
365 return ret;
366 }
367 if (format & COMMAND_FORMAT_RAW)
368 {
369 vici_dump(res, "list-sas reply", format & COMMAND_FORMAT_PRETTY,
370 stdout);
371 }
372 vici_free_res(res);
373 return 0;
374 }
375
376 static int monitor_sas(vici_conn_t *conn)
377 {
378 command_format_options_t format = COMMAND_FORMAT_NONE;
379 char *arg;
380
381 while (TRUE)
382 {
383 switch (command_getopt(&arg))
384 {
385 case 'h':
386 return command_usage(NULL);
387 case 'P':
388 format |= COMMAND_FORMAT_PRETTY;
389 /* fall through to raw */
390 case 'r':
391 format |= COMMAND_FORMAT_RAW;
392 continue;
393 case EOF:
394 break;
395 default:
396 return command_usage("invalid --monitor-sa option");
397 }
398 break;
399 }
400 if (vici_register(conn, "ike-updown", list_cb, &format) != 0)
401 {
402 fprintf(stderr, "registering for IKE_SAs failed: %s\n",
403 strerror(errno));
404 return errno;
405 }
406 if (vici_register(conn, "child-updown", list_cb, &format) != 0)
407 {
408 fprintf(stderr, "registering for CHILD_SAs failed: %s\n",
409 strerror(errno));
410 return errno;
411 }
412
413 wait_sigint();
414
415 fprintf(stderr, "disconnecting...\n");
416
417 return 0;
418 }
419
420 /**
421 * Register the command.
422 */
423 static void __attribute__ ((constructor))reg()
424 {
425 command_register((command_t) {
426 list_sas, 'l', "list-sas", "list currently active IKE_SAs",
427 {"[--raw|--pretty]"},
428 {
429 {"help", 'h', 0, "show usage information"},
430 {"ike", 'i', 1, "filter IKE_SAs by name"},
431 {"ike-id", 'I', 1, "filter IKE_SAs by unique identifier"},
432 {"noblock", 'n', 0, "don't wait for IKE_SAs in use"},
433 {"raw", 'r', 0, "dump raw response message"},
434 {"pretty", 'P', 0, "dump raw response message in pretty print"},
435 }
436 });
437 }
438
439 static void __attribute__ ((constructor))reg_monitor_sa()
440 {
441 command_register((command_t) {
442 monitor_sas, 'm', "monitor-sa", "monitor for IKE_SA and CHILD_SA changes",
443 {"[--raw|--pretty]"},
444 {
445 {"help", 'h', 0, "show usage information"},
446 {"raw", 'r', 0, "dump raw response message"},
447 {"pretty", 'P', 0, "dump raw response message in pretty print"},
448 }
449 });
450 }