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