diff --git a/package/gluon-mesh-batman-adv/src/respondd-neighbours.c b/package/gluon-mesh-batman-adv/src/respondd-neighbours.c index 261d6b2730..3b8ecd52f4 100644 --- a/package/gluon-mesh-batman-adv/src/respondd-neighbours.c +++ b/package/gluon-mesh-batman-adv/src/respondd-neighbours.c @@ -4,7 +4,6 @@ #include "respondd-common.h" #include -#include #include @@ -44,7 +43,8 @@ static struct json_object * ifnames2addrs(struct json_object *interfaces) { return ret; } -static const enum batadv_nl_attrs parse_orig_list_mandatory[] = { +/* Batman IV mandatory attrs */ +static const enum batadv_nl_attrs parse_orig_list_mandatory_batadv_iv[] = { BATADV_ATTR_ORIG_ADDRESS, BATADV_ATTR_NEIGH_ADDRESS, BATADV_ATTR_TQ, @@ -52,20 +52,62 @@ static const enum batadv_nl_attrs parse_orig_list_mandatory[] = { BATADV_ATTR_LAST_SEEN_MSECS, }; -static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg) +/* Batman V mandatory attrs */ +static const enum batadv_nl_attrs parse_neigh_list_mandatory_batadv_v[] = { + BATADV_ATTR_NEIGH_ADDRESS, + BATADV_ATTR_THROUGHPUT, + BATADV_ATTR_HARD_IFINDEX, + BATADV_ATTR_LAST_SEEN_MSECS, +}; + +static int add_neighbour(struct neigh_netlink_opts *opts, struct nlattr **attrs, + uint8_t *mac, const char *metric_name, struct json_object *metric_value) +{ + uint32_t hardif; + uint32_t lastseen; + char ifname_buf[IF_NAMESIZE], *ifname; + char mac1[18]; + + hardif = nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]); + lastseen = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); + + ifname = if_indextoname(hardif, ifname_buf); + if (!ifname) { + json_object_put(metric_value); + return NL_OK; + } + + sprintf(mac1, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + struct json_object *obj = json_object_new_object(); + if (!obj) { + json_object_put(metric_value); + 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, metric_name, metric_value); + json_object_object_add(obj, "lastseen", json_object_new_double(lastseen / 1000.)); + json_object_object_add(obj, "best", json_object_new_boolean(nla_get_flag(attrs[BATADV_ATTR_FLAG_BEST]))); + json_object_object_add(interface, mac1, obj); + + return NL_OK; +} + +static int parse_orig_list_netlink_cb_batadv_iv(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; + uint8_t *mac; struct neigh_netlink_opts *opts; - char mac1[18]; opts = batadv_container_of(query_opts, struct neigh_netlink_opts, query_opts); @@ -82,42 +124,50 @@ static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg) 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))) + if (batadv_genl_missing_attrs(attrs, parse_orig_list_mandatory_batadv_iv, + BATADV_ARRAY_SIZE(parse_orig_list_mandatory_batadv_iv))) 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) + mac = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); + if (memcmp(mac, nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]), 6) != 0) return NL_OK; - ifname = if_indextoname(hardif, ifname_buf); - if (!ifname) + return add_neighbour(opts, attrs, mac, "tq", + json_object_new_int(nla_get_u8(attrs[BATADV_ATTR_TQ]))); +} + +static int parse_neigh_list_netlink_cb_batadv_v(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 *mac; + struct neigh_netlink_opts *opts; + + opts = batadv_container_of(query_opts, struct neigh_netlink_opts, + query_opts); + + if (!genlmsg_valid_hdr(nlh, 0)) return NL_OK; - sprintf(mac1, "%02x:%02x:%02x:%02x:%02x:%02x", - orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); + ghdr = nlmsg_data(nlh); - struct json_object *obj = json_object_new_object(); - if (!obj) + if (ghdr->cmd != BATADV_CMD_GET_NEIGHBORS) 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); - } + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_genl_policy)) + return NL_OK; - 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(nla_get_flag(attrs[BATADV_ATTR_FLAG_BEST]))); - json_object_object_add(interface, mac1, obj); + if (batadv_genl_missing_attrs(attrs, parse_neigh_list_mandatory_batadv_v, + BATADV_ARRAY_SIZE(parse_neigh_list_mandatory_batadv_v))) + return NL_OK; - return NL_OK; + mac = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); + + return add_neighbour(opts, attrs, mac, "throughput", + json_object_new_int64(nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]))); } static struct json_object * get_batadv(void) { @@ -127,14 +177,27 @@ static struct json_object * get_batadv(void) { }, }; int ret; + enum batadv_algo algo; 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 (batadv_genl_get_algo("bat0", &algo) < 0) { + json_object_put(opts.interfaces); + return NULL; + } + + if (algo == BATADV_ALGO_BATMAN_V) { + ret = batadv_genl_query("bat0", BATADV_CMD_GET_NEIGHBORS, + parse_neigh_list_netlink_cb_batadv_v, NLM_F_DUMP, + &opts.query_opts); + } else { + ret = batadv_genl_query("bat0", BATADV_CMD_GET_ORIGINATORS, + parse_orig_list_netlink_cb_batadv_iv, NLM_F_DUMP, + &opts.query_opts); + } + if (ret < 0) { json_object_put(opts.interfaces); return NULL; diff --git a/package/gluon-mesh-batman-adv/src/respondd-statistics.c b/package/gluon-mesh-batman-adv/src/respondd-statistics.c index 28131fda26..004a3b2f81 100644 --- a/package/gluon-mesh-batman-adv/src/respondd-statistics.c +++ b/package/gluon-mesh-batman-adv/src/respondd-statistics.c @@ -34,16 +34,23 @@ struct clients_netlink_opts { struct gw_netlink_opts { struct json_object *obj; + enum batadv_algo algo; struct batadv_nlquery_opts query_opts; }; -static const enum batadv_nl_attrs gateways_mandatory[] = { +static const enum batadv_nl_attrs gateways_mandatory_batadv_iv[] = { BATADV_ATTR_ORIG_ADDRESS, BATADV_ATTR_ROUTER, BATADV_ATTR_TQ, }; +static const enum batadv_nl_attrs gateways_mandatory_batadv_v[] = { + BATADV_ATTR_ORIG_ADDRESS, + BATADV_ATTR_ROUTER, + BATADV_ATTR_THROUGHPUT, +}; + static int parse_gw_list_netlink_cb(struct nl_msg *msg, void *arg) { struct nlattr *attrs[BATADV_ATTR_MAX+1]; @@ -52,7 +59,6 @@ static int parse_gw_list_netlink_cb(struct nl_msg *msg, void *arg) struct genlmsghdr *ghdr; uint8_t *orig; uint8_t *router; - uint8_t tq; struct gw_netlink_opts *opts; char addr[18]; @@ -71,22 +77,34 @@ static int parse_gw_list_netlink_cb(struct nl_msg *msg, void *arg) 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 (opts->algo == BATADV_ALGO_BATMAN_V) { + if (batadv_genl_missing_attrs(attrs, gateways_mandatory_batadv_v, + BATADV_ARRAY_SIZE(gateways_mandatory_batadv_v))) + return NL_OK; + } else { + if (batadv_genl_missing_attrs(attrs, gateways_mandatory_batadv_iv, + BATADV_ARRAY_SIZE(gateways_mandatory_batadv_iv))) + 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]); - tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); 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)); - json_object_object_add(opts->obj, "gateway_tq", json_object_new_int(tq)); + + if (opts->algo == BATADV_ALGO_BATMAN_V) { + uint32_t throughput = nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]); + json_object_object_add(opts->obj, "gateway_throughput", json_object_new_int64(throughput)); + } else { + uint8_t tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); + json_object_object_add(opts->obj, "gateway_tq", json_object_new_int(tq)); + } sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", router[0], router[1], router[2], router[3], router[4], router[5]); @@ -103,6 +121,8 @@ static void add_gateway(struct json_object *obj) { .err = 0, }, }; + if (batadv_genl_get_algo("bat0", &opts.algo) < 0) + return; batadv_genl_query("bat0", BATADV_CMD_GET_GATEWAYS, parse_gw_list_netlink_cb, NLM_F_DUMP, diff --git a/package/gluon-radv-filterd/src/gluon-radv-filterd.c b/package/gluon-radv-filterd/src/gluon-radv-filterd.c index 7a77775226..aefcbd9570 100644 --- a/package/gluon-radv-filterd/src/gluon-radv-filterd.c +++ b/package/gluon-radv-filterd/src/gluon-radv-filterd.c @@ -37,11 +37,11 @@ #include "mac.h" -// Recheck TQs after this time even if no RA was received +// Recheck metrics after this time even if no RA was received #define MAX_INTERVAL 60 -// Recheck TQs at most this often, even if new RAs were received (they won't -// become the preferred routers until the TQs have been rechecked) +// Recheck metrics at most this often, even if new RAs were received (they won't +// become the preferred routers until the metrics have been rechecked) // Also, the first update will take at least this long #define MIN_INTERVAL 15 @@ -52,8 +52,10 @@ // max execution time of a single ebtables call in nanoseconds #define EBTABLES_TIMEOUT 500000000 // 500ms -// TQ value assigned to local routers +// Local-router metric: must dominate any real neighbour value. +// IV TQ max is 255; V reports throughput in kbit/s. #define LOCAL_TQ 512 +#define LOCAL_THROUGHPUT (10 * 1000 * 1000) #define BUFSIZE 1500 @@ -86,7 +88,7 @@ struct router { struct ether_addr src; struct timespec eol; struct ether_addr originator; - uint16_t tq; + uint32_t metric; }; static struct global { @@ -94,9 +96,10 @@ static struct global { struct router *routers; const char *mesh_iface; const char *chain; - uint16_t max_tq; - uint16_t hysteresis_thresh; + uint32_t max_metric; + uint32_t hysteresis_thresh; struct router *best_router; + enum batadv_algo algo; volatile sig_atomic_t stop_daemon; } G = { .mesh_iface = "bat0", @@ -166,8 +169,9 @@ static void usage(const char *msg) { fprintf(stderr, "Usage: %s [-m ] [-t ] -c -i \n\n" " -m B.A.T.M.A.N. advanced mesh interface used to get metric\n" - " information (\"TQ\") for the available gateways. Default: bat0\n" - " -t Minimum TQ difference required to switch the gateway.\n" + " information (TQ or throughput) for the available gateways.\n" + " Default: bat0\n" + " -t Minimum metric difference required to switch the gateway.\n" " Default: 0\n" " -c ebtables chain that should be managed by the daemon. The\n" " chain already has to exist on program invocation and should\n" @@ -267,9 +271,7 @@ static void parse_cmdline(int argc, char *argv[]) { threshold = strtoul(optarg, &endptr, 10); if (*endptr != '\0') exit_errmsg("Threshold must be a number: %s", optarg); - if (threshold >= LOCAL_TQ) - exit_errmsg("Threshold too large: %ld (max is %d)", threshold, LOCAL_TQ); - G.hysteresis_thresh = (uint16_t) threshold; + G.hysteresis_thresh = (uint32_t) threshold; break; case 'h': usage(NULL); @@ -431,21 +433,26 @@ static int parse_tt_global(struct nl_msg *msg, return NL_OK; } +static const enum batadv_nl_attrs originator_mandatory_batadv_iv[] = { + BATADV_ATTR_ORIG_ADDRESS, + BATADV_ATTR_TQ, +}; + +static const enum batadv_nl_attrs originator_mandatory_batadv_v[] = { + BATADV_ATTR_ORIG_ADDRESS, + BATADV_ATTR_THROUGHPUT, +}; + static int parse_originator(struct nl_msg *msg, void *arg __attribute__((unused))) { - - static const enum batadv_nl_attrs mandatory[] = { - BATADV_ATTR_ORIG_ADDRESS, - BATADV_ATTR_TQ, - }; struct nlattr *attrs[BATADV_ATTR_MAX + 1]; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct ether_addr mac_a; struct genlmsghdr *ghdr; struct router *router; uint8_t *orig; - uint8_t tq; + uint32_t metric; // parse netlink entry if (!genlmsg_valid_hdr(nlh, 0)) @@ -461,15 +468,24 @@ static int parse_originator(struct nl_msg *msg, return NL_OK; } - if (batadv_genl_missing_attrs(attrs, mandatory, ARRAY_SIZE(mandatory))) - return NL_OK; - - orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); - tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); + if (G.algo == BATADV_ALGO_BATMAN_V) { + if (batadv_genl_missing_attrs(attrs, originator_mandatory_batadv_v, + ARRAY_SIZE(originator_mandatory_batadv_v))) + return NL_OK; + } else { + if (batadv_genl_missing_attrs(attrs, originator_mandatory_batadv_iv, + ARRAY_SIZE(originator_mandatory_batadv_iv))) + return NL_OK; + } if (!attrs[BATADV_ATTR_FLAG_BEST]) return NL_OK; + orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); + metric = (G.algo == BATADV_ALGO_BATMAN_V) + ? nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]) + : nla_get_u8(attrs[BATADV_ATTR_TQ]); + MAC2ETHER(mac_a, orig); // update router @@ -477,11 +493,11 @@ static int parse_originator(struct nl_msg *msg, if (!router) return NL_OK; - DEBUG_MSG("Found TQ for router " F_MAC " (originator " F_MAC "), it's %d", - F_MAC_VAR(router->src), F_MAC_VAR(router->originator), tq); - router->tq = tq; - if (router->tq > G.max_tq) - G.max_tq = router->tq; + DEBUG_MSG("Found metric for router " F_MAC " (originator " F_MAC "), it's %u", + F_MAC_VAR(router->src), F_MAC_VAR(router->originator), metric); + router->metric = metric; + if (router->metric > G.max_metric) + G.max_metric = router->metric; return NL_OK; } @@ -524,25 +540,26 @@ static int parse_tt_local(struct nl_msg *msg, if (!router) return NL_OK; - DEBUG_MSG("Found router " F_MAC " in transtable_local, assigning TQ %d", - F_MAC_VAR(router->src), LOCAL_TQ); - router->tq = LOCAL_TQ; - if (router->tq > G.max_tq) - G.max_tq = router->tq; + uint32_t local_metric = (G.algo == BATADV_ALGO_BATMAN_V) ? LOCAL_THROUGHPUT : LOCAL_TQ; + DEBUG_MSG("Found router " F_MAC " in transtable_local, assigning metric %u", + F_MAC_VAR(router->src), local_metric); + router->metric = local_metric; + if (router->metric > G.max_metric) + G.max_metric = router->metric; return NL_OK; } -static void update_tqs(void) { +static void update_metrics(void) { static const struct ether_addr unspec = {}; struct router *router; bool update_originators = false; struct batadv_nlquery_opts opts; int ret; - // reset TQs + // reset metrics foreach(router, G.routers) { - router->tq = 0; + router->metric = 0; if (ether_addr_equal(router->originator, unspec)) update_originators = true; } @@ -557,8 +574,8 @@ static void update_tqs(void) { fprintf(stderr, "Parsing of global translation table failed\n"); } - // look up TQs of originators - G.max_tq = 0; + // look up metrics of originators + G.max_metric = 0; opts.err = 0; ret = batadv_genl_query(G.mesh_iface, BATADV_CMD_GET_ORIGINATORS, @@ -566,9 +583,9 @@ static void update_tqs(void) { if (ret < 0) fprintf(stderr, "Parsing of originators failed\n"); - // if all routers have a TQ value, we don't need to check translocal + // if all routers have a metric value, we don't need to check translocal foreach(router, G.routers) { - if (router->tq == 0) + if (router->metric == 0) break; } if (router != NULL) { @@ -577,18 +594,18 @@ static void update_tqs(void) { BATADV_CMD_GET_TRANSTABLE_LOCAL, parse_tt_local, NLM_F_DUMP, &opts); if (ret < 0) - fprintf(stderr, "Parsing of global translation table failed\n"); + fprintf(stderr, "Parsing of local translation table failed\n"); } foreach(router, G.routers) { - if (router->tq == 0) { + if (router->metric == 0) { if (ether_addr_equal(router->originator, unspec)) DEBUG_MSG( "Unable to find router " F_MAC " in transtable_{global,local}", F_MAC_VAR(router->src)); else DEBUG_MSG( - "Unable to find TQ for originator " F_MAC " (router " F_MAC ")", + "Unable to find metric for originator " F_MAC " (router " F_MAC ")", F_MAC_VAR(router->originator), F_MAC_VAR(router->src)); } @@ -654,11 +671,11 @@ static bool election_required(void) if (!G.best_router) return true; - /* should never happen. G.max_tq also contains G.best_router->tq */ - if (G.max_tq < G.best_router->tq) + /* should never happen. G.max_metric also contains G.best_router->metric */ + if (G.max_metric < G.best_router->metric) return false; - if ((G.max_tq - G.best_router->tq) <= G.hysteresis_thresh) + if ((G.max_metric - G.best_router->metric) <= G.hysteresis_thresh) return false; return true; @@ -672,29 +689,29 @@ static void update_ebtables(void) { struct router *router; if (!election_required()) { - DEBUG_MSG(F_MAC " is still good enough with TQ=%d (max_tq=%d), not executing ebtables", + DEBUG_MSG(F_MAC " is still good enough with metric=%u (max_metric=%u), not executing ebtables", F_MAC_VAR(G.best_router->src), - G.best_router->tq, - G.max_tq); + G.best_router->metric, + G.max_metric); return; } foreach(router, G.routers) { - if (router->tq == G.max_tq) { + if (router->metric == G.max_metric) { snprintf(mac, sizeof(mac), F_MAC, F_MAC_VAR(router->src)); break; } } if (G.best_router) - fprintf(stderr, "Switching from " F_MAC " (TQ=%d) to %s (TQ=%d)\n", + fprintf(stderr, "Switching from " F_MAC " (metric=%u) to %s (metric=%u)\n", F_MAC_VAR(G.best_router->src), - G.best_router->tq, + G.best_router->metric, mac, - G.max_tq); + G.max_metric); else - fprintf(stderr, "Switching to %s (TQ=%d)\n", + fprintf(stderr, "Switching to %s (metric=%u)\n", mac, - G.max_tq); + G.max_metric); G.best_router = router; if (fork_execvp_timeout(&timeout, "ebtables-tiny", (const char *[]) @@ -742,6 +759,17 @@ int main(int argc, char *argv[]) { if (G.chain == NULL) usage("No chain set!"); + if (batadv_genl_get_algo(G.mesh_iface, &G.algo) < 0) + exit_errmsg("Failed to detect batman-adv routing algorithm on %s", G.mesh_iface); + + fprintf(stderr, "Using batman-adv algorithm: %s\n", + G.algo == BATADV_ALGO_BATMAN_V ? "BATMAN_V" : "BATMAN_IV"); + + uint32_t max_threshold = (G.algo == BATADV_ALGO_BATMAN_V) ? LOCAL_THROUGHPUT : LOCAL_TQ; + if (G.hysteresis_thresh >= max_threshold) + exit_errmsg("Threshold too large: %u (max is %u)", + G.hysteresis_thresh, max_threshold); + G.stop_daemon = 0; signal(SIGINT, sighandler); signal(SIGTERM, sighandler); @@ -779,7 +807,7 @@ int main(int argc, char *argv[]) { next_invalidation.tv_sec += ORIGINATOR_CACHE_TTL; } - update_tqs(); + update_metrics(); update_ebtables(); next_update = now; diff --git a/package/gluon-status-page-mesh-batman-adv/luasrc/lib/gluon/status-page/mesh.lua b/package/gluon-status-page-mesh-batman-adv/luasrc/lib/gluon/status-page/mesh.lua index 733e6d7c9e..ce7f6d09e3 100644 --- a/package/gluon-status-page-mesh-batman-adv/luasrc/lib/gluon/status-page/mesh.lua +++ b/package/gluon-status-page-mesh-batman-adv/luasrc/lib/gluon/status-page/mesh.lua @@ -1,10 +1,22 @@ +local site = require 'gluon.site' + +local attrs +if site.mesh.batman_adv.routing_algo() == 'BATMAN_V' then + attrs = { + {'tp', 'TP', 'bit/s', 'bitrate'}, + } +else + attrs = { + {'tq', 'TQ', ' %'}, + } +end + return { provider = '/cgi-bin/dyn/neighbours-batadv', -- List of mesh-specific attributes, each a tuple of -- 1) the internal identifier (JSON key) -- 2) human-readable key (not translatable yet) -- 3) value suffix (optional) - attrs = { - {'tq', 'TQ', ' %'}, - }, + -- 4) formatter name (optional) -- key into status-page.js formats{} + attrs = attrs, } diff --git a/package/gluon-status-page-mesh-batman-adv/src/neighbours-batadv.c b/package/gluon-status-page-mesh-batman-adv/src/neighbours-batadv.c index 58e626c9ac..dd84200e5a 100644 --- a/package/gluon-status-page-mesh-batman-adv/src/neighbours-batadv.c +++ b/package/gluon-status-page-mesh-batman-adv/src/neighbours-batadv.c @@ -11,10 +11,12 @@ struct neigh_netlink_opts { struct json_object *obj; + enum batadv_algo algo; struct batadv_nlquery_opts query_opts; }; -static const enum batadv_nl_attrs parse_orig_list_mandatory[] = { +/* Batman IV mandatory attrs */ +static const enum batadv_nl_attrs parse_orig_list_mandatory_batadv_iv[] = { BATADV_ATTR_ORIG_ADDRESS, BATADV_ATTR_NEIGH_ADDRESS, BATADV_ATTR_TQ, @@ -22,15 +24,21 @@ static const enum batadv_nl_attrs parse_orig_list_mandatory[] = { BATADV_ATTR_LAST_SEEN_MSECS, }; -static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg) +/* Batman V mandatory attrs */ +static const enum batadv_nl_attrs parse_neigh_list_mandatory_batadv_v[] = { + BATADV_ATTR_NEIGH_ADDRESS, + BATADV_ATTR_THROUGHPUT, + BATADV_ATTR_HARD_IFINDEX, + BATADV_ATTR_LAST_SEEN_MSECS, +}; + +static int parse_orig_neigh_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; + uint8_t *mac; uint32_t hardif; char ifname_buf[IF_NAMESIZE], *ifname; struct neigh_netlink_opts *opts; @@ -43,37 +51,53 @@ static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg) 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; + if (opts->algo == BATADV_ALGO_BATMAN_V) { + if (ghdr->cmd != BATADV_CMD_GET_NEIGHBORS) + 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]); + if (batadv_genl_missing_attrs(attrs, parse_neigh_list_mandatory_batadv_v, + BATADV_ARRAY_SIZE(parse_neigh_list_mandatory_batadv_v))) + return NL_OK; - if (memcmp(orig, dest, 6) != 0) - return NL_OK; + mac = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); + } else { + if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) + return NL_OK; + + if (batadv_genl_missing_attrs(attrs, parse_orig_list_mandatory_batadv_iv, + BATADV_ARRAY_SIZE(parse_orig_list_mandatory_batadv_iv))) + return NL_OK; + + mac = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); + if (memcmp(mac, nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]), 6) != 0) + return NL_OK; + } + + hardif = nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]); 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]); + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); struct json_object *neigh = json_object_new_object(); if (!neigh) return NL_OK; - json_object_object_add(neigh, "tq", json_object_new_int(tq * 100 / 255)); + if (opts->algo == BATADV_ALGO_BATMAN_V) { + json_object_object_add(neigh, "tp", + json_object_new_int64(nla_get_u32(attrs[BATADV_ATTR_THROUGHPUT]))); + } else { + uint8_t tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); + json_object_object_add(neigh, "tq", json_object_new_int(tq * 100 / 255)); + } + json_object_object_add(neigh, "ifname", json_object_new_string(ifname)); json_object_object_add(neigh, "best", json_object_new_boolean(nla_get_flag(attrs[BATADV_ATTR_FLAG_BEST]))); @@ -94,9 +118,21 @@ static json_object *neighbours(void) { if (!opts.obj) return NULL; - ret = batadv_genl_query("bat0", BATADV_CMD_GET_ORIGINATORS, - parse_orig_list_netlink_cb, NLM_F_DUMP, - &opts.query_opts); + if (batadv_genl_get_algo("bat0", &opts.algo) < 0) { + json_object_put(opts.obj); + return NULL; + } + + if (opts.algo == BATADV_ALGO_BATMAN_V) { + ret = batadv_genl_query("bat0", BATADV_CMD_GET_NEIGHBORS, + parse_orig_neigh_list_netlink_cb, NLM_F_DUMP, + &opts.query_opts); + } else { + ret = batadv_genl_query("bat0", BATADV_CMD_GET_ORIGINATORS, + parse_orig_neigh_list_netlink_cb, NLM_F_DUMP, + &opts.query_opts); + } + if (ret < 0) { json_object_put(opts.obj); return NULL; diff --git a/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html b/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html index 2ed1049a09..67713192b3 100644 --- a/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html +++ b/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html @@ -280,7 +280,7 @@

