Browse Source

Merge pull request #976 from FreifunkVogtland/batadv-netlink

Switch from batman-adv v14/v15 debugfs to netlink
Matthias Schiffer 7 years ago
parent
commit
2796f66fcb

+ 1 - 1
package/gluon-mesh-batman-adv-core/Makefile

@@ -13,7 +13,7 @@ define Package/gluon-mesh-batman-adv-core
   SECTION:=gluon
   CATEGORY:=Gluon
   TITLE:=Support for batman-adv meshing (core)
-  DEPENDS:=+gluon-core +libgluonutil +gluon-client-bridge +gluon-ebtables +firewall +libiwinfo +kmod-dummy +kmod-macvlan
+  DEPENDS:=+gluon-core +libgluonutil +gluon-client-bridge +gluon-ebtables +firewall +libiwinfo +kmod-dummy +kmod-macvlan +libnl-tiny
 endef
 
 define Build/Prepare

+ 19 - 1
package/gluon-mesh-batman-adv-core/src/Makefile

@@ -2,5 +2,23 @@ all: respondd.so
 
 CFLAGS += -Wall
 
-respondd.so: respondd.c
+ifeq ($(origin PKG_CONFIG), undefined)
+  PKG_CONFIG = pkg-config
+  ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null),)
+    $(error $(PKG_CONFIG) not found)
+  endif
+endif
+
+ifeq ($(origin LIBNL_CFLAGS) $(origin LIBNL_LDLIBS), undefined undefined)
+  LIBNL_NAME ?= libnl-tiny
+  ifeq ($(shell $(PKG_CONFIG) --modversion $(LIBNL_NAME) 2>/dev/null),)
+    $(error No $(LIBNL_NAME) development libraries found!)
+  endif
+  LIBNL_CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBNL_NAME))
+  LIBNL_LDLIBS +=  $(shell $(PKG_CONFIG) --libs $(LIBNL_NAME))
+endif
+CFLAGS += $(LIBNL_CFLAGS)
+LDLIBS += $(LIBNL_LDLIBS)
+
+respondd.so: respondd.c batadv-netlink.c
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -liwinfo -luci

+ 149 - 0
package/gluon-mesh-batman-adv-core/src/batadv-netlink.c

@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2009-2016  B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner <mareklindner@neomailbox.ch>, Andrew Lunn <andrew@lunn.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ */
+
+#include "batadv-netlink.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <net/ethernet.h>
+
+#include "batman_adv.h"
+
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((unused))
+#endif
+
+struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
+	[BATADV_ATTR_HARD_IFINDEX]	= { .type = NLA_U32 },
+	[BATADV_ATTR_ORIG_ADDRESS]	= { .type = NLA_UNSPEC,
+					    .minlen = ETH_ALEN,
+					    .maxlen = ETH_ALEN },
+	[BATADV_ATTR_TT_FLAGS]		= { .type = NLA_U32 },
+	[BATADV_ATTR_FLAG_BEST]		= { .type = NLA_FLAG },
+	[BATADV_ATTR_LAST_SEEN_MSECS]	= { .type = NLA_U32 },
+	[BATADV_ATTR_NEIGH_ADDRESS]	= { .type = NLA_UNSPEC,
+					    .minlen = ETH_ALEN,
+					    .maxlen = ETH_ALEN },
+	[BATADV_ATTR_TQ]		= { .type = NLA_U8 },
+	[BATADV_ATTR_ROUTER]		= { .type = NLA_UNSPEC,
+					    .minlen = ETH_ALEN,
+					    .maxlen = ETH_ALEN },
+};
+
+static int nlquery_error_cb(struct sockaddr_nl *nla __maybe_unused,
+			    struct nlmsgerr *nlerr, void *arg)
+{
+	struct batadv_nlquery_opts *query_opts = arg;
+
+	query_opts->err = nlerr->error;
+
+	return NL_STOP;
+}
+
+static int nlquery_stop_cb(struct nl_msg *msg, void *arg)
+{
+	struct nlmsghdr *nlh = nlmsg_hdr(msg);
+	struct batadv_nlquery_opts *query_opts = arg;
+	int *error = nlmsg_data(nlh);
+
+	if (*error)
+		query_opts->err = *error;
+
+	return NL_STOP;
+}
+
+int batadv_nl_query_common(const char *mesh_iface,
+			   enum batadv_nl_commands nl_cmd,
+			   nl_recvmsg_msg_cb_t callback, int flags,
+			   struct batadv_nlquery_opts *query_opts)
+{
+	struct nl_sock *sock;
+	struct nl_msg *msg;
+	struct nl_cb *cb;
+	int ifindex;
+	int family;
+	int ret;
+
+	query_opts->err = 0;
+
+	sock = nl_socket_alloc();
+	if (!sock)
+		return -ENOMEM;
+
+	ret = genl_connect(sock);
+	if (ret < 0) {
+		query_opts->err = ret;
+		goto err_free_sock;
+	}
+
+	family = genl_ctrl_resolve(sock, BATADV_NL_NAME);
+	if (family < 0) {
+		query_opts->err = -EOPNOTSUPP;
+		goto err_free_sock;
+	}
+
+	ifindex = if_nametoindex(mesh_iface);
+	if (!ifindex) {
+		query_opts->err = -ENODEV;
+		goto err_free_sock;
+	}
+
+	cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (!cb) {
+		query_opts->err = -ENOMEM;
+		goto err_free_sock;
+	}
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, callback, query_opts);
+	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nlquery_stop_cb, query_opts);
+	nl_cb_err(cb, NL_CB_CUSTOM, nlquery_error_cb, query_opts);
+
+	msg = nlmsg_alloc();
+	if (!msg) {
+		query_opts->err = -ENOMEM;
+		goto err_free_cb;
+	}
+
+	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, flags,
+		    nl_cmd, 1);
+
+	nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex);
+	nl_send_auto_complete(sock, msg);
+	nlmsg_free(msg);
+
+	nl_recvmsgs(sock, cb);
+
+err_free_cb:
+	nl_cb_put(cb);
+err_free_sock:
+	nl_socket_free(sock);
+
+	return query_opts->err;
+}

