diff --git a/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/300-gluon-client-bridge-network b/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/300-gluon-client-bridge-network
index b344b2b322976233fcca39d7615f2de007abd918..3d4dbac38903fb6c24c86bd7084d22cf251837f2 100755
--- a/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/300-gluon-client-bridge-network
+++ b/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/300-gluon-client-bridge-network
@@ -6,12 +6,8 @@ local util = require 'gluon.util'
 local uci = require('simple-uci').cursor()
 
 
-local interfaces = { 'local-port' }
-if sysconfig.lan_ifname and uci:get_bool('network', 'mesh_lan', 'disabled') then
-	for lanif in sysconfig.lan_ifname:gmatch('%S+') do
-		util.add_to_set(interfaces, lanif)
-	end
-end
+local interfaces = util.get_role_interfaces(uci, 'client', true)
+util.add_to_set(interfaces, 'local-port')
 
 uci:section('network', 'interface', 'client', {
 	type = 'bridge',
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/110-network b/package/gluon-core/luasrc/lib/gluon/upgrade/110-network
index 8903c126a7a257bca5a39624b2c2290cb75db818..ac084e6a0354bb6488cace6e477684e74a108981 100755
--- a/package/gluon-core/luasrc/lib/gluon/upgrade/110-network
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/110-network
@@ -1,7 +1,7 @@
 #!/usr/bin/lua
 
 local uci = require('simple-uci').cursor()
-local sysconfig = require 'gluon.sysconfig'
+local util = require 'gluon.util'
 
 local wan = uci:get_all('network_gluon-old', 'wan') or {}
 local wan6 = uci:get_all('network_gluon-old', 'wan6') or {}
@@ -18,7 +18,7 @@ uci:section('network', 'interface', 'wan', {
 	ipaddr = wan.ipaddr,
 	netmask = wan.netmask,
 	gateway = wan.gateway,
-	ifname = sysconfig.wan_ifname,
+	ifname = util.get_role_interfaces(uci, 'uplink'),
 	type = 'bridge',
 	igmp_snooping = true,
 	multicast_querier = false,
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-mesh b/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-mesh
new file mode 100755
index 0000000000000000000000000000000000000000..7e2f84718c589ba9c955741e897966af3f0cfb09
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-mesh
@@ -0,0 +1,48 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site'
+local uci = require('simple-uci').cursor()
+local util = require 'gluon.util'
+
+local mesh_interfaces = util.get_role_interfaces(uci, 'mesh')
+local uplink_interfaces = util.get_role_interfaces(uci, 'uplink')
+
+local mesh_interfaces_uplink = {}
+local mesh_interfaces_other = {}
+for _, iface in ipairs(mesh_interfaces) do
+	if util.contains(uplink_interfaces, iface) then
+		table.insert(mesh_interfaces_uplink, iface)
+	else
+		table.insert(mesh_interfaces_other, iface)
+	end
+end
+
+if #mesh_interfaces_uplink > 0 then
+	uci:section('network', 'interface', 'mesh_uplink', {
+		ifname = 'br-wan',
+		proto = 'gluon_wired',
+		index = 0,
+		vxlan = site.mesh.vxlan(true),
+	})
+end
+
+if #mesh_interfaces_other > 0 then
+	local iftype, ifname
+	if #mesh_interfaces_other == 1 then
+		ifname = mesh_interfaces_other[1]
+	else
+		iftype = 'bridge'
+		ifname = mesh_interfaces_other
+	end
+
+	uci:section('network', 'interface', 'mesh_other', {
+		ifname = ifname,
+		type = iftype,
+		igmp_snooping = false,
+		proto = 'gluon_wired',
+		index = 4,
+		vxlan = site.mesh.vxlan(true),
+	})
+end
+
+uci:save('network')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-wan b/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-wan
deleted file mode 100755
index 4f24cbc439d928fe89ce762244661508296113ec..0000000000000000000000000000000000000000
--- a/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-wan
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/lua
-
-local site = require 'gluon.site'
-local uci = require('simple-uci').cursor()
-
-local disabled = uci:get('network_gluon-old', 'mesh_wan', 'disabled')
-if disabled == nil then
-	disabled = not site.mesh_on_wan(false)
-end
-
-local transitive = uci:get('network_gluon-old', 'mesh_wan', 'transitive')
-if transitive == nil then
-	transitive = true
-end
-
-uci:section('network', 'interface', 'mesh_wan', {
-	ifname = 'br-wan',
-	proto = 'gluon_wired',
-	index = 0,
-	vxlan = site.mesh.vxlan(true),
-	disabled = disabled,
-	transitive = transitive,
-})
-
-uci:save('network')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/220-interface-lan b/package/gluon-core/luasrc/lib/gluon/upgrade/220-interface-lan
deleted file mode 100755
index cd38f804aa8f6a4530fda2d4cdc6150ac474f7a2..0000000000000000000000000000000000000000
--- a/package/gluon-core/luasrc/lib/gluon/upgrade/220-interface-lan
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/lua
-
-local site = require 'gluon.site'
-local sysconfig = require 'gluon.sysconfig'
-
-local uci = require('simple-uci').cursor()
-
-if not sysconfig.lan_ifname then
-	os.exit(0)
-end
-
-local type
-if sysconfig.lan_ifname:match(' ') then
-	type = 'bridge'
-end
-
-local disabled = uci:get('network_gluon-old', 'mesh_lan', 'disabled')
-if disabled == nil then
-	disabled = not site.mesh_on_lan(false)
-end
-
-local transitive = uci:get('network_gluon-old', 'mesh_lan', 'transitive')
-if transitive == nil then
-	transitive = true
-end
-
-uci:section('network', 'interface', 'mesh_lan', {
-	ifname = sysconfig.lan_ifname,
-	type = type,
-	igmp_snooping = false,
-	proto = 'gluon_wired',
-	index = 4,
-	vxlan = site.mesh.vxlan(true),
-	disabled = disabled,
-	transitive = transitive,
-})
-
-uci:save('network')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/300-firewall-rules b/package/gluon-core/luasrc/lib/gluon/upgrade/300-firewall-rules
index 605f6684acedb7a450ec77c991cb94195d517f26..ef616aef71fab1b0ca4668c0929b91559e3cfec9 100755
--- a/package/gluon-core/luasrc/lib/gluon/upgrade/300-firewall-rules
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/300-firewall-rules
@@ -86,9 +86,9 @@ local wired_mesh_ifaces = {}
 uci:foreach('network', 'interface',
 	function(iface)
 		-- Select all interfaces with proto gluon_wired except for
-		-- mesh_wan into this zone, as mesh_wan is the same
+		-- mesh_uplink into this zone, as mesh_uplink is the same
 		-- interface as wan, which has its own zone
-		if iface['proto'] == 'gluon_wired' and iface['.name'] ~= 'mesh_wan' then
+		if iface['proto'] == 'gluon_wired' and iface['.name'] ~= 'mesh_uplink' then
 			table.insert(wired_mesh_ifaces, iface['.name'])
 		end
 	end
diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua
index 48b8340ee8ee11135e8de59e1ae9f3ecd52043f1..7152bc4d99920ab69f49faf12fc4f55dcb556a68 100644
--- a/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua
+++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua
@@ -138,6 +138,34 @@ function M.get_mesh_devices(uconn)
 	return devices
 end
 
+-- Returns a list of all interfaces with a given role
+--
+-- If exclusive is set to true, only interfaces that have no other role
+-- are returned; this is used to ensure that the client role is not active
+-- at the same time as any other role
+function M.get_role_interfaces(uci, role, exclusive)
+	local ret = {}
+
+	local function add(name)
+		-- Interface names with a / prefix refer to sysconfig interfaces
+		-- (lan_ifname/wan_ifname/single_ifname)
+		if string.sub(name, 1, 1) == '/' then
+			name = sysconfig[string.sub(name, 2) .. '_ifname'] or ''
+		end
+		for iface in string.gmatch(name, '%S+') do
+			M.add_to_set(ret, iface)
+		end
+	end
+
+	uci:foreach('gluon', 'interface', function(s)
+		if M.contains(s.role, role) and (not exclusive or #s.role == 1) then
+			add(s.name)
+		end
+	end)
+
+	return ret
+end
+
 -- Safe glob: returns an empty table when the glob fails because of
 -- a non-existing path
 function M.glob(pattern)
diff --git a/package/gluon-mesh-batman-adv/luasrc/lib/gluon/upgrade/330-gluon-mesh-batman-adv-mac-addresses b/package/gluon-mesh-batman-adv/luasrc/lib/gluon/upgrade/330-gluon-mesh-batman-adv-mac-addresses
index 61153e103082be5522b6ceda06b26fbcf4ac4551..09639e34308a6286b641feab4d70a122ceee2748 100755
--- a/package/gluon-mesh-batman-adv/luasrc/lib/gluon/upgrade/330-gluon-mesh-batman-adv-mac-addresses
+++ b/package/gluon-mesh-batman-adv/luasrc/lib/gluon/upgrade/330-gluon-mesh-batman-adv-mac-addresses
@@ -9,5 +9,7 @@ local uci = require('simple-uci').cursor()
 if not site.mesh.vxlan(true) then
 	uci:set('network', 'wan', 'macaddr', util.generate_mac(0))
 end
-uci:set('network', 'mesh_lan', 'macaddr', util.generate_mac(4))
+if uci:get('network', 'mesh_other') then
+	uci:set('network', 'mesh_other', 'macaddr', util.generate_mac(4))
+end
 uci:save('network')