<%| iface %>

<%:Node%> <% for i, v in ipairs(mesh.attrs or {}) do %> - ><%| v[2] %> + ><%| v[2] %> <% end %> <% if wireless then %> dBm diff --git a/package/gluon-status-page/javascript/status-page.js b/package/gluon-status-page/javascript/status-page.js index 8dd8566e6a..4fcd272704 100644 --- a/package/gluon-status-page/javascript/status-page.js +++ b/package/gluon-status-page/javascript/status-page.js @@ -136,6 +136,10 @@ }, 'tq': function(value) { return formatNumber(100/255 * value, 1) + '%'; + }, + // input value is in kbit/s + 'bitrate': function(value) { + return prettyPrefix([ "", "K", "M", "G", "T" ], 1000, value * 1000); } } @@ -475,6 +479,7 @@ return; var suffix = attr.getAttribute('data-suffix') || ''; + var formatter = attr.getAttribute('data-format') || ''; var td = el.insertCell(); td.textContent = '-'; @@ -483,6 +488,7 @@ meshAttrs[key] = { 'td': td, 'suffix': suffix, + 'formatter': formatter, }; } @@ -667,7 +673,13 @@ 'update_mesh': function(mesh) { Object.keys(meshAttrs).forEach(function(key) { var attr = meshAttrs[key]; - attr.td.textContent = mesh[key] + attr.suffix; + var raw = mesh[key]; + if (raw === undefined) + return; + var rendered = (attr.formatter && formats[attr.formatter]) + ? formats[attr.formatter](raw) + : raw; + attr.td.textContent = rendered + attr.suffix; }); updated(); diff --git a/package/gluon-status-page/javascript/status-page.min.js b/package/gluon-status-page/javascript/status-page.min.js index cc0bcb49a4..d71bf43ed4 100644 --- a/package/gluon-status-page/javascript/status-page.min.js +++ b/package/gluon-status-page/javascript/status-page.min.js @@ -1 +1 @@ -"use strict";!function(){var o=JSON.parse(document.getElementById('request-data').getAttribute("data-translations"));function i(t,e){return t.toFixed(e).replace(/\./,o["."])}function a(t,e){e--;for(var n=t;10<=n&&0e[0]||t[1]e[1]?1:0}),(t=t[0][2])&&!/^fe80:/i.test(t)?t:void 0}return t.wireless&&((g=o.insertCell()).textContent="-",g.setAttribute("data-label",r.children[Object.keys(s).length+1].textContent),(p=o.insertCell()).textContent="-",p.setAttribute("data-label",r.children[Object.keys(s).length+2].textContent),(m=o.insertCell()).textContent="-",m.setAttribute("data-label",r.children[Object.keys(s).length+3].textContent),v=A(e),t.signalgraph.addSignal(v)),o.onmouseenter=function(){o.classList.add("highlight"),v&&(v.highlight=!0)},o.onmouseleave=function(){o.classList.remove("highlight"),v&&(v.highlight=!1)},y(),{get_hostname:function(){return u.textContent},get_addr:function(){return a},update_nodeinfo:function(t){var e,n,i,r,o;(a=x(t.network.addresses))&&("span"===u.nodeName.toLowerCase()&&(e=u,u=document.createElement("a"),e.parentNode.replaceChild(u,e)),u.href="http://["+a+"]/"),u.textContent=t.hostname,E&&t.location&&(e=E.latitude,n=E.longitude,i=t.location.latitude,t=t.location.longitude,r=Math.PI/180,o=(i*=r)-(e*=r),t=(t*=r)-(n*=r),r=Math.sin(o/2)*Math.sin(o/2)+Math.sin(t/2)*Math.sin(t/2)*Math.cos(e)*Math.cos(i),n=6372.8*(2*Math.asin(Math.sqrt(r))),p.textContent=Math.round(1e3*n)+" m"),y()},update_mesh:function(n){Object.keys(s).forEach(function(t){var e=s[t];e.td.textContent=n[t]+e.suffix}),y()},update_wifi:function(t){g.textContent=t.signal,m.textContent=Math.round(t.inactive/1e3)+" s",o.classList.toggle("inactive",200=10&&n>0;r/=10)n--;return e(t,n)}function r(t,e,r){var i=0;if(void 0===r)return"- ";for(;r>e&&i1&&(a+=t["%s days"].sprintf(r)+", "),a+=i+":",n<10&&(a+="0"),a+=n},packetsDiff:function(n,r,i){if(i>0)return a=(n-r)/i,t["%s packets/s"].sprintf(e(a,0));var a},bytesDiff:function(t,e,n){if(n>0)return i(8*((t-e)/n))+"bps"},bytes:function(t){return i(t)+"B"},neighbour:function(t){if(!t)return"";for(var e in d){var n=d[e].lookup_neigh(t);if(n){var r=document.createElement("span");r.appendChild(document.createTextNode("via "));var i=document.createElement("a");return i.href="http://["+n.get_addr()+"]/",i.textContent=n.get_hostname(),r.appendChild(i),r.appendChild(document.createTextNode(" ("+e+")")),r}}return"via "+t+" (unknown iface)"},tq:function(t){return n(100/255*t,1)+"%"},bitrate:function(t){return r(["","K","M","G","T"],1e3,1e3*t)}};function o(t,e){return e.split("/").forEach((function(e){t&&(t=t[e])})),t}function c(t,e){var n=new EventSource(t),r={};n.onmessage=function(t){var n=JSON.parse(t.data);e(n,r),r=n},n.onerror=function(){n.close(),window.setTimeout((function(){c(t,e)}),3e3)}}var u,s=document.getElementById("request-data").getAttribute("data-node-address");try{u=JSON.parse(document.getElementById("request-data").getAttribute("data-node-location"))}catch(t){}var l=document.querySelectorAll("[data-statistics]");c("/cgi-bin/dyn/statistics",(function(e,n){var r=e.uptime-n.uptime;l.forEach((function(t){var i=t.getAttribute("data-statistics"),c=t.getAttribute("data-format"),u=o(n,i),s=o(e,i);try{var l=a[c](s,u,r);if("object"==typeof l)t.lastChild&&t.removeChild(t.lastChild),t.appendChild(l);else t.textContent=l}catch(t){console.error(t)}}));try{!function(e){var n=document.getElementById("mesh-vpn");if(e){n.style.display="";for(var r=document.getElementById("mesh-vpn-peers");r.lastChild;)r.removeChild(r.lastChild);var i=function t(e,n){return Object.keys(n.peers||{}).forEach((function(t){e.push([t,n.peers[t]])})),Object.keys(n.groups||{}).forEach((function(r){t(e,n.groups[r])})),e}([],e);i.sort(),i.forEach((function(e){var n=document.createElement("tr"),i=document.createElement("th");i.textContent=e[0],n.appendChild(i);var o=document.createElement("td");if(e[1]&&null!=e[1].established){var c=e[1].method?", "+e[1].method:"";o.textContent=t.connected+" ("+a.time(e[1].established)+c+")"}else o.textContent=t["not connected"];n.appendChild(o),r.appendChild(n)}))}else n.style.display="none"}(e.mesh_vpn)}catch(t){console.error(t)}}));var d={};function f(t){var e=document.createElement("canvas"),n=e.getContext("2d"),r=null;return{canvas:e,highlight:!1,resize:function(t,r){var i;try{i=n.getImageData(0,0,t,r)}catch(t){}e.width=t,e.height=r,i&&n.putImageData(i,0,0)},draw:function(i,a){var o=a(r);n.clearRect(i,0,5,e.height),o&&function(e,r){n.beginPath(),n.fillStyle=t,n.arc(e,r,1.2,0,2*Math.PI,!1),n.closePath(),n.fill()}(i,o)},set:function(t){r=t}}}function h(){var t=-100,e=0,n=0,r=[],i=document.createElement("canvas");i.className="signalgraph",i.height=200;var a=i.getContext("2d");function o(t,e,n,r){return(e*t+n*(r-t))/r}function c(){i.width=i.clientWidth,r.forEach((function(t){t.resize(i.width,i.height)}))}function u(){if(0!==i.clientWidth){i.width!==i.clientWidth&&c(),a.clearRect(0,0,i.width,i.height);var u=!1;r.forEach((function(t){t.highlight&&(u=!0)})),a.save(),r.forEach((function(r){u&&(a.globalAlpha=.2),r.highlight&&(a.globalAlpha=1),r.draw(n,(function(n){return function(t,e,n,r){return(1-(t-e)/(n-e))*r}(n,t,e,i.height)})),a.drawImage(r.canvas,0,0)})),a.restore(),a.save(),a.beginPath(),a.strokeStyle="rgba(255, 180, 0, 0.15)",a.lineWidth=5,a.moveTo(n+2.5,0),a.lineTo(n+2.5,i.height),a.stroke(),function(){var n=Math.floor(i.height/40);a.save(),a.lineWidth=.5,a.strokeStyle="rgba(0, 0, 0, 0.25)",a.fillStyle="rgba(0, 0, 0, 0.5)",a.textAlign="end",a.textBaseline="bottom",a.beginPath();for(var r=0;r40&&(u(),n=(n+1)%i.width,s=e),window.requestAnimationFrame(t)})),{el:i,addSignal:function(t){r.push(t),t.resize(i.width,i.height)},removeSignal:function(t){r.splice(r.indexOf(t),1)}}}function g(t,e,n,r){var i=t.tbody.querySelector("tr"),o=t.tbody.insertRow(),c=o.insertCell();if(c.setAttribute("data-label",i.children[0].textContent),t.wireless){var l=document.createElement("span");l.textContent="⬤ ",l.style.color=n,c.appendChild(l)}var d=document.createElement("span");d.textContent=e,c.appendChild(d);var h,g,v,m,p,b={};function C(t){var e=t.getAttribute("data-key");if(e){var n=t.getAttribute("data-suffix")||"",r=t.getAttribute("data-format")||"",i=o.insertCell();i.textContent="-",i.setAttribute("data-label",t.textContent),b[e]={td:i,suffix:n,formatter:r}}}for(var y=0;ye[0]||t[1]e[1]?1:0}));var n=t[0][2];return n&&!/^fe80:/i.test(n)?n:void 0}}(t.network.addresses),e){if("span"===d.nodeName.toLowerCase()){var n=d;d=document.createElement("a"),n.parentNode.replaceChild(d,n)}d.href="http://["+e+"]/"}if(d.textContent=t.hostname,u&&t.location){var r=(i=u.latitude,a=u.longitude,o=t.location.latitude,c=t.location.longitude,l=Math.PI/180,f=(o*=l)-(i*=l),h=(c*=l)-(a*=l),v=Math.sin(f/2)*Math.sin(f/2)+Math.sin(h/2)*Math.sin(h/2)*Math.cos(i)*Math.cos(o),2*Math.asin(Math.sqrt(v))*6372.8);g.textContent=Math.round(1e3*r)+" m"}var i,a,o,c,l,f,h,v;w()},update_mesh:function(t){Object.keys(b).forEach((function(e){var n=b[e],r=t[e];if(void 0!==r){var i=n.formatter&&a[n.formatter]?a[n.formatter](r):r;n.td.textContent=i+n.suffix}})),w()},update_wifi:function(t){h.textContent=t.signal,v.textContent=Math.round(t.inactive/1e3)+" s",o.classList.toggle("inactive",t.inactive>200),m.set(t.inactive>200?null:t.signal),w()}}}function v(t,e,n){var r,i={};n&&(r=h(),t.appendChild(r.el));var a={tbody:t.querySelector("tbody"),signalgraph:r,ifname:e,wireless:n},o=!1,u={},s=[];function l(){if(!o){o=!0;var t=new EventSource("/cgi-bin/dyn/neighbours-nodeinfo?"+encodeURIComponent(e));t.addEventListener("neighbour",(function(t){try{var e=JSON.parse(t.data);(n=e,r=[],a=n.network.mesh,Object.keys(a).forEach((function(t){var e=a[t].interfaces;Object.keys(e).forEach((function(t){e[t].forEach((function(t){r.push(t)}))}))})),r).forEach((function(t){var n=i[t];if(n){delete u[t];try{n.update_nodeinfo(e)}catch(t){console.error(t)}}}))}catch(t){console.error(t)}var n,r,a}),!1),t.onerror=function(){t.close(),o=!1,Object.keys(u).forEach((function(t){u[t]>0&&(u[t]--,l())}))}}}function d(t){var e=i[t];return e||(u[t]=3,e=i[t]=g(a,t,(s[0]||(s=["#396AB1","#DA7C30","#3E9651","#CC2529","#535154","#6B4C9A","#922428","#948B3D"]),s.shift()),(function(){delete u[t],delete i[t]})),l()),e}return n&&c("/cgi-bin/dyn/stations?"+encodeURIComponent(e),(function(t){Object.keys(t).forEach((function(e){var n=t[e];d(e).update_wifi(n)}))})),{get_neigh:d,lookup_neigh:function(t){return i[t]}}}document.querySelectorAll("[data-interface]").forEach((function(t){var e=t.getAttribute("data-interface"),n=(t.getAttribute("data-interface-address"),!!t.getAttribute("data-interface-wireless"));d[e]=v(t,e,n)}));var m=document.getElementById("request-data").getAttribute("data-mesh-provider");m&&c(m,(function(t){Object.keys(t).forEach((function(e){var n=t[e],r=d[n.ifname];r&&r.get_neigh(e).update_mesh(n)}))}))}(); \ No newline at end of file diff --git a/package/libbatadv/src/batadv-genl.c b/package/libbatadv/src/batadv-genl.c index 7d421dbea5..b159c03427 100644 --- a/package/libbatadv/src/batadv-genl.c +++ b/package/libbatadv/src/batadv-genl.c @@ -361,3 +361,105 @@ int batadv_genl_query(const char *mesh_iface, enum batadv_nl_commands nl_cmd, return query_opts->err; } + +struct batadv_algoname_opts { + char *algoname; + size_t algoname_len; + bool found; + struct batadv_nlquery_opts query_opts; +}; + +static int batadv_algoname_cb(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX + 1]; + struct batadv_nlquery_opts *query_opts = arg; + struct batadv_algoname_opts *opts; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genlmsghdr *ghdr; + + opts = batadv_container_of(query_opts, struct batadv_algoname_opts, + query_opts); + + if (!genlmsg_valid_hdr(nlh, 0)) + return NL_OK; + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_MESH) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_genl_policy)) + return NL_OK; + + if (!attrs[BATADV_ATTR_ALGO_NAME]) + return NL_OK; + + nla_strlcpy(opts->algoname, attrs[BATADV_ATTR_ALGO_NAME], + opts->algoname_len); + + opts->found = true; + return NL_OK; +} + +/** + * batadv_genl_get_algoname() - Query the routing algorithm name of a mesh + * @mesh_iface: name of the batman-adv mesh interface + * @algoname: buffer to store the algorithm name (e.g. "BATMAN_IV", "BATMAN_V") + * @algoname_len: size of @algoname buffer + * + * Return: 0 on success or negative error value otherwise + */ +__attribute__ ((visibility ("default"))) +int batadv_genl_get_algoname(const char *mesh_iface, char *algoname, + size_t algoname_len) +{ + struct batadv_algoname_opts opts = { + .algoname = algoname, + .algoname_len = algoname_len, + .found = false, + .query_opts = { .err = 0 }, + }; + int ret; + + ret = batadv_genl_query(mesh_iface, BATADV_CMD_GET_MESH, + batadv_algoname_cb, 0, &opts.query_opts); + if (ret < 0) + return ret; + + if (!opts.found) + return -EOPNOTSUPP; + + return 0; +} + +/** + * batadv_genl_get_algo() - Query the routing algorithm of a mesh as enum + * @mesh_iface: name of the batman-adv mesh interface + * @algo: pointer to store the algorithm (BATADV_ALGO_BATMAN_IV or + * BATADV_ALGO_BATMAN_V) + * + * Return: 0 on success or negative error value otherwise + */ +__attribute__ ((visibility ("default"))) +int batadv_genl_get_algo(const char *mesh_iface, enum batadv_algo *algo) +{ + char algoname[256]; + int ret; + + *algo = BATADV_ALGO_UNKNOWN; + + ret = batadv_genl_get_algoname(mesh_iface, algoname, sizeof(algoname)); + if (ret < 0) + return ret; + + if (strcmp(algoname, "BATMAN_IV") == 0) { + *algo = BATADV_ALGO_BATMAN_IV; + return 0; + } else if (strcmp(algoname, "BATMAN_V") == 0) { + *algo = BATADV_ALGO_BATMAN_V; + return 0; + } + + return -EINVAL; +} diff --git a/package/libbatadv/src/batadv-genl.h b/package/libbatadv/src/batadv-genl.h index d9912fb65b..0bef14491a 100644 --- a/package/libbatadv/src/batadv-genl.h +++ b/package/libbatadv/src/batadv-genl.h @@ -29,10 +29,21 @@ #include #include #include +#include #include #include "batman_adv.h" +/* Zero-init default at slot 0: a struct field that misses + * batadv_genl_get_algo() trips a defined sentinel rather than + * silently being treated as BATMAN_IV. + */ +enum batadv_algo { + BATADV_ALGO_UNKNOWN = 0, + BATADV_ALGO_BATMAN_IV, + BATADV_ALGO_BATMAN_V, +}; + /** * struct batadv_nlquery_opts - internal state for batadv_genl_query() * @@ -97,4 +108,9 @@ int batadv_genl_query(const char *mesh_iface, enum batadv_nl_commands nl_cmd, nl_recvmsg_msg_cb_t callback, int flags, struct batadv_nlquery_opts *query_opts); +int batadv_genl_get_algoname(const char *mesh_iface, char *algoname, + size_t algoname_len); + +int batadv_genl_get_algo(const char *mesh_iface, enum batadv_algo *algo); + #endif /* _BATADV_GENL_H_ */ diff --git a/package/libgluonutil/src/libgluonutil.c b/package/libgluonutil/src/libgluonutil.c index 354f717996..8607958529 100644 --- a/package/libgluonutil/src/libgluonutil.c +++ b/package/libgluonutil/src/libgluonutil.c @@ -337,3 +337,4 @@ struct json_object * gluonutil_load_site_config(void) { json_object_put(site); return NULL; } + diff --git a/package/libgluonutil/src/libgluonutil.h b/package/libgluonutil/src/libgluonutil.h index 5ce9d44827..9391dbbd61 100644 --- a/package/libgluonutil/src/libgluonutil.h +++ b/package/libgluonutil/src/libgluonutil.h @@ -8,6 +8,7 @@ #include #include #include +#include char * gluonutil_read_line(const char *filename);