From 2561266c156a1f63ed85fe2865ac607507ae4cef Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 5 Sep 2014 22:43:11 +0200 Subject: Add status socket to get the current status as JSON --- Doxyfile.in | 1 + cmake/config.cmake | 1 + cmake/deps.cmake | 10 +++ src/CMakeLists.txt | 11 +-- src/config.c | 4 + src/config.y | 17 +++- src/fastd.c | 2 + src/fastd.h | 27 +++++- src/fastd_config.h.in | 3 + src/lex.c | 2 + src/log.c | 8 +- src/log.h | 4 +- src/options.c | 10 +++ src/options.def.h | 3 + src/poll.c | 61 +++++++++---- src/status.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++ 16 files changed, 373 insertions(+), 28 deletions(-) create mode 100644 src/status.c diff --git a/Doxyfile.in b/Doxyfile.in index 078a644..db69387 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -1946,6 +1946,7 @@ PREDEFINED = __attribute__(x)= \ VECTOR(x):=VECTOR \ WITH_CAPABILITIES \ WITH_DYNAMIC_PEERS \ + WITH_STATUS_SOCKET \ WITH_CMDLINE_USER \ WITH_CMDLINE_LOGGING \ WITH_CMDLINE_OPERATION \ diff --git a/cmake/config.cmake b/cmake/config.cmake index c1fd68b..8aa6549 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -44,6 +44,7 @@ set(WITH_CMDLINE_OPERATION TRUE CACHE BOOL "Include support for setting options set(WITH_CMDLINE_COMMANDS TRUE CACHE BOOL "Include support for setting handler scripts (e.g. --on-up) on the command line") set(WITH_DYNAMIC_PEERS TRUE CACHE BOOL "Include support for dynamic peers (using on-verify handlers)") +set(WITH_STATUS_SOCKET TRUE CACHE BOOL "Include support for the status socket") set(MAX_CONFIG_DEPTH 10 CACHE STRING "Maximum config include depth") diff --git a/cmake/deps.cmake b/cmake/deps.cmake index ea5a0a0..213b3d1 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -59,3 +59,13 @@ else(WITH_CAPABILITIES) set(CAP_INCLUDE_DIR "") set(CAP_LIBRARY "") endif(WITH_CAPABILITIES) + +if(WITH_STATUS_SOCKET) + pkg_check_modules(JSONC json-c) +else(WITH_STATUS_SOCKET) + set(JSONC_INCLUDE_DIRS "") + set(JSONC_CFLAGS_OTHER "") + set(JSONC_LIBRARY_DIRS "") + set(JSONC_LIBRARIES "") + set(JSONC_LDFLAGS_OTHER "") +endif(WITH_STATUS_SOCKET) 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", "", "Writes fastd's PID to the specified file"); +#ifdef WITH_STATUS_SOCKET +OPTION_ARG(option_status_socket, "--status-socket", "", "Configure a socket to get fastd's status"); +#endif SEPARATOR; OPTION_ARG(option_config, "--config" OR "-c", "", "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 + 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 + +#include + + +/** 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 -- cgit v1.2.3