+ 68 - 0
package/gluon-mesh-batman-adv-core/src/batadv-netlink.h

@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009-2016  B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner <mareklindner@neomailbox.ch>, Andrew Lunn <andrew@lunn.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ */
+
+#ifndef _BATADV_NETLINK_H
+#define _BATADV_NETLINK_H
+
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "batman_adv.h"
+
+struct batadv_nlquery_opts {
+	int err;
+};
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member) __extension__ ({ \
+	const __typeof__(((type *)0)->member) *__pmember = (ptr); \
+	(type *)((char *)__pmember - offsetof(type, member)); })
+#endif
+
+int batadv_nl_query_common(const char *mesh_iface,
+			   enum batadv_nl_commands nl_cmd,
+			   nl_recvmsg_msg_cb_t callback, int flags,
+			   struct batadv_nlquery_opts *query_opts);
+
+static inline bool batadv_nl_missing_attrs(struct nlattr *attrs[],
+					   const enum batadv_nl_attrs mandatory[],
+					   size_t num)
+{
+	size_t i;
+
+	for (i = 0; i < num; i++) {
+		if (!attrs[mandatory[i]])
+			return true;
+	}
+
+	return false;
+}
+
+extern struct nla_policy batadv_netlink_policy[];
+
+#endif /* _BATADV_NETLINK_H */

+ 208 - 0
package/gluon-mesh-batman-adv-core/src/batman_adv.h

