1 /* strongSwan VICI protocol implementation for NHRP
2 * Copyright (c) 2014-2015 Timo Teräs
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.
11 #include <sys/socket.h>
21 #define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
28 static int blob_equal(const struct blob
*b
, const char *str
)
30 if (b
->len
!= (int) strlen(str
)) return 0;
31 return memcmp(b
->ptr
, str
, b
->len
) == 0;
34 static int blob2buf(const struct blob
*b
, char *buf
, size_t n
)
36 if (b
->len
>= (int) n
) return 0;
37 memcpy(buf
, b
->ptr
, b
->len
);
43 struct thread
*t_reconnect
, *t_read
, *t_write
;
45 struct zbuf_queue obuf
;
47 uint8_t ibuf_data
[VICI_MAX_MSGLEN
];
50 struct vici_message_ctx
{
51 const char *sections
[8];
55 static int vici_reconnect(struct thread
*t
);
56 static void vici_submit_request(struct vici_conn
*vici
, const char *name
, ...);
58 static void vici_zbuf_puts(struct zbuf
*obuf
, const char *str
)
60 size_t len
= strlen(str
);
62 zbuf_put(obuf
, str
, len
);
65 static void vici_connection_error(struct vici_conn
*vici
)
69 THREAD_OFF(vici
->t_read
);
70 THREAD_OFF(vici
->t_write
);
71 zbuf_reset(&vici
->ibuf
);
72 zbufq_reset(&vici
->obuf
);
76 THREAD_TIMER_ON(master
, vici
->t_reconnect
, vici_reconnect
, vici
, 2);
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
)
88 while ((type
= zbuf_may_pull(msg
, uint8_t)) != NULL
) {
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
);
97 case VICI_SECTION_END
:
98 debugf(NHRP_DEBUG_VICI
, "VICI: Section end");
99 parser(ctx
, *type
, NULL
, NULL
);
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
);
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
);
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
);
122 debugf(NHRP_DEBUG_VICI
, "VICI: List end");
125 debugf(NHRP_DEBUG_VICI
, "VICI: Unsupported message component type %d", *type
);
131 struct handle_sa_ctx
{
132 struct vici_message_ctx msgctx
;
136 uint32_t child_uniqueid
, ike_uniqueid
;
138 union sockunion host
;
139 struct blob id
, cert
;
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
)
148 struct handle_sa_ctx
*sactx
= container_of(ctx
, struct handle_sa_ctx
, msgctx
);
153 case VICI_SECTION_START
:
154 if (ctx
->nsections
== 3) {
155 /* Begin of child-sa section, reset child vars */
156 sactx
->child_uniqueid
= 0;
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;
165 vc
= nhrp_vc_get(&sactx
->local
.host
, &sactx
->remote
.host
, up
);
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
);
176 nhrp_vc_ipsec_updown(sactx
->child_uniqueid
, 0);
181 switch (key
->ptr
[0]) {
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
;
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
;
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);
211 if (blob_equal(key
, "state") && ctx
->nsections
== 3) {
213 (sactx
->event
== 0 &&
214 (blob_equal(val
, "INSTALLED") ||
215 blob_equal(val
, "REKEYED")));
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
)
232 if (blob_equal(key
, "errmsg") && blob2buf(val
, buf
, sizeof(buf
)))
233 zlog_err("VICI: strongSwan: %s", buf
);
240 static void vici_recv_sa(struct vici_conn
*vici
, struct zbuf
*msg
, int event
)
243 struct handle_sa_ctx ctx
= {
247 vici_parse_message(vici
, msg
, parse_sa_message
, &ctx
.msgctx
);
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
);
254 VICI_KEY_VALUE
, "ike-id", strlen(buf
), buf
,
259 static void vici_recv_message(struct vici_conn
*vici
, struct zbuf
*msg
)
265 msglen
= zbuf_get_be32(msg
);
266 msgtype
= zbuf_get8(msg
);
267 debugf(NHRP_DEBUG_VICI
, "VICI: Message %d, %d bytes", msgtype
, msglen
);
271 name
.len
= zbuf_get8(msg
);
272 name
.ptr
= zbuf_pulln(msg
, name
.len
);
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);
285 case VICI_CMD_RESPONSE
:
286 vici_parse_message(vici
, msg
, parse_cmd_response
, 0);
288 case VICI_EVENT_UNKNOWN
:
289 case VICI_CMD_UNKNOWN
:
290 zlog_err("VICI: StrongSwan does not support mandatory events (unpatched?)");
292 case VICI_EVENT_CONFIRM
:
295 zlog_notice("VICI: Unrecognized message type %d", msgtype
);
300 static int vici_read(struct thread
*t
)
302 struct vici_conn
*vici
= THREAD_ARG(t
);
303 struct zbuf
*ibuf
= &vici
->ibuf
;
307 if (zbuf_read(ibuf
, vici
->fd
, (size_t) -1) < 0) {
308 vici_connection_error(vici
);
312 /* Process all messages in buffer */
314 uint32_t *hdrlen
= zbuf_may_pull(ibuf
, uint32_t);
317 if (!zbuf_may_pulln(ibuf
, ntohl(*hdrlen
))) {
318 zbuf_reset_head(ibuf
, hdrlen
);
323 zbuf_init(&pktbuf
, hdrlen
, htonl(*hdrlen
)+4, htonl(*hdrlen
)+4);
324 vici_recv_message(vici
, &pktbuf
);
327 THREAD_READ_ON(master
, vici
->t_read
, vici_read
, vici
, vici
->fd
);
331 static int vici_write(struct thread
*t
)
333 struct vici_conn
*vici
= THREAD_ARG(t
);
336 vici
->t_write
= NULL
;
337 r
= zbufq_write(&vici
->obuf
, vici
->fd
);
339 THREAD_WRITE_ON(master
, vici
->t_write
, vici_write
, vici
, vici
->fd
);
341 vici_connection_error(vici
);
347 static void vici_submit(struct vici_conn
*vici
, struct zbuf
*obuf
)
354 zbufq_queue(&vici
->obuf
, obuf
);
355 THREAD_WRITE_ON(master
, vici
->t_write
, vici_write
, vici
, vici
->fd
);
358 static void vici_submit_request(struct vici_conn
*vici
, const char *name
, ...)
366 obuf
= zbuf_alloc(256);
369 hdrlen
= zbuf_push(obuf
, uint32_t);
370 zbuf_put8(obuf
, VICI_CMD_REQUEST
);
371 vici_zbuf_puts(obuf
, name
);
374 for (type
= va_arg(va
, int); type
!= VICI_END
; type
= va_arg(va
, int)) {
375 zbuf_put8(obuf
, type
);
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
);
390 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
391 vici_submit(vici
, obuf
);
394 static void vici_register_event(struct vici_conn
*vici
, const char *name
)
400 namelen
= strlen(name
);
401 obuf
= zbuf_alloc(4 + 1 + 1 + namelen
);
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);
410 vici_submit(vici
, obuf
);
413 static int vici_reconnect(struct thread
*t
)
415 struct vici_conn
*vici
= THREAD_ARG(t
);
418 vici
->t_reconnect
= NULL
;
419 if (vici
->fd
>= 0) return 0;
421 fd
= sock_open_unix("/var/run/charon.vici");
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);
429 debugf(NHRP_DEBUG_COMMON
, "VICI: Connected");
431 THREAD_READ_ON(master
, vici
->t_read
, vici_read
, vici
, vici
->fd
);
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
);
445 static struct vici_conn vici_connection
;
449 struct vici_conn
*vici
= &vici_connection
;
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);
457 void vici_terminate(void)
461 void vici_request_vc(const char *profile
, union sockunion
*src
, union sockunion
*dst
, int prio
)
463 struct vici_conn
*vici
= &vici_connection
;
464 char buf
[2][SU_ADDRSTRLEN
];
466 sockunion2str(src
, buf
[0], sizeof buf
[0]);
467 sockunion2str(dst
, buf
[1], sizeof buf
[1]);
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],
480 int sock_open_unix(const char *path
)
483 struct sockaddr_un addr
;
485 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
489 memset(&addr
, 0, sizeof (struct sockaddr_un
));
490 addr
.sun_family
= AF_UNIX
;
491 strncpy(addr
.sun_path
, path
, strlen (path
));
493 ret
= connect(fd
, (struct sockaddr
*) &addr
, sizeof(addr
.sun_family
) + strlen(addr
.sun_path
));
499 fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
);