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 SECTION:=gluon
CATEGORY:=Gluon CATEGORY:=Gluon
TITLE:=Filter IPv6 router advertisements TITLE:=Filter IPv6 router advertisements
DEPENDS:=+gluon-ebtables +libgluonutil DEPENDS:=+gluon-ebtables +libgluonutil +libbatadv +libnl-tiny
endef endef
MAKE_VARS += \
LIBNL_NAME="libnl-tiny" \
LIBNL_GENL_NAME="libnl-tiny"
define Build/Prepare define Build/Prepare
mkdir -p $(PKG_BUILD_DIR) mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/ $(CP) ./src/* $(PKG_BUILD_DIR)/

View File

@ -2,6 +2,46 @@ all: gluon-radv-filterd respondd.so
CPPFLAGS += -D_GNU_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-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 gluon-radv-filterd: gluon-radv-filterd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS) $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS)

View File

@ -1,5 +1,6 @@
/* /*
Copyright (c) 2016 Jan-Philipp Litza <janphilipp@litza.de> Copyright (c) 2016 Jan-Philipp Litza <janphilipp@litza.de>
Copyright (c) 2017 Sven Eckelmann <sven@narfation.org>
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@ -51,6 +52,11 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/ip6.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" #include "mac.h"
// Recheck TQs after this time even if no RA was received // Recheck TQs after this time even if no RA was received
@ -69,11 +75,6 @@
#define BUFSIZE 1500 #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 #ifdef DEBUG
#define CHECK(stmt) \ #define CHECK(stmt) \
if(!(stmt)) { \ if(!(stmt)) { \
@ -397,16 +398,167 @@ static void expire_routers(void) {
} }
} }
static void update_tqs(void) { static int parse_tt_global(struct nl_msg *msg,
FILE *f; void *arg __attribute__((unused)))
struct router *router; {
char path[PATH_MAX]; static const enum batadv_nl_attrs mandatory[] = {
char *line = NULL; BATADV_ATTR_TT_ADDRESS,
size_t len = 0; BATADV_ATTR_ORIG_ADDRESS,
uint8_t tq; };
bool update_originators = false; struct nlattr *attrs[BATADV_ATTR_MAX + 1];
struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct ether_addr mac_a, mac_b; 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 ether_addr unspec;
struct batadv_nlquery_opts opts;
int ret;
// reset TQs // reset TQs
memset(&unspec, 0, sizeof(unspec)); memset(&unspec, 0, sizeof(unspec));
@ -416,60 +568,24 @@ static void update_tqs(void) {
update_originators = true; 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.
if (update_originators) {
// translate all router's MAC addresses to originators simultaneously // translate all router's MAC addresses to originators simultaneously
snprintf(path, PATH_MAX, TRANSTABLE_GLOBAL, G.mesh_iface); if (update_originators) {
f = fopen(path, "r"); opts.err = 0;
// skip header ret = batadv_genl_query(G.mesh_iface,
while (fgetc(f) != '\n') {} BATADV_CMD_GET_TRANSTABLE_GLOBAL,
while (fgetc(f) != '\n') {} parse_tt_global, NLM_F_DUMP, &opts);
while (fscanf(f, " %*[*+] " F_MAC "%*[0-9 -] (%*3u) via " F_MAC " %*[^]]]\n", if (ret < 0)
F_MAC_VAR_REF(mac_a), F_MAC_VAR_REF(mac_b)) == 12) { fprintf(stderr, "Parsing of global translation table failed\n");
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);
} }
// look up TQs of originators // look up TQs of originators
G.max_tq = 0; G.max_tq = 0;
snprintf(path, PATH_MAX, ORIGINATORS, G.mesh_iface); opts.err = 0;
f = fopen(path, "r"); ret = batadv_genl_query(G.mesh_iface,
// skip header BATADV_CMD_GET_ORIGINATORS,
while (fgetc(f) != '\n'); parse_originator, NLM_F_DUMP, &opts);
while (fgetc(f) != '\n'); if (ret < 0)
while (fscanf(f, F_MAC " %*fs (%hhu) %*[^\n]\n", fprintf(stderr, "Parsing of originators failed\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);
// if all routers have a TQ value, we don't need to check translocal // if all routers have a TQ value, we don't need to check translocal
foreach(router, G.routers) { foreach(router, G.routers) {
@ -477,26 +593,12 @@ static void update_tqs(void) {
break; break;
} }
if (router != NULL) { if (router != NULL) {
// rate local routers (if present) the highest opts.err = 0;
snprintf(path, PATH_MAX, TRANSTABLE_LOCAL, G.mesh_iface); ret = batadv_genl_query(G.mesh_iface,
f = fopen(path, "r"); BATADV_CMD_GET_TRANSTABLE_LOCAL,
// skip header parse_tt_local, NLM_F_DUMP, &opts);
while (fgetc(f) != '\n'); if (ret < 0)
while (fgetc(f) != '\n'); fprintf(stderr, "Parsing of global translation table failed\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);
} }
foreach(router, G.routers) { foreach(router, G.routers) {
@ -512,8 +614,6 @@ static void update_tqs(void) {
F_MAC_VAR(router->src)); F_MAC_VAR(router->src));
} }
} }
free(line);
} }
static int fork_execvp_timeout(struct timespec *timeout, const char *file, const char *const argv[]) { 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 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"
#define F_MAC_LEN 17 #define F_MAC_LEN 17
#define F_MAC_IGN "%*2x:%*2x:%*2x:%*2x:%*2x:%*2x"
#define F_MAC_VAR(var) \ #define F_MAC_VAR(var) \
(var).ether_addr_octet[0], (var).ether_addr_octet[1], \ (var).ether_addr_octet[0], (var).ether_addr_octet[1], \
(var).ether_addr_octet[2], (var).ether_addr_octet[3], \ (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[0], &(var).ether_addr_octet[1], \
&(var).ether_addr_octet[2], &(var).ether_addr_octet[3], \ &(var).ether_addr_octet[2], &(var).ether_addr_octet[3], \
&(var).ether_addr_octet[4], &(var).ether_addr_octet[5] &(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, \ #define ether_addr_equal(_a, _b) (memcmp((_a).ether_addr_octet, \
(_b).ether_addr_octet, ETH_ALEN) == 0) (_b).ether_addr_octet, ETH_ALEN) == 0)