@@ -0,0 +1,208 @@
+/* Copyright (C) 2016 B.A.T.M.A.N. contributors:
+ *
+ * Matthias Schiffer
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _UAPI_LINUX_BATMAN_ADV_H_
+#define _UAPI_LINUX_BATMAN_ADV_H_
+
+#define BATADV_NL_NAME "batadv"
+
+#define BATADV_NL_MCAST_GROUP_TPMETER	"tpmeter"
+
+/**
+ * enum batadv_tt_client_flags - TT client specific flags
+ * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
+ * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and the new
+ *  update telling its new real location has not been received/sent yet
+ * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi interface.
+ *  This information is used by the "AP Isolation" feature
+ * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
+ *  information is used by the Extended Isolation feature
+ * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from the table
+ * @BATADV_TT_CLIENT_NEW: this client has been added to the local table but has
+ *  not been announced yet
+ * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it is kept
+ *  in the table for one more originator interval for consistency purposes
+ * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be part of
+ *  the network but no nnode has already announced it
+ *
+ * Bits from 0 to 7 are called _remote flags_ because they are sent on the wire.
+ * Bits from 8 to 15 are called _local flags_ because they are used for local
+ * computations only.
+ *
+ * Bits from 4 to 7 - a subset of remote flags - are ensured to be in sync with
+ * the other nodes in the network. To achieve this goal these flags are included
+ * in the TT CRC computation.
+ */
+enum batadv_tt_client_flags {
+	BATADV_TT_CLIENT_DEL     = (1 << 0),
+	BATADV_TT_CLIENT_ROAM    = (1 << 1),
+	BATADV_TT_CLIENT_WIFI    = (1 << 4),
+	BATADV_TT_CLIENT_ISOLA	 = (1 << 5),
+	BATADV_TT_CLIENT_NOPURGE = (1 << 8),
+	BATADV_TT_CLIENT_NEW     = (1 << 9),
+	BATADV_TT_CLIENT_PENDING = (1 << 10),
+	BATADV_TT_CLIENT_TEMP	 = (1 << 11),
+};
+
+/**
+ * enum batadv_nl_attrs - batman-adv netlink attributes
+ *
+ * @BATADV_ATTR_UNSPEC: unspecified attribute to catch errors
+ * @BATADV_ATTR_VERSION: batman-adv version string
+ * @BATADV_ATTR_ALGO_NAME: name of routing algorithm
+ * @BATADV_ATTR_MESH_IFINDEX: index of the batman-adv interface
+ * @BATADV_ATTR_MESH_IFNAME: name of the batman-adv interface
+ * @BATADV_ATTR_MESH_ADDRESS: mac address of the batman-adv interface
+ * @BATADV_ATTR_HARD_IFINDEX: index of the non-batman-adv interface
+ * @BATADV_ATTR_HARD_IFNAME: name of the non-batman-adv interface
+ * @BATADV_ATTR_HARD_ADDRESS: mac address of the non-batman-adv interface
+ * @BATADV_ATTR_ORIG_ADDRESS: originator mac address
+ * @BATADV_ATTR_TPMETER_RESULT: result of run (see batadv_tp_meter_status)
+ * @BATADV_ATTR_TPMETER_TEST_TIME: time (msec) the run took
+ * @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run
+ * @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session
+ * @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment
+ * @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active
+ * @BATADV_ATTR_TT_ADDRESS: Client MAC address
+ * @BATADV_ATTR_TT_TTVN: Translation table version
+ * @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version
+ * @BATADV_ATTR_TT_CRC32: CRC32 over translation table
+ * @BATADV_ATTR_TT_VID: VLAN ID
+ * @BATADV_ATTR_TT_FLAGS: Translation table client flags
+ * @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best
+ * @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen
+ * @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address
+ * @BATADV_ATTR_TQ: TQ to neighbour
+ * @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour
+ * @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth
+ * @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth
+ * @BATADV_ATTR_ROUTER: Gateway router MAC address
+ * @BATADV_ATTR_BLA_OWN: Flag indicating own originator
+ * @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address
+ * @BATADV_ATTR_BLA_VID: BLA VLAN ID
+ * @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address
+ * @BATADV_ATTR_BLA_CRC: BLA CRC
+ * @__BATADV_ATTR_AFTER_LAST: internal use
+ * @NUM_BATADV_ATTR: total number of batadv_nl_attrs available
+ * @BATADV_ATTR_MAX: highest attribute number currently defined
+ */
+enum batadv_nl_attrs {
+	BATADV_ATTR_UNSPEC,
+	BATADV_ATTR_VERSION,
+	BATADV_ATTR_ALGO_NAME,
+	BATADV_ATTR_MESH_IFINDEX,
+	BATADV_ATTR_MESH_IFNAME,
+	BATADV_ATTR_MESH_ADDRESS,
+	BATADV_ATTR_HARD_IFINDEX,
+	BATADV_ATTR_HARD_IFNAME,
+	BATADV_ATTR_HARD_ADDRESS,
+	BATADV_ATTR_ORIG_ADDRESS,
+	BATADV_ATTR_TPMETER_RESULT,
+	BATADV_ATTR_TPMETER_TEST_TIME,
+	BATADV_ATTR_TPMETER_BYTES,
+	BATADV_ATTR_TPMETER_COOKIE,
+	BATADV_ATTR_PAD,
+	BATADV_ATTR_ACTIVE,
+	BATADV_ATTR_TT_ADDRESS,
+	BATADV_ATTR_TT_TTVN,
+	BATADV_ATTR_TT_LAST_TTVN,
+	BATADV_ATTR_TT_CRC32,
+	BATADV_ATTR_TT_VID,
+	BATADV_ATTR_TT_FLAGS,
+	BATADV_ATTR_FLAG_BEST,
+	BATADV_ATTR_LAST_SEEN_MSECS,
+	BATADV_ATTR_NEIGH_ADDRESS,
+	BATADV_ATTR_TQ,
+	BATADV_ATTR_THROUGHPUT,
+	BATADV_ATTR_BANDWIDTH_UP,
+	BATADV_ATTR_BANDWIDTH_DOWN,
+	BATADV_ATTR_ROUTER,
+	BATADV_ATTR_BLA_OWN,
+	BATADV_ATTR_BLA_ADDRESS,
+	BATADV_ATTR_BLA_VID,
+	BATADV_ATTR_BLA_BACKBONE,
+	BATADV_ATTR_BLA_CRC,
+	/* add attributes above here, update the policy in netlink.c */
+	__BATADV_ATTR_AFTER_LAST,
+	NUM_BATADV_ATTR = __BATADV_ATTR_AFTER_LAST,
+	BATADV_ATTR_MAX = __BATADV_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum batadv_nl_commands - supported batman-adv netlink commands
+ *
+ * @BATADV_CMD_UNSPEC: unspecified command to catch errors
+ * @BATADV_CMD_GET_MESH_INFO: Query basic information about batman-adv device
+ * @BATADV_CMD_TP_METER: Start a tp meter session
+ * @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session
+ * @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms.
+ * @BATADV_CMD_GET_HARDIFS: Query list of hard interfaces
+ * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations
+ * @BATADV_CMD_GET_TRANSTABLE_GLOBAL Query list of global translations
+ * @BATADV_CMD_GET_ORIGINATORS: Query list of originators
+ * @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours
+ * @BATADV_CMD_GET_GATEWAYS: Query list of gateways
+ * @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims
+ * @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance backbones
+ * @__BATADV_CMD_AFTER_LAST: internal use
+ * @BATADV_CMD_MAX: highest used command number
+ */
+enum batadv_nl_commands {
+	BATADV_CMD_UNSPEC,
+	BATADV_CMD_GET_MESH_INFO,
+	BATADV_CMD_TP_METER,
+	BATADV_CMD_TP_METER_CANCEL,
+	BATADV_CMD_GET_ROUTING_ALGOS,
+	BATADV_CMD_GET_HARDIFS,
+	BATADV_CMD_GET_TRANSTABLE_LOCAL,
+	BATADV_CMD_GET_TRANSTABLE_GLOBAL,
+	BATADV_CMD_GET_ORIGINATORS,
+	BATADV_CMD_GET_NEIGHBORS,
+	BATADV_CMD_GET_GATEWAYS,
+	BATADV_CMD_GET_BLA_CLAIM,
+	BATADV_CMD_GET_BLA_BACKBONE,
+	/* add new commands above here */
+	__BATADV_CMD_AFTER_LAST,
+	BATADV_CMD_MAX = __BATADV_CMD_AFTER_LAST - 1
+};
+
+/**
+ * enum batadv_tp_meter_reason - reason of a tp meter test run stop
+ * @BATADV_TP_REASON_COMPLETE: sender finished tp run
+ * @BATADV_TP_REASON_CANCEL: sender was stopped during run
+ * @BATADV_TP_REASON_DST_UNREACHABLE: receiver could not be reached or didn't
+ *  answer
+ * @BATADV_TP_REASON_RESEND_LIMIT: (unused) sender retry reached limit
+ * @BATADV_TP_REASON_ALREADY_ONGOING: test to or from the same node already
+ *  ongoing
+ * @BATADV_TP_REASON_MEMORY_ERROR: test was stopped due to low memory
+ * @BATADV_TP_REASON_CANT_SEND: failed to send via outgoing interface
+ * @BATADV_TP_REASON_TOO_MANY: too many ongoing sessions
+ */
+enum batadv_tp_meter_reason {
+	BATADV_TP_REASON_COMPLETE		= 3,
+	BATADV_TP_REASON_CANCEL			= 4,
+	/* error status >= 128 */
+	BATADV_TP_REASON_DST_UNREACHABLE	= 128,
+	BATADV_TP_REASON_RESEND_LIMIT		= 129,
+	BATADV_TP_REASON_ALREADY_ONGOING	= 130,
+	BATADV_TP_REASON_MEMORY_ERROR		= 131,
+	BATADV_TP_REASON_CANT_SEND		= 132,
+	BATADV_TP_REASON_TOO_MANY		= 133,
+};
+
+#endif /* _UAPI_LINUX_BATMAN_ADV_H_ */

+ 217 - 74
package/gluon-mesh-batman-adv-core/src/respondd.c

@@ -51,11 +51,28 @@
 #include <linux/if_addr.h>
 #include <linux/sockios.h>
 
+#include "batadv-netlink.h"
 
 
 #define _STRINGIFY(s) #s
 #define STRINGIFY(s) _STRINGIFY(s)
 
+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 total;
+	size_t wifi;
+	struct batadv_nlquery_opts query_opts;
+};
+
 
 static struct json_object * get_addresses(void) {
 	FILE *f = fopen("/proc/net/if_inet6", "r");
@@ -223,29 +240,70 @@ static struct json_object * respondd_provider_nodeinfo(void) {
 	return ret;
 }
 
+static const enum batadv_nl_attrs gateways_mandatory[] = {
+	BATADV_ATTR_ORIG_ADDRESS,
+	BATADV_ATTR_ROUTER,
+};
 
-static void add_gateway(struct json_object *obj) {
-	FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/gateways", "r");
-	if (!f)
-		return;
+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];
 
-	char *line = NULL;
-	size_t len = 0;
+	opts = container_of(query_opts, struct gw_netlink_opts, query_opts);
 
-	while (getline(&line, &len, f) >= 0) {
-		char addr[18];
-		char nexthop[18];
+	if (!genlmsg_valid_hdr(nlh, 0))
+		return NL_OK;
 
-		if (sscanf(line, "=> %17[0-9a-fA-F:] ( %*u) %17[0-9a-fA-F:]", addr, nexthop) != 2)
-			continue;
+	ghdr = nlmsg_data(nlh);
 
-		json_object_object_add(obj, "gateway", json_object_new_string(addr));
-		json_object_object_add(obj, "gateway_nexthop", json_object_new_string(nexthop));
-		break;
-	}
+	if (ghdr->cmd != BATADV_CMD_GET_GATEWAYS)
+		return NL_OK;
 
-	free(line);
-	fclose(f);
+	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
+		      genlmsg_len(ghdr), batadv_netlink_policy))
+		return NL_OK;
+
+	if (batadv_nl_missing_attrs(attrs, gateways_mandatory,
+				    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_nl_query_common("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) {
@@ -421,39 +479,69 @@ static void count_stations(size_t *wifi24, size_t *wifi5) {
 	uci_free_context(ctx);
 }
 
-static struct json_object * get_clients(void) {
-	size_t total = 0, wifi = 0, wifi24 = 0, wifi5 = 0;
+static const enum batadv_nl_attrs clients_mandatory[] = {
+	BATADV_ATTR_TT_FLAGS,
+};
 
-	FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/transtable_local", "r");
-	if (!f)
-		return NULL;
+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;
 
-	char *line = NULL;
-	size_t len = 0;
+	opts = container_of(query_opts, struct clients_netlink_opts, query_opts);
 
-	while (getline(&line, &len, f) >= 0) {
-		char flags[16];
+	if (!genlmsg_valid_hdr(nlh, 0))
+		return NL_OK;
 
-		if (sscanf(line, " * %*[^[] [%15[^]]]", flags) != 1)
-			continue;
+	ghdr = nlmsg_data(nlh);
 
-		if (strchr(flags, 'P'))
-			continue;
+	if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL)
+		return NL_OK;
 
-		total++;
+	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
+		      genlmsg_len(ghdr), batadv_netlink_policy))
+		return NL_OK;
 
-		if (strchr(flags, 'W'))
-			wifi++;
-	}
+	if (batadv_nl_missing_attrs(attrs, clients_mandatory,
+				    ARRAY_SIZE(clients_mandatory)))
+		return NL_OK;
 
-	free(line);
-	fclose(f);
+	flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]);
+
+	if (flags & BATADV_TT_CLIENT_NOPURGE)
+		return NL_OK;
+
+	if (flags & BATADV_TT_CLIENT_WIFI)
+		opts->wifi++;
+
+	opts->total++;
+
+	return NL_OK;
+}
+
+static struct json_object * get_clients(void) {
+	size_t wifi24 = 0, wifi5 = 0;
+	struct clients_netlink_opts opts = {
+		.total = 0,
+		.wifi = 0,
+		.query_opts = {
+			.err = 0,
+		},
+	};
+
+	batadv_nl_query_common("bat0", BATADV_CMD_GET_TRANSTABLE_LOCAL,
+			       parse_clients_list_netlink_cb, NLM_F_DUMP,
+			       &opts.query_opts);
 
 	count_stations(&wifi24, &wifi5);
 
 	struct json_object *ret = json_object_new_object();
-	json_object_object_add(ret, "total", json_object_new_int(total));
-	json_object_object_add(ret, "wifi", json_object_new_int(wifi));
+	json_object_object_add(ret, "total", json_object_new_int(opts.total));
+	json_object_object_add(ret, "wifi", json_object_new_int(opts.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;
@@ -492,49 +580,104 @@ static struct json_object * ifnames2addrs(struct json_object *interfaces) {
 	return ret;
 }
 
-static struct json_object * get_batadv(void) {
-	FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/originators", "r");
-	if (!f)
-		return NULL;
-
-	char *line = NULL;
-	size_t len = 0;
-
-	struct json_object *interfaces = json_object_new_object();
+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,
+};
 
-	while (getline(&line, &len, f) >= 0) {
-		char mac1[18], mac2[18];
-		/* IF_NAMESIZE would be enough, but adding 1 here is simpler than subtracting 1 in the format string */
-		char ifname[IF_NAMESIZE+1];
-		double lastseen;
-		int tq;
+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 = 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_netlink_policy))
+		return NL_OK;
+
+	if (batadv_nl_missing_attrs(attrs, parse_orig_list_mandatory,
+				    ARRAY_SIZE(parse_orig_list_mandatory)))
+		return NL_OK;
+
+	if (!attrs[BATADV_ATTR_FLAG_BEST])
+		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);
+	}
 
