summaryrefslogtreecommitdiff
path: root/nhrp/admin.c
diff options
context:
space:
mode:
Diffstat (limited to 'nhrp/admin.c')
-rw-r--r--nhrp/admin.c609
1 files changed, 609 insertions, 0 deletions
diff --git a/nhrp/admin.c b/nhrp/admin.c
new file mode 100644
index 0000000..68a3e9e
--- /dev/null
+++ b/nhrp/admin.c
@@ -0,0 +1,609 @@
+/* admin.c - OpenNHRP administrative interface implementation
+ *
+ * Copyright (C) 2007-2009 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 or later as
+ * published by the Free Software Foundation.
+ *
+ * See http://www.gnu.org/ for details.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+
+#include "nhrp_common.h"
+#include "nhrp_peer.h"
+#include "nhrp_address.h"
+#include "nhrp_interface.h"
+
+static struct ev_io accept_io;
+
+struct admin_remote {
+ struct ev_timer timeout;
+ struct ev_io io;
+ int num_read;
+ char cmd[512];
+};
+
+static int parse_word(const char **bufptr, size_t len, char *word)
+{
+ const char *buf = *bufptr;
+ int i, pos = 0;
+
+ while (isspace(buf[pos]) && buf[pos] != '\n' && buf[pos])
+ pos++;
+
+ if (buf[pos] == '\n' || buf[pos] == 0)
+ return FALSE;
+
+ for (i = 0; i < len-1 && !isspace(buf[pos+i]); i++)
+ word[i] = buf[pos+i];
+ word[i] = 0;
+
+ *bufptr += i + pos;
+ return TRUE;
+}
+
+
+static void admin_write(void *ctx, const char *format, ...)
+{
+ struct admin_remote *rmt = (struct admin_remote *) ctx;
+ char msg[1024];
+ va_list ap;
+ size_t len;
+
+ va_start(ap, format);
+ len = vsnprintf(msg, sizeof(msg), format, ap);
+ va_end(ap);
+
+ if (write(rmt->io.fd, msg, len) != len) {
+ }
+}
+
+static void admin_free_remote(struct admin_remote *rm)
+{
+ int fd = rm->io.fd;
+
+ ev_io_stop(&rm->io);
+ ev_timer_stop(&rm->timeout);
+ shutdown(fd, SHUT_RDWR);
+ close(fd);
+ free(rm);
+}
+
+static int admin_show_peer(void *ctx, struct nhrp_peer *peer)
+{
+ char buf[512], tmp[32];
+ char *str;
+ size_t len = sizeof(buf);
+ int i = 0, rel;
+
+ if (peer->interface != NULL)
+ i += snprintf(&buf[i], len - i,
+ "Interface: %s\n",
+ peer->interface->name);
+
+ i += snprintf(&buf[i], len - i,
+ "Type: %s\n"
+ "Protocol-Address: %s/%d\n",
+ nhrp_peer_type[peer->type],
+ nhrp_address_format(&peer->protocol_address, sizeof(tmp), tmp),
+ peer->prefix_length);
+
+ if (peer->next_hop_address.type != PF_UNSPEC) {
+ switch (peer->type) {
+ case NHRP_PEER_TYPE_SHORTCUT_ROUTE:
+ case NHRP_PEER_TYPE_LOCAL_ROUTE:
+ str = "Next-hop-Address";
+ break;
+ case NHRP_PEER_TYPE_LOCAL_ADDR:
+ str = "Alias-Address";
+ break;
+ default:
+ str = "NBMA-Address";
+ break;
+ }
+ i += snprintf(&buf[i], len - i, "%s: %s\n",
+ str,
+ nhrp_address_format(&peer->next_hop_address,
+ sizeof(tmp), tmp));
+ }
+ if (peer->nbma_hostname) {
+ i += snprintf(&buf[i], len - i, "Hostname: %s\n",
+ peer->nbma_hostname);
+ }
+ if (peer->next_hop_nat_oa.type != PF_UNSPEC) {
+ i += snprintf(&buf[i], len - i, "NBMA-NAT-OA-Address: %s\n",
+ nhrp_address_format(&peer->next_hop_nat_oa,
+ sizeof(tmp), tmp));
+ }
+ if (peer->flags & (NHRP_PEER_FLAG_USED | NHRP_PEER_FLAG_UNIQUE |
+ NHRP_PEER_FLAG_UP | NHRP_PEER_FLAG_LOWER_UP)) {
+ i += snprintf(&buf[i], len - i, "Flags:");
+ if (peer->flags & NHRP_PEER_FLAG_UNIQUE)
+ i += snprintf(&buf[i], len - i, " unique");
+
+ if (peer->flags & NHRP_PEER_FLAG_USED)
+ i += snprintf(&buf[i], len - i, " used");
+ if (peer->flags & NHRP_PEER_FLAG_UP)
+ i += snprintf(&buf[i], len - i, " up");
+ else if (peer->flags & NHRP_PEER_FLAG_LOWER_UP)
+ i += snprintf(&buf[i], len - i, " lower-up");
+ i += snprintf(&buf[i], len - i, "\n");
+ }
+ if (peer->expire_time) {
+ rel = (int) (peer->expire_time - ev_now());
+ if (rel >= 0) {
+ i += snprintf(&buf[i], len - i, "Expires-In: %d:%02d\n",
+ rel / 60, rel % 60);
+ }
+ }
+
+ admin_write(ctx, "%s\n", buf);
+ return 0;
+}
+
+static void admin_free_selector(struct nhrp_peer_selector *sel)
+{
+ if (sel->hostname != NULL) {
+ free((void *) sel->hostname);
+ sel->hostname = NULL;
+ }
+}
+
+static int admin_parse_selector(void *ctx, const char *cmd,
+ struct nhrp_peer_selector *sel)
+{
+ char keyword[64], tmp[64];
+ struct nhrp_address address;
+ uint8_t prefix_length;
+
+ while (parse_word(&cmd, sizeof(keyword), keyword)) {
+ if (!parse_word(&cmd, sizeof(tmp), tmp)) {
+ admin_write(ctx,
+ "Status: failed\n"
+ "Reason: missing-argument\n"
+ "Near-Keyword: '%s'\n",
+ keyword);
+ return FALSE;
+ }
+
+ if (strcmp(keyword, "interface") == 0 ||
+ strcmp(keyword, "iface") == 0 ||
+ strcmp(keyword, "dev") == 0) {
+ if (sel->interface != NULL)
+ goto err_conflict;
+ sel->interface = nhrp_interface_get_by_name(tmp, FALSE);
+ if (sel->interface == NULL)
+ goto err_noiface;
+ continue;
+ } else if (strcmp(keyword, "host") == 0 ||
+ strcmp(keyword, "hostname") == 0) {
+ if (sel->hostname != NULL)
+ goto err_conflict;
+ sel->hostname = strdup(tmp);
+ continue;
+ }
+
+ if (!nhrp_address_parse(tmp, &address, &prefix_length)) {
+ admin_write(ctx,
+ "Status: failed\n"
+ "Reason: invalid-address\n"
+ "Near-Keyword: '%s'\n",
+ keyword);
+ return FALSE;
+ }
+
+ if (strcmp(keyword, "protocol") == 0) {
+ if (sel->protocol_address.type != AF_UNSPEC)
+ goto err_conflict;
+ sel->protocol_address = address;
+ sel->prefix_length = prefix_length;
+ } else if (strcmp(keyword, "nbma") == 0) {
+ if (sel->next_hop_address.type != AF_UNSPEC)
+ goto err_conflict;
+ sel->type_mask &= ~BIT(NHRP_PEER_TYPE_SHORTCUT_ROUTE);
+ sel->next_hop_address = address;
+ } else if (strcmp(keyword, "local-protocol") == 0) {
+ if (sel->interface != NULL)
+ goto err_conflict;
+ sel->interface = nhrp_interface_get_by_protocol(&address);
+ if (sel->interface == NULL)
+ goto err_noiface;
+ } else if (strcmp(keyword, "local-nbma") == 0) {
+ if (sel->interface != NULL)
+ goto err_conflict;
+ sel->interface = nhrp_interface_get_by_nbma(&address);
+ if (sel->interface == NULL)
+ goto err_noiface;
+ } else {
+ admin_write(ctx,
+ "Status: failed\n"
+ "Reason: syntax-error\n"
+ "Near-Keyword: '%s'\n",
+ keyword);
+ return FALSE;
+ }
+ }
+ return TRUE;
+
+err_conflict:
+ admin_write(ctx,
+ "Status: failed\n"
+ "Reason: conflicting-keyword\n"
+ "Near-Keyword: '%s'\n",
+ keyword);
+ goto err;
+err_noiface:
+ admin_write(ctx,
+ "Status: failed\n"
+ "Reason: interface-not-found\n"
+ "Near-Keyword: '%s'\n"
+ "Argument: '%s'\n",
+ keyword, tmp);
+err:
+ admin_free_selector(sel);
+ return FALSE;
+}
+
+static void admin_route_show(void *ctx, const char *cmd)
+{
+ struct nhrp_peer_selector sel;
+
+ memset(&sel, 0, sizeof(sel));
+ sel.type_mask = BIT(NHRP_PEER_TYPE_LOCAL_ROUTE);
+ if (!admin_parse_selector(ctx, cmd, &sel))
+ return;
+
+ admin_write(ctx, "Status: ok\n\n");
+ nhrp_peer_foreach(admin_show_peer, ctx, &sel);
+ admin_free_selector(&sel);
+}
+
+static void admin_cache_show(void *ctx, const char *cmd)
+{
+ struct nhrp_peer_selector sel;
+
+ memset(&sel, 0, sizeof(sel));
+ sel.type_mask = NHRP_PEER_TYPEMASK_ALL &
+ ~BIT(NHRP_PEER_TYPE_LOCAL_ROUTE);
+ if (!admin_parse_selector(ctx, cmd, &sel))
+ return;
+
+ admin_write(ctx, "Status: ok\n\n");
+ nhrp_peer_foreach(admin_show_peer, ctx, &sel);
+ admin_free_selector(&sel);
+}
+
+static void admin_cache_purge(void *ctx, const char *cmd)
+{
+ struct nhrp_peer_selector sel;
+ int count = 0;
+
+ memset(&sel, 0, sizeof(sel));
+ sel.type_mask = NHRP_PEER_TYPEMASK_PURGEABLE;
+ if (!admin_parse_selector(ctx, cmd, &sel))
+ return;
+
+ nhrp_peer_foreach(nhrp_peer_purge_matching, &count, &sel);
+ admin_free_selector(&sel);
+
+ admin_write(ctx,
+ "Status: ok\n"
+ "Entries-Affected: %d\n",
+ count);
+}
+
+static void admin_cache_lower_down(void *ctx, const char *cmd)
+{
+ struct nhrp_peer_selector sel;
+ int count = 0;
+
+ memset(&sel, 0, sizeof(sel));
+ sel.type_mask = NHRP_PEER_TYPEMASK_PURGEABLE;
+ if (!admin_parse_selector(ctx, cmd, &sel))
+ return;
+
+ nhrp_peer_foreach(nhrp_peer_lowerdown_matching, &count, &sel);
+ admin_free_selector(&sel);
+
+ admin_write(ctx,
+ "Status: ok\n"
+ "Entries-Affected: %d\n",
+ count);
+}
+
+static void admin_cache_flush(void *ctx, const char *cmd)
+{
+ struct nhrp_peer_selector sel;
+ int count = 0;
+
+ memset(&sel, 0, sizeof(sel));
+ sel.type_mask = NHRP_PEER_TYPEMASK_REMOVABLE;
+ if (!admin_parse_selector(ctx, cmd, &sel))
+ return;
+
+ nhrp_peer_foreach(nhrp_peer_remove_matching, &count, &sel);
+ admin_free_selector(&sel);
+
+ admin_write(ctx,
+ "Status: ok\n"
+ "Entries-Affected: %d\n",
+ count);
+}
+
+static int admin_show_interface(void *ctx, struct nhrp_interface *iface)
+{
+ char buf[512], tmp[32];
+ size_t len = sizeof(buf);
+ int i = 0;
+
+ i += snprintf(&buf[i], len - i,
+ "Interface: %s\n"
+ "Index: %d\n",
+ iface->name,
+ iface->index);
+
+ if (iface->protocol_address.addr_len != 0) {
+ i += snprintf(&buf[i], len - i,
+ "Protocol-Address: %s/%d\n",
+ nhrp_address_format(&iface->protocol_address, sizeof(tmp), tmp),
+ iface->protocol_address_prefix);
+ }
+
+ if (iface->flags) {
+ i += snprintf(&buf[i], len - i,
+ "Flags:%s%s%s%s%s\n",
+ (iface->flags & NHRP_INTERFACE_FLAG_NON_CACHING) ? " non-caching" : "",
+ (iface->flags & NHRP_INTERFACE_FLAG_SHORTCUT) ? " shortcut" : "",
+ (iface->flags & NHRP_INTERFACE_FLAG_REDIRECT) ? " redirect" : "",
+ (iface->flags & NHRP_INTERFACE_FLAG_SHORTCUT_DEST) ? " shortcut-dest" : "",
+ (iface->flags & NHRP_INTERFACE_FLAG_CONFIGURED) ? " configured" : "");
+ }
+
+ if (!(iface->flags & NHRP_INTERFACE_FLAG_CONFIGURED))
+ goto done;
+
+ i += snprintf(&buf[i], len - i,
+ "Holding-Time: %u\n"
+ "Route-Table: %u\n"
+ "GRE-Key: %u\n"
+ "MTU: %u\n",
+ iface->holding_time,
+ iface->route_table,
+ iface->gre_key,
+ iface->mtu);
+
+ if (iface->link_index) {
+ struct nhrp_interface *link;
+
+ i += snprintf(&buf[i], len - i, "Link-Index: %d\n", iface->link_index);
+ link = nhrp_interface_get_by_index(iface->link_index, FALSE);
+ if (link != NULL)
+ i += snprintf(&buf[i], len - i, "Link-Name: %s\n", link->name);
+ }
+
+ if (iface->nbma_address.addr_len != 0) {
+ i += snprintf(&buf[i], len - i,
+ "NBMA-MTU: %u\n"
+ "NBMA-Address: %s\n",
+ iface->nbma_mtu,
+ nhrp_address_format(&iface->nbma_address, sizeof(tmp), tmp));
+ }
+ if (iface->nat_cie.nbma_address.addr_len != 0) {
+ i += snprintf(&buf[i], len - i,
+ "NBMA-NAT-OA: %s\n",
+ nhrp_address_format(&iface->nat_cie.nbma_address, sizeof(tmp), tmp));
+ }
+done:
+ admin_write(ctx, "%s\n", buf);
+ return 0;
+}
+
+static void admin_interface_show(void *ctx, const char *cmd)
+{
+ admin_write(ctx, "Status: ok\n\n");
+ nhrp_interface_foreach(admin_show_interface, ctx);
+}
+
+static void admin_redirect_purge(void *ctx, const char *cmd)
+{
+ char keyword[64];
+ struct nhrp_address addr;
+ uint8_t prefix;
+ int count;
+
+ nhrp_address_set_type(&addr, PF_UNSPEC);
+
+ if (parse_word(&cmd, sizeof(keyword), keyword)) {
+ if (!nhrp_address_parse(keyword, &addr, &prefix)) {
+ admin_write(ctx,
+ "Status: failed\n"
+ "Reason: invalid-address\n"
+ "Near-Keyword: '%s'\n",
+ keyword);
+ return;
+ }
+ }
+
+ count = nhrp_rate_limit_clear(&addr, prefix);
+ admin_write(ctx,
+ "Status: ok\n"
+ "Entries-Affected: %d\n",
+ count);
+}
+
+struct update_nbma {
+ struct nhrp_address addr;
+ int count;
+};
+
+static int update_nbma(void *ctx, struct nhrp_peer *p)
+{
+ struct update_nbma *un = (struct update_nbma *) ctx;
+
+ nhrp_peer_discover_nhs(p, &un->addr);
+ un->count++;
+
+ return 0;
+}
+
+static void admin_update_nbma(void *ctx, const char *cmd)
+{
+ char keyword[64];
+ struct nhrp_peer_selector sel;
+ struct update_nbma un;
+
+ memset(&sel, 0, sizeof(sel));
+ sel.type_mask = BIT(NHRP_PEER_TYPE_DYNAMIC_NHS);
+
+ if (!parse_word(&cmd, sizeof(keyword), keyword))
+ goto err;
+ if (!nhrp_address_parse(keyword, &sel.next_hop_address, NULL))
+ goto err;
+ if (!parse_word(&cmd, sizeof(keyword), keyword))
+ goto err;
+ if (!nhrp_address_parse(keyword, &un.addr, NULL))
+ goto err;
+
+ un.count = 0;
+ nhrp_peer_foreach(update_nbma, &un, &sel);
+
+ admin_write(ctx,
+ "Status: ok\n"
+ "Entries-Affected: %d\n",
+ un.count);
+ return;
+err:
+ admin_write(ctx,
+ "Status: failed\n"
+ "Reason: syntax-error\n"
+ "Near-Keyword: '%s'\n",
+ keyword);
+ return;
+}
+
+static struct {
+ const char *command;
+ void (*handler)(void *ctx, const char *cmd);
+} admin_handler[] = {
+ { "route show", admin_route_show },
+ { "show", admin_cache_show },
+ { "cache show", admin_cache_show },
+ { "flush", admin_cache_flush },
+ { "cache flush", admin_cache_flush },
+ { "purge", admin_cache_purge },
+ { "cache purge", admin_cache_purge },
+ { "cache lowerdown", admin_cache_lower_down },
+ { "interface show", admin_interface_show },
+ { "redirect purge", admin_redirect_purge },
+ { "update nbma", admin_update_nbma },
+};
+
+static void admin_receive_cb(struct ev_io *w, int revents)
+{
+ struct admin_remote *rm = container_of(w, struct admin_remote, io);
+ int fd = rm->io.fd;
+ ssize_t len;
+ int i, cmdlen;
+
+ len = recv(fd, rm->cmd, sizeof(rm->cmd) - rm->num_read, MSG_DONTWAIT);
+ if (len < 0 && errno == EAGAIN)
+ return;
+ if (len <= 0)
+ goto err;
+
+ rm->num_read += len;
+ if (rm->num_read >= sizeof(rm->cmd))
+ goto err;
+
+ if (rm->cmd[rm->num_read-1] != '\n')
+ return;
+ rm->cmd[--rm->num_read] = 0;
+
+ for (i = 0; i < ARRAY_SIZE(admin_handler); i++) {
+ cmdlen = strlen(admin_handler[i].command);
+ if (rm->num_read >= cmdlen &&
+ strncasecmp(rm->cmd, admin_handler[i].command, cmdlen) == 0) {
+ nhrp_debug("Admin: %s", rm->cmd);
+ admin_handler[i].handler(rm, &rm->cmd[cmdlen]);
+ break;
+ }
+ }
+ if (i >= ARRAY_SIZE(admin_handler)) {
+ admin_write(rm,
+ "Status: error\n"
+ "Reason: unrecognized command\n");
+ }
+
+err:
+ admin_free_remote(rm);
+}
+
+static void admin_timeout_cb(struct ev_timer *t, int revents)
+{
+ admin_free_remote(container_of(t, struct admin_remote, timeout));
+}
+
+static void admin_accept_cb(ev_io *w, int revents)
+{
+ struct admin_remote *rm;
+ struct sockaddr_storage from;
+ socklen_t fromlen = sizeof(from);
+ int cnx;
+
+ cnx = accept(w->fd, (struct sockaddr *) &from, &fromlen);
+ if (cnx < 0)
+ return;
+ fcntl(cnx, F_SETFD, FD_CLOEXEC);
+
+ rm = calloc(1, sizeof(struct admin_remote));
+
+ ev_io_init(&rm->io, admin_receive_cb, cnx, EV_READ);
+ ev_io_start(&rm->io);
+ ev_timer_init(&rm->timeout, admin_timeout_cb, 10.0, 0.);
+ ev_timer_start(&rm->timeout);
+}
+
+int admin_init(const char *opennhrp_socket)
+{
+ struct sockaddr_un sun;
+ int fd;
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strncpy(sun.sun_path, opennhrp_socket, sizeof(sun.sun_path));
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ return 0;
+
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ unlink(opennhrp_socket);
+ if (bind(fd, (struct sockaddr *) &sun, sizeof(sun)) != 0)
+ goto err_close;
+
+ if (listen(fd, 5) != 0)
+ goto err_close;
+
+ ev_io_init(&accept_io, admin_accept_cb, fd, EV_READ);
+ ev_io_start(&accept_io);
+
+ return 1;
+
+err_close:
+ nhrp_error("Failed initialize admin socket [%s]: %s",
+ opennhrp_socket, strerror(errno));
+ close(fd);
+ return 0;
+}