From f34b302b2253da7fcc0af2fe7d81534cd22a6d9f Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 23 Nov 2019 19:01:24 +0100 Subject: [PATCH 1/3] gluon-mesh-babel: remove explicit ubus socket specification, convert timeout to expected units --- package/gluon-mesh-babel/src/respondd.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package/gluon-mesh-babel/src/respondd.c b/package/gluon-mesh-babel/src/respondd.c index 80c4f970..5251e89c 100644 --- a/package/gluon-mesh-babel/src/respondd.c +++ b/package/gluon-mesh-babel/src/respondd.c @@ -74,8 +74,7 @@ #define IFNAMELEN 32 #define PROTOLEN 32 -#define UBUS_TIMEOUT 30 -#define UBUS_SOCKET "/var/run/ubus.sock" +#define UBUS_TIMEOUT 30000 static struct babelhelper_ctx bhelper_ctx = {}; @@ -330,7 +329,7 @@ static struct json_object * get_mesh_ifs() { unsigned int id=8; - ubus_ctx = ubus_connect(UBUS_SOCKET); + ubus_ctx = ubus_connect(NULL); if (!ubus_ctx) { fprintf(stderr,"could not connect to ubus, not providing mesh-data\n"); goto end; @@ -338,7 +337,7 @@ static struct json_object * get_mesh_ifs() { blob_buf_init(&b, 0); ubus_lookup_id(ubus_ctx, "network.interface", &id); - int uret = ubus_invoke(ubus_ctx, id, "dump", b.head, receive_call_result_data, &ret, UBUS_TIMEOUT * 1000); + int uret = ubus_invoke(ubus_ctx, id, "dump", b.head, receive_call_result_data, &ret, UBUS_TIMEOUT); if (uret > 0) fprintf(stderr, "ubus command failed: %s\n", ubus_strerror(uret)); From 0f1fa243f781ee5eef9ed4bc1b3c5397136ac2da Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 23 Nov 2019 21:32:15 +0100 Subject: [PATCH 2/3] Move common code from gluon-mesh-babel and -batman-adv respondd providers to gluon-respondd In addition this PR contains: - split of gluon-respondd provider into multiple source files - minor additional cleanups in gluon-mesh-babel respondd provider (untested, as the babel respondd provider already doesn't compile prior to these changes...) --- package/gluon-mesh-babel/src/Makefile | 2 +- package/gluon-mesh-babel/src/respondd.c | 230 ++----------- package/gluon-mesh-batman-adv/src/Makefile | 2 +- package/gluon-mesh-batman-adv/src/respondd.c | 175 +--------- package/gluon-respondd/src/Makefile | 6 +- package/gluon-respondd/src/respondd-common.h | 32 ++ .../gluon-respondd/src/respondd-neighbours.c | 130 ++++++++ .../gluon-respondd/src/respondd-nodeinfo.c | 132 ++++++++ .../gluon-respondd/src/respondd-statistics.c | 309 +++++++++++++++++ package/gluon-respondd/src/respondd.c | 310 +----------------- 10 files changed, 649 insertions(+), 679 deletions(-) create mode 100644 package/gluon-respondd/src/respondd-common.h create mode 100644 package/gluon-respondd/src/respondd-neighbours.c create mode 100644 package/gluon-respondd/src/respondd-nodeinfo.c create mode 100644 package/gluon-respondd/src/respondd-statistics.c diff --git a/package/gluon-mesh-babel/src/Makefile b/package/gluon-mesh-babel/src/Makefile index 98a22d5e..80d29fa9 100644 --- a/package/gluon-mesh-babel/src/Makefile +++ b/package/gluon-mesh-babel/src/Makefile @@ -25,7 +25,7 @@ LDFLAGS_JSONC = $(shell pkg-config --libs json-c) respondd.so: respondd.c handle_neighbour.c - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -shared $(LDFLAGS_JSONC) -o $@ $^ -lgluonutil -lblobmsg_json -lubox -lubus -liwinfo -luci + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -shared $(LDFLAGS_JSONC) -o $@ $^ -lgluonutil -lblobmsg_json -lubox -lubus -luci neighbours-babel: neighbours-babel.c handle_neighbour.c $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDLIBS) $(LDFLAGS_JSONC) -o $@ $^ diff --git a/package/gluon-mesh-babel/src/respondd.c b/package/gluon-mesh-babel/src/respondd.c index 5251e89c..96195d89 100644 --- a/package/gluon-mesh-babel/src/respondd.c +++ b/package/gluon-mesh-babel/src/respondd.c @@ -26,17 +26,15 @@ #include -#include #include #include #include -#include -#include #include #include #include #include +#include #include #include @@ -49,29 +47,15 @@ #include #include -#include -#include -#include - #include -#include "errno.h" +#include #include #include -#include "libubus.h" - -#define _STRINGIFY(s) #s -#define STRINGIFY(s) _STRINGIFY(s) -#include - -#define MAX_INACTIVITY 60000 +#include #define SOCKET_INPUT_BUFFER_SIZE 255 -#define BABEL_PORT 33123 -#define VPN_INTERFACE "mesh-vpn" -#define l3rdctl "/var/run/l3roamd.sock" -#define IFNAMELEN 32 #define PROTOLEN 32 #define UBUS_TIMEOUT 30000 @@ -257,9 +241,9 @@ static void blobmsg_handle_element(struct blob_attr *attr, bool head, char **ifn switch (blob_id(attr)) { case BLOBMSG_TYPE_STRING: - if (!strncmp(blobmsg_name(attr),"device", 6)) { + if (!strncmp(blobmsg_name(attr), "device", 6)) { free(*ifname); - *ifname = strndup(data, IFNAMELEN); + *ifname = strndup(data, IF_NAMESIZE); } else if (!strncmp(blobmsg_name(attr), "proto", 5)) { free(*proto); *proto = strndup(data, PROTOLEN); @@ -385,34 +369,40 @@ static struct json_object * respondd_provider_nodeinfo(void) { return ret; } -static uint64_t getnumber(const char *ifname, const char *stat) { +static struct json_object * read_number(const char *ifname, const char *stat) { const char *format = "/sys/class/net/%s/statistics/%s"; + + struct json_object *ret = NULL; + int64_t i; + char path[strlen(format) + strlen(ifname) + strlen(stat) + 1]; snprintf(path, sizeof(path), format, ifname, stat); - if (! access(path, F_OK)) { - char *line=gluonutil_read_line(path); - long long i = atoll(line); - free(line); - return(i); - } - return 0; + + FILE *f = fopen(path, "r"); + if (!f) + return NULL; + + if (fscanf(f, "%"SCNd64, &i) == 1) + ret = json_object_new_int64(i); + + fclose(f); + + return ret; } static struct json_object * get_traffic(void) { - char ifname[16]; - - strncpy(ifname, "br-client", 16); + const char *ifname = "br-client"; struct json_object *ret = NULL; struct json_object *rx = json_object_new_object(); struct json_object *tx = json_object_new_object(); - json_object_object_add(rx, "packets", json_object_new_int64(getnumber(ifname, "rx_packets"))); - json_object_object_add(rx, "bytes", json_object_new_int64(getnumber(ifname, "rx_bytes"))); - json_object_object_add(rx, "dropped", json_object_new_int64(getnumber(ifname, "rx_dropped"))); - json_object_object_add(tx, "packets", json_object_new_int64(getnumber(ifname, "tx_packets"))); - json_object_object_add(tx, "dropped", json_object_new_int64(getnumber(ifname, "tx_dropped"))); - json_object_object_add(tx, "bytes", json_object_new_int64(getnumber(ifname, "tx_bytes"))); + json_object_object_add(rx, "packets", read_number(ifname, "rx_packets")); + json_object_object_add(rx, "bytes", read_number(ifname, "rx_bytes")); + json_object_object_add(rx, "dropped", read_number(ifname, "rx_dropped")); + json_object_object_add(tx, "packets", read_number(ifname, "tx_packets")); + json_object_object_add(tx, "dropped", read_number(ifname, "tx_dropped")); + json_object_object_add(tx, "bytes", read_number(ifname, "tx_bytes")); ret = json_object_new_object(); json_object_object_add(ret, "rx", rx); @@ -421,72 +411,6 @@ static struct json_object * get_traffic(void) { 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++) { - if (entry->inactive > MAX_INACTIVITY) - continue; - - (*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 bool handle_route_addgw_nexthop(char **data, void *arg) { struct json_object *obj = (struct json_object*) arg; if (data[PREFIX] && data[FROM] && data[VIA] && data[IF]) { @@ -568,21 +492,12 @@ end: } static struct json_object * get_clients(void) { - size_t wifi24 = 0, wifi5 = 0; - - count_stations(&wifi24, &wifi5); - - int total = ask_l3roamd_for_client_count(); - - size_t wifi = wifi24 + wifi5; struct json_object *ret = json_object_new_object(); + int total = ask_l3roamd_for_client_count(); if (total >= 0) 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; } @@ -597,89 +512,6 @@ static struct json_object * respondd_provider_statistics(void) { return ret; } -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++) { - if (entry->inactive > MAX_INACTIVITY) - continue; - - 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) { - - struct uci_context *ctx = uci_alloc_context(); - ctx->flags &= ~UCI_FLAG_STRICT; - - struct json_object *ret = json_object_new_object(); - - struct uci_package *p; - if (uci_load(ctx, "network", &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, "interface")) - continue; - - const char *proto = uci_lookup_option_string(ctx, s, "proto"); - if (!proto || strcmp(proto, "gluon_mesh")) - continue; - - const char *ifname = uci_lookup_option_string(ctx, s, "ifname"); - if (!ifname) - continue; - - 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); - } - -end: - uci_free_context(ctx); - return ret; -} - static struct json_object * respondd_provider_neighbours(void) { struct json_object *ret = json_object_new_object(); @@ -688,10 +520,6 @@ static struct json_object * respondd_provider_neighbours(void) { json_object_object_add(ret, "babel", babel); - struct json_object *wifi = get_wifi(); - if (wifi) - json_object_object_add(ret, "wifi", wifi); - return ret; } diff --git a/package/gluon-mesh-batman-adv/src/Makefile b/package/gluon-mesh-batman-adv/src/Makefile index 0974669b..b40c52b1 100644 --- a/package/gluon-mesh-batman-adv/src/Makefile +++ b/package/gluon-mesh-batman-adv/src/Makefile @@ -32,4 +32,4 @@ CFLAGS += $(LIBBATADV_CFLAGS) LDLIBS += $(LIBBATADV_LDLIBS) respondd.so: respondd.c - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -liwinfo -luci + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci diff --git a/package/gluon-mesh-batman-adv/src/respondd.c b/package/gluon-mesh-batman-adv/src/respondd.c index c2b20399..1df8d43f 100644 --- a/package/gluon-mesh-batman-adv/src/respondd.c +++ b/package/gluon-mesh-batman-adv/src/respondd.c @@ -26,8 +26,6 @@ #include -#include -#include #include #include #include @@ -59,9 +57,6 @@ #include -#define _STRINGIFY(s) #s -#define STRINGIFY(s) _STRINGIFY(s) - #define MAX_INACTIVITY 60000 @@ -76,7 +71,7 @@ struct gw_netlink_opts { }; struct clients_netlink_opts { - size_t non_wifi; + size_t clients; struct batadv_nlquery_opts query_opts; }; @@ -429,74 +424,6 @@ static struct json_object * get_traffic(void) { 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++) { - if (entry->inactive > MAX_INACTIVITY) - continue; - - (*wifi)++; - } -} - -static void count_stations(size_t *wifi24, size_t *wifi5) { - struct uci_context *ctx = uci_alloc_context(); - if (!ctx) - return; - 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 const enum batadv_nl_attrs clients_mandatory[] = { BATADV_ATTR_TT_FLAGS, /* Entries without the BATADV_TT_CLIENT_NOPURGE flag do not have a @@ -537,24 +464,21 @@ static int parse_clients_list_netlink_cb(struct nl_msg *msg, void *arg) flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]); - if (flags & (BATADV_TT_CLIENT_NOPURGE | BATADV_TT_CLIENT_WIFI)) + if (flags & (BATADV_TT_CLIENT_NOPURGE)) return NL_OK; lastseen = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); if (lastseen > MAX_INACTIVITY) return NL_OK; - opts->non_wifi++; + opts->clients++; return NL_OK; } static struct json_object * get_clients(void) { - size_t wifi24 = 0, wifi5 = 0; - size_t total; - size_t wifi; struct clients_netlink_opts opts = { - .non_wifi = 0, + .clients = 0, .query_opts = { .err = 0, }, @@ -564,15 +488,10 @@ static struct json_object * get_clients(void) { parse_clients_list_netlink_cb, NLM_F_DUMP, &opts.query_opts); - count_stations(&wifi24, &wifi5); - wifi = wifi24 + wifi5; - total = wifi + opts.non_wifi; - 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)); + + json_object_object_add(ret, "total", json_object_new_int(opts.clients)); + return ret; } @@ -679,7 +598,7 @@ static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg) json_object_object_add(obj, "tq", json_object_new_int(tq)); json_object_object_add(obj, "lastseen", json_object_new_double(lastseen / 1000.)); - json_object_object_add(obj, "best", json_object_new_boolean(attrs[BATADV_ATTR_FLAG_BEST])); + json_object_object_add(obj, "best", json_object_new_boolean(!!attrs[BATADV_ATTR_FLAG_BEST])); json_object_object_add(interface, mac1, obj); return NL_OK; @@ -708,80 +627,6 @@ static struct json_object * get_batadv(void) { return ifnames2addrs(opts.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++) { - if (entry->inactive > MAX_INACTIVITY) - continue; - - 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(); @@ -789,10 +634,6 @@ static struct json_object * respondd_provider_neighbours(void) { 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; } diff --git a/package/gluon-respondd/src/Makefile b/package/gluon-respondd/src/Makefile index f26b59a2..9e1e831d 100644 --- a/package/gluon-respondd/src/Makefile +++ b/package/gluon-respondd/src/Makefile @@ -2,5 +2,7 @@ all: respondd.so CFLAGS += -Wall -respondd.so: respondd.c - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -lplatforminfo -luci +SOURCES = respondd.c respondd-nodeinfo.c respondd-statistics.c respondd-neighbours.c + +respondd.so: $(SOURCES) respondd-common.h + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -fvisibility=hidden -D_GNU_SOURCE -o $@ $(SOURCES) $(LDLIBS) -lgluonutil -lplatforminfo -luci -liwinfo diff --git a/package/gluon-respondd/src/respondd-common.h b/package/gluon-respondd/src/respondd-common.h new file mode 100644 index 00000000..db40cbaf --- /dev/null +++ b/package/gluon-respondd/src/respondd-common.h @@ -0,0 +1,32 @@ +/* + Copyright (c) 2016-2019, 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. +*/ + +#pragma once + +#define MAX_INACTIVITY 60000 + +struct json_object * respondd_provider_nodeinfo(void); +struct json_object * respondd_provider_statistics(void); +struct json_object * respondd_provider_neighbours(void); diff --git a/package/gluon-respondd/src/respondd-neighbours.c b/package/gluon-respondd/src/respondd-neighbours.c new file mode 100644 index 00000000..2a07634f --- /dev/null +++ b/package/gluon-respondd/src/respondd-neighbours.c @@ -0,0 +1,130 @@ +/* + Copyright (c) 2016-2019, 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. +*/ + +#include "respondd-common.h" + +#include + +#include +#include + + +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++) { + if (entry->inactive > MAX_INACTIVITY) + continue; + + 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) { + struct uci_context *ctx = uci_alloc_context(); + if (!ctx) + return NULL; + + ctx->flags &= ~UCI_FLAG_STRICT; + + struct json_object *ret = json_object_new_object(); + + struct uci_package *p; + if (uci_load(ctx, "network", &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, "interface")) + continue; + + const char *proto = uci_lookup_option_string(ctx, s, "proto"); + if (!proto || strcmp(proto, "gluon_mesh")) + continue; + + const char *ifname = uci_lookup_option_string(ctx, s, "ifname"); + if (!ifname) + continue; + + 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); + } + +end: + uci_free_context(ctx); + return ret; +} + +struct json_object * respondd_provider_neighbours(void) { + struct json_object *ret = json_object_new_object(); + + json_object_object_add(ret, "node_id", gluonutil_wrap_and_free_string(gluonutil_get_node_id())); + + struct json_object *wifi = get_wifi(); + if (wifi) + json_object_object_add(ret, "wifi", wifi); + + + return ret; +} diff --git a/package/gluon-respondd/src/respondd-nodeinfo.c b/package/gluon-respondd/src/respondd-nodeinfo.c new file mode 100644 index 00000000..f70abc9a --- /dev/null +++ b/package/gluon-respondd/src/respondd-nodeinfo.c @@ -0,0 +1,132 @@ +/* + Copyright (c) 2016-2019, 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. +*/ + +#include "respondd-common.h" + +#include +#include + +#include +#include + +#include +#include +#include + + +static struct json_object * gluon_version(void) { + char *version = gluonutil_read_line("/lib/gluon/gluon-version"); + if (!version) + return NULL; + + char full_version[6 + strlen(version) + 1]; + snprintf(full_version, sizeof(full_version), "gluon-%s", version); + + free(version); + + + return json_object_new_string(full_version); +} + +static struct json_object * get_site_code(void) { + struct json_object *site = gluonutil_load_site_config(); + if (!site) + return NULL; + + struct json_object *ret = NULL; + json_object_object_get_ex(site, "site_code", &ret); + if (ret) + json_object_get(ret); + + json_object_put(site); + return ret; +} + +static struct json_object * get_domain_code(void) { + return gluonutil_wrap_and_free_string(gluonutil_get_domain()); +} + +static struct json_object * get_hostname(void) { + struct json_object *ret = NULL; + + struct uci_context *ctx = uci_alloc_context(); + if (!ctx) + return NULL; + ctx->flags &= ~UCI_FLAG_STRICT; + + char section[] = "system.@system[0]"; + struct uci_ptr ptr; + if (uci_lookup_ptr(ctx, &ptr, section, true)) + goto error; + + struct uci_section *s = ptr.s; + + const char *hostname = uci_lookup_option_string(ctx, s, "pretty_hostname"); + + if (!hostname) + hostname = uci_lookup_option_string(ctx, s, "hostname"); + + ret = gluonutil_wrap_string(hostname); + +error: + uci_free_context(ctx); + + return ret; +} + +struct json_object * respondd_provider_nodeinfo(void) { + struct json_object *ret = json_object_new_object(); + + json_object_object_add(ret, "node_id", gluonutil_wrap_and_free_string(gluonutil_get_node_id())); + json_object_object_add(ret, "hostname", get_hostname()); + + struct json_object *hardware = json_object_new_object(); + + const char *model = platforminfo_get_model(); + if (model) + json_object_object_add(hardware, "model", json_object_new_string(model)); + + json_object_object_add(hardware, "nproc", json_object_new_int(sysconf(_SC_NPROCESSORS_ONLN))); + json_object_object_add(ret, "hardware", hardware); + + struct json_object *network = json_object_new_object(); + json_object_object_add(network, "mac", gluonutil_wrap_and_free_string(gluonutil_get_sysconfig("primary_mac"))); + json_object_object_add(ret, "network", network); + + struct json_object *software = json_object_new_object(); + struct json_object *software_firmware = json_object_new_object(); + json_object_object_add(software_firmware, "base", gluon_version()); + json_object_object_add(software_firmware, "release", gluonutil_wrap_and_free_string(gluonutil_read_line("/lib/gluon/release"))); + json_object_object_add(software, "firmware", software_firmware); + json_object_object_add(ret, "software", software); + + struct json_object *system = json_object_new_object(); + json_object_object_add(system, "site_code", get_site_code()); + if (gluonutil_has_domains()) + json_object_object_add(system, "domain_code", get_domain_code()); + json_object_object_add(ret, "system", system); + + return ret; +} diff --git a/package/gluon-respondd/src/respondd-statistics.c b/package/gluon-respondd/src/respondd-statistics.c new file mode 100644 index 00000000..634b2942 --- /dev/null +++ b/package/gluon-respondd/src/respondd-statistics.c @@ -0,0 +1,309 @@ +/* + Copyright (c) 2016-2019, 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. +*/ + +#include "respondd-common.h" + +#include + +#include +#include + +#include +#include +#include +#include + +#include + + +static void add_uptime(struct json_object *obj) { + FILE *f = fopen("/proc/uptime", "r"); + struct json_object* jso; + if (!f) + return; + + double uptime, idletime; + if (fscanf(f, "%lf %lf", &uptime, &idletime) == 2) { + jso = json_object_new_double(uptime); + json_object_set_serializer(jso, json_object_double_to_json_string, "%.2f", NULL); + json_object_object_add(obj, "uptime", jso); + jso = json_object_new_double(idletime); + json_object_set_serializer(jso, json_object_double_to_json_string, "%.2f", NULL); + json_object_object_add(obj, "idletime", jso); + } + + fclose(f); +} + +static void add_loadavg(struct json_object *obj) { + FILE *f = fopen("/proc/loadavg", "r"); + if (!f) + return; + + double loadavg; + unsigned proc_running, proc_total; + if (fscanf(f, "%lf %*f %*f %u/%u", &loadavg, &proc_running, &proc_total) == 3) { + struct json_object *jso = json_object_new_double(loadavg); + json_object_set_serializer(jso, json_object_double_to_json_string, "%.2f", NULL); + json_object_object_add(obj, "loadavg", jso); + + struct json_object *processes = json_object_new_object(); + json_object_object_add(processes, "running", json_object_new_int(proc_running)); + json_object_object_add(processes, "total", json_object_new_int(proc_total)); + json_object_object_add(obj, "processes", processes); + } + + fclose(f); +} + +static struct json_object * get_memory(void) { + FILE *f = fopen("/proc/meminfo", "r"); + if (!f) + return NULL; + + struct json_object *ret = json_object_new_object(); + + char *line = NULL; + size_t len = 0; + + while (getline(&line, &len, f) >= 0) { + char label[32]; + unsigned value; + + if (sscanf(line, "%31[^:]: %u", label, &value) != 2) + continue; + + if (!strcmp(label, "MemTotal")) + json_object_object_add(ret, "total", json_object_new_int(value)); + else if (!strcmp(label, "MemFree")) + json_object_object_add(ret, "free", json_object_new_int(value)); + else if (!strcmp(label, "MemAvailable")) + json_object_object_add(ret, "available", json_object_new_int(value)); + else if (!strcmp(label, "Buffers")) + json_object_object_add(ret, "buffers", json_object_new_int(value)); + else if (!strcmp(label, "Cached")) + json_object_object_add(ret, "cached", json_object_new_int(value)); + } + + free(line); + fclose(f); + + return ret; +} + +static struct json_object * get_stat(void) { + FILE *f = fopen("/proc/stat", "r"); + if (!f) + return NULL; + + struct json_object *stat = json_object_new_object(); + struct json_object *ret = NULL; + + char *line = NULL; + size_t len = 0; + + while (getline(&line, &len, f) >= 0) { + char label[32]; + + if (sscanf(line, "%31s", label) != 1){ + goto invalid_stat_format; + } + + if (!strcmp(label, "cpu")) { + int64_t user, nice, system, idle, iowait, irq, softirq; + if (sscanf(line, "%*s %"SCNd64" %"SCNd64" %"SCNd64" %"SCNd64" %"SCNd64" %"SCNd64" %"SCNd64, + &user, &nice, &system, &idle, &iowait, &irq, &softirq) != 7) + goto invalid_stat_format; + + struct json_object *cpu = json_object_new_object(); + + json_object_object_add(cpu, "user", json_object_new_int64(user)); + json_object_object_add(cpu, "nice", json_object_new_int64(nice)); + json_object_object_add(cpu, "system", json_object_new_int64(system)); + json_object_object_add(cpu, "idle", json_object_new_int64(idle)); + json_object_object_add(cpu, "iowait", json_object_new_int64(iowait)); + json_object_object_add(cpu, "irq", json_object_new_int64(irq)); + json_object_object_add(cpu, "softirq", json_object_new_int64(softirq)); + + json_object_object_add(stat, "cpu", cpu); + } else if (!strcmp(label, "ctxt")) { + int64_t ctxt; + if (sscanf(line, "%*s %"SCNd64, &ctxt) != 1) + goto invalid_stat_format; + + json_object_object_add(stat, "ctxt", json_object_new_int64(ctxt)); + } else if (!strcmp(label, "intr")) { + int64_t total_intr; + if (sscanf(line, "%*s %"SCNd64, &total_intr) != 1) + goto invalid_stat_format; + + json_object_object_add(stat, "intr", json_object_new_int64(total_intr)); + } else if (!strcmp(label, "softirq")) { + int64_t total_softirq; + if (sscanf(line, "%*s %"SCNd64, &total_softirq) != 1) + goto invalid_stat_format; + + json_object_object_add(stat, "softirq", json_object_new_int64(total_softirq)); + } else if (!strcmp(label, "processes")) { + int64_t processes; + if (sscanf(line, "%*s %"SCNd64, &processes) != 1) + goto invalid_stat_format; + + json_object_object_add(stat, "processes", json_object_new_int64(processes)); + } + + } + + ret = stat; + +invalid_stat_format: + if (!ret) + json_object_put(stat); + + free(line); + fclose(f); + + return ret; +} + + +static struct json_object * get_rootfs_usage(void) { + struct statfs s; + if (statfs("/", &s)) + return NULL; + + struct json_object *jso = json_object_new_double(1 - (double)s.f_bfree / s.f_blocks); + json_object_set_serializer(jso, json_object_double_to_json_string, "%.4f", NULL); + return jso; +} + +static struct json_object * get_time(void) { + struct timespec now; + + if (clock_gettime(CLOCK_REALTIME, &now) != 0) + return NULL; + + return json_object_new_int64(now.tv_sec); +} + +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++) { + if (entry->inactive > MAX_INACTIVITY) + continue; + + (*wifi)++; + } +} + +static void count_stations(size_t *wifi24, size_t *wifi5) { + struct uci_context *ctx = uci_alloc_context(); + if (!ctx) + return; + 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 wifi24 = 0, wifi5 = 0; + + count_stations(&wifi24, &wifi5); + + struct json_object *ret = json_object_new_object(); + + json_object_object_add(ret, "wifi", json_object_new_int(wifi24 + wifi5)); + json_object_object_add(ret, "wifi24", json_object_new_int(wifi24)); + json_object_object_add(ret, "wifi5", json_object_new_int(wifi5)); + + return ret; +} + +struct json_object * respondd_provider_statistics(void) { + struct json_object *ret = json_object_new_object(); + + json_object_object_add(ret, "node_id", gluonutil_wrap_and_free_string(gluonutil_get_node_id())); + + json_object_object_add(ret, "time", get_time()); + json_object_object_add(ret, "rootfs_usage", get_rootfs_usage()); + json_object_object_add(ret, "memory", get_memory()); + json_object_object_add(ret, "stat", get_stat()); + + json_object_object_add(ret, "clients", get_clients()); + + add_uptime(ret); + add_loadavg(ret); + + return ret; +} diff --git a/package/gluon-respondd/src/respondd.c b/package/gluon-respondd/src/respondd.c index 04010c68..36188e46 100644 --- a/package/gluon-respondd/src/respondd.c +++ b/package/gluon-respondd/src/respondd.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Matthias Schiffer + Copyright (c) 2016-2019, Matthias Schiffer All rights reserved. Redistribution and use in source and binary forms, with or without @@ -23,316 +23,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "respondd-common.h" #include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - - -static struct json_object * gluon_version(void) { - char *version = gluonutil_read_line("/lib/gluon/gluon-version"); - if (!version) - return NULL; - - char full_version[6 + strlen(version) + 1]; - snprintf(full_version, sizeof(full_version), "gluon-%s", version); - - free(version); - - - return json_object_new_string(full_version); -} - -static struct json_object * get_site_code(void) { - struct json_object *site = gluonutil_load_site_config(); - if (!site) - return NULL; - - struct json_object *ret = NULL; - json_object_object_get_ex(site, "site_code", &ret); - if (ret) - json_object_get(ret); - - json_object_put(site); - return ret; -} - -static struct json_object * get_domain_code(void) { - return gluonutil_wrap_and_free_string(gluonutil_get_domain()); -} - -static struct json_object * get_hostname(void) { - struct json_object *ret = NULL; - - struct uci_context *ctx = uci_alloc_context(); - if (!ctx) - return NULL; - ctx->flags &= ~UCI_FLAG_STRICT; - - char section[] = "system.@system[0]"; - struct uci_ptr ptr; - if (uci_lookup_ptr(ctx, &ptr, section, true)) - goto error; - - struct uci_section *s = ptr.s; - - const char *hostname = uci_lookup_option_string(ctx, s, "pretty_hostname"); - - if (!hostname) - hostname = uci_lookup_option_string(ctx, s, "hostname"); - - ret = gluonutil_wrap_string(hostname); - -error: - uci_free_context(ctx); - - return ret; -} - -static struct json_object * respondd_provider_nodeinfo(void) { - struct json_object *ret = json_object_new_object(); - - json_object_object_add(ret, "node_id", gluonutil_wrap_and_free_string(gluonutil_get_node_id())); - json_object_object_add(ret, "hostname", get_hostname()); - - struct json_object *hardware = json_object_new_object(); - - const char *model = platforminfo_get_model(); - if (model) - json_object_object_add(hardware, "model", json_object_new_string(model)); - - json_object_object_add(hardware, "nproc", json_object_new_int(sysconf(_SC_NPROCESSORS_ONLN))); - json_object_object_add(ret, "hardware", hardware); - - struct json_object *network = json_object_new_object(); - json_object_object_add(network, "mac", gluonutil_wrap_and_free_string(gluonutil_get_sysconfig("primary_mac"))); - json_object_object_add(ret, "network", network); - - struct json_object *software = json_object_new_object(); - struct json_object *software_firmware = json_object_new_object(); - json_object_object_add(software_firmware, "base", gluon_version()); - json_object_object_add(software_firmware, "release", gluonutil_wrap_and_free_string(gluonutil_read_line("/lib/gluon/release"))); - json_object_object_add(software, "firmware", software_firmware); - json_object_object_add(ret, "software", software); - - struct json_object *system = json_object_new_object(); - json_object_object_add(system, "site_code", get_site_code()); - if (gluonutil_has_domains()) - json_object_object_add(system, "domain_code", get_domain_code()); - json_object_object_add(ret, "system", system); - - return ret; -} - - -static void add_uptime(struct json_object *obj) { - FILE *f = fopen("/proc/uptime", "r"); - struct json_object* jso; - if (!f) - return; - - double uptime, idletime; - if (fscanf(f, "%lf %lf", &uptime, &idletime) == 2) { - jso = json_object_new_double(uptime); - json_object_set_serializer(jso, json_object_double_to_json_string, "%.2f", NULL); - json_object_object_add(obj, "uptime", jso); - jso = json_object_new_double(idletime); - json_object_set_serializer(jso, json_object_double_to_json_string, "%.2f", NULL); - json_object_object_add(obj, "idletime", jso); - } - - fclose(f); -} - -static void add_loadavg(struct json_object *obj) { - FILE *f = fopen("/proc/loadavg", "r"); - if (!f) - return; - - double loadavg; - unsigned proc_running, proc_total; - if (fscanf(f, "%lf %*f %*f %u/%u", &loadavg, &proc_running, &proc_total) == 3) { - struct json_object *jso = json_object_new_double(loadavg); - json_object_set_serializer(jso, json_object_double_to_json_string, "%.2f", NULL); - json_object_object_add(obj, "loadavg", jso); - - struct json_object *processes = json_object_new_object(); - json_object_object_add(processes, "running", json_object_new_int(proc_running)); - json_object_object_add(processes, "total", json_object_new_int(proc_total)); - json_object_object_add(obj, "processes", processes); - } - - fclose(f); -} - -static struct json_object * get_memory(void) { - FILE *f = fopen("/proc/meminfo", "r"); - if (!f) - return NULL; - - struct json_object *ret = json_object_new_object(); - - char *line = NULL; - size_t len = 0; - - while (getline(&line, &len, f) >= 0) { - char label[32]; - unsigned value; - - if (sscanf(line, "%31[^:]: %u", label, &value) != 2) - continue; - - if (!strcmp(label, "MemTotal")) - json_object_object_add(ret, "total", json_object_new_int(value)); - else if (!strcmp(label, "MemFree")) - json_object_object_add(ret, "free", json_object_new_int(value)); - else if (!strcmp(label, "MemAvailable")) - json_object_object_add(ret, "available", json_object_new_int(value)); - else if (!strcmp(label, "Buffers")) - json_object_object_add(ret, "buffers", json_object_new_int(value)); - else if (!strcmp(label, "Cached")) - json_object_object_add(ret, "cached", json_object_new_int(value)); - } - - free(line); - fclose(f); - - return ret; -} - -static struct json_object * get_stat(void) { - FILE *f = fopen("/proc/stat", "r"); - if (!f) - return NULL; - - struct json_object *stat = json_object_new_object(); - struct json_object *ret = NULL; - - char *line = NULL; - size_t len = 0; - - while (getline(&line, &len, f) >= 0) { - char label[32]; - - if (sscanf(line, "%31s", label) != 1){ - goto invalid_stat_format; - } - - if (!strcmp(label, "cpu")) { - int64_t user, nice, system, idle, iowait, irq, softirq; - if (sscanf(line, "%*s %"SCNd64" %"SCNd64" %"SCNd64" %"SCNd64" %"SCNd64" %"SCNd64" %"SCNd64, - &user, &nice, &system, &idle, &iowait, &irq, &softirq) != 7) - goto invalid_stat_format; - - struct json_object *cpu = json_object_new_object(); - - json_object_object_add(cpu, "user", json_object_new_int64(user)); - json_object_object_add(cpu, "nice", json_object_new_int64(nice)); - json_object_object_add(cpu, "system", json_object_new_int64(system)); - json_object_object_add(cpu, "idle", json_object_new_int64(idle)); - json_object_object_add(cpu, "iowait", json_object_new_int64(iowait)); - json_object_object_add(cpu, "irq", json_object_new_int64(irq)); - json_object_object_add(cpu, "softirq", json_object_new_int64(softirq)); - - json_object_object_add(stat, "cpu", cpu); - } else if (!strcmp(label, "ctxt")) { - int64_t ctxt; - if (sscanf(line, "%*s %"SCNd64, &ctxt) != 1) - goto invalid_stat_format; - - json_object_object_add(stat, "ctxt", json_object_new_int64(ctxt)); - } else if (!strcmp(label, "intr")) { - int64_t total_intr; - if (sscanf(line, "%*s %"SCNd64, &total_intr) != 1) - goto invalid_stat_format; - - json_object_object_add(stat, "intr", json_object_new_int64(total_intr)); - } else if (!strcmp(label, "softirq")) { - int64_t total_softirq; - if (sscanf(line, "%*s %"SCNd64, &total_softirq) != 1) - goto invalid_stat_format; - - json_object_object_add(stat, "softirq", json_object_new_int64(total_softirq)); - } else if (!strcmp(label, "processes")) { - int64_t processes; - if (sscanf(line, "%*s %"SCNd64, &processes) != 1) - goto invalid_stat_format; - - json_object_object_add(stat, "processes", json_object_new_int64(processes)); - } - - } - - ret = stat; - -invalid_stat_format: - if (!ret) - json_object_put(stat); - - free(line); - fclose(f); - - return ret; -} - - -static struct json_object * get_rootfs_usage(void) { - struct statfs s; - if (statfs("/", &s)) - return NULL; - - struct json_object *jso = json_object_new_double(1 - (double)s.f_bfree / s.f_blocks); - json_object_set_serializer(jso, json_object_double_to_json_string, "%.4f", NULL); - return jso; -} - -static struct json_object * get_time(void) { - struct timespec now; - - if (clock_gettime(CLOCK_REALTIME, &now) != 0) - return NULL; - - return json_object_new_int64(now.tv_sec); -} - -static struct json_object * respondd_provider_statistics(void) { - struct json_object *ret = json_object_new_object(); - - json_object_object_add(ret, "node_id", gluonutil_wrap_and_free_string(gluonutil_get_node_id())); - - json_object *time = get_time(); - if (time != NULL) - json_object_object_add(ret, "time", time); - - json_object_object_add(ret, "rootfs_usage", get_rootfs_usage()); - json_object_object_add(ret, "memory", get_memory()); - json_object_object_add(ret, "stat", get_stat()); - - add_uptime(ret); - add_loadavg(ret); - - return ret; -} - - -static struct json_object * respondd_provider_neighbours(void) { - struct json_object *ret = json_object_new_object(); - json_object_object_add(ret, "node_id", gluonutil_wrap_and_free_string(gluonutil_get_node_id())); - return ret; -} - +__attribute__ ((visibility ("default"))) const struct respondd_provider_info respondd_providers[] = { {"nodeinfo", respondd_provider_nodeinfo}, {"statistics", respondd_provider_statistics}, From ecc29e0b09c1e669899c97e211dc63063b064a3e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 23 Nov 2019 22:31:42 +0100 Subject: [PATCH 3/3] gluon-mesh-batman-adv: further cleanup of respondd provider - Split into multiple files - Avoid alloca() --- package/gluon-mesh-batman-adv/src/Makefile | 7 +- .../src/respondd-common.h | 30 + .../src/respondd-neighbours.c | 176 +++++ .../src/respondd-nodeinfo.c | 219 +++++++ .../src/respondd-statistics.c | 322 +++++++++ package/gluon-mesh-batman-adv/src/respondd.c | 615 +----------------- 6 files changed, 755 insertions(+), 614 deletions(-) create mode 100644 package/gluon-mesh-batman-adv/src/respondd-common.h create mode 100644 package/gluon-mesh-batman-adv/src/respondd-neighbours.c create mode 100644 package/gluon-mesh-batman-adv/src/respondd-nodeinfo.c create mode 100644 package/gluon-mesh-batman-adv/src/respondd-statistics.c diff --git a/package/gluon-mesh-batman-adv/src/Makefile b/package/gluon-mesh-batman-adv/src/Makefile index b40c52b1..e72dce56 100644 --- a/package/gluon-mesh-batman-adv/src/Makefile +++ b/package/gluon-mesh-batman-adv/src/Makefile @@ -31,5 +31,8 @@ endif CFLAGS += $(LIBBATADV_CFLAGS) LDLIBS += $(LIBBATADV_LDLIBS) -respondd.so: respondd.c - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci + +SOURCES = respondd.c respondd-nodeinfo.c respondd-statistics.c respondd-neighbours.c + +respondd.so: $(SOURCES) respondd-common.h + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -fvisibility=hidden -D_GNU_SOURCE -o $@ $(SOURCES) $(LDLIBS) -lgluonutil diff --git a/package/gluon-mesh-batman-adv/src/respondd-common.h b/package/gluon-mesh-batman-adv/src/respondd-common.h new file mode 100644 index 00000000..54145ff9 --- /dev/null +++ b/package/gluon-mesh-batman-adv/src/respondd-common.h @@ -0,0 +1,30 @@ +/* + Copyright (c) 2016-2019, 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. +*/ + +#pragma once + +struct json_object * respondd_provider_nodeinfo(void); +struct json_object * respondd_provider_statistics(void); +struct json_object * respondd_provider_neighbours(void); diff --git a/package/gluon-mesh-batman-adv/src/respondd-neighbours.c b/package/gluon-mesh-batman-adv/src/respondd-neighbours.c new file mode 100644 index 00000000..bf5b0315 --- /dev/null +++ b/package/gluon-mesh-batman-adv/src/respondd-neighbours.c @@ -0,0 +1,176 @@ +/* + Copyright (c) 2016-2019, 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. +*/ + +#include "respondd-common.h" + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + + +struct neigh_netlink_opts { + struct json_object *interfaces; + struct batadv_nlquery_opts query_opts; +}; + + +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 const enum batadv_nl_attrs parse_orig_list_mandatory[] = { + BATADV_ATTR_ORIG_ADDRESS, + BATADV_ATTR_NEIGH_ADDRESS, + BATADV_ATTR_TQ, + BATADV_ATTR_HARD_IFINDEX, + BATADV_ATTR_LAST_SEEN_MSECS, +}; + +static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct batadv_nlquery_opts *query_opts = arg; + struct genlmsghdr *ghdr; + uint8_t *orig; + uint8_t *dest; + uint8_t tq; + uint32_t hardif; + uint32_t lastseen; + char ifname_buf[IF_NAMESIZE], *ifname; + struct neigh_netlink_opts *opts; + char mac1[18]; + + opts = batadv_container_of(query_opts, struct neigh_netlink_opts, + query_opts); + + if (!genlmsg_valid_hdr(nlh, 0)) + return NL_OK; + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_genl_policy)) + return NL_OK; + + if (batadv_genl_missing_attrs(attrs, parse_orig_list_mandatory, + BATADV_ARRAY_SIZE(parse_orig_list_mandatory))) + return NL_OK; + + orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); + dest = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); + tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); + hardif = nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]); + lastseen = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); + + if (memcmp(orig, dest, 6) != 0) + return NL_OK; + + ifname = if_indextoname(hardif, ifname_buf); + if (!ifname) + return NL_OK; + + sprintf(mac1, "%02x:%02x:%02x:%02x:%02x:%02x", + orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); + + struct json_object *obj = json_object_new_object(); + if (!obj) + return NL_OK; + + struct json_object *interface; + if (!json_object_object_get_ex(opts->interfaces, ifname, &interface)) { + interface = json_object_new_object(); + json_object_object_add(opts->interfaces, ifname, interface); + } + + json_object_object_add(obj, "tq", json_object_new_int(tq)); + json_object_object_add(obj, "lastseen", json_object_new_double(lastseen / 1000.)); + json_object_object_add(obj, "best", json_object_new_boolean(!!attrs[BATADV_ATTR_FLAG_BEST])); + json_object_object_add(interface, mac1, obj); + + return NL_OK; +} + +static struct json_object * get_batadv(void) { + struct neigh_netlink_opts opts = { + .query_opts = { + .err = 0, + }, + }; + int ret; + + opts.interfaces = json_object_new_object(); + if (!opts.interfaces) + return NULL; + + ret = batadv_genl_query("bat0", BATADV_CMD_GET_ORIGINATORS, + parse_orig_list_netlink_cb, NLM_F_DUMP, + &opts.query_opts); + if (ret < 0) { + json_object_put(opts.interfaces); + return NULL; + } + + return ifnames2addrs(opts.interfaces); +} + +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); + + return ret; +} diff --git a/package/gluon-mesh-batman-adv/src/respondd-nodeinfo.c b/package/gluon-mesh-batman-adv/src/respondd-nodeinfo.c new file mode 100644 index 00000000..36e330f9 --- /dev/null +++ b/package/gluon-mesh-batman-adv/src/respondd-nodeinfo.c @@ -0,0 +1,219 @@ +/* + Copyright (c) 2016-2019, 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. +*/ + +#include "respondd-common.h" + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +struct ip_address_information { + unsigned int ifindex; + struct json_object *addresses; +}; + +static int get_addresses_cb(struct nl_msg *msg, void *arg) { + struct ip_address_information *info = (struct ip_address_information*) arg; + + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct ifaddrmsg *msg_content = NLMSG_DATA(nlh); + int remaining = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + struct rtattr *hdr; + + for (hdr = IFA_RTA(msg_content); RTA_OK(hdr, remaining); hdr = RTA_NEXT(hdr, remaining)) { + char addr_str_buf[INET6_ADDRSTRLEN]; + + /* We are only interested in IP-addresses of br-client */ + if (hdr->rta_type != IFA_ADDRESS || + msg_content->ifa_index != info->ifindex || + msg_content->ifa_flags & (IFA_F_TENTATIVE|IFA_F_DEPRECATED)) { + continue; + } + + if (inet_ntop(AF_INET6, (struct in6_addr *) RTA_DATA(hdr), addr_str_buf, INET6_ADDRSTRLEN)) { + json_object_array_add(info->addresses, json_object_new_string(addr_str_buf)); + } + } + + return NL_OK; +} + +static struct json_object *get_addresses(void) { + struct ip_address_information info = { + .ifindex = if_nametoindex("br-client"), + .addresses = json_object_new_array(), + }; + int err; + + /* Open socket */ + struct nl_sock *socket = nl_socket_alloc(); + if (!socket) { + return info.addresses; + } + + err = nl_connect(socket, NETLINK_ROUTE); + if (err < 0) { + goto out_free; + } + + /* Send message */ + struct ifaddrmsg rt_hdr = { .ifa_family = AF_INET6, }; + err = nl_send_simple(socket, RTM_GETADDR, NLM_F_REQUEST | NLM_F_ROOT, &rt_hdr, sizeof(struct ifaddrmsg)); + if (err < 0) { + goto out_free; + } + + /* Retrieve answer. Message is handled by get_addresses_cb */ + nl_socket_modify_cb(socket, NL_CB_VALID, NL_CB_CUSTOM, get_addresses_cb, &info); + nl_recvmsgs_default(socket); + +out_free: + nl_socket_free(socket); + return info.addresses; +} + +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)); + + char lowername[IFNAMSIZ]; + strncpy(lowername, ifname, sizeof(lowername)-1); + lowername[sizeof(lowername)-1] = 0; + + const char *format = "/sys/class/net/%s/lower_*"; + char pattern[strlen(format) + IFNAMSIZ]; + + /* In case of VLAN and bridge interfaces, we want the lower interface + * to determine the interface type (but not for the interface address) */ + while (true) { + snprintf(pattern, sizeof(pattern), format, lowername); + size_t pattern_len = strlen(pattern); + + glob_t lower; + if (glob(pattern, GLOB_NOSORT, NULL, &lower)) + break; + + strncpy(lowername, lower.gl_pathv[0] + pattern_len - 1, sizeof(lowername)-1); + + globfree(&lower); + } + + if (interface_file_exists(lowername, "wireless")) + json_object_array_add(wireless, address); + else if (interface_file_exists(lowername, "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; +} + +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", json_object_new_int(15)); + json_object_object_add(software, "batman-adv", software_batman_adv); + json_object_object_add(ret, "software", software); + + return ret; +} diff --git a/package/gluon-mesh-batman-adv/src/respondd-statistics.c b/package/gluon-mesh-batman-adv/src/respondd-statistics.c new file mode 100644 index 00000000..c9394d87 --- /dev/null +++ b/package/gluon-mesh-batman-adv/src/respondd-statistics.c @@ -0,0 +1,322 @@ +/* + Copyright (c) 2016-2019, 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. +*/ + +#include "respondd-common.h" + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +#define MAX_INACTIVITY 60000 + + +struct clients_netlink_opts { + size_t clients; + struct batadv_nlquery_opts query_opts; +}; + +struct gw_netlink_opts { + struct json_object *obj; + struct batadv_nlquery_opts query_opts; +}; + + +static const enum batadv_nl_attrs gateways_mandatory[] = { + BATADV_ATTR_ORIG_ADDRESS, + BATADV_ATTR_ROUTER, +}; + +static int parse_gw_list_netlink_cb(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct batadv_nlquery_opts *query_opts = arg; + struct genlmsghdr *ghdr; + uint8_t *orig; + uint8_t *router; + struct gw_netlink_opts *opts; + char addr[18]; + + opts = batadv_container_of(query_opts, struct gw_netlink_opts, + query_opts); + + if (!genlmsg_valid_hdr(nlh, 0)) + return NL_OK; + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_GATEWAYS) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_genl_policy)) + return NL_OK; + + if (batadv_genl_missing_attrs(attrs, gateways_mandatory, + BATADV_ARRAY_SIZE(gateways_mandatory))) + return NL_OK; + + if (!attrs[BATADV_ATTR_FLAG_BEST]) + return NL_OK; + + orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); + router = nla_data(attrs[BATADV_ATTR_ROUTER]); + + sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", + orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); + + json_object_object_add(opts->obj, "gateway", json_object_new_string(addr)); + + sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", + router[0], router[1], router[2], router[3], router[4], router[5]); + + json_object_object_add(opts->obj, "gateway_nexthop", json_object_new_string(addr)); + + return NL_STOP; +} + +static void add_gateway(struct json_object *obj) { + struct gw_netlink_opts opts = { + .obj = obj, + .query_opts = { + .err = 0, + }, + }; + + batadv_genl_query("bat0", BATADV_CMD_GET_GATEWAYS, + parse_gw_list_netlink_cb, NLM_F_DUMP, + &opts.query_opts); +} + +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) { + struct { + struct ethtool_sset_info info; + uint32_t buf; + } sset = {}; + + sset.info.cmd = ETHTOOL_GSSET_INFO; + sset.info.sset_mask = (uint64_t)1 << 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 const enum batadv_nl_attrs clients_mandatory[] = { + BATADV_ATTR_TT_FLAGS, + /* Entries without the BATADV_TT_CLIENT_NOPURGE flag do not have a + * BATADV_ATTR_LAST_SEEN_MSECS attribute. We can still make this attr + * mandatory here, as entries without BATADV_TT_CLIENT_NOPURGE are + * ignored anyways. + */ + BATADV_ATTR_LAST_SEEN_MSECS, +}; + +static int parse_clients_list_netlink_cb(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct batadv_nlquery_opts *query_opts = arg; + struct genlmsghdr *ghdr; + struct clients_netlink_opts *opts; + uint32_t flags, lastseen; + + opts = batadv_container_of(query_opts, struct clients_netlink_opts, + query_opts); + + if (!genlmsg_valid_hdr(nlh, 0)) + return NL_OK; + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_genl_policy)) + return NL_OK; + + if (batadv_genl_missing_attrs(attrs, clients_mandatory, + BATADV_ARRAY_SIZE(clients_mandatory))) + return NL_OK; + + flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]); + + if (flags & (BATADV_TT_CLIENT_NOPURGE)) + return NL_OK; + + lastseen = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); + if (lastseen > MAX_INACTIVITY) + return NL_OK; + + opts->clients++; + + return NL_OK; +} + +static struct json_object * get_clients(void) { + struct clients_netlink_opts opts = { + .clients = 0, + .query_opts = { + .err = 0, + }, + }; + + batadv_genl_query("bat0", BATADV_CMD_GET_TRANSTABLE_LOCAL, + parse_clients_list_netlink_cb, NLM_F_DUMP, + &opts.query_opts); + + struct json_object *ret = json_object_new_object(); + + json_object_object_add(ret, "total", json_object_new_int(opts.clients)); + + return ret; +} + +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; +} diff --git a/package/gluon-mesh-batman-adv/src/respondd.c b/package/gluon-mesh-batman-adv/src/respondd.c index 1df8d43f..36188e46 100644 --- a/package/gluon-mesh-batman-adv/src/respondd.c +++ b/package/gluon-mesh-batman-adv/src/respondd.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, Matthias Schiffer + Copyright (c) 2016-2019, Matthias Schiffer All rights reserved. Redistribution and use in source and binary forms, with or without @@ -23,621 +23,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "respondd-common.h" #include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - - -#define MAX_INACTIVITY 60000 - - -struct neigh_netlink_opts { - struct json_object *interfaces; - struct batadv_nlquery_opts query_opts; -}; - -struct gw_netlink_opts { - struct json_object *obj; - struct batadv_nlquery_opts query_opts; -}; - -struct clients_netlink_opts { - size_t clients; - struct batadv_nlquery_opts query_opts; -}; - -struct ip_address_information { - unsigned int ifindex; - struct json_object *addresses; -}; - -static int get_addresses_cb(struct nl_msg *msg, void *arg) { - struct ip_address_information *info = (struct ip_address_information*) arg; - - struct nlmsghdr *nlh = nlmsg_hdr(msg); - struct ifaddrmsg *msg_content = NLMSG_DATA(nlh); - int remaining = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - struct rtattr *hdr; - - for (hdr = IFA_RTA(msg_content); RTA_OK(hdr, remaining); hdr = RTA_NEXT(hdr, remaining)) { - char addr_str_buf[INET6_ADDRSTRLEN]; - - /* We are only interested in IP-addresses of br-client */ - if (hdr->rta_type != IFA_ADDRESS || - msg_content->ifa_index != info->ifindex || - msg_content->ifa_flags & (IFA_F_TENTATIVE|IFA_F_DEPRECATED)) { - continue; - } - - if (inet_ntop(AF_INET6, (struct in6_addr *) RTA_DATA(hdr), addr_str_buf, INET6_ADDRSTRLEN)) { - json_object_array_add(info->addresses, json_object_new_string(addr_str_buf)); - } - } - - return NL_OK; -} - -static struct json_object *get_addresses(void) { - struct ip_address_information info = { - .ifindex = if_nametoindex("br-client"), - .addresses = json_object_new_array(), - }; - int err; - - /* Open socket */ - struct nl_sock *socket = nl_socket_alloc(); - if (!socket) { - return info.addresses; - } - - err = nl_connect(socket, NETLINK_ROUTE); - if (err < 0) { - goto out_free; - } - - /* Send message */ - struct ifaddrmsg rt_hdr = { .ifa_family = AF_INET6, }; - err = nl_send_simple(socket, RTM_GETADDR, NLM_F_REQUEST | NLM_F_ROOT, &rt_hdr, sizeof(struct ifaddrmsg)); - if (err < 0) { - goto out_free; - } - - /* Retrieve answer. Message is handled by get_addresses_cb */ - nl_socket_modify_cb(socket, NL_CB_VALID, NL_CB_CUSTOM, get_addresses_cb, &info); - nl_recvmsgs_default(socket); - -out_free: - nl_socket_free(socket); - return info.addresses; -} - -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)); - - char lowername[IFNAMSIZ]; - strncpy(lowername, ifname, sizeof(lowername)-1); - lowername[sizeof(lowername)-1] = 0; - - const char *format = "/sys/class/net/%s/lower_*"; - char pattern[strlen(format) + IFNAMSIZ]; - - /* In case of VLAN and bridge interfaces, we want the lower interface - * to determine the interface type (but not for the interface address) */ - while (true) { - snprintf(pattern, sizeof(pattern), format, lowername); - size_t pattern_len = strlen(pattern); - - glob_t lower; - if (glob(pattern, GLOB_NOSORT, NULL, &lower)) - break; - - strncpy(lowername, lower.gl_pathv[0] + pattern_len - 1, sizeof(lowername)-1); - - globfree(&lower); - } - - if (interface_file_exists(lowername, "wireless")) - json_object_array_add(wireless, address); - else if (interface_file_exists(lowername, "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 * 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", json_object_new_int(15)); - json_object_object_add(software, "batman-adv", software_batman_adv); - json_object_object_add(ret, "software", software); - - return ret; -} - -static const enum batadv_nl_attrs gateways_mandatory[] = { - BATADV_ATTR_ORIG_ADDRESS, - BATADV_ATTR_ROUTER, -}; - -static int parse_gw_list_netlink_cb(struct nl_msg *msg, void *arg) -{ - struct nlattr *attrs[BATADV_ATTR_MAX+1]; - struct nlmsghdr *nlh = nlmsg_hdr(msg); - struct batadv_nlquery_opts *query_opts = arg; - struct genlmsghdr *ghdr; - uint8_t *orig; - uint8_t *router; - struct gw_netlink_opts *opts; - char addr[18]; - - opts = batadv_container_of(query_opts, struct gw_netlink_opts, - query_opts); - - if (!genlmsg_valid_hdr(nlh, 0)) - return NL_OK; - - ghdr = nlmsg_data(nlh); - - if (ghdr->cmd != BATADV_CMD_GET_GATEWAYS) - return NL_OK; - - if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), - genlmsg_len(ghdr), batadv_genl_policy)) - return NL_OK; - - if (batadv_genl_missing_attrs(attrs, gateways_mandatory, - BATADV_ARRAY_SIZE(gateways_mandatory))) - return NL_OK; - - if (!attrs[BATADV_ATTR_FLAG_BEST]) - return NL_OK; - - orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); - router = nla_data(attrs[BATADV_ATTR_ROUTER]); - - sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", - orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); - - json_object_object_add(opts->obj, "gateway", json_object_new_string(addr)); - - sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", - router[0], router[1], router[2], router[3], router[4], router[5]); - - json_object_object_add(opts->obj, "gateway_nexthop", json_object_new_string(addr)); - - return NL_STOP; -} - -static void add_gateway(struct json_object *obj) { - struct gw_netlink_opts opts = { - .obj = obj, - .query_opts = { - .err = 0, - }, - }; - - batadv_genl_query("bat0", BATADV_CMD_GET_GATEWAYS, - parse_gw_list_netlink_cb, NLM_F_DUMP, - &opts.query_opts); -} - -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 const enum batadv_nl_attrs clients_mandatory[] = { - BATADV_ATTR_TT_FLAGS, - /* Entries without the BATADV_TT_CLIENT_NOPURGE flag do not have a - * BATADV_ATTR_LAST_SEEN_MSECS attribute. We can still make this attr - * mandatory here, as entries without BATADV_TT_CLIENT_NOPURGE are - * ignored anyways. - */ - BATADV_ATTR_LAST_SEEN_MSECS, -}; - -static int parse_clients_list_netlink_cb(struct nl_msg *msg, void *arg) -{ - struct nlattr *attrs[BATADV_ATTR_MAX+1]; - struct nlmsghdr *nlh = nlmsg_hdr(msg); - struct batadv_nlquery_opts *query_opts = arg; - struct genlmsghdr *ghdr; - struct clients_netlink_opts *opts; - uint32_t flags, lastseen; - - opts = batadv_container_of(query_opts, struct clients_netlink_opts, - query_opts); - - if (!genlmsg_valid_hdr(nlh, 0)) - return NL_OK; - - ghdr = nlmsg_data(nlh); - - if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL) - return NL_OK; - - if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), - genlmsg_len(ghdr), batadv_genl_policy)) - return NL_OK; - - if (batadv_genl_missing_attrs(attrs, clients_mandatory, - BATADV_ARRAY_SIZE(clients_mandatory))) - return NL_OK; - - flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]); - - if (flags & (BATADV_TT_CLIENT_NOPURGE)) - return NL_OK; - - lastseen = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); - if (lastseen > MAX_INACTIVITY) - return NL_OK; - - opts->clients++; - - return NL_OK; -} - -static struct json_object * get_clients(void) { - struct clients_netlink_opts opts = { - .clients = 0, - .query_opts = { - .err = 0, - }, - }; - - batadv_genl_query("bat0", BATADV_CMD_GET_TRANSTABLE_LOCAL, - parse_clients_list_netlink_cb, NLM_F_DUMP, - &opts.query_opts); - - struct json_object *ret = json_object_new_object(); - - json_object_object_add(ret, "total", json_object_new_int(opts.clients)); - - 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 const enum batadv_nl_attrs parse_orig_list_mandatory[] = { - BATADV_ATTR_ORIG_ADDRESS, - BATADV_ATTR_NEIGH_ADDRESS, - BATADV_ATTR_TQ, - BATADV_ATTR_HARD_IFINDEX, - BATADV_ATTR_LAST_SEEN_MSECS, -}; - -static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg) -{ - struct nlattr *attrs[BATADV_ATTR_MAX+1]; - struct nlmsghdr *nlh = nlmsg_hdr(msg); - struct batadv_nlquery_opts *query_opts = arg; - struct genlmsghdr *ghdr; - uint8_t *orig; - uint8_t *dest; - uint8_t tq; - uint32_t hardif; - uint32_t lastseen; - char ifname_buf[IF_NAMESIZE], *ifname; - struct neigh_netlink_opts *opts; - char mac1[18]; - - opts = batadv_container_of(query_opts, struct neigh_netlink_opts, - query_opts); - - if (!genlmsg_valid_hdr(nlh, 0)) - return NL_OK; - - ghdr = nlmsg_data(nlh); - - if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) - return NL_OK; - - if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), - genlmsg_len(ghdr), batadv_genl_policy)) - return NL_OK; - - if (batadv_genl_missing_attrs(attrs, parse_orig_list_mandatory, - BATADV_ARRAY_SIZE(parse_orig_list_mandatory))) - return NL_OK; - - orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); - dest = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); - tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); - hardif = nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]); - lastseen = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); - - if (memcmp(orig, dest, 6) != 0) - return NL_OK; - - ifname = if_indextoname(hardif, ifname_buf); - if (!ifname) - return NL_OK; - - sprintf(mac1, "%02x:%02x:%02x:%02x:%02x:%02x", - orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); - - struct json_object *obj = json_object_new_object(); - if (!obj) - return NL_OK; - - struct json_object *interface; - if (!json_object_object_get_ex(opts->interfaces, ifname, &interface)) { - interface = json_object_new_object(); - json_object_object_add(opts->interfaces, ifname, interface); - } - - json_object_object_add(obj, "tq", json_object_new_int(tq)); - json_object_object_add(obj, "lastseen", json_object_new_double(lastseen / 1000.)); - json_object_object_add(obj, "best", json_object_new_boolean(!!attrs[BATADV_ATTR_FLAG_BEST])); - json_object_object_add(interface, mac1, obj); - - return NL_OK; -} - -static struct json_object * get_batadv(void) { - struct neigh_netlink_opts opts = { - .query_opts = { - .err = 0, - }, - }; - int ret; - - opts.interfaces = json_object_new_object(); - if (!opts.interfaces) - return NULL; - - ret = batadv_genl_query("bat0", BATADV_CMD_GET_ORIGINATORS, - parse_orig_list_netlink_cb, NLM_F_DUMP, - &opts.query_opts); - if (ret < 0) { - json_object_put(opts.interfaces); - return NULL; - } - - return ifnames2addrs(opts.interfaces); -} - -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); - - return ret; -} - +__attribute__ ((visibility ("default"))) const struct respondd_provider_info respondd_providers[] = { {"nodeinfo", respondd_provider_nodeinfo}, {"statistics", respondd_provider_statistics},