-		if (sscanf(line,
-			   "%17[0-9a-fA-F:] %lfs ( %i ) %17[0-9a-fA-F:] [ %"STRINGIFY(IF_NAMESIZE)"[^]] ]",
-			   mac1, &lastseen, &tq, mac2, ifname) != 5)
-			continue;
+	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(interface, mac1, obj);
 
-		if (strcmp(mac1, mac2))
-			continue;
+	return NL_OK;
+}
 
-		struct json_object *interface;
-		if (!json_object_object_get_ex(interfaces, ifname, &interface)) {
-			interface = json_object_new_object();
-			json_object_object_add(interfaces, ifname, interface);
-		}
+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;
 
-		struct json_object *obj = json_object_new_object();
-		json_object_object_add(obj, "tq", json_object_new_int(tq));
-		struct json_object *jso = json_object_new_double(lastseen);
-		json_object_set_serializer(jso, json_object_double_to_json_string, "%.3f", NULL);
-		json_object_object_add(obj, "lastseen", jso);
-		json_object_object_add(interface, mac1, obj);
+	ret = batadv_nl_query_common("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;
 	}
 
-	fclose(f);
-	free(line);
-
-	return ifnames2addrs(interfaces);
+	return ifnames2addrs(opts.interfaces);
 }
 
 static struct json_object * get_wifi_neighbours(const char *ifname) {

+ 1 - 1
package/gluon-status-page-api/Makefile

@@ -14,7 +14,7 @@ define Package/gluon-status-page-api
   SECTION:=gluon
   CATEGORY:=Gluon
   TITLE:=API for gluon-status-page
-  DEPENDS:=+gluon-core +uhttpd +sse-multiplex +gluon-neighbour-info +gluon-respondd +libiwinfo +libjson-c
+  DEPENDS:=+gluon-core +uhttpd +sse-multiplex +gluon-neighbour-info +gluon-respondd +libiwinfo +libjson-c +libnl-tiny
 endef
 
 define Build/Prepare

+ 19 - 2
package/gluon-status-page-api/src/Makefile

@@ -1,12 +1,29 @@
 CFLAGS += -std=c99 -D_BSD_SOURCE
+CPPFLAGS +=  -D_GNU_SOURCE
+
+ifeq ($(origin PKG_CONFIG), undefined)
+  PKG_CONFIG = pkg-config
+  ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null),)
+    $(error $(PKG_CONFIG) not found)
+  endif
+endif
+
+ifeq ($(origin LIBNL_CFLAGS) $(origin LIBNL_LDLIBS), undefined undefined)
+  LIBNL_NAME ?= libnl-tiny
+  ifeq ($(shell $(PKG_CONFIG) --modversion $(LIBNL_NAME) 2>/dev/null),)
+    $(error No $(LIBNL_NAME) development libraries found!)
+  endif
+  LIBNL_CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBNL_NAME))
+  LIBNL_LDLIBS +=  $(shell $(PKG_CONFIG) --libs $(LIBNL_NAME))
+endif
 
 CFLAGS_JSONC = $(shell pkg-config --cflags json-c)
 LDFLAGS_JSONC = $(shell pkg-config --libs json-c)
 
 all: neighbours-batadv stations respondd.so
 
