blob: aab9ec642f77e5dd7e5ef74685215151bd0bed4f [file] [log] [blame]
/* NHRP event manager
* Copyright (c) 2014-2015 Timo Teräs
*
* This file is free software: you may copy, redistribute and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*/
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "thread.h"
#include "zbuf.h"
#include "log.h"
#include "nhrpd.h"
const char *nhrp_event_socket_path;
struct nhrp_reqid_pool nhrp_event_reqid;
struct event_manager {
struct thread *t_reconnect, *t_read, *t_write;
struct zbuf ibuf;
struct zbuf_queue obuf;
int fd;
uint8_t ibuf_data[4*1024];
};
static int evmgr_reconnect(struct thread *t);
static void evmgr_connection_error(struct event_manager *evmgr)
{
THREAD_OFF(evmgr->t_read);
THREAD_OFF(evmgr->t_write);
zbuf_reset(&evmgr->ibuf);
zbufq_reset(&evmgr->obuf);
if (evmgr->fd >= 0)
close(evmgr->fd);
evmgr->fd = -1;
if (nhrp_event_socket_path)
THREAD_TIMER_MSEC_ON(master, evmgr->t_reconnect, evmgr_reconnect,
evmgr, 10);
}
static void evmgr_recv_message(struct event_manager *evmgr, struct zbuf *zb)
{
struct zbuf zl;
uint32_t eventid = 0;
size_t len;
char buf[256], result[64] = "";
while (zbuf_may_pull_until(zb, "\n", &zl)) {
len = zbuf_used(&zl) - 1;
if (len >= sizeof(buf)-1)
continue;
memcpy(buf, zbuf_pulln(&zl, len), len);
buf[len] = 0;
debugf(NHRP_DEBUG_EVENT, "evmgr: msg: %s", buf);
sscanf(buf, "eventid=%d", &eventid);
sscanf(buf, "result=%63s", result);
}
debugf(NHRP_DEBUG_EVENT, "evmgr: received: eventid=%d result=%s", eventid, result);
if (eventid && result[0]) {
struct nhrp_reqid *r = nhrp_reqid_lookup(&nhrp_event_reqid, eventid);
if (r) r->cb(r, result);
}
}
static int evmgr_read(struct thread *t)
{
struct event_manager *evmgr = THREAD_ARG(t);
struct zbuf *ibuf = &evmgr->ibuf;
struct zbuf msg;
evmgr->t_read = NULL;
if (zbuf_read(ibuf, evmgr->fd, (size_t) -1) < 0) {
evmgr_connection_error(evmgr);
return 0;
}
/* Process all messages in buffer */
while (zbuf_may_pull_until(ibuf, "\n\n", &msg))
evmgr_recv_message(evmgr, &msg);
THREAD_READ_ON(master, evmgr->t_read, evmgr_read, evmgr, evmgr->fd);
return 0;
}
static int evmgr_write(struct thread *t)
{
struct event_manager *evmgr = THREAD_ARG(t);
int r;
evmgr->t_write = NULL;
r = zbufq_write(&evmgr->obuf, evmgr->fd);
if (r > 0) {
THREAD_WRITE_ON(master, evmgr->t_write, evmgr_write, evmgr, evmgr->fd);
} else if (r < 0) {
evmgr_connection_error(evmgr);
}
return 0;
}
static void evmgr_hexdump(struct zbuf *zb, const uint8_t *val, size_t vallen)
{
static const char xd[] = "0123456789abcdef";
size_t i;
char *ptr;
ptr = zbuf_pushn(zb, 2*vallen);
if (!ptr) return;
for (i = 0; i < vallen; i++) {
uint8_t b = val[i];
*(ptr++) = xd[b >> 4];
*(ptr++) = xd[b & 0xf];
}
}
static void evmgr_put(struct zbuf *zb, const char *fmt, ...)
{
const char *pos, *nxt, *str;
const uint8_t *bin;
const union sockunion *su;
int len;
va_list va;
va_start(va, fmt);
for (pos = fmt; (nxt = strchr(pos, '%')) != NULL; pos = nxt + 2) {
zbuf_put(zb, pos, nxt-pos);
switch (nxt[1]) {
case '%':
zbuf_put8(zb, '%');
break;
case 'u':
zb->tail += snprintf((char *) zb->tail, zbuf_tailroom(zb), "%u", va_arg(va, uint32_t));
break;
case 's':
str = va_arg(va, const char *);
zbuf_put(zb, str, strlen(str));
break;
case 'U':
su = va_arg(va, const union sockunion *);
if (sockunion2str(su, (char *) zb->tail, zbuf_tailroom(zb)))
zb->tail += strlen((char *) zb->tail);
else
zbuf_set_werror(zb);
break;
case 'H':
bin = va_arg(va, const uint8_t *);
len = va_arg(va, int);
evmgr_hexdump(zb, bin, len);
break;
}
}
va_end(va);
zbuf_put(zb, pos, strlen(pos));
}
static void evmgr_submit(struct event_manager *evmgr, struct zbuf *obuf)
{
if (obuf->error) {
zbuf_free(obuf);
return;
}
zbuf_put(obuf, "\n", 1);
zbufq_queue(&evmgr->obuf, obuf);
if (evmgr->fd >= 0)
THREAD_WRITE_ON(master, evmgr->t_write, evmgr_write, evmgr, evmgr->fd);
}
static int evmgr_reconnect(struct thread *t)
{
struct event_manager *evmgr = THREAD_ARG(t);
int fd;
evmgr->t_reconnect = NULL;
if (evmgr->fd >= 0 || !nhrp_event_socket_path) return 0;
fd = sock_open_unix(nhrp_event_socket_path);
if (fd < 0) {
zlog_warn("%s: failure connecting nhrp-event socket: %s",
__PRETTY_FUNCTION__, strerror(errno));
zbufq_reset(&evmgr->obuf);
THREAD_TIMER_ON(master, evmgr->t_reconnect, evmgr_reconnect, evmgr, 10);
return 0;
}
zlog_info("Connected to Event Manager");
evmgr->fd = fd;
THREAD_READ_ON(master, evmgr->t_read, evmgr_read, evmgr, evmgr->fd);
return 0;
}
static struct event_manager evmgr_connection;
void evmgr_init(void)
{
struct event_manager *evmgr = &evmgr_connection;
evmgr->fd = -1;
zbuf_init(&evmgr->ibuf, evmgr->ibuf_data, sizeof(evmgr->ibuf_data), 0);
zbufq_init(&evmgr->obuf);
THREAD_TIMER_MSEC_ON(master, evmgr->t_reconnect, evmgr_reconnect, evmgr, 10);
}
void evmgr_set_socket(const char *socket)
{
if (nhrp_event_socket_path)
free((char *) nhrp_event_socket_path);
nhrp_event_socket_path = strdup(socket);
evmgr_connection_error(&evmgr_connection);
}
void evmgr_terminate(void)
{
}
void evmgr_notify(const char *name, struct nhrp_cache *c, void (*cb)(struct nhrp_reqid *, void *))
{
struct event_manager *evmgr = &evmgr_connection;
struct nhrp_vc *vc;
struct nhrp_interface *nifp = c->ifp->info;
struct zbuf *zb;
afi_t afi = family2afi(sockunion_family(&c->remote_addr));
if (!nhrp_event_socket_path) {
cb(&c->eventid, (void*) "accept");
return;
}
debugf(NHRP_DEBUG_EVENT, "evmgr: sending event %s", name);
vc = c->new.peer ? c->new.peer->vc : NULL;
zb = zbuf_alloc(1024 + (vc ? (vc->local.certlen + vc->remote.certlen) * 2 : 0));
if (cb) {
nhrp_reqid_free(&nhrp_event_reqid, &c->eventid);
evmgr_put(zb,
"eventid=%u\n",
nhrp_reqid_alloc(&nhrp_event_reqid, &c->eventid, cb));
}
evmgr_put(zb,
"event=%s\n"
"type=%s\n"
"old_type=%s\n"
"num_nhs=%u\n"
"interface=%s\n"
"local_addr=%U\n",
name,
nhrp_cache_type_str[c->new.type],
nhrp_cache_type_str[c->cur.type],
(unsigned int) nhrp_cache_counts[NHRP_CACHE_NHS],
c->ifp->name,
&nifp->afi[afi].addr);
if (vc) {
evmgr_put(zb,
"vc_initiated=%s\n"
"local_nbma=%U\n"
"local_cert=%H\n"
"remote_addr=%U\n"
"remote_nbma=%U\n"
"remote_cert=%H\n",
c->new.peer->requested ? "yes" : "no",
&vc->local.nbma,
vc->local.cert, vc->local.certlen,
&c->remote_addr, &vc->remote.nbma,
vc->remote.cert, vc->remote.certlen);
}
evmgr_submit(evmgr, zb);
}