Ver código fonte

gluon-mesh-batman-adv-core: Switch from debugfs to batadv netlink

The batadv debugfs requires large memory blocks to write the text debug
tables. This is inefficient for large tables like the global translation
table or the originators table.

The memory requirement can be reduced by using netlink. It copies smaller
packets in a binary format to the userspace program. The respondd module of
gluon-mesh-batman-adv-core can therefore parse larger originator tables
without causing an OOM on systems which are tight on memory.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
Sven Eckelmann 7 anos atrás
pai
commit
22946b14b6

+ 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) {