vici: Match identity with wildcards against remote ID in redirect command
[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",
202 ike->get(ike, "local-id"), ike->get(ike, "local-host"));
203 if (ike->get(ike, "local-vips"))
204 {
205 printf(" [%s]", ike->get(ike, "local-vips"));
206 }
207 printf("\n");
208
209 printf(" remote '%s' @ %s",
210 ike->get(ike, "remote-id"), ike->get(ike, "remote-host"));
211 if (ike->get(ike, "remote-eap-id"))
212 {
213 printf(" EAP: '%s'", ike->get(ike, "remote-eap-id"));
214 }
215 if (ike->get(ike, "remote-xauth-id"))
216 {
217 printf(" XAuth: '%s'", ike->get(ike, "remote-xauth-id"));
218 }
219 if (ike->get(ike, "remote-vips"))
220 {
221 printf(" [%s]", ike->get(ike, "remote-vips"));
222 }
223 printf("\n");
224
225 if (ike->get(ike, "encr-alg"))
226 {
227 printf(" %s", ike->get(ike, "encr-alg"));
228 if (ike->get(ike, "encr-keysize"))
229 {
230 printf("-%s", ike->get(ike, "encr-keysize"));
231 }
232 if (ike->get(ike, "integ-alg"))
233 {
234 printf("/%s", ike->get(ike, "integ-alg"));
235 }
236 if (ike->get(ike, "integ-keysize"))
237 {
238 printf("-%s", ike->get(ike, "integ-keysize"));
239 }
240 printf("/%s", ike->get(ike, "prf-alg"));
241 printf("/%s", ike->get(ike, "dh-group"));
242 printf("\n");
243 }
244
245 if (ike->get(ike, "established"))
246 {
247 printf(" established %ss ago", ike->get(ike, "established"));
248 if (ike->get(ike, "rekey-time"))
249 {
250 printf(", rekeying in %ss", ike->get(ike, "rekey-time"));
251 }
252 if (ike->get(ike, "reauth-time"))
253 {
254 printf(", reauth in %ss", ike->get(ike, "reauth-time"));
255 }
256 if (ike->get(ike, "life-time"))
257 {
258 printf(", expires in %ss", ike->get(ike, "life-time"));
259 }
260 printf("\n");
261 }
262
263 if (ike->get(ike, "tasks-queued"))
264 {
265 printf(" queued: %s\n", ike->get(ike, "tasks-queued"));
266 }
267 if (ike->get(ike, "tasks-active"))
268 {
269 printf(" active: %s\n", ike->get(ike, "tasks-active"));
270 }
271 if (ike->get(ike, "tasks-passive"))
272 {
273 printf(" passive: %s\n", ike->get(ike, "tasks-passive"));
274 }
275
276 return vici_parse_cb(res, child_sas, NULL, NULL, ike);
277 }
278 return 0;
279 }
280
281 CALLBACK(ike_sas, int,
282 void *null, vici_res_t *res, char *name)
283 {
284 hashtable_t *ike;
285 int ret;
286
287 ike = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1);
288 ike->put(ike, "name", strdup(name));
289 ret = vici_parse_cb(res, ike_sa, sa_values, sa_list, ike);
290 free_hashtable(ike);
291 return ret;
292 }
293
294 CALLBACK(list_cb, void,
295 command_format_options_t *format, char *name, vici_res_t *res)
296 {
297 char buf[256];
298
299 if (*format & COMMAND_FORMAT_RAW)
300 {
301 snprintf(buf, sizeof(buf), "%s event", name);
302 vici_dump(res, buf, *format & COMMAND_FORMAT_PRETTY,
303 stdout);
304 }
305 else
306 {
307 if (vici_parse_cb(res, ike_sas, NULL, NULL, NULL) != 0)
308 {
309 fprintf(stderr, "parsing SA event failed: %s\n", strerror(errno));
310 }
311 }
312 }
313
314 static int list_sas(vici_conn_t *conn)
315 {
316 vici_req_t *req;
317 vici_res_t *res;
318 bool noblock = FALSE;
319 command_format_options_t format = COMMAND_FORMAT_NONE;
320 char *arg, *ike = NULL;
321 int ike_id = 0, ret;
322
323 while (TRUE)
324 {
325 switch (command_getopt(&arg))
326 {
327 case 'h':
328 return command_usage(NULL);
329 case 'i':
330 ike = arg;
331 continue;
332 case 'I':
333 ike_id = atoi(arg);
334 continue;
335 case 'n':
336 noblock = TRUE;
337 continue;
338 case 'P':
339 format |= COMMAND_FORMAT_PRETTY;
340 /* fall through to raw */
341 case 'r':
342 format |= COMMAND_FORMAT_RAW;
343 continue;
344 case EOF:
345 break;
346 default:
347 return command_usage("invalid --list-sas option");
348 }
349 break;
350 }
351 if (vici_register(conn, "list-sa", list_cb, &format) != 0)
352 {
353 ret = errno;
354 fprintf(stderr, "registering for SAs failed: %s\n", strerror(errno));
355 return ret;
356 }
357 req = vici_begin("list-sas");
358 if (ike)
359 {
360 vici_add_key_valuef(req, "ike", "%s", ike);
361 }
362 if (ike_id)
363 {
364 vici_add_key_valuef(req, "ike-id", "%d", ike_id);
365 }
366 if (noblock)
367 {
368 vici_add_key_valuef(req, "noblock", "yes");
369 }
370 res = vici_submit(req, conn);
371 if (!res)
372 {
373 ret = errno;
374 fprintf(stderr, "list-sas request failed: %s\n", strerror(errno));
375 return ret;
376 }
377 if (format & COMMAND_FORMAT_RAW)
378 {
379 vici_dump(res, "list-sas reply", format & COMMAND_FORMAT_PRETTY,
380 stdout);
381 }
382 vici_free_res(res);
383 return 0;
384 }
385
386 static int monitor_sas(vici_conn_t *conn)
387 {
388 command_format_options_t format = COMMAND_FORMAT_NONE;
389 char *arg;
390
391 while (TRUE)
392 {
393 switch (command_getopt(&arg))
394 {
395 case 'h':
396 return command_usage(NULL);
397 case 'P':
398 format |= COMMAND_FORMAT_PRETTY;
399 /* fall through to raw */
400 case 'r':
401 format |= COMMAND_FORMAT_RAW;
402 continue;
403 case EOF:
404 break;
405 default:
406 return command_usage("invalid --monitor-sa option");
407 }
408 break;
409 }
410 if (vici_register(conn, "ike-updown", list_cb, &format) != 0)
411 {
412 fprintf(stderr, "registering for IKE_SAs failed: %s\n",
413 strerror(errno));
414 return errno;
415 }
416 if (vici_register(conn, "child-updown", list_cb, &format) != 0)
417 {
418 fprintf(stderr, "registering for CHILD_SAs failed: %s\n",
419 strerror(errno));
420 return errno;
421 }
422
423 wait_sigint();
424
425 fprintf(stderr, "disconnecting...\n");
426
427 return 0;
428 }
429
430 /**
431 * Register the command.
432 */
433 static void __attribute__ ((constructor))reg()
434 {
435 command_register((command_t) {
436 list_sas, 'l', "list-sas", "list currently active IKE_SAs",
437 {"[--raw|--pretty]"},
438 {
439 {"help", 'h', 0, "show usage information"},
440 {"ike", 'i', 1, "filter IKE_SAs by name"},
441 {"ike-id", 'I', 1, "filter IKE_SAs by unique identifier"},
442 {"noblock", 'n', 0, "don't wait for IKE_SAs in use"},
443 {"raw", 'r', 0, "dump raw response message"},
444 {"pretty", 'P', 0, "dump raw response message in pretty print"},
445 }
446 });
447 }
448
449 static void __attribute__ ((constructor))reg_monitor_sa()
450 {
451 command_register((command_t) {
452 monitor_sas, 'm', "monitor-sa", "monitor for IKE_SA and CHILD_SA changes",
453 {"[--raw|--pretty]"},
454 {
455 {"help", 'h', 0, "show usage information"},
456 {"raw", 'r', 0, "dump raw response message"},
457 {"pretty", 'P', 0, "dump raw response message in pretty print"},
458 }
459 });
460 }