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