From 72c71d35ac7e9ff91ce577257f8d9161ab7d0ee0 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Mon, 1 Jun 2020 14:50:40 +0200 Subject: [PATCH 1/3] gluon-mesh-babel: clean up link-local address handling - Rename obtain_if_addr() to get_linklocal_address() - Pass buffer of size INET6_ADDRSTRLEN instead of the oversized NI_MAXHOST - Check if an address is link-local before converting to a string - Replace an incorrect use of strncmp() with strcmp() - Return status to caller - Streamline control flow While we're at it, the function handle_neighbour(), which is one of the callers of get_linklocal_address() is slightly cleaned up as well. --- package/gluon-mesh-babel/src/respondd.c | 65 +++++++++++++------------ 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/package/gluon-mesh-babel/src/respondd.c b/package/gluon-mesh-babel/src/respondd.c index 96195d89..124500a2 100644 --- a/package/gluon-mesh-babel/src/respondd.c +++ b/package/gluon-mesh-babel/src/respondd.c @@ -62,38 +62,40 @@ static struct babelhelper_ctx bhelper_ctx = {}; -static void obtain_if_addr(const char *ifname, char *lladdr) { +static bool get_linklocal_address(const char *ifname, char lladdr[INET6_ADDRSTRLEN]) { struct ifaddrs *ifaddr, *ifa; - int family, n; + bool ret = false; if (getifaddrs(&ifaddr) == -1) { perror("getifaddrs"); - exit(EXIT_FAILURE); + return false; } - for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) { - if (ifa->ifa_addr == NULL) + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) continue; - family = ifa->ifa_addr->sa_family; + if (ifa->ifa_addr->sa_family != AF_INET6) + continue; - if ( (family == AF_INET6) && ( ! strncmp(ifname, ifa->ifa_name, strlen(ifname)) ) ) { - char lhost[INET6_ADDRSTRLEN]; - struct in6_addr *address = &((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr; - if (inet_ntop(AF_INET6, address, lhost, INET6_ADDRSTRLEN) == NULL) { - fprintf(stderr, "obtain_if_addr: could not convert ip to string\n"); - goto cleanup; - } + if (strcmp(ifname, ifa->ifa_name) != 0) + continue; - if (! strncmp("fe80:", lhost, 5) ) { - snprintf( lladdr, NI_MAXHOST, "%s", lhost ); - goto cleanup; - } + const struct in6_addr *address = &((const struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + if (!IN6_IS_ADDR_LINKLOCAL(address)) + continue; + + if (!inet_ntop(AF_INET6, address, lladdr, INET6_ADDRSTRLEN)) { + perror("inet_ntop"); + continue; } + + ret = true; + break; } -cleanup: freeifaddrs(ifaddr); + return ret; } @@ -164,9 +166,10 @@ static bool interface_file_exists(const char *ifname, const char *name) { static void mesh_add_if(const char *ifname, struct json_object *wireless, struct json_object *tunnel, struct json_object *other) { - char str_ip[NI_MAXHOST] = {}; + char str_ip[INET6_ADDRSTRLEN]; - obtain_if_addr(ifname, str_ip); + if (!get_linklocal_address(ifname, str_ip)) + return; struct json_object *address = json_object_new_string(str_ip); @@ -193,24 +196,26 @@ static bool handle_neighbour(char **data, void *obj) { if (data[REACH]) json_object_object_add(neigh, "reachability", json_object_new_double(strtod(data[REACH], NULL))); - struct json_object *nif = 0; - if (data[IF] && !json_object_object_get_ex(obj, data[IF], &nif)) { - char str_ip[NI_MAXHOST] = {}; - obtain_if_addr( (const char*)data[IF], str_ip ); + if (!data[IF]) + return true; + + struct json_object *nif; + if (!json_object_object_get_ex(obj, data[IF], &nif)) { + char str_ip[INET6_ADDRSTRLEN]; nif = json_object_new_object(); - json_object_object_add(nif, "ll-addr", json_object_new_string(str_ip)); + if (get_linklocal_address(data[IF], str_ip)) + json_object_object_add(nif, "ll-addr", json_object_new_string(str_ip)); + json_object_object_add(nif, "protocol", json_object_new_string("babel")); json_object_object_add(obj, data[IF], nif); - } - struct json_object *neighborcollector = 0; - if (!json_object_object_get_ex(nif, "neighbours", &neighborcollector)) { - neighborcollector = json_object_new_object(); - json_object_object_add(nif, "neighbours", neighborcollector); + json_object_object_add(nif, "neighbours", json_object_new_object()); } + struct json_object *neighborcollector; + json_object_object_get_ex(nif, "neighbours", &neighborcollector); json_object_object_add(neighborcollector, data[ADDRESS], neigh); } From 41b3c912846edfefdfa8837e6cb200ef4b904f15 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Mon, 1 Jun 2020 18:03:50 +0200 Subject: [PATCH 2/3] libgluonutil: introduce gluonutil_get_interface_lower() and gluonutil_get_interface_type() gluonutil_get_interface_lower() recursively determines the lower of an interface until the base interface is found. gluonutil_get_interface_type() tries do distinguish wired, wireless and tunnel interfaces. For tunnel interfaces, TUN/TAP, L2TPETH and Wireguard are supported at the moment. --- package/libgluonutil/src/libgluonutil.c | 75 ++++++++++++++++++++++++- package/libgluonutil/src/libgluonutil.h | 12 ++++ 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/package/libgluonutil/src/libgluonutil.c b/package/libgluonutil/src/libgluonutil.c index e7a88fd1..0950f0d8 100644 --- a/package/libgluonutil/src/libgluonutil.c +++ b/package/libgluonutil/src/libgluonutil.c @@ -28,13 +28,16 @@ #include #include + #include + +#include +#include +#include #include #include #include #include -#include -#include /** * Merges two JSON objects @@ -129,6 +132,74 @@ char * gluonutil_get_interface_address(const char *ifname) { return gluonutil_read_line(path); } +void gluonutil_get_interface_lower(char out[IF_NAMESIZE], const char *ifname) { + strncpy(out, ifname, IF_NAMESIZE-1); + out[IF_NAMESIZE-1] = 0; + + const char *format = "/sys/class/net/%s/lower_*"; + char pattern[strlen(format) + IF_NAMESIZE]; + + while (true) { + snprintf(pattern, sizeof(pattern), format, out); + size_t pattern_len = strlen(pattern); + + glob_t lower; + if (glob(pattern, GLOB_NOSORT, NULL, &lower) != 0) + break; + + strncpy(out, lower.gl_pathv[0] + pattern_len - 1, IF_NAMESIZE-1); + + globfree(&lower); + } +} + +enum gluonutil_interface_type lookup_interface_type(const char *devtype) { + if (strcmp(devtype, "wlan") == 0) + return GLUONUTIL_INTERFACE_TYPE_WIRELESS; + + if (strcmp(devtype, "l2tpeth") == 0 || strcmp(devtype, "wireguard") == 0) + return GLUONUTIL_INTERFACE_TYPE_TUNNEL; + + /* Regular wired interfaces do not set DEVTYPE, so if this point is + * reached, we have something different */ + return GLUONUTIL_INTERFACE_TYPE_UNKNOWN; +} + +enum gluonutil_interface_type gluonutil_get_interface_type(const char *ifname) { + const char *pattern = "/sys/class/net/%s/%s"; + + /* Default to wired type when no DEVTYPE is set */ + enum gluonutil_interface_type ret = GLUONUTIL_INTERFACE_TYPE_WIRED; + char *line = NULL, path[PATH_MAX]; + size_t buflen = 0; + ssize_t len; + FILE *f; + + snprintf(path, sizeof(path), pattern, ifname, "tun_flags"); + if (access(path, F_OK) == 0) + return GLUONUTIL_INTERFACE_TYPE_TUNNEL; + + snprintf(path, sizeof(path), pattern, ifname, "uevent"); + f = fopen(path, "r"); + if (!f) + return GLUONUTIL_INTERFACE_TYPE_UNKNOWN; + + while ((len = getline(&line, &buflen, f)) >= 0) { + if (len == 0) + continue; + + if (line[len-1] == '\n') + line[len-1] = '\0'; + + if (strncmp(line, "DEVTYPE=", 8) == 0) { + ret = lookup_interface_type(line+8); + break; + } + } + + fclose(f); + return ret; +} struct json_object * gluonutil_wrap_string(const char *str) { diff --git a/package/libgluonutil/src/libgluonutil.h b/package/libgluonutil/src/libgluonutil.h index 39b253ba..b33b93e7 100644 --- a/package/libgluonutil/src/libgluonutil.h +++ b/package/libgluonutil/src/libgluonutil.h @@ -27,6 +27,7 @@ #ifndef _LIBGLUON_LIBGLUON_H_ #define _LIBGLUON_LIBGLUON_H_ +#include #include #include @@ -34,7 +35,18 @@ char * gluonutil_read_line(const char *filename); char * gluonutil_get_sysconfig(const char *key); char * gluonutil_get_node_id(void); + +enum gluonutil_interface_type { + GLUONUTIL_INTERFACE_TYPE_UNKNOWN, + GLUONUTIL_INTERFACE_TYPE_WIRED, + GLUONUTIL_INTERFACE_TYPE_WIRELESS, + GLUONUTIL_INTERFACE_TYPE_TUNNEL, +}; + +void gluonutil_get_interface_lower(char out[IF_NAMESIZE], const char *ifname); char * gluonutil_get_interface_address(const char *ifname); +enum gluonutil_interface_type gluonutil_get_interface_type(const char *ifname); + bool gluonutil_get_node_prefix6(struct in6_addr *prefix); struct json_object * gluonutil_wrap_string(const char *str); From 3fda210f852258df4ddcb07b0bc924bbf16723d6 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Mon, 1 Jun 2020 21:44:05 +0200 Subject: [PATCH 3/3] gluon-mesh-{batman-adv,babel}: respondd: use libgluonutil to determine interface type Also make babel match batman-adv and only emit the wireless/tunnel/other fields when they are non-empty. Fixes: #1783 --- package/gluon-mesh-babel/src/respondd.c | 43 +++++++++++-------- .../src/respondd-nodeinfo.c | 36 ++++++---------- 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/package/gluon-mesh-babel/src/respondd.c b/package/gluon-mesh-babel/src/respondd.c index 124500a2..94fc3f87 100644 --- a/package/gluon-mesh-babel/src/respondd.c +++ b/package/gluon-mesh-babel/src/respondd.c @@ -156,14 +156,6 @@ free: return retval; } -static bool interface_file_exists(const char *ifname, const char *name) { - const char *format = "/sys/class/net/%s/%s"; - char path[strlen(format) + strlen(ifname) + strlen(name)+1]; - snprintf(path, sizeof(path), format, ifname, name); - - return !access(path, F_OK); -} - static void mesh_add_if(const char *ifname, struct json_object *wireless, struct json_object *tunnel, struct json_object *other) { char str_ip[INET6_ADDRSTRLEN]; @@ -173,13 +165,23 @@ static void mesh_add_if(const char *ifname, struct json_object *wireless, struct json_object *address = json_object_new_string(str_ip); - if (interface_file_exists(ifname, "wireless")) - json_object_array_add(wireless, address); - else if (interface_file_exists(ifname, "tun_flags")) - json_object_array_add(tunnel, address); - else - json_object_array_add(other, address); + /* In case of VLAN and bridge interfaces, we want the lower interface + * to determine the interface type (but not for the interface address) */ + char lowername[IF_NAMESIZE]; + gluonutil_get_interface_lower(lowername, ifname); + switch(gluonutil_get_interface_type(lowername)) { + case GLUONUTIL_INTERFACE_TYPE_WIRELESS: + json_object_array_add(wireless, address); + break; + + case GLUONUTIL_INTERFACE_TYPE_TUNNEL: + json_object_array_add(tunnel, address); + break; + + default: + json_object_array_add(other, address); + } } @@ -282,6 +284,13 @@ static void blobmsg_handle_list(struct blob_attr *attr, int len, bool array, str free(proto); } +static void add_if_not_empty(struct json_object *obj, const char *key, struct json_object *val) { + if (json_object_array_length(val)) + json_object_object_add(obj, key, val); + else + json_object_put(val); +} + static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg) { struct json_object *ret = json_object_new_object(); struct json_object *wireless = json_object_new_array(); @@ -303,9 +312,9 @@ static void receive_call_result_data(struct ubus_request *req, int type, struct blobmsg_handle_list(blobmsg_data(msg), blobmsg_data_len(msg), false, wireless, tunnel, other); - json_object_object_add(ret, "wireless", wireless); - json_object_object_add(ret, "tunnel", tunnel); - json_object_object_add(ret, "other", other); + add_if_not_empty(ret, "wireless", wireless); + add_if_not_empty(ret, "tunnel", tunnel); + add_if_not_empty(ret, "other", other); *((struct json_object**)(req->priv)) = ret; } diff --git a/package/gluon-mesh-batman-adv/src/respondd-nodeinfo.c b/package/gluon-mesh-batman-adv/src/respondd-nodeinfo.c index 36e330f9..268a3623 100644 --- a/package/gluon-mesh-batman-adv/src/respondd-nodeinfo.c +++ b/package/gluon-mesh-batman-adv/src/respondd-nodeinfo.c @@ -131,35 +131,23 @@ static void mesh_add_subif(const char *ifname, struct json_object *wireless, struct json_object *tunnel, struct json_object *other) { struct json_object *address = gluonutil_wrap_and_free_string(gluonutil_get_interface_address(ifname)); - char lowername[IFNAMSIZ]; - strncpy(lowername, ifname, sizeof(lowername)-1); - lowername[sizeof(lowername)-1] = 0; - - const char *format = "/sys/class/net/%s/lower_*"; - char pattern[strlen(format) + IFNAMSIZ]; - /* In case of VLAN and bridge interfaces, we want the lower interface * to determine the interface type (but not for the interface address) */ - while (true) { - snprintf(pattern, sizeof(pattern), format, lowername); - size_t pattern_len = strlen(pattern); + char lowername[IF_NAMESIZE]; + gluonutil_get_interface_lower(lowername, ifname); - glob_t lower; - if (glob(pattern, GLOB_NOSORT, NULL, &lower)) - break; - - strncpy(lowername, lower.gl_pathv[0] + pattern_len - 1, sizeof(lowername)-1); - - globfree(&lower); - } - - if (interface_file_exists(lowername, "wireless")) + switch(gluonutil_get_interface_type(lowername)) { + case GLUONUTIL_INTERFACE_TYPE_WIRELESS: json_object_array_add(wireless, address); - else if (interface_file_exists(lowername, "tun_flags")) - json_object_array_add(tunnel, address); - else - json_object_array_add(other, address); + break; + case GLUONUTIL_INTERFACE_TYPE_TUNNEL: + json_object_array_add(tunnel, address); + break; + + default: + json_object_array_add(other, address); + } } static struct json_object * get_mesh_subifs(const char *ifname) {