Skip to content
Snippets Groups Projects
Commit 9004028c authored by Matthias Schiffer's avatar Matthias Schiffer
Browse files

Convert gluon-announce Lua code to C modules, rename to gluon-respondd

gluon-announced and gluon-announce are merged to gluon-respondd.
parent 840d07dd
No related branches found
No related tags found
No related merge requests found
Showing
with 1119 additions and 143 deletions
local gateway = ''
for line in io.lines('/sys/kernel/debug/batman_adv/bat0/gateways') do
if line:sub(1, 3) == '=> ' then
gateway = line:sub(4, 20)
break
end
end
if gateway ~= '' then
return gateway
end
local ethtool = require 'ethtool_stats'
local fields = ethtool.interface_stats('bat0')
local traffic = {}
for _, class in ipairs({'rx', 'tx', 'forward', 'mgmt_rx', 'mgmt_tx'}) do
traffic[class] = {
bytes = fields[class .. '_bytes'],
packets = fields[class],
}
end
traffic['tx']['dropped'] = fields['tx_dropped']
return traffic
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -liwinfo -luci
/*
Copyright (c) 2016, 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.
*/
#include <respondd.h>
#include <iwinfo.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <alloca.h>
#include <glob.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/ethtool.h>
#include <linux/if_addr.h>
#include <linux/sockios.h>
#define _STRINGIFY(s) #s
#define STRINGIFY(s) _STRINGIFY(s)
static struct json_object * get_addresses(void) {
FILE *f = fopen("/proc/net/if_inet6", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
struct json_object *ret = json_object_new_array();
while (getline(&line, &len, f) >= 0) {
/* IF_NAMESIZE would be enough, but adding 1 here is simpler than subtracting 1 in the format string */
char ifname[IF_NAMESIZE+1];
unsigned int flags;
struct in6_addr addr;
char buf[INET6_ADDRSTRLEN];
if (sscanf(line,
"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8
"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8
" %*2x %*2x %*2x %2x %"STRINGIFY(IF_NAMESIZE)"s",
&addr.s6_addr[0], &addr.s6_addr[1], &addr.s6_addr[2], &addr.s6_addr[3],
&addr.s6_addr[4], &addr.s6_addr[5], &addr.s6_addr[6], &addr.s6_addr[7],
&addr.s6_addr[8], &addr.s6_addr[9], &addr.s6_addr[10], &addr.s6_addr[11],
&addr.s6_addr[12], &addr.s6_addr[13], &addr.s6_addr[14], &addr.s6_addr[15],
&flags, ifname) != 18)
continue;
if (strcmp(ifname, "br-client"))
continue;
if (flags & (IFA_F_TENTATIVE|IFA_F_DEPRECATED))
continue;
inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
json_object_array_add(ret, json_object_new_string(buf));
}
fclose(f);
free(line);
return ret;
}
static void add_if_not_empty(struct json_object *obj, const char *key, struct json_object *val) {
if (json_object_array_length(val))
json_object_object_add(obj, key, val);
else
json_object_put(val);
}
static bool interface_file_exists(const char *ifname, const char *name) {
const char *format = "/sys/class/net/%s/%s";
char path[strlen(format) + strlen(ifname) + strlen(name)];
snprintf(path, sizeof(path), format, ifname, name);
return !access(path, F_OK);
}
static void mesh_add_subif(const char *ifname, struct json_object *wireless,
struct json_object *tunnel, struct json_object *other) {
struct json_object *address = gluonutil_wrap_and_free_string(gluonutil_get_interface_address(ifname));
if (interface_file_exists(ifname, "wireless"))
json_object_array_add(wireless, address);
else if (interface_file_exists(ifname, "tun_flags"))
json_object_array_add(tunnel, address);
else
json_object_array_add(other, address);
}
static struct json_object * get_mesh_subifs(const char *ifname) {
struct json_object *wireless = json_object_new_array();
struct json_object *tunnel = json_object_new_array();
struct json_object *other = json_object_new_array();
const char *format = "/sys/class/net/%s/lower_*";
char pattern[strlen(format) + strlen(ifname) - 1];
snprintf(pattern, sizeof(pattern), format, ifname);
size_t pattern_len = strlen(pattern);
glob_t lower;
if (!glob(pattern, GLOB_NOSORT, NULL, &lower)) {
size_t i;
for (i = 0; i < lower.gl_pathc; i++) {
mesh_add_subif(lower.gl_pathv[i] + pattern_len - 1,
wireless, tunnel, other);
}
globfree(&lower);
}
struct json_object *ret = json_object_new_object();
add_if_not_empty(ret, "wireless", wireless);
add_if_not_empty(ret, "tunnel", tunnel);
add_if_not_empty(ret, "other", other);
return ret;
}
static struct json_object * get_mesh(void) {
struct json_object *ret = json_object_new_object();
struct json_object *bat0_interfaces = json_object_new_object();
json_object_object_add(bat0_interfaces, "interfaces", get_mesh_subifs("bat0"));
json_object_object_add(ret, "bat0", bat0_interfaces);
return ret;
}
static struct json_object * get_batman_adv_compat(void) {
FILE *f = fopen("/lib/gluon/mesh-batman-adv-core/compat", "r");
if (!f)
return NULL;
struct json_object *ret = NULL;
int compat;
if (fscanf(f, "%i", &compat) == 1)
ret = json_object_new_int(compat);
fclose(f);
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct json_object *network = json_object_new_object();
json_object_object_add(network, "addresses", get_addresses());
json_object_object_add(network, "mesh", get_mesh());
json_object_object_add(ret, "network", network);
struct json_object *software = json_object_new_object();
struct json_object *software_batman_adv = json_object_new_object();
json_object_object_add(software_batman_adv, "version", gluonutil_wrap_and_free_string(gluonutil_read_line("/sys/module/batman_adv/version")));
json_object_object_add(software_batman_adv, "compat", get_batman_adv_compat());
json_object_object_add(software, "batman-adv", software_batman_adv);
json_object_object_add(ret, "software", software);
return ret;
}
static void add_gateway(struct json_object *obj) {
FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/gateways", "r");
if (!f)
return;
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, f) >= 0) {
char addr[18];
if (sscanf(line, "=> %17[0-9a-fA-F:]", addr) != 1)
continue;
json_object_object_add(obj, "gateway", json_object_new_string(addr));
break;
}
free(line);
fclose(f);
}
static inline bool ethtool_ioctl(int fd, struct ifreq *ifr, void *data) {
ifr->ifr_data = data;
return (ioctl(fd, SIOCETHTOOL, ifr) >= 0);
}
static uint32_t ethtool_get_stats_length(int fd, struct ifreq *ifr) {
const size_t sset_info_len = sizeof(struct ethtool_sset_info) + sizeof(uint32_t);
struct ethtool_sset_info *sset_info = alloca(sset_info_len);
memset(sset_info, 0, sset_info_len);
sset_info->cmd = ETHTOOL_GSSET_INFO;
sset_info->sset_mask = 1ull << ETH_SS_STATS;
if (!ethtool_ioctl(fd, ifr, sset_info))
return 0;
return sset_info->sset_mask ? sset_info->data[0] : 0;
}
static struct ethtool_gstrings * ethtool_get_stats_strings(int fd, struct ifreq *ifr) {
uint32_t n_stats = ethtool_get_stats_length(fd, ifr);
if (!n_stats)
return NULL;
struct ethtool_gstrings *strings = calloc(1, sizeof(*strings) + n_stats * ETH_GSTRING_LEN);
strings->cmd = ETHTOOL_GSTRINGS;
strings->string_set = ETH_SS_STATS;
strings->len = n_stats;
if (!ethtool_ioctl(fd, ifr, strings)) {
free(strings);
return NULL;
}
return strings;
}
static struct json_object * get_traffic(void) {
struct ethtool_gstrings *strings = NULL;
struct ethtool_stats *stats = NULL;
struct ifreq ifr = {};
strncpy(ifr.ifr_name, "bat0", IF_NAMESIZE);
struct json_object *ret = NULL;
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return NULL;
strings = ethtool_get_stats_strings(fd, &ifr);
if (!strings)
goto out;
stats = calloc(1, sizeof(struct ethtool_stats) + strings->len * sizeof(uint64_t));
stats->cmd = ETHTOOL_GSTATS;
stats->n_stats = strings->len;
if (!ethtool_ioctl(fd, &ifr, stats))
goto out;
struct json_object *rx = json_object_new_object();
struct json_object *tx = json_object_new_object();
struct json_object *forward = json_object_new_object();
struct json_object *mgmt_rx = json_object_new_object();
struct json_object *mgmt_tx = json_object_new_object();
size_t i;
for (i = 0; i < strings->len; i++) {
if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "rx", ETH_GSTRING_LEN))
json_object_object_add(rx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "rx_bytes", ETH_GSTRING_LEN))
json_object_object_add(rx, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx", ETH_GSTRING_LEN))
json_object_object_add(tx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx_dropped", ETH_GSTRING_LEN))
json_object_object_add(tx, "dropped", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx_bytes", ETH_GSTRING_LEN))
json_object_object_add(tx, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "forward", ETH_GSTRING_LEN))
json_object_object_add(forward, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "forward_bytes", ETH_GSTRING_LEN))
json_object_object_add(forward, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_rx", ETH_GSTRING_LEN))
json_object_object_add(mgmt_rx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_rx_bytes", ETH_GSTRING_LEN))
json_object_object_add(mgmt_rx, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_tx", ETH_GSTRING_LEN))
json_object_object_add(mgmt_tx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_tx_bytes", ETH_GSTRING_LEN))
json_object_object_add(mgmt_tx, "bytes", json_object_new_int64(stats->data[i]));
}
ret = json_object_new_object();
json_object_object_add(ret, "rx", rx);
json_object_object_add(ret, "tx", tx);
json_object_object_add(ret, "forward", forward);
json_object_object_add(ret, "mgmt_rx", mgmt_rx);
json_object_object_add(ret, "mgmt_tx", mgmt_tx);
out:
free(stats);
free(strings);
close(fd);
return ret;
}
static void count_iface_stations(size_t *wifi24, size_t *wifi5, const char *ifname) {
const struct iwinfo_ops *iw = iwinfo_backend(ifname);
if (!iw)
return;
int freq;
if (iw->frequency(ifname, &freq) < 0)
return;
size_t *wifi;
if (freq >= 2400 && freq < 2500)
wifi = wifi24;
else if (freq >= 5000 && freq < 6000)
wifi = wifi5;
else
return;
int len;
char buf[IWINFO_BUFSIZE];
if (iw->assoclist(ifname, buf, &len) < 0)
return;
struct iwinfo_assoclist_entry *entry;
for (entry = (struct iwinfo_assoclist_entry *)buf; (char*)(entry+1) <= buf + len; entry++)
(*wifi)++;
}
static void count_stations(size_t *wifi24, size_t *wifi5) {
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "wireless", &p))
goto end;
struct uci_element *e;
uci_foreach_element(&p->sections, e) {
struct uci_section *s = uci_to_section(e);
if (strcmp(s->type, "wifi-iface"))
continue;
const char *network = uci_lookup_option_string(ctx, s, "network");
if (!network || strcmp(network, "client"))
continue;
const char *mode = uci_lookup_option_string(ctx, s, "mode");
if (!mode || strcmp(mode, "ap"))
continue;
const char *ifname = uci_lookup_option_string(ctx, s, "ifname");
if (!ifname)
continue;
count_iface_stations(wifi24, wifi5, ifname);
}
end:
uci_free_context(ctx);
}
static struct json_object * get_clients(void) {
size_t total = 0, wifi = 0, wifi24 = 0, wifi5 = 0;
FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/transtable_local", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, f) >= 0) {
char addr[18], flags[16];
if (sscanf(line, " * %17[0-9a-fA-F:] [%15[^]]]", addr, flags) != 2)
continue;
if (strchr(flags, 'P'))
continue;
total++;
if (strchr(flags, 'W'))
wifi++;
}
free(line);
fclose(f);
count_stations(&wifi24, &wifi5);
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "total", json_object_new_int(total));
json_object_object_add(ret, "wifi", json_object_new_int(wifi));
json_object_object_add(ret, "wifi24", json_object_new_int(wifi24));
json_object_object_add(ret, "wifi5", json_object_new_int(wifi5));
return ret;
}
static struct json_object * respondd_provider_statistics(void) {
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "clients", get_clients());
json_object_object_add(ret, "traffic", get_traffic());
add_gateway(ret);
return ret;
}
static struct json_object * ifnames2addrs(struct json_object *interfaces) {
struct json_object *ret = json_object_new_object();
json_object_object_foreach(interfaces, ifname, interface) {
char *ifaddr = gluonutil_get_interface_address(ifname);
if (!ifaddr)
continue;
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "neighbours", json_object_get(interface));
json_object_object_add(ret, ifaddr, obj);
free(ifaddr);
}
json_object_put(interfaces);
return ret;
}
static struct json_object * get_batadv(void) {
FILE *f = fopen("/tmp/batman-adv-visdata/bat0/originators", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
struct json_object *interfaces = json_object_new_object();
while (getline(&line, &len, f) >= 0) {
char mac1[18], mac2[18];
/* IF_NAMESIZE would be enough, but adding 1 here is simpler than subtracting 1 in the format string */
char ifname[IF_NAMESIZE+1];
double lastseen;
int tq;
if (sscanf(line,
"%17[0-9a-fA-F:] %lfs ( %i ) %17[0-9a-fA-F:] [ %"STRINGIFY(IF_NAMESIZE)"[^]] ]",
mac1, &lastseen, &tq, mac2, ifname) != 5)
continue;
if (strcmp(mac1, mac2))
continue;
struct json_object *interface;
if (!json_object_object_get_ex(interfaces, ifname, &interface)) {
interface = json_object_new_object();
json_object_object_add(interfaces, ifname, interface);
}
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "tq", json_object_new_int(tq));
json_object_object_add(obj, "lastseen", json_object_new_double(lastseen));
json_object_object_add(interface, mac1, obj);
}
fclose(f);
free(line);
return ifnames2addrs(interfaces);
}
static struct json_object * get_wifi_neighbours(const char *ifname) {
const struct iwinfo_ops *iw = iwinfo_backend(ifname);
if (!iw)
return NULL;
int len;
char buf[IWINFO_BUFSIZE];
if (iw->assoclist(ifname, buf, &len) < 0)
return NULL;
struct json_object *neighbours = json_object_new_object();
struct iwinfo_assoclist_entry *entry;
for (entry = (struct iwinfo_assoclist_entry *)buf; (char*)(entry+1) <= buf + len; entry++) {
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "signal", json_object_new_int(entry->signal));
json_object_object_add(obj, "noise", json_object_new_int(entry->noise));
json_object_object_add(obj, "inactive", json_object_new_int(entry->inactive));
char mac[18];
snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
entry->mac[0], entry->mac[1], entry->mac[2],
entry->mac[3], entry->mac[4], entry->mac[5]);
json_object_object_add(neighbours, mac, obj);
}
struct json_object *ret = json_object_new_object();
if (json_object_object_length(neighbours))
json_object_object_add(ret, "neighbours", neighbours);
else
json_object_put(neighbours);
return ret;
}
static struct json_object * get_wifi(void) {
const char *mesh = "bat0";
struct json_object *ret = json_object_new_object();
const char *format = "/sys/class/net/%s/lower_*";
char pattern[strlen(format) + strlen(mesh)];
snprintf(pattern, sizeof(pattern), format, mesh);
size_t pattern_len = strlen(pattern);
glob_t lower;
if (!glob(pattern, GLOB_NOSORT, NULL, &lower)) {
size_t i;
for (i = 0; i < lower.gl_pathc; i++) {
const char *ifname = lower.gl_pathv[i] + pattern_len - 1;
char *ifaddr = gluonutil_get_interface_address(ifname);
if (!ifaddr)
continue;
struct json_object *neighbours = get_wifi_neighbours(ifname);
if (neighbours)
json_object_object_add(ret, ifaddr, neighbours);
free(ifaddr);
}
globfree(&lower);
}
return ret;
}
static struct json_object * respondd_provider_neighbours(void) {
struct json_object *ret = json_object_new_object();
struct json_object *batadv = get_batadv();
if (batadv)
json_object_object_add(ret, "batadv", batadv);
struct json_object *wifi = get_wifi();
if (wifi)
json_object_object_add(ret, "wifi", wifi);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{"statistics", respondd_provider_statistics},
{"neighbours", respondd_provider_neighbours},
{}
};
......@@ -4,6 +4,7 @@ PKG_NAME:=gluon-mesh-vpn-fastd
PKG_VERSION:=3
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(GLUONDIR)/include/package.mk
......@@ -11,25 +12,19 @@ define Package/gluon-mesh-vpn-fastd
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Support for connecting batman-adv meshes via fastd
DEPENDS:=+gluon-core gluon-mesh-batman-adv +gluon-wan-dnsmasq +fastd +iptables-mod-extra +simple-tc
endef
define Package/gluon-mesh-vpn-fastd/description
Gluon community wifi mesh firmware framework: fastd support
DEPENDS:=+gluon-core +libgluonutil gluon-mesh-batman-adv +gluon-wan-dnsmasq +fastd +iptables-mod-extra +simple-tc
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-mesh-vpn-fastd/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/mesh-vpn-fastd.so
endef
define Package/gluon-mesh-vpn-fastd/postinst
......
local ret = {
enabled = uci:get('fastd', 'mesh_vpn') and (uci:get('fastd', 'mesh_vpn', 'enabled') == nil or uci:get_bool('fastd', 'mesh_vpn', 'enabled')),
version = util.readline(io.popen('exec fastd -v')):match('^[^%s]+%s+(.+)'),
}
return ret
local json = require 'luci.jsonc'
local ltn12 = require 'luci.ltn12'
local nixio = require 'nixio'
local site = require 'gluon.site_config'
local fastd_sock = nixio.socket('unix', 'stream')
local socket_path = uci:get('fastd', 'mesh_vpn', 'status_socket')
if not fastd_sock:connect(socket_path) then
return nil
end
local decoder = json.new()
ltn12.pump.all(ltn12.source.file(fastd_sock), decoder:sink())
local status = decoder:get()
local peer_groups
local function peer_connection(config)
local peer = status.peers[config.key]
if peer then
if peer.connection then
return {
established = peer.connection.established/1000
}
else
return function()end -- nil
end
end
end
local function peer_group(config)
local ret = {}
if config.peers then
local peers = {}
for peername, peerconfig in pairs(config.peers) do
peers[peername] = peer_connection(peerconfig)
end
if next(peers) then
ret.peers = peers
end
end
ret.groups = peer_groups(config.groups)
if next(ret) then
return ret
end
end
function peer_groups(groups)
if groups then
local ret = {}
for name, group in pairs(groups) do
ret[name] = peer_group(group)
end
if next(ret) then
return ret
end
end
end
return peer_group(site.fastd_mesh_vpn)
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci
/*
Copyright (c) 2016, 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.
*/
#include <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
static struct json_object * get_peer_groups(struct json_object *groups, struct json_object *peers);
static struct json_object * get_fastd_version(void) {
FILE *f = popen("exec fastd -v", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
ssize_t r = getline(&line, &len, f);
pclose(f);
if (r >= 0) {
len = strlen(line); /* The len given by getline is the buffer size, not the string length */
if (len && line[len-1] == '\n')
line[len-1] = 0;
}
else {
free(line);
line = NULL;
}
const char *version = line;
if (strncmp(version, "fastd ", 6) == 0)
version += 6;
struct json_object *ret = gluonutil_wrap_string(version);
free(line);
return ret;
}
static struct json_object * get_fastd(void) {
bool enabled = false;
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "fastd", &p))
goto disabled;
struct uci_section *s = uci_lookup_section(ctx, p, "mesh_vpn");
if (!s)
goto disabled;
const char *enabled_str = uci_lookup_option_string(ctx, s, "enabled");
if (!enabled_str || !strcmp(enabled_str, "1"))
enabled = true;
disabled:
uci_free_context(ctx);
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "version", get_fastd_version());
json_object_object_add(ret, "enabled", json_object_new_boolean(enabled));
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct json_object *software = json_object_new_object();
json_object_object_add(software, "fastd", get_fastd());
json_object_object_add(ret, "software", software);
return ret;
}
static const char * get_status_socket(struct uci_context *ctx, struct uci_section *s) {
return uci_lookup_option_string(ctx, s, "status_socket");
}
static struct json_object * read_status(struct uci_context *ctx, struct uci_section *s) {
const char *path = get_status_socket(ctx, s);
size_t addrlen = strlen(path);
/* Allocate enough space for arbitrary-length paths */
char addrbuf[offsetof(struct sockaddr_un, sun_path) + addrlen + 1];
memset(addrbuf, 0, sizeof(addrbuf));
struct sockaddr_un *addr = (struct sockaddr_un *)addrbuf;
addr->sun_family = AF_UNIX;
memcpy(addr->sun_path, path, addrlen+1);
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
return NULL;
if (connect(fd, (struct sockaddr*)addr, sizeof(addrbuf)) < 0) {
close(fd);
return NULL;
}
struct json_object *ret = NULL;
struct json_tokener *tok = json_tokener_new();
do {
char buf[1024];
size_t len = read(fd, buf, sizeof(buf));
if (len <= 0)
break;
ret = json_tokener_parse_ex(tok, buf, len);
} while (!ret && json_tokener_get_error(tok) == json_tokener_continue);
json_tokener_free(tok);
close(fd);
return ret;
}
static struct json_object * get_status(void) {
struct json_object *ret = NULL;
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (!uci_load(ctx, "fastd", &p)) {
struct uci_section *s = uci_lookup_section(ctx, p, "mesh_vpn");
if (s)
ret = read_status(ctx, s);
}
uci_free_context(ctx);
return ret;
}
static bool get_peer_connection(struct json_object **ret, struct json_object *config, struct json_object *peers) {
struct json_object *key_object;
if (!json_object_object_get_ex(config, "key", &key_object))
return false;
const char *key = json_object_get_string(key_object);
if (!key)
return false;
struct json_object *peer, *connection, *established;
if (!json_object_object_get_ex(peers, key, &peer) ||
!json_object_object_get_ex(peer, "connection", &connection))
return false;
if (json_object_object_get_ex(connection, "established", &established)) {
int64_t established_time = json_object_get_int64(established);
*ret = json_object_new_object();
json_object_object_add(*ret, "established", json_object_new_double(established_time/1000.0));
}
else {
*ret = NULL;
}
return true;
}
static struct json_object * get_peer_group(struct json_object *config, struct json_object *peers) {
struct json_object *ret = json_object_new_object();
struct json_object *config_peers;
if (json_object_object_get_ex(config, "peers", &config_peers) &&
json_object_is_type(config_peers, json_type_object)) {
struct json_object *ret_peers = json_object_new_object();
json_object_object_foreach(config_peers, peername, peerconfig) {
struct json_object *obj;
if (get_peer_connection(&obj, peerconfig, peers))
json_object_object_add(ret_peers, peername, obj);
}
if (json_object_object_length(ret_peers))
json_object_object_add(ret, "peers", ret_peers);
else
json_object_put(ret_peers);
}
struct json_object *config_groups;
if (json_object_object_get_ex(config, "groups", &config_groups)) {
struct json_object *obj = get_peer_groups(config_groups, peers);
if (obj)
json_object_object_add(ret, "groups", obj);
}
if (!json_object_object_length(ret)) {
json_object_put(ret);
return NULL;
}
return ret;
}
static struct json_object * get_peer_groups(struct json_object *groups, struct json_object *peers) {
if (!json_object_is_type(groups, json_type_object))
return NULL;
struct json_object *ret = json_object_new_object();
json_object_object_foreach(groups, name, group) {
struct json_object *g = get_peer_group(group, peers);
if (g)
json_object_object_add(ret, name, g);
}
if (!json_object_object_length(ret)) {
json_object_put(ret);
return NULL;
}
return ret;
}
static struct json_object * get_mesh_vpn(void) {
struct json_object *ret = NULL;
struct json_object *status = NULL;
struct json_object *site = NULL;
status = get_status();
if (!status)
goto end;
struct json_object *peers;
if (!json_object_object_get_ex(status, "peers", &peers))
goto end;
site = gluonutil_load_site_config();
if (!site)
goto end;
struct json_object *fastd_mesh_vpn;
if (!json_object_object_get_ex(site, "fastd_mesh_vpn", &fastd_mesh_vpn))
goto end;
ret = get_peer_group(fastd_mesh_vpn, peers);
end:
json_object_put(site);
json_object_put(status);
return ret;
}
static struct json_object * respondd_provider_statistics(void) {
struct json_object *ret = json_object_new_object();
struct json_object *mesh_vpn = get_mesh_vpn();
if (mesh_vpn)
json_object_object_add(ret, "mesh_vpn", mesh_vpn);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{"statistics", respondd_provider_statistics},
{}
};
......@@ -5,6 +5,7 @@ PKG_VERSION:=1
PKG_RELEASE:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(GLUONDIR)/include/package.mk
......@@ -12,25 +13,19 @@ define Package/gluon-node-info
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Add /etc/config/gluon-node-info to uci
DEPENDS:=+gluon-core
endef
define Package/gluon-node-info/description
This packages creates /etc/config/gluon-node-info.
DEPENDS:=+gluon-core +libgluonutil
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-node-info/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/node-info.so
endef
define Package/gluon-node-info/postinst
......
if uci:get_first('gluon-node-info', 'location', 'share_location', false) then
return {
latitude = tonumber(uci:get_first('gluon-node-info', 'location', 'latitude')),
longitude = tonumber(uci:get_first('gluon-node-info', 'location', 'longitude')),
altitude = tonumber(uci:get_first('gluon-node-info', 'location', 'altitude')),
}
end
local contact = uci:get_first('gluon-node-info', 'owner', 'contact', '')
if contact ~= '' then
return { contact = contact }
end
local role = uci:get_first('gluon-node-info', 'system', 'role', '')
if role ~= '' then
return role
end
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci
/*
Copyright (c) 2016, 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.
*/
#include <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <stdlib.h>
#include <string.h>
static struct uci_section * get_first_section(struct uci_package *p, const char *type) {
struct uci_element *e;
uci_foreach_element(&p->sections, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, type))
return s;
}
return NULL;
}
static const char * get_first_option(struct uci_context *ctx, struct uci_package *p, const char *type, const char *option) {
struct uci_section *s = get_first_section(p, type);
if (s)
return uci_lookup_option_string(ctx, s, option);
else
return NULL;
}
static struct json_object * get_number(struct uci_context *ctx, struct uci_section *s, const char *name) {
const char *val = uci_lookup_option_string(ctx, s, name);
if (!val || !*val)
return NULL;
char *end;
double d = strtod(val, &end);
if (*end)
return NULL;
return json_object_new_double(d);
}
static struct json_object * get_location(struct uci_context *ctx, struct uci_package *p) {
struct uci_section *s = get_first_section(p, "location");
if (!s)
return NULL;
const char *share = uci_lookup_option_string(ctx, s, "share_location");
if (!share || strcmp(share, "1"))
return NULL;
struct json_object *ret = json_object_new_object();
struct json_object *latitude = get_number(ctx, s, "latitude");
if (latitude)
json_object_object_add(ret, "latitude", latitude);
struct json_object *longitude = get_number(ctx, s, "longitude");
if (longitude)
json_object_object_add(ret, "longitude", longitude);
struct json_object *altitude = get_number(ctx, s, "altitude");
if (altitude)
json_object_object_add(ret, "altitude", altitude);
return ret;
}
static struct json_object * get_owner(struct uci_context *ctx, struct uci_package *p) {
const char *contact = get_first_option(ctx, p, "owner", "contact");
if (!contact || !*contact)
return NULL;
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "contact", gluonutil_wrap_string(contact));
return ret;
}
static struct json_object * get_system(struct uci_context *ctx, struct uci_package *p) {
struct json_object *ret = json_object_new_object();
const char *role = get_first_option(ctx, p, "system", "role");
if (role && *role)
json_object_object_add(ret, "role", gluonutil_wrap_string(role));
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (!uci_load(ctx, "gluon-node-info", &p)) {
struct json_object *location = get_location(ctx, p);
if (location)
json_object_object_add(ret, "location", location);
struct json_object *owner = get_owner(ctx, p);
if (owner)
json_object_object_add(ret, "owner", owner);
json_object_object_add(ret, "system", get_system(ctx, p));
}
uci_free_context(ctx);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{}
};
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-announced
PKG_VERSION:=2
PKG_RELEASE:=1
PKG_NAME:=gluon-respondd
PKG_VERSION:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/gluon-announced
define Package/gluon-respondd
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Provides node information to the network
DEPENDS:=+gluon-announce +respondd +lua-deflate
DEPENDS:=+gluon-core +libplatforminfo +libgluonutil +respondd
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/gluon-announced/install
define Package/gluon-respondd/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/respondd.so
endef
$(eval $(call BuildPackage,gluon-announced))
$(eval $(call BuildPackage,gluon-respondd))
......@@ -3,7 +3,7 @@
. /usr/share/libubox/jshn.sh
. /lib/functions/service.sh
DEVLIST=/var/run/gluon-announced.devs
DEVLIST=/var/run/gluon-respondd.devs
DAEMON=/usr/bin/respondd
ifname_to_dev () {
......@@ -13,7 +13,7 @@ ifname_to_dev () {
echo "$dev"
}
restart_announced () {
restart_respondd () {
SERVICE_USE_PID=1
SERVICE_WRITE_PID=1
SERVICE_DAEMONIZE=1
......@@ -21,7 +21,7 @@ restart_announced () {
DEVS=$(cat $DEVLIST | while read dev iface; do echo -n " -i $dev"; done)
service_stop $DAEMON
service_start $DAEMON -g ff02::2:1001 -p 1001 -c 'return require("gluon.announced").handle_request' $DEVS
service_start $DAEMON -g ff02::2:1001 -p 1001 -d /lib/gluon/respondd $DEVS
}
case "$ACTION" in
......@@ -38,8 +38,7 @@ case "$ACTION" in
echo "$DEVS" | sort -u > $DEVLIST
restart_announced
restart_respondd
;;
esac
10000
300000
5000
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment