diff --git a/package/gluon-core/luasrc/usr/bin/gluon-info b/package/gluon-core/luasrc/usr/bin/gluon-info
index 74ffe59d1ca6d67ce9e8c14642ca9b8354dd5877..916c0643cb6a913f2243a712d7c0cafef1b5bf55 100755
--- a/package/gluon-core/luasrc/usr/bin/gluon-info
+++ b/package/gluon-core/luasrc/usr/bin/gluon-info
@@ -1,39 +1,13 @@
 #!/usr/bin/lua
 
-local uci = require('simple-uci').cursor()
-local pretty_hostname = require 'pretty_hostname'
+local info = require 'gluon.info'
 
-local site = require 'gluon.site'
-local sysconfig = require 'gluon.sysconfig'
-local platform = require 'gluon.platform'
-local util = require 'gluon.util'
-local has_vpn, vpn = pcall(require, 'gluon.mesh-vpn')
-
-local pubkey
-if has_vpn and vpn.enabled() then
-	local _, active_vpn = vpn.get_active_provider()
-
-	if active_vpn ~= nil then
-		pubkey = active_vpn.public_key()
-	end
-end
-
-local values = {
-	{ 'Hostname', pretty_hostname.get(uci) },
-	{ 'MAC address', sysconfig.primary_mac },
-	{ 'Hardware model', platform.get_model() },
-	{ 'Gluon version / Site version', util.trim(util.readfile('/lib/gluon/gluon-version'))
-		.. ' / ' .. util.trim(util.readfile('/lib/gluon/site-version')) },
-	{ 'Firmware release', util.trim(util.readfile('/lib/gluon/release')) },
-	{ 'Site', site.site_name() },
-	{ 'Domain', uci:get('gluon', 'core', 'domain') or 'n/a' },
-	{ 'Public VPN key', pubkey or 'n/a' },
-}
+local values = info.get_info_pretty(function(str) return str end)
 
 local padTo = 24
 
-for _, info in ipairs(values) do
-	local labelLen = string.len(info[1]) + 1
+for _, value in ipairs(values) do
+	local labelLen = string.len(value[1]) + 1
 
-	print(info[1] .. ':' .. string.rep(' ', padTo - labelLen), info[2])
+	print(value[1] .. ':' .. string.rep(' ', padTo - labelLen), value[2])
 end
diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/ethernet.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/ethernet.lua
new file mode 100644
index 0000000000000000000000000000000000000000..cefd1240232378e2119a0f0d408c7257574e5559
--- /dev/null
+++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/ethernet.lua
@@ -0,0 +1,83 @@
+local util = require 'gluon.util'
+local unistd = require 'posix.unistd'
+local dirent = require 'posix.dirent'
+local uci = require('simple-uci').cursor()
+
+local M = {}
+
+local function has_devtype(iface_dir, devtype)
+	return util.file_contains_line(iface_dir..'/uevent', 'DEVTYPE='..devtype)
+end
+
+local function is_physical(iface_dir)
+	return unistd.access(iface_dir .. '/device') == 0
+end
+
+local function is_swconfig()
+	local has = false
+
+	uci:foreach("system", "switch", function()
+		has = true
+	end)
+
+	uci:foreach("system", "switch_vlan", function()
+		has = true
+	end)
+
+	return has
+end
+
+local function interfaces_raw()
+	local eth_ifaces = {}
+	local ifaces_dir = '/sys/class/net/'
+
+	for iface in dirent.files(ifaces_dir) do
+		if iface ~= '.' and iface ~= '..' then
+			local iface_dir = ifaces_dir .. iface
+			if is_physical(iface_dir) and not has_devtype(iface_dir, 'wlan') then
+				table.insert(eth_ifaces, iface)
+			end
+		end
+	end
+
+	return eth_ifaces
+end
+
+-- In comparison to interfaces_raw, this skips non-DSA ports on DSA devices,
+-- as for ex. hap ac² has a special eth0 that shouldn't be touched
+function M.interfaces()
+	local intfs = interfaces_raw()
+
+	if M.get_switch_type() == 'dsa' then
+		local new_intfs = {}
+		for _, intf in ipairs(intfs) do
+			if has_devtype('/sys/class/net/' .. intf, 'dsa') then
+				table.insert(new_intfs, intf)
+			end
+		end
+
+		return new_intfs
+	end
+
+	return intfs
+end
+
+function M.is_vlan(intf)
+	return has_devtype('/sys/class/net/' .. intf, 'vlan')
+end
+
+function M.get_switch_type()
+	if is_swconfig() then
+		return 'swconfig'
+	end
+
+	for _, intf in ipairs(interfaces_raw()) do
+		if has_devtype('/sys/class/net/' .. intf, 'dsa') then
+			return 'dsa'
+		end
+	end
+
+	return 'none'
+end
+
+return M
diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/info.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/info.lua
new file mode 100644
index 0000000000000000000000000000000000000000..60df0f4744a4047c143cd7f5c0d4664dc78274af
--- /dev/null
+++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/info.lua
@@ -0,0 +1,53 @@
+local uci = require('simple-uci').cursor()
+local pretty_hostname = require 'pretty_hostname'
+
+local site = require 'gluon.site'
+local sysconfig = require 'gluon.sysconfig'
+local platform = require 'gluon.platform'
+local util = require 'gluon.util'
+local has_vpn, vpn = pcall(require, 'gluon.mesh-vpn')
+local ethernet = require 'gluon.ethernet'
+
+local pubkey
+if has_vpn and vpn.enabled() then
+	local _, active_vpn = vpn.get_active_provider()
+
+	if active_vpn ~= nil then
+		pubkey = active_vpn.public_key()
+	end
+end
+
+local M = {}
+
+function M.get_info()
+	return {
+		hostname = pretty_hostname.get(uci),
+		mac_address = sysconfig.primary_mac,
+		hardware_model = platform.get_model(),
+		gluon_version = util.trim(util.readfile('/lib/gluon/gluon-version')),
+		site_version = util.trim(util.readfile('/lib/gluon/site-version')),
+		firmware_release = util.trim(util.readfile('/lib/gluon/release')),
+		site = site.site_name(),
+		domain = uci:get('gluon', 'core', 'domain'),
+		public_vpn_key = pubkey,
+		switch_type = ethernet.get_switch_type(),
+	}
+end
+
+function M.get_info_pretty(_)
+	local data = M.get_info()
+
+	return {
+		{ _('Hostname'), data.hostname },
+		{ _('MAC address'), data.mac_address },
+		{ _('Hardware model'), data.hardware_model },
+		{ _('Gluon version') .. " / " .. _('Site version'), data.gluon_version .. " / " .. data.site_version },
+		{ _('Firmware release'), data.firmware_release },
+		{ _('Site'), data.site },
+		{ _('Domain'), data.domain or 'n/a' },
+		{ _('Public VPN key'), data.public_vpn_key or 'n/a' },
+		{ _('Switch type'), data.switch_type },
+	}
+end
+
+return M
diff --git a/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/info.html b/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/info.html
index 7e2c9e2df195acfb50ac49e3495618baeaa213b6..36eabb64d0ea5007a6406b39d46f9037fbeb6d40 100644
--- a/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/info.html
+++ b/package/gluon-web-admin/files/lib/gluon/config-mode/view/admin/info.html
@@ -1,35 +1,7 @@
 <%-
