gluon-radv-filterd: Use generic netlink to request batman-adv data

The correct way to get the data from batman-adv is not to try to parse the
freeform debugfs files. Instead, the generic netlink family "batadv" should
be used to request the tables in binary form.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
Sven Eckelmann 2017-12-20 15:08:18 +01:00 committed by Jan-Philipp Litza
parent 34daf35529
commit b06f12669a
No known key found for this signature in database
GPG Key ID: 1FB658053CE27196
4 changed files with 231 additions and 86 deletions

View File

@ -12,9 +12,13 @@ define Package/gluon-radv-filterd
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Filter IPv6 router advertisements
DEPENDS:=+gluon-ebtables +libgluonutil
DEPENDS:=+gluon-ebtables +libgluonutil +libbatadv +libnl-tiny
endef
MAKE_VARS += \
LIBNL_NAME="libnl-tiny" \
LIBNL_GENL_NAME="libnl-tiny"
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/

View File

@ -2,6 +2,46 @@ all: gluon-radv-filterd respondd.so
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-3.0
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)
ifeq ($(origin LIBNL_GENL_CFLAGS) $(origin LIBNL_GENL_LDLIBS), undefined undefined)
LIBNL_GENL_NAME ?= libnl-genl-3.0
ifeq ($(shell $(PKG_CONFIG) --modversion $(LIBNL_GENL_NAME) 2>/dev/null),)
$(error No $(LIBNL_GENL_NAME) development libraries found!)
endif
LIBNL_GENL_CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBNL_GENL_NAME))
LIBNL_GENL_LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBNL_GENL_NAME))
endif
CFLAGS += $(LIBNL_GENL_CFLAGS)
LDLIBS += $(LIBNL_GENL_LDLIBS)
ifeq ($(origin LIBBATADV_CFLAGS) $(origin LIBBATADV_LDLIBS), undefined undefined)
LIBBATADV_NAME ?= libbatadv
ifeq ($(shell $(PKG_CONFIG) --modversion $(LIBBATADV_NAME) 2>/dev/null),)
$(error No $(LIBBATADV_NAME) development libraries found!)
endif
LIBBATADV_CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBBATADV_NAME))
LIBBATADV_LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBBATADV_NAME))
endif
CFLAGS += $(LIBBATADV_CFLAGS)
LDLIBS += $(LIBBATADV_LDLIBS)
gluon-radv-filterd: gluon-radv-filterd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS)

View File

