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:
parent
27501ae694
commit
4bbd073f73
@ -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
|
||||
|
@ -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
|
||||
|
@ -2,47 +2,112 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <json-c/json.h>
|
||||
#include <linux/batman_adv.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include "netlink.h"
|
||||
|
||||
#define STR(x) #x
|
||||
#define XSTR(x) STR(x)
|
||||
|
||||
static json_object *neighbours(void) {
|
||||
struct json_object *obj = json_object_new_object();
|
||||
struct neigh_netlink_opts {
|
||||
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");
|
||||
|
||||
if (f == NULL)
|
||||
return NULL;
|
||||
|
||||
while (!feof(f)) {
|
||||
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];
|
||||
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)
|
||||
continue;
|
||||
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]);
|
||||
|
||||
if (strcmp(mac1, mac2) == 0) {
|
||||
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));
|
||||
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(obj, mac1, neigh);
|
||||
}
|
||||
json_object_object_add(opts->obj, mac1, neigh);
|
||||
|
||||
return NL_OK;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
static json_object *neighbours(void) {
|
||||
struct neigh_netlink_opts opts = {
|
||||
.query_opts = {
|
||||
.err = 0,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
return obj;
|
||||
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;
|
||||
}
|
||||
|
||||
return opts.obj;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
155
package/gluon-status-page-api/src/netlink.c
Normal file
155
package/gluon-status-page-api/src/netlink.c
Normal 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;
|
||||
}
|
49
package/gluon-status-page-api/src/netlink.h
Normal file
49
package/gluon-status-page-api/src/netlink.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user