summaryrefslogtreecommitdiffstats
path: root/src/status.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/status.c')
-rw-r--r--src/status.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/status.c b/src/status.c
new file mode 100644
index 0000000..a7f8441
--- /dev/null
+++ b/src/status.c
@@ -0,0 +1,237 @@
+/*
+ Copyright (c) 2012-2014, Matthias Schiffer <mschiffer@universe-factory.net>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ \file
+
+ Status socket support
+*/
+
+
+#include "peer.h"
+
+
+#ifdef WITH_STATUS_SOCKET
+
+#include <sys/un.h>
+
+#include <json.h>
+
+
+/** Argument for dump_thread */
+typedef struct dump_thread_arg {
+ int fd; /**< The file descriptor of an accepted socket connection */
+ struct json_object *json; /**< The JSON object to write to the status socket */
+} dump_thread_arg_t;
+
+
+/** Thread to write the status JSON to the status socket */
+static void * dump_thread(void *p) {
+ dump_thread_arg_t *arg = p;
+
+ const char *str = json_object_to_json_string(arg->json);
+ size_t left = strlen(str);
+
+ while (left > 0) {
+ ssize_t written = write(arg->fd, str, left);
+ if (written < 0) {
+ pr_error_errno("can't dump status: write");
+ break;
+ }
+
+ left -= written;
+ str += written;
+ }
+
+ close(arg->fd);
+ json_object_put(arg->json);
+ free(arg);
+
+ return NULL;
+}
+
+
+/** Dumps a fastd_stats_t as a JSON object */
+static json_object * dump_stats(const fastd_stats_t *stats) {
+ struct json_object *ret = json_object_new_object();
+
+ json_object_object_add(ret, "packets", json_object_new_int64(stats->packets));
+ json_object_object_add(ret, "bytes", json_object_new_int64(stats->bytes));
+
+ return ret;
+}
+
+/** Dumps a peer's status as a JSON object */
+static json_object * dump_peer(const fastd_peer_t *peer) {
+ struct json_object *ret = json_object_new_object();
+
+ /* '[' + IPv6 addresss + '%' + interface + ']:' + port + NUL */
+ char addr_buf[1 + INET6_ADDRSTRLEN + 2 + IFNAMSIZ + 1 + 5 + 1];
+ fastd_snprint_peer_address(addr_buf, sizeof(addr_buf), &peer->address, NULL, false, false);
+
+ json_object_object_add(ret, "address", json_object_new_string(addr_buf));
+
+ struct json_object *connection = NULL;
+
+ if (fastd_peer_is_established(peer)) {
+ connection = json_object_new_object();
+
+ if (conf.mode == MODE_TAP) {
+ struct json_object *mac_addresses = json_object_new_array();
+ json_object_object_add(connection, "mac_addresses", mac_addresses);
+
+ size_t i;
+ for (i = 0; i < VECTOR_LEN(ctx.eth_addrs); i++) {
+ fastd_peer_eth_addr_t *addr = &VECTOR_INDEX(ctx.eth_addrs, i);
+
+ if (addr->peer != peer)
+ continue;
+
+ const uint8_t *d = addr->addr.data;
+
+ char eth_addr_buf[18];
+ snprintf(eth_addr_buf, sizeof(eth_addr_buf),
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ d[0], d[1], d[2], d[3], d[4], d[5]);
+
+ json_object_array_add(mac_addresses, json_object_new_string(eth_addr_buf));
+ }
+ }
+ }
+
+ json_object_object_add(ret, "connection", connection);
+
+ return ret;
+}
+
+/** Dumps fastd's status to a connected socket */
+static void dump_status(int fd) {
+ struct json_object *json = json_object_new_object();
+
+ struct json_object *statistics = json_object_new_object();
+ json_object_object_add(json, "statistics", statistics);
+
+ json_object_object_add(statistics, "rx", dump_stats(&ctx.rx));
+ json_object_object_add(statistics, "tx", dump_stats(&ctx.tx));
+ json_object_object_add(statistics, "tx_dropped", dump_stats(&ctx.tx_dropped));
+ json_object_object_add(statistics, "tx_error", dump_stats(&ctx.tx_error));
+
+ struct json_object *peers = json_object_new_object();
+ json_object_object_add(json, "peers", peers);
+
+ size_t i;
+ for (i = 0; i < VECTOR_LEN(ctx.peers); i++) {
+ fastd_peer_t *peer = VECTOR_INDEX(ctx.peers, i);
+
+ if (!fastd_peer_is_enabled(peer))
+ continue;
+
+ json_object_object_add(peers, peer->name, dump_peer(peer));
+ }
+
+
+ dump_thread_arg_t *arg = fastd_new(dump_thread_arg_t);
+
+ arg->json = json;
+ arg->fd = fd;
+
+ pthread_t thread;
+ if ((errno = pthread_create(&thread, &ctx.detached_thread, dump_thread, arg)) != 0) {
+ pr_error_errno("unable to create status dump thread");
+
+ close(arg->fd);
+ json_object_put(arg->json);
+ free(arg);
+ }
+}
+
+/** Initialized the status socket */
+void fastd_status_init(void) {
+ if (!conf.status_socket)
+ return;
+
+ uid_t uid = geteuid();
+ gid_t gid = getegid();
+
+ if (conf.user || conf.group) {
+ if (setegid(conf.gid) < 0)
+ pr_debug_errno("setegid");
+ if (seteuid(conf.uid) < 0)
+ pr_debug_errno("seteuid");
+ }
+
+
+ ctx.status_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ctx.status_fd < 0)
+ exit_errno("fastd_status_init: socket");
+
+
+ size_t len = offsetof(struct sockaddr_un, sun_path) + strlen(conf.status_socket) + 1;
+ uint8_t buf[len];
+ memset(buf, 0, len);
+
+ struct sockaddr_un *sa = (void*)buf;
+
+ sa->sun_family = AF_UNIX;
+ strcpy(sa->sun_path, conf.status_socket);
+
+ if (bind(ctx.status_fd, (struct sockaddr*)sa, len))
+ exit_errno("fastd_status_init: bind");
+
+ if (listen(ctx.status_fd, 4))
+ exit_errno("fastd_status_init: listen");
+
+
+ if (seteuid(uid) < 0)
+ pr_debug_errno("seteuid");
+ if (setegid(gid) < 0)
+ pr_debug_errno("setegid");
+}
+
+/** Closes the status socket */
+void fastd_status_close(void) {
+ if (!conf.status_socket)
+ return;
+
+ if (close(ctx.status_fd))
+ pr_warn_errno("fastd_status_cleanup: close");
+
+ if (unlink(conf.status_socket))
+ pr_warn_errno("fastd_status_cleanup: unlink");
+}
+
+/** Handles a single connection on the status socket */
+void fastd_status_handle(void) {
+ int fd = accept(ctx.status_fd, NULL, NULL);
+
+ if (fd < 0) {
+ pr_warn_errno("fastd_status_handle: accept");
+ return;
+ }
+
+ dump_status(fd);
+}
+
+#endif