-neighbours-batadv: neighbours-batadv.c
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDFLAGS_JSONC) -Wall -o $@ $^ $(LDLIBS)
+neighbours-batadv: neighbours-batadv.c batadv-netlink.c
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LIBNL_CFLAGS) $(LDFLAGS) $(LDFLAGS_JSONC) $(LIBNL_LDLIBS) -Wall -o $@ $^ $(LDLIBS)
 
 stations: stations.c
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDFLAGS_JSONC) -Wall -o $@ $^ $(LDLIBS) -liwinfo

+ 145 - 0
package/gluon-status-page-api/src/batadv-netlink.c

@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2009-2016  B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner <mareklindner@neomailbox.ch>, Andrew Lunn <andrew@lunn.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ */
+
+#include "batadv-netlink.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <net/ethernet.h>
+
+#include "batman_adv.h"
+
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((unused))
+#endif
+
+struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = {
+	[BATADV_ATTR_HARD_IFINDEX]	= { .type = NLA_U32 },
+	[BATADV_ATTR_ORIG_ADDRESS]	= { .type = NLA_UNSPEC,
+					    .minlen = ETH_ALEN,
+					    .maxlen = ETH_ALEN },
+	[BATADV_ATTR_FLAG_BEST]		= { .type = NLA_FLAG },
+	[BATADV_ATTR_LAST_SEEN_MSECS]	= { .type = NLA_U32 },
+	[BATADV_ATTR_NEIGH_ADDRESS]	= { .type = NLA_UNSPEC,
+					    .minlen = ETH_ALEN,
+					    .maxlen = ETH_ALEN },
+	[BATADV_ATTR_TQ]		= { .type = NLA_U8 },
+};
+
+static int nlquery_error_cb(struct sockaddr_nl *nla __maybe_unused,
+			    struct nlmsgerr *nlerr, void *arg)
+{
+	struct batadv_nlquery_opts *query_opts = arg;
+
+	query_opts->err = nlerr->error;
+
+	return NL_STOP;
+}
+
+static int nlquery_stop_cb(struct nl_msg *msg, void *arg)
+{
+	struct nlmsghdr *nlh = nlmsg_hdr(msg);
+	struct batadv_nlquery_opts *query_opts = arg;
+	int *error = nlmsg_data(nlh);
+
+	if (*error)
+		query_opts->err = *error;
+
+	return NL_STOP;
+}
+
+int batadv_nl_query_common(const char *mesh_iface,
+			   enum batadv_nl_commands nl_cmd,
+			   nl_recvmsg_msg_cb_t callback, int flags,
+			   struct batadv_nlquery_opts *query_opts)
+{
+	struct nl_sock *sock;
+	struct nl_msg *msg;
+	struct nl_cb *cb;
+	int ifindex;
+	int family;
+	int ret;
+
+	query_opts->err = 0;
+
+	sock = nl_socket_alloc();
+	if (!sock)
+		return -ENOMEM;
+
+	ret = genl_connect(sock);
+	if (ret < 0) {
+		query_opts->err = ret;
+		goto err_free_sock;
+	}
+
+	family = genl_ctrl_resolve(sock, BATADV_NL_NAME);
+	if (family < 0) {
+		query_opts->err = -EOPNOTSUPP;
+		goto err_free_sock;
+	}
+
+	ifindex = if_nametoindex(mesh_iface);
+	if (!ifindex) {
+		query_opts->err = -ENODEV;
+		goto err_free_sock;
+	}
+
+	cb = nl_cb_alloc(NL_CB_DEFAULT);
+	if (!cb) {
+		query_opts->err = -ENOMEM;
+		goto err_free_sock;
+	}
+
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, callback, query_opts);
+	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nlquery_stop_cb, query_opts);
+	nl_cb_err(cb, NL_CB_CUSTOM, nlquery_error_cb, query_opts);
+
+	msg = nlmsg_alloc();
+	if (!msg) {
+		query_opts->err = -ENOMEM;
+		goto err_free_cb;
+	}
+
+	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, flags,
+		    nl_cmd, 1);
+
+	nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, ifindex);
+	nl_send_auto_complete(sock, msg);
+	nlmsg_free(msg);
+
+	nl_recvmsgs(sock, cb);
+
+err_free_cb:
+	nl_cb_put(cb);
+err_free_sock:
+	nl_socket_free(sock);
+
+	return query_opts->err;
+}

