diff --git a/package/gluon-mesh-babel/src/respondd.c b/package/gluon-mesh-babel/src/respondd.c
index 96195d899eb4aa1b816912cada3459fbc9e14ed5..94fc3f871df934e145ee85e1bb9488caf5414359 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;
 }
 
 
@@ -154,29 +156,32 @@ 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[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);
 
-	if (interface_file_exists(ifname, "wireless"))
+	/* 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);
-	else if (interface_file_exists(ifname, "tun_flags"))
+		break;
+
+	case GLUONUTIL_INTERFACE_TYPE_TUNNEL:
 		json_object_array_add(tunnel, address);
-	else
-		json_object_array_add(other, address);
+		break;
 
+	default:
+		json_object_array_add(other, address);
+	}
 }
 
 
@@ -193,24 +198,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);
 
 	}
@@ -277,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();
@@ -298,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 36e330f9e7958bb5c63f39190da330d7214047d5..268a3623e77b011cd9237fec6858a56e9bd31cd1 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);
-
-		glob_t lower;
-		if (glob(pattern, GLOB_NOSORT, NULL, &lower))
-			break;
+	char lowername[IF_NAMESIZE];
+	gluonutil_get_interface_lower(lowername, ifname);
 
-		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"))
+		break;
+
+	case GLUONUTIL_INTERFACE_TYPE_TUNNEL:
 		json_object_array_add(tunnel, address);
-	else
-		json_object_array_add(other, address);
+		break;
 
+	default:
+		json_object_array_add(other, address);
+	}
 }
 
 static struct json_object * get_mesh_subifs(const char *ifname) {
diff --git a/package/libgluonutil/src/libgluonutil.c b/package/libgluonutil/src/libgluonutil.c
index e7a88fd1bb6bd20c1f05599272ec1a68b610a963..0950f0d8f1b5ca84ebc6736c69841768cb3d84be 100644
--- a/package/libgluonutil/src/libgluonutil.c
+++ b/package/libgluonutil/src/libgluonutil.c
@@ -28,13 +28,16 @@
 
 #include <json-c/json.h>
 #include <uci.h>
+
 #include <arpa/inet.h>
+
+#include <errno.h>
+#include <glob.h>
+#include <limits.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <limits.h>
-#include <errno.h>
 
 /**
  * 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 39b253baa4348b8d73a284dac098895a670c9b7e..b33b93e741af76b3123b392bd651404dda69aa87 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 <net/if.h>
 #include <netinet/in.h>
 #include <stdbool.h>
 
@@ -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);