From 4bbd073f7325fe187f257070c3ea8a020ac5c025 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 24 Sep 2016 19:59:44 +0200 Subject: [PATCH] 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/neighbours-batadv.c | 125 ++++++++++---- package/gluon-status-page-api/src/netlink.c | 155 ++++++++++++++++++ package/gluon-status-page-api/src/netlink.h | 49 ++++++ 5 files changed, 320 insertions(+), 32 deletions(-) create mode 100644 package/gluon-status-page-api/src/netlink.c create mode 100644 package/gluon-status-page-api/src/netlink.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..319ee5a5 100644 --- a/package/gluon-status-page-api/src/Makefile +++ b/package/gluon-status-page-api/src/Makefile @@ -1,11 +1,30 @@ 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 += $(LIBNL_CFLAGS) +LDLIBS += $(LIBNL_LDLIBS) 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 +neighbours-batadv: neighbours-batadv.c netlink.c $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDFLAGS_JSONC) -Wall -o $@ $^ $(LDLIBS) stations: stations.c diff --git a/package/gluon-status-page-api/src/neighbours-batadv.c b/package/gluon-status-page-api/src/neighbours-batadv.c index f0a484b2..8cc3f0a6 100644 --- a/package/gluon-status-page-api/src/neighbours-batadv.c +++ b/package/gluon-status-page-api/src/neighbours-batadv.c @@ -2,47 +2,112 @@ #include #include #include +#include #include +#include "netlink.h" + #define STR(x) #x #define XSTR(x) STR(x) +struct neigh_netlink_opts { + struct json_object *obj; + struct nlquery_opts query_opts; +}; + +static const int 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 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 (missing_mandatory_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 = netlink_query_common("bat0", BATADV_CMD_GET_ORIGINATORS, + parse_orig_list_netlink_cb, &opts.query_opts); + if (ret < 0) { + json_object_put(opts.obj); + return NULL; } - fclose(f); - - return obj; + return opts.obj; } int main(void) { diff --git a/package/gluon-status-page-api/src/netlink.c b/package/gluon-status-page-api/src/netlink.c new file mode 100644 index 00000000..127b6c03 --- /dev/null +++ b/package/gluon-status-page-api/src/netlink.c @@ -0,0 +1,155 @@ +/* + * 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 "netlink.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 }, +}; + +int missing_mandatory_attrs(struct nlattr *attrs[], const int mandatory[], + size_t num) +{ + size_t i; + + for (i = 0; i < num; i++) + if (!attrs[mandatory[i]]) + return -EINVAL; + + return 0; +} + +static int nlquery_error_cb(struct sockaddr_nl *nla __maybe_unused, + struct nlmsgerr *nlerr, void *arg) +{ + struct 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 nlquery_opts *query_opts = arg; + int *error = nlmsg_data(nlh); + + if (*error) + query_opts->err = *error; + + return NL_STOP; +} + +int netlink_query_common(const char *mesh_iface, uint8_t nl_cmd, + nl_recvmsg_msg_cb_t callback, + struct 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, NLM_F_DUMP, + 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/netlink.h b/package/gluon-status-page-api/src/netlink.h new file mode 100644 index 00000000..56e841a6 --- /dev/null +++ b/package/gluon-status-page-api/src/netlink.h @@ -0,0 +1,49 @@ +/* + * 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 + +struct nlquery_opts { + int err; +}; + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +#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 netlink_query_common(const char *mesh_iface, uint8_t nl_cmd, + nl_recvmsg_msg_cb_t callback, + struct nlquery_opts *query_opts); +int missing_mandatory_attrs(struct nlattr *attrs[], const int mandatory[], + size_t num); + +extern struct nla_policy batadv_netlink_policy[]; + +#endif /* _BATADV_NETLINK_H */