@ -1,5 +1,6 @@
/*
Copyright (c) 2016 Jan-Philipp Litza <janphilipp@litza.de>
Copyright (c) 2017 Sven Eckelmann <sven@narfation.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -51,6 +52,11 @@
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <batadv-genl.h>
#include "mac.h"
// Recheck TQs after this time even if no RA was received
@ -69,11 +75,6 @@
#define BUFSIZE 1500
#define DEBUGFS "/sys/kernel/debug/batman_adv/%s/"
#define ORIGINATORS DEBUGFS "originators"
#define TRANSTABLE_GLOBAL DEBUGFS "transtable_global"
#define TRANSTABLE_LOCAL DEBUGFS "transtable_local"
#ifdef DEBUG
#define CHECK(stmt) \
if(!(stmt)) { \
@ -397,16 +398,167 @@ static void expire_routers(void) {
}
}
static void update_tqs(void) {
FILE *f;
struct router *router;
char path[PATH_MAX];
char *line = NULL;
size_t len = 0;
uint8_t tq;
bool update_originators = false;
static int parse_tt_global(struct nl_msg *msg,
void *arg __attribute__((unused)))
{
static const enum batadv_nl_attrs mandatory[] = {
BATADV_ATTR_TT_ADDRESS,
BATADV_ATTR_ORIG_ADDRESS,
};
struct nlattr *attrs[BATADV_ATTR_MAX + 1];
struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct ether_addr mac_a, mac_b;
struct genlmsghdr *ghdr;
struct router *router;
uint8_t *addr;
uint8_t *orig;
// parse netlink entry
if (!genlmsg_valid_hdr(nlh, 0))
return NL_OK;
ghdr = nlmsg_data(nlh);
if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_GLOBAL)
return NL_OK;
if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
genlmsg_len(ghdr), batadv_genl_policy)) {
return NL_OK;
}
if (batadv_genl_missing_attrs(attrs, mandatory, ARRAY_SIZE(mandatory)))
return NL_OK;
addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]);
orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
if (!attrs[BATADV_ATTR_FLAG_BEST])
return NL_OK;
MAC2ETHER(mac_a, addr);
MAC2ETHER(mac_b, orig);
// update router
router = router_find_src(&mac_a);
if (!router)
return NL_OK;
DEBUG_MSG("Found originator for " F_MAC ", it's " F_MAC,
F_MAC_VAR(router->src), F_MAC_VAR(mac_b));
router->originator = mac_b;
return NL_OK;
}
static int parse_originator(struct nl_msg *msg,
void *arg __attribute__((unused)))
{
static const enum batadv_nl_attrs mandatory[] = {
BATADV_ATTR_ORIG_ADDRESS,
BATADV_ATTR_TQ,
};
struct nlattr *attrs[BATADV_ATTR_MAX + 1];
struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct ether_addr mac_a;
struct genlmsghdr *ghdr;
struct router *router;
uint8_t *orig;
uint8_t tq;
// parse netlink entry
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_genl_policy)) {
return NL_OK;
}
if (batadv_genl_missing_attrs(attrs, mandatory, ARRAY_SIZE(mandatory)))
return NL_OK;
orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]);
tq = nla_get_u8(attrs[BATADV_ATTR_TQ]);
if (!attrs[BATADV_ATTR_FLAG_BEST])
return NL_OK;
MAC2ETHER(mac_a, orig);
// update router
router = router_find_orig(&mac_a);
if (!router)
return NL_OK;
DEBUG_MSG("Found TQ for router " F_MAC " (originator " F_MAC "), it's %d",
F_MAC_VAR(router->src), F_MAC_VAR(router->originator), tq);
router->tq = tq;
if (router->tq > G.max_tq)
G.max_tq = router->tq;
return NL_OK;
}
static int parse_tt_local(struct nl_msg *msg,
void *arg __attribute__((unused)))
{
static const enum batadv_nl_attrs mandatory[] = {
BATADV_ATTR_TT_ADDRESS,
};
struct nlattr *attrs[BATADV_ATTR_MAX + 1];
struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct ether_addr mac_a;
struct genlmsghdr *ghdr;
struct router *router;
uint8_t *addr;
// parse netlink entry
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_genl_policy)) {
return NL_OK;
}
if (batadv_genl_missing_attrs(attrs, mandatory, ARRAY_SIZE(mandatory)))
return NL_OK;
addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]);
MAC2ETHER(mac_a, addr);
// update router
router = router_find_src(&mac_a);
if (!router)
return NL_OK;
DEBUG_MSG("Found router " F_MAC " in transtable_local, assigning TQ %d",
F_MAC_VAR(router->src), LOCAL_TQ);
router->tq = LOCAL_TQ;
if (router->tq > G.max_tq)
G.max_tq = router->tq;
return NL_OK;
}
static void update_tqs(void) {
struct router *router;
bool update_originators = false;
struct ether_addr unspec;
struct batadv_nlquery_opts opts;
int ret;
// reset TQs
memset(&unspec, 0, sizeof(unspec));
@ -416,60 +568,24 @@ static void update_tqs(void) {
update_originators = true;
}
// TODO: Currently, we iterate over the whole list of routers all the
// time. Maybe it would be a good idea to sort routers that already
// have the current piece of information to the back. That way, we
// could abort as soon as we hit the first router with the current
// information filled in.
// translate all router's MAC addresses to originators simultaneously
if (update_originators) {
// translate all router's MAC addresses to originators simultaneously
snprintf(path, PATH_MAX, TRANSTABLE_GLOBAL, G.mesh_iface);
f = fopen(path, "r");
// skip header
while (fgetc(f) != '\n') {}
while (fgetc(f) != '\n') {}
while (fscanf(f, " %*[*+] " F_MAC "%*[0-9 -] (%*3u) via " F_MAC " %*[^]]]\n",
F_MAC_VAR_REF(mac_a), F_MAC_VAR_REF(mac_b)) == 12) {
router = router_find_src(&mac_a);
if (!router)
continue;
DEBUG_MSG("Found originator for " F_MAC ", it's " F_MAC, F_MAC_VAR(router->src), F_MAC_VAR(mac_b));
router->originator = mac_b;
}
if (!feof(f)) {
getline(&line, &len, f);
fprintf(stderr, "Parsing transtable_global aborted at this line: %s\n", line);
}
fclose(f);
opts.err = 0;
ret = batadv_genl_query(G.mesh_iface,
BATADV_CMD_GET_TRANSTABLE_GLOBAL,
parse_tt_global, NLM_F_DUMP, &opts);
if (ret < 0)
fprintf(stderr, "Parsing of global translation table failed\n");
}
// look up TQs of originators
G.max_tq = 0;
snprintf(path, PATH_MAX, ORIGINATORS, G.mesh_iface);
f = fopen(path, "r");
// skip header
while (fgetc(f) != '\n');
while (fgetc(f) != '\n');
while (fscanf(f, F_MAC " %*fs (%hhu) %*[^\n]\n",
F_MAC_VAR_REF(mac_a), &tq) == 7) {
router = router_find_orig(&mac_a);
if (!router)
continue;
DEBUG_MSG("Found TQ for router " F_MAC " (originator " F_MAC "), it's %d", F_MAC_VAR(router->src), F_MAC_VAR(router->originator), tq);
router->tq = tq;
if (tq > G.max_tq)
G.max_tq = tq;
}
if (!feof(f)) {
getline(&line, &len, f);
fprintf(stderr, "Parsing originators aborted at this line: %s\n", line);
}
fclose(f);
opts.err = 0;
ret = batadv_genl_query(G.mesh_iface,
BATADV_CMD_GET_ORIGINATORS,
parse_originator, NLM_F_DUMP, &opts);
if (ret < 0)
fprintf(stderr, "Parsing of originators failed\n");
// if all routers have a TQ value, we don't need to check translocal
foreach(router, G.routers) {
@ -477,26 +593,12 @@ static void update_tqs(void) {
break;
}
if (router != NULL) {
// rate local routers (if present) the highest
snprintf(path, PATH_MAX, TRANSTABLE_LOCAL, G.mesh_iface);
f = fopen(path, "r");
// skip header
while (fgetc(f) != '\n');
while (fgetc(f) != '\n');
while (fscanf(f, " * " F_MAC " [%*5s] %*f", F_MAC_VAR_REF(mac_a)) == 6) {
router = router_find_src(&mac_a);
if (!router)
continue;
DEBUG_MSG("Found router " F_MAC " in transtable_local, assigning TQ %d", F_MAC_VAR(router->src), LOCAL_TQ);
router->tq = LOCAL_TQ;
G.max_tq = LOCAL_TQ;
}
if (!feof(f)) {
getline(&line, &len, f);
fprintf(stderr, "Parsing transtable_local aborted at this line: %s\n", line);
}
fclose(f);
opts.err = 0;
ret = batadv_genl_query(G.mesh_iface,
BATADV_CMD_GET_TRANSTABLE_LOCAL,
parse_tt_local, NLM_F_DUMP, &opts);
if (ret < 0)
fprintf(stderr, "Parsing of global translation table failed\n");
}
foreach(router, G.routers) {
@ -512,8 +614,6 @@ static void update_tqs(void) {
F_MAC_VAR(router->src));
}
}
free(line);
}
static int fork_execvp_timeout(struct timespec *timeout, const char *file, const char *const argv[]) {

View File

@ -3,7 +3,6 @@
#define F_MAC "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
#define F_MAC_LEN 17
#define F_MAC_IGN "%*2x:%*2x:%*2x:%*2x:%*2x:%*2x"
#define F_MAC_VAR(var) \
(var).ether_addr_octet[0], (var).ether_addr_octet[1], \
(var).ether_addr_octet[2], (var).ether_addr_octet[3], \
@ -12,6 +11,8 @@
&(var).ether_addr_octet[0], &(var).ether_addr_octet[1], \
&(var).ether_addr_octet[2], &(var).ether_addr_octet[3], \
&(var).ether_addr_octet[4], &(var).ether_addr_octet[5]
#define MAC2ETHER(_ether, _mac) memcpy((_ether).ether_addr_octet, \
(_mac), ETH_ALEN)
#define ether_addr_equal(_a, _b) (memcmp((_a).ether_addr_octet, \
(_b).ether_addr_octet, ETH_ALEN) == 0)