-	local uci = require('simple-uci').cursor()
-	local pretty_hostname = require 'pretty_hostname'
+	local info = require 'gluon.info'
 
-	local site = require 'gluon.site'
-	local sysconfig = require 'gluon.sysconfig'
-	local platform = require 'gluon.platform'
-	local util = require "gluon.util"
-	local has_vpn, vpn = pcall(require, 'gluon.mesh-vpn')
-
-	local _ = translate
-
-
-	local pubkey
-	if has_vpn and vpn.enabled() then
-		local _, active_vpn = vpn.get_active_provider()
-
-		if active_vpn ~= nil then
-			pubkey = active_vpn.public_key()
-		end
-	end
-
-	local values = {
-		{ _('Hostname'), pretty_hostname.get(uci) },
-		{ _('MAC address'), sysconfig.primary_mac },
-		{ _('Hardware model'), platform.get_model() },
-		{ _('Gluon version') .. " / " .. _('Site version'), util.trim(util.readfile('/lib/gluon/gluon-version'))
-			.. " / " .. util.trim(util.readfile('/lib/gluon/site-version')) },
-		{ _('Firmware release'), util.trim(util.readfile('/lib/gluon/release')) },
-		{ _('Site'), site.site_name() },
-		{ _('Public VPN key'), pubkey },
-	}
+	local values = info.get_info_pretty(translate)
 -%>
 <h2><%:Information%></h2>
 <% for _, v in ipairs(values) do %>
diff --git a/package/gluon-web-admin/i18n/de.po b/package/gluon-web-admin/i18n/de.po
index 33b459a07a00bc86ee6f7cbf58479a7e3ad67c0c..537593eefa4a0fe70f80bff5a54fd22f6ed089e9 100644
--- a/package/gluon-web-admin/i18n/de.po
+++ b/package/gluon-web-admin/i18n/de.po
@@ -77,6 +77,12 @@ msgstr "Passwort gelöscht."
 msgid "Public VPN key"
 msgstr "Öffentlicher VPN-Schlüssel"
 
+msgid "Domain"
+msgstr "Domäne"
+
+msgid "Switch type"
+msgstr "Switch Typ"
+
 msgid "Remote access"
 msgstr "Remotezugriff"
 
diff --git a/package/gluon-web-admin/i18n/gluon-web-admin.pot b/package/gluon-web-admin/i18n/gluon-web-admin.pot
index 48c5f2bb78238726f8c1d816af89a4a6c66d221a..2511fc89b0b49e0ca2fee0b31b4287b0c8088eba 100644
--- a/package/gluon-web-admin/i18n/gluon-web-admin.pot
+++ b/package/gluon-web-admin/i18n/gluon-web-admin.pot
@@ -64,6 +64,12 @@ msgstr ""
 msgid "Public VPN key"
 msgstr ""
 
+msgid "Domain"
+msgstr ""
+
+msgid "Switch type"
+msgstr ""
+
 msgid "Remote access"
 msgstr ""