]> git.proxmox.com Git - mirror_frr.git/blob - nhrpd/vici.c
Merge pull request #2858 from Jafaral/sphinx
[mirror_frr.git] / nhrpd / vici.c
1 /* strongSwan VICI protocol implementation for NHRP
2 * Copyright (c) 2014-2015 Timo Teräs
3 *
4 * This file is free software: you may copy, redistribute and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10 #include <string.h>
11 #include <sys/socket.h>
12 #include <sys/un.h>
13
14 #include "thread.h"
15 #include "zbuf.h"
16 #include "log.h"
17 #include "lib_errors.h"
18
19 #include "nhrpd.h"
20 #include "vici.h"
21 #include "nhrp_errors.h"
22
23 #define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
24
25 struct blob {
26 char *ptr;
27 int len;
28 };
29
30 static int blob_equal(const struct blob *b, const char *str)
31 {
32 if (!b || b->len != (int)strlen(str))
33 return 0;
34 return memcmp(b->ptr, str, b->len) == 0;
35 }
36
37 static int blob2buf(const struct blob *b, char *buf, size_t n)
38 {
39 if (!b || b->len >= (int)n)
40 return 0;
41 memcpy(buf, b->ptr, b->len);
42 buf[b->len] = 0;
43 return 1;
44 }
45
46 struct vici_conn {
47 struct thread *t_reconnect, *t_read, *t_write;
48 struct zbuf ibuf;
49 struct zbuf_queue obuf;
50 int fd;
51 uint8_t ibuf_data[VICI_MAX_MSGLEN];
52 };
53
54 struct vici_message_ctx {
55 const char *sections[8];
56 int nsections;
57 };
58
59 static int vici_reconnect(struct thread *t);
60 static void vici_submit_request(struct vici_conn *vici, const char *name, ...);
61
62 static void vici_zbuf_puts(struct zbuf *obuf, const char *str)
63 {
64 size_t len = strlen(str);
65 zbuf_put8(obuf, len);
66 zbuf_put(obuf, str, len);
67 }
68
69 static void vici_connection_error(struct vici_conn *vici)
70 {
71 nhrp_vc_reset();
72
73 THREAD_OFF(vici->t_read);
74 THREAD_OFF(vici->t_write);
75 zbuf_reset(&vici->ibuf);
76 zbufq_reset(&vici->obuf);
77
78 close(vici->fd);
79 vici->fd = -1;
80 thread_add_timer(master, vici_reconnect, vici, 2, &vici->t_reconnect);
81 }
82
83 static void vici_parse_message(struct vici_conn *vici, struct zbuf *msg,
84 void (*parser)(struct vici_message_ctx *ctx,
85 enum vici_type_t msgtype,
86 const struct blob *key,
87 const struct blob *val),
88 struct vici_message_ctx *ctx)
89 {
90 uint8_t *type;
91 struct blob key = {0};
92 struct blob val = {0};
93
94 while ((type = zbuf_may_pull(msg, uint8_t)) != NULL) {
95 switch (*type) {
96 case VICI_SECTION_START:
97 key.len = zbuf_get8(msg);
98 key.ptr = zbuf_pulln(msg, key.len);
99 debugf(NHRP_DEBUG_VICI, "VICI: Section start '%.*s'",
100 key.len, key.ptr);
101 parser(ctx, *type, &key, NULL);
102 ctx->nsections++;
103 break;
104 case VICI_SECTION_END:
105 debugf(NHRP_DEBUG_VICI, "VICI: Section end");
106 parser(ctx, *type, NULL, NULL);
107 ctx->nsections--;
108 break;
109 case VICI_KEY_VALUE:
110 key.len = zbuf_get8(msg);
111 key.ptr = zbuf_pulln(msg, key.len);
112 val.len = zbuf_get_be16(msg);
113 val.ptr = zbuf_pulln(msg, val.len);
114 debugf(NHRP_DEBUG_VICI, "VICI: Key '%.*s'='%.*s'",
115 key.len, key.ptr, val.len, val.ptr);
116 parser(ctx, *type, &key, &val);
117 break;
118 case VICI_LIST_START:
119 key.len = zbuf_get8(msg);
120 key.ptr = zbuf_pulln(msg, key.len);
121 debugf(NHRP_DEBUG_VICI, "VICI: List start '%.*s'",
122 key.len, key.ptr);
123 break;
124 case VICI_LIST_ITEM:
125 val.len = zbuf_get_be16(msg);
126 val.ptr = zbuf_pulln(msg, val.len);
127 debugf(NHRP_DEBUG_VICI, "VICI: List item: '%.*s'",
128 val.len, val.ptr);
129 parser(ctx, *type, &key, &val);
130 break;
131 case VICI_LIST_END:
132 debugf(NHRP_DEBUG_VICI, "VICI: List end");
133 break;
134 default:
135 debugf(NHRP_DEBUG_VICI,
136 "VICI: Unsupported message component type %d",
137 *type);
138 return;
139 }
140 }
141 }
142
143 struct handle_sa_ctx {
144 struct vici_message_ctx msgctx;
145 int event;
146 int child_ok;
147 int kill_ikesa;
148 uint32_t child_uniqueid, ike_uniqueid;
149 struct {
150 union sockunion host;
151 struct blob id, cert;
152 } local, remote;
153 };
154
155 static void parse_sa_message(struct vici_message_ctx *ctx,
156 enum vici_type_t msgtype, const struct blob *key,
157 const struct blob *val)
158 {
159 struct handle_sa_ctx *sactx =
160 container_of(ctx, struct handle_sa_ctx, msgctx);
161 struct nhrp_vc *vc;
162 char buf[512];
163
164 switch (msgtype) {
165 case VICI_SECTION_START:
166 if (ctx->nsections == 3) {
167 /* Begin of child-sa section, reset child vars */
168 sactx->child_uniqueid = 0;
169 sactx->child_ok = 0;
170 }
171 break;
172 case VICI_SECTION_END:
173 if (ctx->nsections == 3) {
174 /* End of child-sa section, update nhrp_vc */
175 int up = sactx->child_ok || sactx->event == 1;
176 if (up) {
177 vc = nhrp_vc_get(&sactx->local.host,
178 &sactx->remote.host, up);
179 if (vc) {
180 blob2buf(&sactx->local.id, vc->local.id,
181 sizeof(vc->local.id));
182 if (blob2buf(&sactx->local.cert,
183 (char *)vc->local.cert,
184 sizeof(vc->local.cert)))
185 vc->local.certlen =
186 sactx->local.cert.len;
187 blob2buf(&sactx->remote.id,
188 vc->remote.id,
189 sizeof(vc->remote.id));
190 if (blob2buf(&sactx->remote.cert,
191 (char *)vc->remote.cert,
192 sizeof(vc->remote.cert)))
193 vc->remote.certlen =
194 sactx->remote.cert.len;
195 sactx->kill_ikesa |=
196 nhrp_vc_ipsec_updown(
197 sactx->child_uniqueid,
198 vc);
199 }
200 } else {
201 nhrp_vc_ipsec_updown(sactx->child_uniqueid, 0);
202 }
203 }
204 break;
205 default:
206 if (!key)
207 break;
208
209 switch (key->ptr[0]) {
210 case 'l':
211 if (blob_equal(key, "local-host")
212 && ctx->nsections == 1) {
213 if (blob2buf(val, buf, sizeof(buf)))
214 if (str2sockunion(buf,
215 &sactx->local.host)
216 < 0)
217 flog_err(NHRP_ERR_SWAN,
218 "VICI: bad strongSwan local-host: %s",
219 buf);
220 } else if (blob_equal(key, "local-id")
221 && ctx->nsections == 1) {
222 sactx->local.id = *val;
223 } else if (blob_equal(key, "local-cert-data")
224 && ctx->nsections == 1) {
225 sactx->local.cert = *val;
226 }
227 break;
228 case 'r':
229 if (blob_equal(key, "remote-host")
230 && ctx->nsections == 1) {
231 if (blob2buf(val, buf, sizeof(buf)))
232 if (str2sockunion(buf,
233 &sactx->remote.host)
234 < 0)
235 flog_err(NHRP_ERR_SWAN,
236 "VICI: bad strongSwan remote-host: %s",
237 buf);
238 } else if (blob_equal(key, "remote-id")
239 && ctx->nsections == 1) {
240 sactx->remote.id = *val;
241 } else if (blob_equal(key, "remote-cert-data")
242 && ctx->nsections == 1) {
243 sactx->remote.cert = *val;
244 }
245 break;
246 case 'u':
247 if (blob_equal(key, "uniqueid")
248 && blob2buf(val, buf, sizeof(buf))) {
249 if (ctx->nsections == 3)
250 sactx->child_uniqueid =
251 strtoul(buf, NULL, 0);
252 else if (ctx->nsections == 1)
253 sactx->ike_uniqueid =
254 strtoul(buf, NULL, 0);
255 }
256 break;
257 case 's':
258 if (blob_equal(key, "state") && ctx->nsections == 3) {
259 sactx->child_ok =
260 (sactx->event == 0
261 && (blob_equal(val, "INSTALLED")
262 || blob_equal(val, "REKEYED")));
263 }
264 break;
265 }
266 break;
267 }
268 }
269
270 static void parse_cmd_response(struct vici_message_ctx *ctx,
271 enum vici_type_t msgtype, const struct blob *key,
272 const struct blob *val)
273 {
274 char buf[512];
275
276 switch (msgtype) {
277 case VICI_KEY_VALUE:
278 if (blob_equal(key, "errmsg")
279 && blob2buf(val, buf, sizeof(buf)))
280 flog_err(NHRP_ERR_SWAN, "VICI: strongSwan: %s", buf);
281 break;
282 default:
283 break;
284 }
285 }
286
287 static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event)
288 {
289 char buf[32];
290 struct handle_sa_ctx ctx = {
291 .event = event,
292 .msgctx.nsections = 0
293 };
294
295 vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx);
296
297 if (ctx.kill_ikesa && ctx.ike_uniqueid) {
298 debugf(NHRP_DEBUG_COMMON, "VICI: Deleting IKE_SA %u",
299 ctx.ike_uniqueid);
300 snprintf(buf, sizeof buf, "%u", ctx.ike_uniqueid);
301 vici_submit_request(vici, "terminate", VICI_KEY_VALUE, "ike-id",
302 strlen(buf), buf, VICI_END);
303 }
304 }
305
306 static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg)
307 {
308 uint32_t msglen;
309 uint8_t msgtype;
310 struct blob name;
311 struct vici_message_ctx ctx = { .nsections = 0 };
312
313 msglen = zbuf_get_be32(msg);
314 msgtype = zbuf_get8(msg);
315 debugf(NHRP_DEBUG_VICI, "VICI: Message %d, %d bytes", msgtype, msglen);
316
317 switch (msgtype) {
318 case VICI_EVENT:
319 name.len = zbuf_get8(msg);
320 name.ptr = zbuf_pulln(msg, name.len);
321
322 debugf(NHRP_DEBUG_VICI, "VICI: Event '%.*s'", name.len,
323 name.ptr);
324 if (blob_equal(&name, "list-sa")
325 || blob_equal(&name, "child-updown")
326 || blob_equal(&name, "child-rekey"))
327 vici_recv_sa(vici, msg, 0);
328 else if (blob_equal(&name, "child-state-installed")
329 || blob_equal(&name, "child-state-rekeyed"))
330 vici_recv_sa(vici, msg, 1);
331 else if (blob_equal(&name, "child-state-destroying"))
332 vici_recv_sa(vici, msg, 2);
333 break;
334 case VICI_CMD_RESPONSE:
335 vici_parse_message(vici, msg, parse_cmd_response, &ctx);
336 break;
337 case VICI_EVENT_UNKNOWN:
338 case VICI_CMD_UNKNOWN:
339 flog_err(NHRP_ERR_SWAN,
340 "VICI: StrongSwan does not support mandatory events (unpatched?)");
341 break;
342 case VICI_EVENT_CONFIRM:
343 break;
344 default:
345 zlog_notice("VICI: Unrecognized message type %d", msgtype);
346 break;
347 }
348 }
349
350 static int vici_read(struct thread *t)
351 {
352 struct vici_conn *vici = THREAD_ARG(t);
353 struct zbuf *ibuf = &vici->ibuf;
354 struct zbuf pktbuf;
355
356 vici->t_read = NULL;
357 if (zbuf_read(ibuf, vici->fd, (size_t)-1) < 0) {
358 vici_connection_error(vici);
359 return 0;
360 }
361
362 /* Process all messages in buffer */
363 do {
364 uint32_t *hdrlen = zbuf_may_pull(ibuf, uint32_t);
365 if (!hdrlen)
366 break;
367 if (!zbuf_may_pulln(ibuf, ntohl(*hdrlen))) {
368 zbuf_reset_head(ibuf, hdrlen);
369 break;
370 }
371
372 /* Handle packet */
373 zbuf_init(&pktbuf, hdrlen, htonl(*hdrlen) + 4,
374 htonl(*hdrlen) + 4);
375 vici_recv_message(vici, &pktbuf);
376 } while (1);
377
378 thread_add_read(master, vici_read, vici, vici->fd, &vici->t_read);
379 return 0;
380 }
381
382 static int vici_write(struct thread *t)
383 {
384 struct vici_conn *vici = THREAD_ARG(t);
385 int r;
386
387 vici->t_write = NULL;
388 r = zbufq_write(&vici->obuf, vici->fd);
389 if (r > 0) {
390 thread_add_write(master, vici_write, vici, vici->fd,
391 &vici->t_write);
392 } else if (r < 0) {
393 vici_connection_error(vici);
394 }
395
396 return 0;
397 }
398
399 static void vici_submit(struct vici_conn *vici, struct zbuf *obuf)
400 {
401 if (vici->fd < 0) {
402 zbuf_free(obuf);
403 return;
404 }
405
406 zbufq_queue(&vici->obuf, obuf);
407 thread_add_write(master, vici_write, vici, vici->fd, &vici->t_write);
408 }
409
410 static void vici_submit_request(struct vici_conn *vici, const char *name, ...)
411 {
412 struct zbuf *obuf;
413 uint32_t *hdrlen;
414 va_list va;
415 size_t len;
416 int type;
417
418 obuf = zbuf_alloc(256);
419 if (!obuf)
420 return;
421
422 hdrlen = zbuf_push(obuf, uint32_t);
423 zbuf_put8(obuf, VICI_CMD_REQUEST);
424 vici_zbuf_puts(obuf, name);
425
426 va_start(va, name);
427 for (type = va_arg(va, int); type != VICI_END; type = va_arg(va, int)) {
428 zbuf_put8(obuf, type);
429 switch (type) {
430 case VICI_KEY_VALUE:
431 vici_zbuf_puts(obuf, va_arg(va, const char *));
432 len = va_arg(va, size_t);
433 zbuf_put_be16(obuf, len);
434 zbuf_put(obuf, va_arg(va, void *), len);
435 break;
436 default:
437 break;
438 }
439 }
440 va_end(va);
441 *hdrlen = htonl(zbuf_used(obuf) - 4);
442 vici_submit(vici, obuf);
443 }
444
445 static void vici_register_event(struct vici_conn *vici, const char *name)
446 {
447 struct zbuf *obuf;
448 uint32_t *hdrlen;
449 uint8_t namelen;
450
451 namelen = strlen(name);
452 obuf = zbuf_alloc(4 + 1 + 1 + namelen);
453 if (!obuf)
454 return;
455
456 hdrlen = zbuf_push(obuf, uint32_t);
457 zbuf_put8(obuf, VICI_EVENT_REGISTER);
458 zbuf_put8(obuf, namelen);
459 zbuf_put(obuf, name, namelen);
460 *hdrlen = htonl(zbuf_used(obuf) - 4);
461
462 vici_submit(vici, obuf);
463 }
464
465 static int vici_reconnect(struct thread *t)
466 {
467 struct vici_conn *vici = THREAD_ARG(t);
468 int fd;
469
470 vici->t_reconnect = NULL;
471 if (vici->fd >= 0)
472 return 0;
473
474 fd = sock_open_unix("/var/run/charon.vici");
475 if (fd < 0) {
476 debugf(NHRP_DEBUG_VICI,
477 "%s: failure connecting VICI socket: %s",
478 __PRETTY_FUNCTION__, strerror(errno));
479 thread_add_timer(master, vici_reconnect, vici, 2,
480 &vici->t_reconnect);
481 return 0;
482 }
483
484 debugf(NHRP_DEBUG_COMMON, "VICI: Connected");
485 vici->fd = fd;
486 thread_add_read(master, vici_read, vici, vici->fd, &vici->t_read);
487
488 /* Send event subscribtions */
489 // vici_register_event(vici, "child-updown");
490 // vici_register_event(vici, "child-rekey");
491 vici_register_event(vici, "child-state-installed");
492 vici_register_event(vici, "child-state-rekeyed");
493 vici_register_event(vici, "child-state-destroying");
494 vici_register_event(vici, "list-sa");
495 vici_submit_request(vici, "list-sas", VICI_END);
496
497 return 0;
498 }
499
500 static struct vici_conn vici_connection;
501
502 void vici_init(void)
503 {
504 struct vici_conn *vici = &vici_connection;
505
506 vici->fd = -1;
507 zbuf_init(&vici->ibuf, vici->ibuf_data, sizeof(vici->ibuf_data), 0);
508 zbufq_init(&vici->obuf);
509 thread_add_timer_msec(master, vici_reconnect, vici, 10,
510 &vici->t_reconnect);
511 }
512
513 void vici_terminate(void)
514 {
515 }
516
517 void vici_request_vc(const char *profile, union sockunion *src,
518 union sockunion *dst, int prio)
519 {
520 struct vici_conn *vici = &vici_connection;
521 char buf[2][SU_ADDRSTRLEN];
522
523 sockunion2str(src, buf[0], sizeof buf[0]);
524 sockunion2str(dst, buf[1], sizeof buf[1]);
525
526 vici_submit_request(vici, "initiate", VICI_KEY_VALUE, "child",
527 strlen(profile), profile, VICI_KEY_VALUE, "timeout",
528 (size_t)2, "-1", VICI_KEY_VALUE, "async", (size_t)1,
529 "1", VICI_KEY_VALUE, "init-limits", (size_t)1,
530 prio ? "0" : "1", VICI_KEY_VALUE, "my-host",
531 strlen(buf[0]), buf[0], VICI_KEY_VALUE,
532 "other-host", strlen(buf[1]), buf[1], VICI_END);
533 }
534
535 int sock_open_unix(const char *path)
536 {
537 int ret, fd;
538 struct sockaddr_un addr;
539
540 fd = socket(AF_UNIX, SOCK_STREAM, 0);
541 if (fd < 0)
542 return -1;
543
544 memset(&addr, 0, sizeof(struct sockaddr_un));
545 addr.sun_family = AF_UNIX;
546 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
547
548 ret = connect(fd, (struct sockaddr *)&addr,
549 sizeof(addr.sun_family) + strlen(addr.sun_path));
550 if (ret < 0) {
551 close(fd);
552 return -1;
553 }
554
555 ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
556 if (ret < 0) {
557 close(fd);
558 return -1;
559 }
560
561 return fd;
562 }