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);