+ 68 - 0
package/gluon-status-page-api/src/batadv-netlink.h

@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009-2016  B.A.T.M.A.N. contributors:
+ *
+ * Marek Lindner <mareklindner@neomailbox.ch>, Andrew Lunn <andrew@lunn.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ */
+
+#ifndef _BATADV_NETLINK_H
+#define _BATADV_NETLINK_H
+
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "batman_adv.h"
+
+struct batadv_nlquery_opts {
+	int err;
+};
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member) __extension__ ({ \
+	const __typeof__(((type *)0)->member) *__pmember = (ptr); \
+	(type *)((char *)__pmember - offsetof(type, member)); })
+#endif
+
+int batadv_nl_query_common(const char *mesh_iface,
+			   enum batadv_nl_commands nl_cmd,
+			   nl_recvmsg_msg_cb_t callback, int flags,
+			   struct batadv_nlquery_opts *query_opts);
+
+static inline bool batadv_nl_missing_attrs(struct nlattr *attrs[],
+					   const enum batadv_nl_attrs mandatory[],
+					   size_t num)
+{
+	size_t i;
+
+	for (i = 0; i < num; i++) {
+		if (!attrs[mandatory[i]])
+			return true;
+	}
+
+	return false;
+}
+
+extern struct nla_policy batadv_netlink_policy[];
+
+#endif /* _BATADV_NETLINK_H */

+ 208 - 0
package/gluon-status-page-api/src/batman_adv.h

