From 6ac5b9cbc85985d5c64a9487aa95f8ce5ab4195e Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 24 Sep 2016 19:59:44 +0200 Subject: [PATCH 1/2] gluon-status-page-api: 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. gluon-status-page-api can therefore parse larger originator tables without causing an OOM on systems which are tight on memory. Signed-off-by: Sven Eckelmann --- package/gluon-status-page-api/Makefile | 2 +- package/gluon-status-page-api/src/Makefile | 21 +- .../src/batadv-netlink.c | 145 ++++++++++++ .../src/batadv-netlink.h | 68 ++++++ .../gluon-status-page-api/src/batman_adv.h | 208 ++++++++++++++++++ .../src/neighbours-batadv.c | 125 ++++++++--- 6 files changed, 536 insertions(+), 33 deletions(-) create mode 100644 package/gluon-status-page-api/src/batadv-netlink.c create mode 100644 package/gluon-status-page-api/src/batadv-netlink.h create mode 100644 package/gluon-status-page-api/src/batman_adv.h diff --git a/package/gluon-status-page-api/Makefile b/package/gluon-status-page-api/Makefile index ddad2cab..703d97f6 100644 --- a/package/gluon-status-page-api/Makefile +++ b/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 diff --git a/package/gluon-status-page-api/src/Makefile b/package/gluon-status-page-api/src/Makefile index 81b48b6d..e67a595e 100644 --- a/package/gluon-status-page-api/src/Makefile +++ b/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 diff --git a/package/gluon-status-page-api/src/batadv-netlink.c b/package/gluon-status-page-api/src/batadv-netlink.c new file mode 100644 index 00000000..ca965d48 --- /dev/null +++ b/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 , Andrew Lunn + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/package/gluon-status-page-api/src/batadv-netlink.h b/package/gluon-status-page-api/src/batadv-netlink.h new file mode 100644 index 00000000..8b85a6c2 --- /dev/null +++ b/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 , Andrew Lunn + * + * 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 +#include +#include +#include +#include + +#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 */ diff --git a/package/gluon-status-page-api/src/batman_adv.h b/package/gluon-status-page-api/src/batman_adv.h new file mode 100644 index 00000000..734fe83a --- /dev/null +++ b/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_ */ diff --git a/package/gluon-status-page-api/src/neighbours-batadv.c b/package/gluon-status-page-api/src/neighbours-batadv.c index f0a484b2..7d5d0211 100644 --- a/package/gluon-status-page-api/src/neighbours-batadv.c +++ b/package/gluon-status-page-api/src/neighbours-batadv.c @@ -4,45 +4,110 @@ #include #include +#include "batadv-netlink.h" + #define STR(x) #x #define XSTR(x) STR(x) +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; +} + static json_object *neighbours(void) { - struct json_object *obj = json_object_new_object(); + struct neigh_netlink_opts opts = { + .query_opts = { + .err = 0, + }, + }; + int ret; - FILE *f; - - f = fopen("/sys/kernel/debug/batman_adv/bat0/originators" , "r"); - - if (f == NULL) + 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) { From 22946b14b6b29caa7361256215eb42643bf68055 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 24 Sep 2016 19:59:44 +0200 Subject: [PATCH 2/2] 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 --- package/gluon-mesh-batman-adv-core/Makefile | 2 +- .../gluon-mesh-batman-adv-core/src/Makefile | 20 +- .../src/batadv-netlink.c | 149 +++++++++ .../src/batadv-netlink.h | 68 ++++ .../src/batman_adv.h | 208 ++++++++++++ .../gluon-mesh-batman-adv-core/src/respondd.c | 303 +++++++++++++----- 6 files changed, 668 insertions(+), 82 deletions(-) create mode 100644 package/gluon-mesh-batman-adv-core/src/batadv-netlink.c create mode 100644 package/gluon-mesh-batman-adv-core/src/batadv-netlink.h create mode 100644 package/gluon-mesh-batman-adv-core/src/batman_adv.h diff --git a/package/gluon-mesh-batman-adv-core/Makefile b/package/gluon-mesh-batman-adv-core/Makefile index 25c3b9b0..1989ab78 100644 --- a/package/gluon-mesh-batman-adv-core/Makefile +++ b/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 diff --git a/package/gluon-mesh-batman-adv-core/src/Makefile b/package/gluon-mesh-batman-adv-core/src/Makefile index 84d9d48e..9206a067 100644 --- a/package/gluon-mesh-batman-adv-core/src/Makefile +++ b/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 diff --git a/package/gluon-mesh-batman-adv-core/src/batadv-netlink.c b/package/gluon-mesh-batman-adv-core/src/batadv-netlink.c new file mode 100644 index 00000000..aeea7624 --- /dev/null +++ b/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 , Andrew Lunn + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/package/gluon-mesh-batman-adv-core/src/batadv-netlink.h b/package/gluon-mesh-batman-adv-core/src/batadv-netlink.h new file mode 100644 index 00000000..8b85a6c2 --- /dev/null +++ b/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 , Andrew Lunn + * + * 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 +#include +#include +#include +#include + +#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 */ diff --git a/package/gluon-mesh-batman-adv-core/src/batman_adv.h b/package/gluon-mesh-batman-adv-core/src/batman_adv.h new file mode 100644 index 00000000..734fe83a --- /dev/null +++ b/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_ */ diff --git a/package/gluon-mesh-batman-adv-core/src/respondd.c b/package/gluon-mesh-batman-adv-core/src/respondd.c index a84076e9..33e1e0d5 100644 --- a/package/gluon-mesh-batman-adv-core/src/respondd.c +++ b/package/gluon-mesh-batman-adv-core/src/respondd.c @@ -51,11 +51,28 @@ #include #include +#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 int parse_gw_list_netlink_cb(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct batadv_nlquery_opts *query_opts = arg; + struct genlmsghdr *ghdr; + uint8_t *orig; + uint8_t *router; + struct gw_netlink_opts *opts; + char addr[18]; + + opts = container_of(query_opts, struct gw_netlink_opts, query_opts); + + if (!genlmsg_valid_hdr(nlh, 0)) + return NL_OK; + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_GATEWAYS) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_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) { - FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/gateways", "r"); - if (!f) - return; + struct gw_netlink_opts opts = { + .obj = obj, + .query_opts = { + .err = 0, + }, + }; - char *line = NULL; - size_t len = 0; - - while (getline(&line, &len, f) >= 0) { - char addr[18]; - char nexthop[18]; - - if (sscanf(line, "=> %17[0-9a-fA-F:] ( %*u) %17[0-9a-fA-F:]", addr, nexthop) != 2) - continue; - - json_object_object_add(obj, "gateway", json_object_new_string(addr)); - json_object_object_add(obj, "gateway_nexthop", json_object_new_string(nexthop)); - break; - } - - free(line); - fclose(f); + 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 const enum batadv_nl_attrs clients_mandatory[] = { + BATADV_ATTR_TT_FLAGS, +}; + +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; + + opts = container_of(query_opts, struct clients_netlink_opts, query_opts); + + if (!genlmsg_valid_hdr(nlh, 0)) + return NL_OK; + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_netlink_policy)) + return NL_OK; + + if (batadv_nl_missing_attrs(attrs, clients_mandatory, + ARRAY_SIZE(clients_mandatory))) + return NL_OK; + + flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]); + + if (flags & BATADV_TT_CLIENT_NOPURGE) + return NL_OK; + + if (flags & BATADV_TT_CLIENT_WIFI) + opts->wifi++; + + opts->total++; + + return NL_OK; +} + static struct json_object * get_clients(void) { - size_t total = 0, wifi = 0, wifi24 = 0, wifi5 = 0; + size_t wifi24 = 0, wifi5 = 0; + struct clients_netlink_opts opts = { + .total = 0, + .wifi = 0, + .query_opts = { + .err = 0, + }, + }; - FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/transtable_local", "r"); - if (!f) - return NULL; - - char *line = NULL; - size_t len = 0; - - while (getline(&line, &len, f) >= 0) { - char flags[16]; - - if (sscanf(line, " * %*[^[] [%15[^]]]", flags) != 1) - continue; - - if (strchr(flags, 'P')) - continue; - - total++; - - if (strchr(flags, 'W')) - wifi++; - } - - free(line); - fclose(f); + 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; +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, +}; - char *line = NULL; - size_t len = 0; +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]; - struct json_object *interfaces = json_object_new_object(); + opts = container_of(query_opts, struct neigh_netlink_opts, query_opts); - while (getline(&line, &len, f) >= 0) { - char mac1[18], mac2[18]; - /* IF_NAMESIZE would be enough, but adding 1 here is simpler than subtracting 1 in the format string */ - char ifname[IF_NAMESIZE+1]; - double lastseen; - int tq; + if (!genlmsg_valid_hdr(nlh, 0)) + return NL_OK; - 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; + ghdr = nlmsg_data(nlh); - if (strcmp(mac1, mac2)) - continue; + if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) + 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); - } + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_netlink_policy)) + return NL_OK; - 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); + 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); } - fclose(f); - free(line); + 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); - return ifnames2addrs(interfaces); + return NL_OK; +} + +static struct json_object * get_batadv(void) { + struct neigh_netlink_opts opts = { + .query_opts = { + .err = 0, + }, + }; + int ret; + + opts.interfaces = json_object_new_object(); + if (!opts.interfaces) + return NULL; + + ret = batadv_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; + } + + return ifnames2addrs(opts.interfaces); } static struct json_object * get_wifi_neighbours(const char *ifname) {