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