@@ -0,0 +1,208 @@
+/* Copyright (C) 2016 B.A.T.M.A.N. contributors:
+ *
+ * Matthias Schiffer
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _UAPI_LINUX_BATMAN_ADV_H_
+#define _UAPI_LINUX_BATMAN_ADV_H_
+
+#define BATADV_NL_NAME "batadv"
+
+#define BATADV_NL_MCAST_GROUP_TPMETER	"tpmeter"
+
+/**
+ * enum batadv_tt_client_flags - TT client specific flags
+ * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
+ * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and the new
+ *  update telling its new real location has not been received/sent yet
+ * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi interface.
+ *  This information is used by the "AP Isolation" feature
+ * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
+ *  information is used by the Extended Isolation feature
+ * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from the table
+ * @BATADV_TT_CLIENT_NEW: this client has been added to the local table but has
+ *  not been announced yet
+ * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it is kept
+ *  in the table for one more originator interval for consistency purposes
+ * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be part of
+ *  the network but no nnode has already announced it
+ *
+ * Bits from 0 to 7 are called _remote flags_ because they are sent on the wire.
+ * Bits from 8 to 15 are called _local flags_ because they are used for local
+ * computations only.
+ *
+ * Bits from 4 to 7 - a subset of remote flags - are ensured to be in sync with
+ * the other nodes in the network. To achieve this goal these flags are included
+ * in the TT CRC computation.
+ */
+enum batadv_tt_client_flags {
+	BATADV_TT_CLIENT_DEL     = (1 << 0),
+	BATADV_TT_CLIENT_ROAM    = (1 << 1),
+	BATADV_TT_CLIENT_WIFI    = (1 << 4),
+	BATADV_TT_CLIENT_ISOLA	 = (1 << 5),
+	BATADV_TT_CLIENT_NOPURGE = (1 << 8),
+	BATADV_TT_CLIENT_NEW     = (1 << 9),
+	BATADV_TT_CLIENT_PENDING = (1 << 10),
+	BATADV_TT_CLIENT_TEMP	 = (1 << 11),
+};
+
+/**
+ * enum batadv_nl_attrs - batman-adv netlink attributes
+ *
+ * @BATADV_ATTR_UNSPEC: unspecified attribute to catch errors
+ * @BATADV_ATTR_VERSION: batman-adv version string
+ * @BATADV_ATTR_ALGO_NAME: name of routing algorithm
+ * @BATADV_ATTR_MESH_IFINDEX: index of the batman-adv interface
+ * @BATADV_ATTR_MESH_IFNAME: name of the batman-adv interface
+ * @BATADV_ATTR_MESH_ADDRESS: mac address of the batman-adv interface
+ * @BATADV_ATTR_HARD_IFINDEX: index of the non-batman-adv interface
+ * @BATADV_ATTR_HARD_IFNAME: name of the non-batman-adv interface
+ * @BATADV_ATTR_HARD_ADDRESS: mac address of the non-batman-adv interface
+ * @BATADV_ATTR_ORIG_ADDRESS: originator mac address
+ * @BATADV_ATTR_TPMETER_RESULT: result of run (see batadv_tp_meter_status)
+ * @BATADV_ATTR_TPMETER_TEST_TIME: time (msec) the run took
+ * @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run
+ * @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session
+ * @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment
+ * @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active
+ * @BATADV_ATTR_TT_ADDRESS: Client MAC address
+ * @BATADV_ATTR_TT_TTVN: Translation table version
+ * @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version
+ * @BATADV_ATTR_TT_CRC32: CRC32 over translation table
+ * @BATADV_ATTR_TT_VID: VLAN ID
+ * @BATADV_ATTR_TT_FLAGS: Translation table client flags
+ * @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best
+ * @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen
+ * @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address
+ * @BATADV_ATTR_TQ: TQ to neighbour
+ * @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour
+ * @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth
+ * @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth
+ * @BATADV_ATTR_ROUTER: Gateway router MAC address
+ * @BATADV_ATTR_BLA_OWN: Flag indicating own originator
+ * @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address
+ * @BATADV_ATTR_BLA_VID: BLA VLAN ID
+ * @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address
+ * @BATADV_ATTR_BLA_CRC: BLA CRC
+ * @__BATADV_ATTR_AFTER_LAST: internal use
+ * @NUM_BATADV_ATTR: total number of batadv_nl_attrs available
+ * @BATADV_ATTR_MAX: highest attribute number currently defined
+ */
+enum batadv_nl_attrs {
+	BATADV_ATTR_UNSPEC,
+	BATADV_ATTR_VERSION,
+	BATADV_ATTR_ALGO_NAME,
+	BATADV_ATTR_MESH_IFINDEX,
+	BATADV_ATTR_MESH_IFNAME,
+	BATADV_ATTR_MESH_ADDRESS,
+	BATADV_ATTR_HARD_IFINDEX,
+	BATADV_ATTR_HARD_IFNAME,
+	BATADV_ATTR_HARD_ADDRESS,
+	BATADV_ATTR_ORIG_ADDRESS,
+	BATADV_ATTR_TPMETER_RESULT,
+	BATADV_ATTR_TPMETER_TEST_TIME,
+	BATADV_ATTR_TPMETER_BYTES,
+	BATADV_ATTR_TPMETER_COOKIE,
+	BATADV_ATTR_PAD,
+	BATADV_ATTR_ACTIVE,
+	BATADV_ATTR_TT_ADDRESS,
+	BATADV_ATTR_TT_TTVN,
+	BATADV_ATTR_TT_LAST_TTVN,
+	BATADV_ATTR_TT_CRC32,
+	BATADV_ATTR_TT_VID,
+	BATADV_ATTR_TT_FLAGS,
+	BATADV_ATTR_FLAG_BEST,
+	BATADV_ATTR_LAST_SEEN_MSECS,
+	BATADV_ATTR_NEIGH_ADDRESS,
+	BATADV_ATTR_TQ,
+	BATADV_ATTR_THROUGHPUT,
+	BATADV_ATTR_BANDWIDTH_UP,
+	BATADV_ATTR_BANDWIDTH_DOWN,
+	BATADV_ATTR_ROUTER,
+	BATADV_ATTR_BLA_OWN,
+	BATADV_ATTR_BLA_ADDRESS,
+	BATADV_ATTR_BLA_VID,
+	BATADV_ATTR_BLA_BACKBONE,
+	BATADV_ATTR_BLA_CRC,
+	/* add attributes above here, update the policy in netlink.c */
+	__BATADV_ATTR_AFTER_LAST,
+	NUM_BATADV_ATTR = __BATADV_ATTR_AFTER_LAST,
+	BATADV_ATTR_MAX = __BATADV_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum batadv_nl_commands - supported batman-adv netlink commands
+ *
+ * @BATADV_CMD_UNSPEC: unspecified command to catch errors
+ * @BATADV_CMD_GET_MESH_INFO: Query basic information about batman-adv device
+ * @BATADV_CMD_TP_METER: Start a tp meter session
+ * @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session
+ * @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms.
+ * @BATADV_CMD_GET_HARDIFS: Query list of hard interfaces
+ * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations
+ * @BATADV_CMD_GET_TRANSTABLE_GLOBAL Query list of global translations
+ * @BATADV_CMD_GET_ORIGINATORS: Query list of originators
+ * @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours
+ * @BATADV_CMD_GET_GATEWAYS: Query list of gateways
+ * @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims
+ * @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance backbones
+ * @__BATADV_CMD_AFTER_LAST: internal use
+ * @BATADV_CMD_MAX: highest used command number
+ */
+enum batadv_nl_commands {
+	BATADV_CMD_UNSPEC,
+	BATADV_CMD_GET_MESH_INFO,
+	BATADV_CMD_TP_METER,
+	BATADV_CMD_TP_METER_CANCEL,
+	BATADV_CMD_GET_ROUTING_ALGOS,
+	BATADV_CMD_GET_HARDIFS,
+	BATADV_CMD_GET_TRANSTABLE_LOCAL,
+	BATADV_CMD_GET_TRANSTABLE_GLOBAL,
+	BATADV_CMD_GET_ORIGINATORS,
+	BATADV_CMD_GET_NEIGHBORS,
+	BATADV_CMD_GET_GATEWAYS,
+	BATADV_CMD_GET_BLA_CLAIM,
+	BATADV_CMD_GET_BLA_BACKBONE,
+	/* add new commands above here */
+	__BATADV_CMD_AFTER_LAST,
+	BATADV_CMD_MAX = __BATADV_CMD_AFTER_LAST - 1
+};
+
+/**
+ * enum batadv_tp_meter_reason - reason of a tp meter test run stop
+ * @BATADV_TP_REASON_COMPLETE: sender finished tp run
+ * @BATADV_TP_REASON_CANCEL: sender was stopped during run
+ * @BATADV_TP_REASON_DST_UNREACHABLE: receiver could not be reached or didn't
+ *  answer
+ * @BATADV_TP_REASON_RESEND_LIMIT: (unused) sender retry reached limit
+ * @BATADV_TP_REASON_ALREADY_ONGOING: test to or from the same node already
+ *  ongoing
+ * @BATADV_TP_REASON_MEMORY_ERROR: test was stopped due to low memory
+ * @BATADV_TP_REASON_CANT_SEND: failed to send via outgoing interface
+ * @BATADV_TP_REASON_TOO_MANY: too many ongoing sessions
+ */
+enum batadv_tp_meter_reason {
+	BATADV_TP_REASON_COMPLETE		= 3,
+	BATADV_TP_REASON_CANCEL			= 4,
+	/* error status >= 128 */
+	BATADV_TP_REASON_DST_UNREACHABLE	= 128,
+	BATADV_TP_REASON_RESEND_LIMIT		= 129,
+	BATADV_TP_REASON_ALREADY_ONGOING	= 130,
+	BATADV_TP_REASON_MEMORY_ERROR		= 131,
+	BATADV_TP_REASON_CANT_SEND		= 132,
+	BATADV_TP_REASON_TOO_MANY		= 133,
+};
+
+#endif /* _UAPI_LINUX_BATMAN_ADV_H_ */

+ 96 - 31
package/gluon-status-page-api/src/neighbours-batadv.c

@@ -4,45 +4,110 @@
 #include <json-c/json.h>
 #include <net/if.h>
 
+#include "batadv-netlink.h"
+
 #define STR(x) #x
 #define XSTR(x) STR(x)
 
-static json_object *neighbours(void) {
-  struct json_object *obj = json_object_new_object();
-
-  FILE *f;
-
-  f = fopen("/sys/kernel/debug/batman_adv/bat0/originators" , "r");
+struct neigh_netlink_opts {
+  struct json_object *obj;
+  struct batadv_nlquery_opts query_opts;
+};
+
+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 = 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_netlink_policy))
+    return NL_OK;
+
+  if (batadv_nl_missing_attrs(attrs, parse_orig_list_mandatory,
+              ARRAY_SIZE(parse_orig_list_mandatory)))
+    return NL_OK;
+
+  if (!attrs[BATADV_ATTR_FLAG_BEST])
+    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 *neigh = json_object_new_object();
+  if (!neigh)
+    return NL_OK;
+
+  json_object_object_add(neigh, "tq", json_object_new_int(tq));
+  json_object_object_add(neigh, "lastseen", json_object_new_double(lastseen / 1000.));
+  json_object_object_add(neigh, "ifname", json_object_new_string(ifname));
+
+  json_object_object_add(opts->obj, mac1, neigh);
+
+  return NL_OK;
+}
 
