source: src/router/quagga/nhrpd/vici.c @ 31640

Last change on this file since 31640 was 31640, checked in by brainslayer, 4 months ago

update quagga

File size: 12.2 KB
Line 
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
23struct blob {
24        char *ptr;
25        int len;
26};
27
28static 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
34static 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
42struct 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
50struct vici_message_ctx {
51        const char *sections[8];
52        int nsections;
53};
54
55static int vici_reconnect(struct thread *t);
56static void vici_submit_request(struct vici_conn *vici, const char *name, ...);
57
58static 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
65static 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
79static 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
131struct 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
143static 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
223static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event)
224{
225        char buf[32];
226        struct handle_sa_ctx ctx = {
227                .event = event,
228        };
229
230        vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx);
231
232        if (ctx.kill_ikesa && ctx.ike_uniqueid) {
233                debugf(NHRP_DEBUG_COMMON, "VICI: Deleting IKE_SA %u", ctx.ike_uniqueid);
234                snprintf(buf, sizeof buf, "%u", ctx.ike_uniqueid);
235                vici_submit_request(
236                        vici, "terminate",
237                        VICI_KEY_VALUE, "ike-id", strlen(buf), buf,
238                        VICI_END);
239        }
240}
241
242static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg)
243{
244        uint32_t msglen;
245        uint8_t msgtype;
246        struct blob name;
247
248        msglen = zbuf_get_be32(msg);
249        msgtype = zbuf_get8(msg);
250        debugf(NHRP_DEBUG_VICI, "VICI: Message %d, %d bytes", msgtype, msglen);
251
252        switch (msgtype) {
253        case VICI_EVENT:
254                name.len = zbuf_get8(msg);
255                name.ptr = zbuf_pulln(msg, name.len);
256
257                debugf(NHRP_DEBUG_VICI, "VICI: Event '%.*s'", name.len, name.ptr);
258                if (blob_equal(&name, "list-sa") ||
259                    blob_equal(&name, "child-updown") ||
260                    blob_equal(&name, "child-rekey"))
261                        vici_recv_sa(vici, msg, 0);
262                else if (blob_equal(&name, "child-state-installed") ||
263                         blob_equal(&name, "child-state-rekeyed"))
264                        vici_recv_sa(vici, msg, 1);
265                else if (blob_equal(&name, "child-state-destroying"))
266                        vici_recv_sa(vici, msg, 2);
267                break;
268        case VICI_EVENT_UNKNOWN:
269                zlog_err("VICI: StrongSwan does not support mandatory events (unpatched?)");
270                break;
271        case VICI_EVENT_CONFIRM:
272        case VICI_CMD_RESPONSE:
273                break;
274        default:
275                zlog_notice("VICI: Unrecognized message type %d", msgtype);
276                break;
277        }
278}
279
280static int vici_read(struct thread *t)
281{
282        struct vici_conn *vici = THREAD_ARG(t);
283        struct zbuf *ibuf = &vici->ibuf;
284        struct zbuf pktbuf;
285
286        vici->t_read = NULL;
287        if (zbuf_read(ibuf, vici->fd, (size_t) -1) < 0) {
288                vici_connection_error(vici);
289                return 0;
290        }
291
292        /* Process all messages in buffer */
293        do {
294                uint32_t *hdrlen = zbuf_may_pull(ibuf, uint32_t);
295                if (!hdrlen)
296                        break;
297                if (!zbuf_may_pulln(ibuf, ntohl(*hdrlen))) {
298                        zbuf_reset_head(ibuf, hdrlen);
299                        break;
300                }
301
302                /* Handle packet */
303                zbuf_init(&pktbuf, hdrlen, htonl(*hdrlen)+4, htonl(*hdrlen)+4);
304                vici_recv_message(vici, &pktbuf);
305        } while (1);
306
307        THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd);
308        return 0;
309}
310
311static int vici_write(struct thread *t)
312{
313        struct vici_conn *vici = THREAD_ARG(t);
314        int r;
315
316        vici->t_write = NULL;
317        r = zbufq_write(&vici->obuf, vici->fd);
318        if (r > 0) {
319                THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd);
320        } else if (r < 0) {
321                vici_connection_error(vici);
322        }
323
324        return 0;
325}
326
327static void vici_submit(struct vici_conn *vici, struct zbuf *obuf)
328{
329        if (vici->fd < 0) {
330                zbuf_free(obuf);
331                return;
332        }
333
334        zbufq_queue(&vici->obuf, obuf);
335        THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd);
336}
337
338static void vici_submit_request(struct vici_conn *vici, const char *name, ...)
339{
340        struct zbuf *obuf;
341        uint32_t *hdrlen;
342        va_list va;
343        size_t len;
344        int type;
345
346        obuf = zbuf_alloc(256);
347        if (!obuf) return;
348
349        hdrlen = zbuf_push(obuf, uint32_t);
350        zbuf_put8(obuf, VICI_CMD_REQUEST);
351        vici_zbuf_puts(obuf, name);
352
353        va_start(va, name);
354        for (type = va_arg(va, int); type != VICI_END; type = va_arg(va, int)) {
355                zbuf_put8(obuf, type);
356                switch (type) {
357                case VICI_KEY_VALUE:
358                        vici_zbuf_puts(obuf, va_arg(va, const char *));
359                        len = va_arg(va, size_t);
360                        zbuf_put_be16(obuf, len);
361                        zbuf_put(obuf, va_arg(va, void *), len);
362                        break;
363                case VICI_END:
364                        break;
365                default:
366                        break;
367                }
368        }
369        va_end(va);
370        *hdrlen = htonl(zbuf_used(obuf) - 4);
371        vici_submit(vici, obuf);
372}
373
374static void vici_register_event(struct vici_conn *vici, const char *name)
375{
376        struct zbuf *obuf;
377        uint32_t *hdrlen;
378        uint8_t namelen;
379
380        namelen = strlen(name);
381        obuf = zbuf_alloc(4 + 1 + 1 + namelen);
382        if (!obuf) return;
383
384        hdrlen = zbuf_push(obuf, uint32_t);
385        zbuf_put8(obuf, VICI_EVENT_REGISTER);
386        zbuf_put8(obuf, namelen);
387        zbuf_put(obuf, name, namelen);
388        *hdrlen = htonl(zbuf_used(obuf) - 4);
389
390        vici_submit(vici, obuf);
391}
392
393static int vici_reconnect(struct thread *t)
394{
395        struct vici_conn *vici = THREAD_ARG(t);
396        int fd;
397
398        vici->t_reconnect = NULL;
399        if (vici->fd >= 0) return 0;
400
401        fd = sock_open_unix("/var/run/charon.vici");
402        if (fd < 0) {
403                zlog_warn("%s: failure connecting VICI socket: %s",
404                        __PRETTY_FUNCTION__, strerror(errno));
405                THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2);
406                return 0;
407        }
408
409        debugf(NHRP_DEBUG_COMMON, "VICI: Connected");
410        vici->fd = fd;
411        THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd);
412
413        /* Send event subscribtions */
414        //vici_register_event(vici, "child-updown");
415        //vici_register_event(vici, "child-rekey");
416        vici_register_event(vici, "child-state-installed");
417        vici_register_event(vici, "child-state-rekeyed");
418        vici_register_event(vici, "child-state-destroying");
419        vici_register_event(vici, "list-sa");
420        vici_submit_request(vici, "list-sas", VICI_END);
421
422        return 0;
423}
424
425static struct vici_conn vici_connection;
426
427void vici_init(void)
428{
429        struct vici_conn *vici = &vici_connection;
430
431        vici->fd = -1;
432        zbuf_init(&vici->ibuf, vici->ibuf_data, sizeof(vici->ibuf_data), 0);
433        zbufq_init(&vici->obuf);
434        THREAD_TIMER_MSEC_ON(master, vici->t_reconnect, vici_reconnect, vici, 10);
435}
436
437void vici_terminate(void)
438{
439}
440
441void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio)
442{
443        struct vici_conn *vici = &vici_connection;
444        char buf[2][SU_ADDRSTRLEN];
445
446        sockunion2str(src, buf[0], sizeof buf[0]);
447        sockunion2str(dst, buf[1], sizeof buf[1]);
448
449        vici_submit_request(
450                vici, "initiate",
451                VICI_KEY_VALUE, "child", strlen(profile), profile,
452                VICI_KEY_VALUE, "timeout", 2, "-1",
453                VICI_KEY_VALUE, "async", 1, "1",
454                VICI_KEY_VALUE, "init-limits", 1, prio ? "0" : "1",
455                VICI_KEY_VALUE, "my-host", strlen(buf[0]), buf[0],
456                VICI_KEY_VALUE, "other-host", strlen(buf[1]), buf[1],
457                VICI_END);
458}
459
460int sock_open_unix(const char *path)
461{
462        int ret, fd;
463        struct sockaddr_un addr;
464
465        fd = socket(AF_UNIX, SOCK_STREAM, 0);
466        if (fd < 0)
467                return -1;
468
469        memset(&addr, 0, sizeof (struct sockaddr_un));
470        addr.sun_family = AF_UNIX;
471        strncpy(addr.sun_path, path, strlen (path));
472
473        ret = connect(fd, (struct sockaddr *) &addr, sizeof(addr.sun_family) + strlen(addr.sun_path));
474        if (ret < 0) {
475                close(fd);
476                return -1;
477        }
478
479        fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
480
481        return fd;
482}
Note: See TracBrowser for help on using the repository browser.