summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2014-09-05 22:43:11 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2014-09-05 22:43:11 +0200
commit2561266c156a1f63ed85fe2865ac607507ae4cef (patch)
tree9fbc7cf5fb9a045e7bbf70ab07f9b5b4e518a782 /src
parent5f898aa52f81671cbc54beea19211c8a75e0962f (diff)
downloadfastd-2561266c156a1f63ed85fe2865ac607507ae4cef.tar
fastd-2561266c156a1f63ed85fe2865ac607507ae4cef.zip
Add status socket to get the current status as JSON
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt11
-rw-r--r--src/config.c4
-rw-r--r--src/config.y17
-rw-r--r--src/fastd.c2
-rw-r--r--src/fastd.h27
-rw-r--r--src/fastd_config.h.in3
-rw-r--r--src/lex.c2
-rw-r--r--src/log.c8
-rw-r--r--src/log.h4
-rw-r--r--src/options.c10
-rw-r--r--src/options.def.h3
-rw-r--r--src/poll.c61
-rw-r--r--src/status.c237
13 files changed, 361 insertions, 28 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ad92508..978d8e9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,8 +1,8 @@
set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS _GNU_SOURCE __APPLE_USE_RFC_3542)
-set(FASTD_CFLAGS "${PTHREAD_CFLAGS} -std=c99 ${UECC_CFLAGS_OTHER} ${NACL_CFLAGS_OTHER} ${OPENSSL_CRYPTO_CFLAGS_OTHER} ${CFLAGS_LTO} -Wall")
+set(FASTD_CFLAGS "${PTHREAD_CFLAGS} -std=c99 ${UECC_CFLAGS_OTHER} ${NACL_CFLAGS_OTHER} ${OPENSSL_CRYPTO_CFLAGS_OTHER} ${JSONC_CFLAGS_OTHER} ${CFLAGS_LTO} -Wall")
include_directories(${FASTD_SOURCE_DIR} ${FASTD_BINARY_DIR}/src)
-link_directories(${UECC_LIBRARY_DIRS} ${NACL_LIBRARY_DIRS} ${OPENSSL_CRYPTO_LIBRARY_DIRS})
+link_directories(${UECC_LIBRARY_DIRS} ${NACL_LIBRARY_DIRS} ${OPENSSL_CRYPTO_LIBRARY_DIRS} ${JSONC_LIBRARY_DIRS})
include(generate_version)
@@ -37,15 +37,16 @@ add_executable(fastd
sha256.c
shell.c
socket.c
+ status.c
tuntap.c
vector.c
verify.c
${BISON_fastd_config_parse_OUTPUTS}
)
set_property(TARGET fastd PROPERTY COMPILE_FLAGS "${FASTD_CFLAGS}")
-set_property(TARGET fastd PROPERTY LINK_FLAGS "${PTHREAD_LDFLAGS} ${UECC_LDFLAGS_OTHER} ${NACL_LDFLAGS_OTHER} ${OPENSSL_CRYPTO_LDFLAGS_OTHER} ${LDFLAGS_LTO}")
-set_property(TARGET fastd APPEND PROPERTY INCLUDE_DIRECTORIES ${CAP_INCLUDE_DIR} ${NACL_INCLUDE_DIRS})
-target_link_libraries(fastd protocols methods ciphers macs ${RT_LIBRARY} ${CAP_LIBRARY} ${UECC_LIBRARIES} ${NACL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES})
+set_property(TARGET fastd PROPERTY LINK_FLAGS "${PTHREAD_LDFLAGS} ${UECC_LDFLAGS_OTHER} ${NACL_LDFLAGS_OTHER} ${OPENSSL_CRYPTO_LDFLAGS_OTHER} ${JSONC_LDFLAGS_OTHER} ${LDFLAGS_LTO}")
+set_property(TARGET fastd APPEND PROPERTY INCLUDE_DIRECTORIES ${CAP_INCLUDE_DIR} ${NACL_INCLUDE_DIRS} ${JSONC_INCLUDE_DIRS})
+target_link_libraries(fastd protocols methods ciphers macs ${RT_LIBRARY} ${CAP_LIBRARY} ${UECC_LIBRARIES} ${NACL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES} ${JSONC_LIBRARIES})
add_dependencies(fastd version)
diff --git a/src/config.c b/src/config.c
index e38bab5..7b87dcf 100644
--- a/src/config.c
+++ b/src/config.c
@@ -650,6 +650,10 @@ void fastd_config_release(void) {
fastd_shell_command_unset(&conf.on_verify);
#endif
+#ifdef WITH_STATUS_SOCKET
+ free(conf.status_socket);
+#endif
+
free(conf.user);
free(conf.group);
free(conf.groups);
diff --git a/src/config.y b/src/config.y
index 389cf31..35b8901 100644
--- a/src/config.y
+++ b/src/config.y
@@ -116,6 +116,8 @@
%token TOK_REMOTE
%token TOK_SECRET
%token TOK_SECURE
+%token TOK_SOCKET
+%token TOK_STATUS
%token TOK_STDERR
%token TOK_SYNC
%token TOK_SYSLOG
@@ -199,6 +201,7 @@ statement: peer_group_statement
| TOK_ON TOK_ESTABLISH on_establish ';'
| TOK_ON TOK_DISESTABLISH on_disestablish ';'
| TOK_ON TOK_VERIFY on_verify ';'
+ | TOK_STATUS TOK_SOCKET status_socket ';'
| TOK_FORWARD forward ';'
;
@@ -415,8 +418,18 @@ on_verify: sync_def_async TOK_STRING {
#ifdef WITH_DYNAMIC_PEERS
fastd_shell_command_set(&conf.on_verify, $2->str, $1);
#else
- fastd_config_error(&@$, state, "`on verify' is not supported by this version of fastd");
- YYERROR;
+ fastd_config_error(&@$, state, "`on verify' is not supported by this version of fastd");
+ YYERROR;
+#endif
+ }
+ ;
+
+status_socket: TOK_STRING {
+#ifdef WITH_STATUS_SOCKET
+ free(conf.status_socket); conf.status_socket = fastd_strdup($1->str);
+#else
+ fastd_config_error(&@$, state, "status sockets aren't supported by this version of fastd");
+ YYERROR;
#endif
}
;
diff --git a/src/fastd.c b/src/fastd.c
index da4268e..b1f2457 100644
--- a/src/fastd.c
+++ b/src/fastd.c
@@ -481,6 +481,7 @@ static inline void init(int argc, char *argv[]) {
fastd_cap_init();
init_sockets();
+ fastd_status_init();
fastd_async_init();
fastd_poll_init();
@@ -601,6 +602,7 @@ static inline void cleanup(void) {
delete_peers();
fastd_tuntap_close();
+ fastd_status_close();
close_sockets();
fastd_poll_free();
diff --git a/src/fastd.h b/src/fastd.h
index 17689cd..6eaf831 100644
--- a/src/fastd.h
+++ b/src/fastd.h
@@ -27,7 +27,7 @@
\file
\em fastd main header file defining most data structures
- */
+*/
#pragma once
@@ -217,6 +217,10 @@ struct fastd_config {
fastd_shell_command_t on_verify; /**< The command to execute to check if a connection from an unknown peer should be allowed */
#endif
+#ifdef WITH_STATUS_SOCKET
+ char *status_socket; /**< The path of the status socket */
+#endif
+
bool daemon; /**< Set to make fastd fork to the background after initialization */
char *pid_file; /**< A filename to write fastd's PID to */
@@ -250,6 +254,10 @@ struct fastd_context {
VECTOR(struct pollfd) pollfds; /**< The vector of pollfds for all file descriptors */
#endif
+#ifdef WITH_STATUS_SOCKET
+ int status_fd; /**< The file descriptor of the status socket */
+#endif
+
bool has_floating; /**< Specifies if any of the configured peers have floating remotes */
uint32_t peer_addr_ht_seed; /**< The hash seed used for peer_addr_ht */
@@ -325,6 +333,23 @@ void fastd_cap_drop(void);
void fastd_random_bytes(void *buffer, size_t len, bool secure);
+#ifdef WITH_STATUS_SOCKET
+
+void fastd_status_init(void);
+void fastd_status_close(void);
+void fastd_status_handle(void);
+
+#else
+
+static inline void fastd_status_init(void) {
+}
+
+static inline void fastd_status_close(void) {
+}
+
+#endif
+
+
/** Returns a random number between \a min (inclusively) and \a max (exclusively) */
static inline int fastd_rand(int min, int max) {
unsigned int r = (unsigned int)random();
diff --git a/src/fastd_config.h.in b/src/fastd_config.h.in
index e396bf8..430efbb 100644
--- a/src/fastd_config.h.in
+++ b/src/fastd_config.h.in
@@ -93,6 +93,9 @@
/** Defined if on-verify support is enabled */
#cmakedefine WITH_DYNAMIC_PEERS
+/** Defined if status socket support is enabled */
+#cmakedefine WITH_STATUS_SOCKET
+
/** Defined if systemd support is enabled */
#cmakedefine ENABLE_SYSTEMD
diff --git a/src/lex.c b/src/lex.c
index c6b771b..c2376c6 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -113,6 +113,8 @@ static const keyword_t keywords[] = {
{ "remote", TOK_REMOTE },
{ "secret", TOK_SECRET },
{ "secure", TOK_SECURE },
+ { "socket", TOK_SOCKET },
+ { "status", TOK_STATUS },
{ "stderr", TOK_STDERR },
{ "sync", TOK_SYNC },
{ "syslog", TOK_SYSLOG },
diff --git a/src/log.c b/src/log.c
index 7295ba7..1559496 100644
--- a/src/log.c
+++ b/src/log.c
@@ -52,7 +52,7 @@ static inline size_t snprintf_safe(char *buffer, size_t size, const char *format
}
/** Creates a string representation of a peer address */
-static size_t snprint_peer_address(char *buffer, size_t size, const fastd_peer_address_t *address, const char *iface, bool bind_address) {
+size_t fastd_snprint_peer_address(char *buffer, size_t size, const fastd_peer_address_t *address, const char *iface, bool bind_address, bool hide) {
char addr_buf[INET6_ADDRSTRLEN] = "";
switch (address->sa.sa_family) {
@@ -63,7 +63,7 @@ static size_t snprint_peer_address(char *buffer, size_t size, const fastd_peer_a
return snprintf(buffer, size, "any");
case AF_INET:
- if (!bind_address && conf.hide_ip_addresses)
+ if (!bind_address && hide)
return snprintf_safe(buffer, size, "[hidden]:%u", ntohs(address->in.sin_port));
else if (inet_ntop(AF_INET, &address->in.sin_addr, addr_buf, sizeof(addr_buf)))
return snprintf_safe(buffer, size, "%s:%u", addr_buf, ntohs(address->in.sin_port));
@@ -71,7 +71,7 @@ static size_t snprint_peer_address(char *buffer, size_t size, const fastd_peer_a
return 0;
case AF_INET6:
- if (!bind_address && conf.hide_ip_addresses)
+ if (!bind_address && hide)
return snprintf_safe(buffer, size, "[hidden]:%u", ntohs(address->in6.sin6_port));
if (inet_ntop(AF_INET6, &address->in6.sin6_addr, addr_buf, sizeof(addr_buf))) {
char ifname_buf[IF_NAMESIZE];
@@ -179,7 +179,7 @@ static int fastd_vsnprintf(char *buffer, size_t size, const char *format, va_lis
iface = (*format == 'L') ? va_arg(ap, const char *) : NULL;
if (p)
- buffer += snprint_peer_address(buffer, buffer_end-buffer, (const fastd_peer_address_t *)p, iface, *format != 'I');
+ buffer += fastd_snprint_peer_address(buffer, buffer_end-buffer, (const fastd_peer_address_t *)p, iface, *format != 'I', conf.hide_ip_addresses);
else
buffer += snprintf_safe(buffer, buffer_end-buffer, "(null)");
break;
diff --git a/src/log.h b/src/log.h
index 6cf1487..5fce09f 100644
--- a/src/log.h
+++ b/src/log.h
@@ -53,7 +53,9 @@ typedef enum fastd_loglevel {
} fastd_loglevel_t;
-/** Logs a formatted string with the specified log level */
+size_t fastd_snprint_peer_address(char *buffer, size_t size, const fastd_peer_address_t *address, const char *iface, bool bind_address, bool hide);
+
+
void fastd_logf(const fastd_loglevel_t level, const char *format, ...);
/** Logs a formatted fatal error message */
diff --git a/src/options.c b/src/options.c
index 0af48f4..2a67f13 100644
--- a/src/options.c
+++ b/src/options.c
@@ -106,6 +106,16 @@ static void option_pid_file(const char *arg) {
}
+#ifdef WITH_STATUS_SOCKET
+
+/** Handles the --status-socket option */
+static void option_status_socket(const char *arg) {
+ free(conf.status_socket);
+ conf.status_socket = fastd_strdup(arg);
+}
+
+#endif
+
/** Handles the --config option */
static void option_config(const char *arg) {
if (!strcmp(arg, "-"))
diff --git a/src/options.def.h b/src/options.def.h
index 114b6e9..9ea0476 100644
--- a/src/options.def.h
+++ b/src/options.def.h
@@ -2,6 +2,9 @@ OPTION(usage, "--help" OR "-h", "Shows this help text");
OPTION(version, "--version" OR "-v", "Shows the fastd version");
OPTION(option_daemon, "--daemon" OR "-d", "Runs fastd in the background");
OPTION_ARG(option_pid_file, "--pid-file", "<filename>", "Writes fastd's PID to the specified file");
+#ifdef WITH_STATUS_SOCKET
+OPTION_ARG(option_status_socket, "--status-socket", "<socket>", "Configure a socket to get fastd's status");
+#endif
SEPARATOR;
OPTION_ARG(option_config, "--config" OR "-c", "<filename>", "Loads a config file");
diff --git a/src/poll.c b/src/poll.c
index cee827b..f585cfb 100644
--- a/src/poll.c
+++ b/src/poll.c
@@ -76,12 +76,22 @@ void fastd_poll_init(void) {
if (ctx.epoll_fd < 0)
exit_errno("epoll_create1");
- struct epoll_event event = {
+ struct epoll_event event_async = {
.events = EPOLLIN,
.data.ptr = &ctx.async_rfd,
};
- if (epoll_ctl(ctx.epoll_fd, EPOLL_CTL_ADD, ctx.async_rfd, &event) < 0)
+ if (epoll_ctl(ctx.epoll_fd, EPOLL_CTL_ADD, ctx.async_rfd, &event_async) < 0)
+ exit_errno("epoll_ctl");
+
+#ifdef WITH_STATUS_SOCKET
+ struct epoll_event event_status = {
+ .events = EPOLLIN,
+ .data.ptr = &ctx.status_fd,
+ };
+
+ if (epoll_ctl(ctx.epoll_fd, EPOLL_CTL_ADD, ctx.status_fd, &event_status) < 0)
exit_errno("epoll_ctl");
+#endif
}
void fastd_poll_free(void) {
@@ -161,6 +171,12 @@ void fastd_poll_handle(void) {
if (events[i].events & EPOLLIN)
fastd_async_handle();
}
+#ifdef WITH_STATUS_SOCKET
+ else if (events[i].data.ptr == &ctx.status_fd) {
+ if (events[i].events & EPOLLIN)
+ fastd_status_handle();
+ }
+#endif
else {
fastd_socket_t *sock = events[i].data.ptr;
@@ -180,7 +196,7 @@ void fastd_poll_handle(void) {
#else
void fastd_poll_init(void) {
- VECTOR_RESIZE(ctx.pollfds, 2 + ctx.n_socks);
+ VECTOR_RESIZE(ctx.pollfds, 3 + ctx.n_socks);
VECTOR_INDEX(ctx.pollfds, 0) = (struct pollfd) {
.fd = -1,
@@ -194,9 +210,19 @@ void fastd_poll_init(void) {
.revents = 0,
};
+ VECTOR_INDEX(ctx.pollfds, 2) = (struct pollfd) {
+#ifdef WITH_STATUS_SOCKET
+ .fd = ctx.status_fd,
+#else
+ .fd = -1,
+#endif
+ .events = POLLIN,
+ .revents = 0,
+ };
+
size_t i;
for (i = 0; i < ctx.n_socks; i++) {
- VECTOR_INDEX(ctx.pollfds, 2+i) = (struct pollfd) {
+ VECTOR_INDEX(ctx.pollfds, 3+i) = (struct pollfd) {
.fd = -1,
.events = POLLIN,
.revents = 0,
@@ -214,16 +240,16 @@ void fastd_poll_set_fd_tuntap(void) {
}
void fastd_poll_set_fd_sock(size_t i) {
- VECTOR_INDEX(ctx.pollfds, 2+i).fd = ctx.socks[i].fd;
+ VECTOR_INDEX(ctx.pollfds, 3+i).fd = ctx.socks[i].fd;
}
void fastd_poll_set_fd_peer(size_t i) {
fastd_peer_t *peer = VECTOR_INDEX(ctx.peers, i);
if (!peer->sock || !fastd_peer_is_socket_dynamic(peer))
- VECTOR_INDEX(ctx.pollfds, 2+ctx.n_socks+i).fd = -1;
+ VECTOR_INDEX(ctx.pollfds, 3+ctx.n_socks+i).fd = -1;
else
- VECTOR_INDEX(ctx.pollfds, 2+ctx.n_socks+i).fd = peer->sock->fd;
+ VECTOR_INDEX(ctx.pollfds, 3+ctx.n_socks+i).fd = peer->sock->fd;
}
void fastd_poll_add_peer(void) {
@@ -237,7 +263,7 @@ void fastd_poll_add_peer(void) {
}
void fastd_poll_delete_peer(size_t i) {
- VECTOR_DELETE(ctx.pollfds, 2+ctx.n_socks+i);
+ VECTOR_DELETE(ctx.pollfds, 3+ctx.n_socks+i);
}
@@ -253,7 +279,7 @@ void fastd_poll_handle(void) {
if (timeout < 0 || timeout > maintenance_timeout)
timeout = maintenance_timeout;
- if (VECTOR_LEN(ctx.pollfds) != 2 + ctx.n_socks + VECTOR_LEN(ctx.peers))
+ if (VECTOR_LEN(ctx.pollfds) != 3 + ctx.n_socks + VECTOR_LEN(ctx.peers))
exit_bug("fd count mismatch");
sigset_t set, oldset;
@@ -324,12 +350,17 @@ void fastd_poll_handle(void) {
if (VECTOR_INDEX(ctx.pollfds, 1).revents & POLLIN)
fastd_async_handle();
+#ifdef WITH_STATUS_SOCKET
+ if (VECTOR_INDEX(ctx.pollfds, 2).revents & POLLIN)
+ fastd_status_handle();
+#endif
+
for (i = 0; i < ctx.n_socks; i++) {
- if (VECTOR_INDEX(ctx.pollfds, 2+i).revents & (POLLERR|POLLHUP|POLLNVAL)) {
+ if (VECTOR_INDEX(ctx.pollfds, 3+i).revents & (POLLERR|POLLHUP|POLLNVAL)) {
fastd_socket_error(&ctx.socks[i]);
- VECTOR_INDEX(ctx.pollfds, 2+i).fd = -1;
+ VECTOR_INDEX(ctx.pollfds, 3+i).fd = -1;
}
- else if (VECTOR_INDEX(ctx.pollfds, 2+i).revents & POLLIN) {
+ else if (VECTOR_INDEX(ctx.pollfds, 3+i).revents & POLLIN) {
fastd_receive(&ctx.socks[i]);
}
}
@@ -337,15 +368,15 @@ void fastd_poll_handle(void) {
for (i = 0; i < VECTOR_LEN(ctx.peers); i++) {
fastd_peer_t *peer = VECTOR_INDEX(ctx.peers, i);
- if (VECTOR_INDEX(ctx.pollfds, 2+ctx.n_socks+i).revents & (POLLERR|POLLHUP|POLLNVAL)) {
+ if (VECTOR_INDEX(ctx.pollfds, 3+ctx.n_socks+i).revents & (POLLERR|POLLHUP|POLLNVAL)) {
fastd_peer_reset_socket(peer);
}
- else if (VECTOR_INDEX(ctx.pollfds, 2+ctx.n_socks+i).revents & POLLIN) {
+ else if (VECTOR_INDEX(ctx.pollfds, 3+ctx.n_socks+i).revents & POLLIN) {
fastd_receive(peer->sock);
}
}
- if (VECTOR_LEN(ctx.pollfds) != 2 + ctx.n_socks + VECTOR_LEN(ctx.peers))
+ if (VECTOR_LEN(ctx.pollfds) != 3 + ctx.n_socks + VECTOR_LEN(ctx.peers))
exit_bug("fd count mismatch");
}
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