-  if (f == NULL)
+static json_object *neighbours(void) {
+  struct neigh_netlink_opts opts = {
+    .query_opts = {
+      .err = 0,
+    },
+  };
+  int ret;
+
+  opts.obj = json_object_new_object();
+  if (!opts.obj)
     return NULL;
 
-  while (!feof(f)) {
-    char mac1[18];
-    char mac2[18];
-    char ifname[IF_NAMESIZE+1];
-    int tq;
-    double lastseen;
-
-    int count = fscanf(f, "%17s%*[\t ]%lfs%*[\t (]%d) %17s%*[[ ]%" XSTR(IF_NAMESIZE) "[^]]]", mac1, &lastseen, &tq, mac2, ifname);
-
-    if (count != 5)
-      continue;
-
-    if (strcmp(mac1, mac2) == 0) {
-      struct json_object *neigh = json_object_new_object();
-
-      json_object_object_add(neigh, "tq", json_object_new_int(tq));
-      json_object_object_add(neigh, "lastseen", json_object_new_double(lastseen));
-      json_object_object_add(neigh, "ifname", json_object_new_string(ifname));
-
-      json_object_object_add(obj, mac1, neigh);
-    }
+  ret = batadv_nl_query_common("bat0", BATADV_CMD_GET_ORIGINATORS,
+                               parse_orig_list_netlink_cb, NLM_F_DUMP,
+                               &opts.query_opts);
+  if (ret < 0) {
+    json_object_put(opts.obj);
+    return NULL;
   }
 
-  fclose(f);
-
-  return obj;
+  return opts.obj;
 }
 
 int main(void) {