m = Map("network", translate("Switch"), translate("The network ports on this device can be combined to several <abbr title=\"Virtual Local Area Network\">VLAN</abbr>s in which computers can communicate directly with each other. <abbr title=\"Virtual Local Area Network\">VLAN</abbr>s are often used to separate different network segments. Often there is by default one Uplink port for a connection to the next greater network like the internet and other ports for a local network."))
local fs = require "nixio.fs"
+local nw = require "luci.model.network"
local switches = { }
+nw.init(m.uci)
+
+local topologies = nw:get_switch_topologies() or {}
+
+local update_interfaces = function(old_ifname, new_ifname)
+ local info = { }
+
+ m.uci:foreach("network", "interface", function(section)
+ local old_ifnames = m.uci:get("network", section[".name"], "ifname")
+ local new_ifnames = { }
+ local cur_ifname
+ local changed = false
+ for cur_ifname in luci.util.imatch(old_ifnames) do
+ if cur_ifname == old_ifname then
+ new_ifnames[#new_ifnames+1] = new_ifname
+ changed = true
+ else
+ new_ifnames[#new_ifnames+1] = cur_ifname
+ end
+ end
+ if changed then
+ m.uci:set("network", section[".name"], "ifname", table.concat(new_ifnames, " "))
+
+ info[#info+1] = translatef("Interface %q device auto-migrated from %q to %q.",
+ section[".name"], old_ifname, new_ifname)
+ end
+ end)
+
+ if #info > 0 then
+ m.message = (m.message and m.message .. "\n" or "") .. table.concat(info, "\n")
+ end
+end
+
m.uci:foreach("network", "switch",
function(x)
local sid = x['.name']
local min_vid = 0
local max_vid = 16
local num_vlans = 16
- local cpu_port = tonumber(fs.readfile("/proc/switch/eth0/cpuport") or 5)
- local num_ports = cpu_port + 1
local switch_title
local enable_vlan4k = false
+ local topo = topologies[switch_name]
+
+ if not topo then
+ m.message = translatef("Switch %q has an unknown topology - the VLAN settings might not be accurate.", switch_name)
+ topo = {
+ ports = {
+ { num = 0, label = "Port 1" },
+ { num = 1, label = "Port 2" },
+ { num = 2, label = "Port 3" },
+ { num = 3, label = "Port 4" },
+ { num = 4, label = "Port 5" },
+ { num = 5, label = "CPU (eth0)", tagged = false }
+ }
+ }
+ end
+
-- Parse some common switch properties from swconfig help output.
local swc = io.popen("swconfig dev %q help 2>/dev/null" % switch_name)
if swc then
elseif line:match("cpu @") then
switch_title = line:match("^switch%d: %w+%((.-)%)")
- num_ports, cpu_port, num_vlans =
- line:match("ports: (%d+) %(cpu @ (%d+)%), vlans: (%d+)")
-
- num_ports = tonumber(num_ports) or 6
- num_vlans = tonumber(num_vlans) or 16
- cpu_port = tonumber(cpu_port) or 5
+ num_vlans = tonumber(line:match("vlans: (%d+)")) or 16
min_vid = 1
elseif line:match(": pvid") or line:match(": tag") or line:match(": vid") then
local sp = s:option(ListValue, "mirror_source_port", translate("Mirror source port"))
local mp = s:option(ListValue, "mirror_monitor_port", translate("Mirror monitor port"))
- local pt
- for pt = 0, num_ports - 1 do
- local name
+ sp:depends("enable_mirror_tx", "1")
+ sp:depends("enable_mirror_rx", "1")
- name = (pt == cpu_port) and translate("CPU") or translatef("Port %d", pt)
+ mp:depends("enable_mirror_tx", "1")
+ mp:depends("enable_mirror_rx", "1")
- sp:value(pt, name)
- mp:value(pt, name)
+ local _, pt
+ for _, pt in ipairs(topo.ports) do
+ sp:value(pt.num, pt.label)
+ mp:value(pt.num, pt.label)
end
end
if value == "u" then
if not untagged[self.option] then
untagged[self.option] = true
- elseif min_vid > 0 or tonumber(self.option) ~= cpu_port then -- enable multiple untagged cpu ports due to weird broadcom default setup
+ else
return nil,
- translatef("Port %d is untagged in multiple VLANs!", tonumber(self.option) + 1)
+ translatef("%s is untagged in multiple VLANs!", self.title)
end
end
return value
-- When writing the "vid" or "vlan" option, serialize the port states
-- as well and write them as "ports" option to uci.
- vid.write = function(self, section, value)
+ vid.write = function(self, section, new_vid)
local o
local p = { }
-
for _, o in ipairs(port_opts) do
- local v = o:formvalue(section)
- if v == "t" then
- p[#p+1] = o.option .. v
- elseif v == "u" then
+ local new_tag = o:formvalue(section)
+ if new_tag == "t" then
+ p[#p+1] = o.option .. new_tag
+ elseif new_tag == "u" then
p[#p+1] = o.option
end
+
+ if o.info and o.info.device then
+ local old_tag = o:cfgvalue(section)
+ local old_vid = self:cfgvalue(section)
+ if old_tag ~= new_tag or old_vid ~= new_vid then
+ local old_ifname = (old_tag == "u") and o.info.device
+ or "%s.%s" %{ o.info.device, old_vid }
+
+ local new_ifname = (new_tag == "u") and o.info.device
+ or "%s.%s" %{ o.info.device, new_vid }
+
+ if old_ifname ~= new_ifname then
+ update_interfaces(old_ifname, new_ifname)
+ end
+ end
+ end
end
if enable_vlan4k then
end
m:set(section, "ports", table.concat(p, " "))
- return Value.write(self, section, value)
+ return Value.write(self, section, new_vid)
end
-- Fallback to "vlan" option if "vid" option is supported but unset.
or m:get(section, "vlan")
end
- -- Build per-port off/untagged/tagged choice lists.
- local pt
- for pt = 0, num_ports - 1 do
- local title
- if pt == cpu_port then
- title = translate("CPU")
- else
- title = translatef("Port %d", pt)
- end
-
- local po = s:option(ListValue, tostring(pt), title)
+ local _, pt
+ for _, pt in ipairs(topo.ports) do
+ local po = s:option(ListValue, tostring(pt.num), pt.label, '<div id="portstatus-%s-%d"></div>' %{ switch_name, pt.num })
po:value("", translate("off"))
- po:value("u", translate("untagged"))
+
+ if not pt.tagged then
+ po:value("u", translate("untagged"))
+ end
+
po:value("t", translate("tagged"))
po.cfgvalue = portvalue
po.validate = portvalidate
po.write = function() end
+ po.info = pt
port_opts[#port_opts+1] = po
end
+ table.sort(port_opts, function(a, b) return a.option < b.option end)
switches[#switches+1] = switch_name
end
)