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 <sven@narfation.org>
This commit is contained in:
Sven Eckelmann 2016-09-24 19:59:44 +02:00
parent 27501ae694
commit 4bbd073f73
5 changed files with 320 additions and 32 deletions

View File

@ -14,7 +14,7 @@ define Package/gluon-status-page-api
SECTION:=gluon SECTION:=gluon
CATEGORY:=Gluon CATEGORY:=Gluon
TITLE:=API for gluon-status-page 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 endef
define Build/Prepare define Build/Prepare

View File

@ -1,11 +1,30 @@
CFLAGS += -std=c99 -D_BSD_SOURCE 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) CFLAGS_JSONC = $(shell pkg-config --cflags json-c)
LDFLAGS_JSONC = $(shell pkg-config --libs json-c) LDFLAGS_JSONC = $(shell pkg-config --libs json-c)
all: neighbours-batadv stations respondd.so 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) $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDFLAGS_JSONC) -Wall -o $@ $^ $(LDLIBS)
stations: stations.c stations: stations.c

View File

@ -2,47 +2,112 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <json-c/json.h> #include <json-c/json.h>
#include <linux/batman_adv.h>
#include <net/if.h> #include <net/if.h>
#include "netlink.h"
#define STR(x) #x #define STR(x) #x
#define XSTR(x) STR(x) #define XSTR(x) STR(x)
static json_object *neighbours(void) { struct neigh_netlink_opts {
struct json_object *obj = json_object_new_object(); struct json_object *obj;
struct nlquery_opts query_opts;
};
FILE *f; 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,
};
f = fopen("/sys/kernel/debug/batman_adv/bat0/originators" , "r"); static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg)
{
if (f == NULL) struct nlattr *attrs[BATADV_ATTR_MAX+1];
return NULL; struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct nlquery_opts *query_opts = arg;
while (!feof(f)) { 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]; 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); opts = container_of(query_opts, struct neigh_netlink_opts, query_opts);
if (count != 5) if (!genlmsg_valid_hdr(nlh, 0))
continue; 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]);
if (strcmp(mac1, mac2) == 0) {
struct json_object *neigh = json_object_new_object(); 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, "tq", json_object_new_int(tq));
json_object_object_add(neigh, "lastseen", json_object_new_double(lastseen)); 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(neigh, "ifname", json_object_new_string(ifname));
json_object_object_add(obj, mac1, neigh); json_object_object_add(opts->obj, mac1, neigh);
}
return NL_OK;
}
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;
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 opts.obj;
return obj;
} }
int main(void) { int main(void) {

View File

@ -0,0 +1,155 @@
/*
* 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 "netlink.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <linux/batman_adv.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>
#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;
}

View File

@ -0,0 +1,49 @@
/*
* 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/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <stddef.h>
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 */