From b06f12669a25d1f64641045d5b1de97eee76651c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 20 Dec 2017 15:08:18 +0100 Subject: [PATCH] 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 --- package/gluon-radv-filterd/Makefile | 6 +- package/gluon-radv-filterd/src/Makefile | 40 +++ .../src/gluon-radv-filterd.c | 268 ++++++++++++------ package/gluon-radv-filterd/src/mac.h | 3 +- 4 files changed, 231 insertions(+), 86 deletions(-) diff --git a/package/gluon-radv-filterd/Makefile b/package/gluon-radv-filterd/Makefile index 444894ca..23daa42f 100644 --- a/package/gluon-radv-filterd/Makefile +++ b/package/gluon-radv-filterd/Makefile @@ -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)/ diff --git a/package/gluon-radv-filterd/src/Makefile b/package/gluon-radv-filterd/src/Makefile index 87000139..17b65584 100644 --- a/package/gluon-radv-filterd/src/Makefile +++ b/package/gluon-radv-filterd/src/Makefile @@ -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) diff --git a/package/gluon-radv-filterd/src/gluon-radv-filterd.c b/package/gluon-radv-filterd/src/gluon-radv-filterd.c index b36a058b..60786a16 100644 --- a/package/gluon-radv-filterd/src/gluon-radv-filterd.c +++ b/package/gluon-radv-filterd/src/gluon-radv-filterd.c @@ -1,5 +1,6 @@ /* Copyright (c) 2016 Jan-Philipp Litza + Copyright (c) 2017 Sven Eckelmann All rights reserved. Redistribution and use in source and binary forms, with or without @@ -51,6 +52,11 @@ #include #include +#include +#include +#include +#include + #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[]) { diff --git a/package/gluon-radv-filterd/src/mac.h b/package/gluon-radv-filterd/src/mac.h index fb1ff398..cc24d907 100644 --- a/package/gluon-radv-filterd/src/mac.h +++ b/package/gluon-radv-filterd/src/mac.h @@ -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)