2015-01-16 22:38:38 +00:00
-- Copyright 2008 Steven Barth <steven@midlink.org>
2015-01-16 22:46:42 +00:00
-- Copyright 2010-2011 Jo-Philipp Wich <jow@openwrt.org>
2015-01-16 22:38:38 +00:00
-- Licensed to the public under the Apache License 2.0.
2010-10-12 23:12:30 +00:00
2012-07-01 20:05:05 +00:00
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. " ) )
2008-04-11 19:03:30 +00:00
2013-04-11 13:36:22 +00:00
local fs = require " nixio.fs "
2012-11-01 20:03:36 +00:00
local switches = { }
2010-03-06 19:13:03 +00:00
m.uci : foreach ( " network " , " switch " ,
function ( x )
2011-10-09 22:28:14 +00:00
local sid = x [ ' .name ' ]
local switch_name = x.name or sid
2011-05-20 11:49:09 +00:00
local has_vlan = nil
2011-10-09 22:28:14 +00:00
local has_learn = nil
2010-10-12 23:12:30 +00:00
local has_vlan4k = nil
2011-05-20 11:56:42 +00:00
local has_jumbo3 = nil
2014-10-10 13:56:42 +00:00
local has_mirror = nil
2011-05-19 23:51:50 +00:00
local min_vid = 0
2010-10-12 23:12:30 +00:00
local max_vid = 16
local num_vlans = 16
2013-04-11 13:36:22 +00:00
local cpu_port = tonumber ( fs.readfile ( " /proc/switch/eth0/cpuport " ) or 5 )
2013-04-11 17:32:24 +00:00
local num_ports = cpu_port + 1
2010-10-12 23:12:30 +00:00
2011-10-09 23:21:19 +00:00
local switch_title
2011-01-10 18:40:35 +00:00
local enable_vlan4k = false
2010-10-14 23:38:54 +00:00
-- Parse some common switch properties from swconfig help output.
2010-10-12 23:12:30 +00:00
local swc = io.popen ( " swconfig dev %q help 2>/dev/null " % switch_name )
if swc then
local is_port_attr = false
local is_vlan_attr = false
while true do
local line = swc : read ( " *l " )
if not line then break end
if line : match ( " ^%s+%-%-vlan " ) then
is_vlan_attr = true
elseif line : match ( " ^%s+%-%-port " ) then
is_vlan_attr = false
is_port_attr = true
2011-10-09 23:21:19 +00:00
elseif line : match ( " cpu @ " ) then
switch_title = line : match ( " ^switch%d: %w+%((.-)%) " )
2010-10-12 23:12:30 +00:00
num_ports , cpu_port , num_vlans =
line : match ( " ports: (%d+) %(cpu @ (%d+)%), vlans: (%d+) " )
2011-07-17 05:46:54 +00:00
num_ports = tonumber ( num_ports ) or 6
num_vlans = tonumber ( num_vlans ) or 16
cpu_port = tonumber ( cpu_port ) or 5
2011-05-20 11:49:09 +00:00
min_vid = 1
2010-10-12 23:12:30 +00:00
2010-10-14 23:38:54 +00:00
elseif line : match ( " : pvid " ) or line : match ( " : tag " ) or line : match ( " : vid " ) then
if is_vlan_attr then has_vlan4k = line : match ( " : (%w+) " ) end
2010-10-12 23:12:30 +00:00
2011-07-17 08:28:56 +00:00
elseif line : match ( " : enable_vlan4k " ) then
enable_vlan4k = true
2011-05-20 11:49:09 +00:00
elseif line : match ( " : enable_vlan " ) then
has_vlan = " enable_vlan "
elseif line : match ( " : enable_learning " ) then
has_learn = " enable_learning "
2014-10-10 13:56:42 +00:00
elseif line : match ( " : enable_mirror_rx " ) then
has_mirror = " enable_mirror_rx "
2011-05-20 11:56:42 +00:00
elseif line : match ( " : max_length " ) then
has_jumbo3 = " max_length "
2010-10-12 23:12:30 +00:00
end
end
swc : close ( )
end
2008-04-11 19:03:30 +00:00
2010-10-14 23:38:54 +00:00
2010-03-06 19:13:03 +00:00
-- Switch properties
2011-10-09 23:21:19 +00:00
s = m : section ( NamedSection , x [ ' .name ' ] , " switch " ,
switch_title and translatef ( " Switch %q (%s) " , switch_name , switch_title )
or translatef ( " Switch %q " , switch_name ) )
2010-03-06 19:13:03 +00:00
s.addremove = false
2008-04-11 19:03:30 +00:00
2011-05-20 11:49:09 +00:00
if has_vlan then
2011-07-17 05:46:54 +00:00
s : option ( Flag , has_vlan , translate ( " Enable VLAN functionality " ) )
2011-05-20 11:49:09 +00:00
end
2010-03-06 19:13:03 +00:00
2011-05-20 11:49:09 +00:00
if has_learn then
x = s : option ( Flag , has_learn , translate ( " Enable learning and aging " ) )
x.default = x.enabled
end
2011-05-20 11:56:42 +00:00
if has_jumbo3 then
x = s : option ( Flag , has_jumbo3 , translate ( " Enable Jumbo Frame passthrough " ) )
x.enabled = " 3 "
x.rmempty = true
end
2014-10-10 13:56:42 +00:00
-- Does this switch support port mirroring?
if has_mirror then
s : option ( Flag , " enable_mirror_rx " , translate ( " Enable mirroring of incoming packets " ) )
s : option ( Flag , " enable_mirror_tx " , translate ( " Enable mirroring of outgoing packets " ) )
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
name = ( pt == cpu_port ) and translate ( " CPU " ) or translatef ( " Port %d " , pt )
sp : value ( pt , name )
mp : value ( pt , name )
end
end
2010-03-06 19:13:03 +00:00
-- VLAN table
2011-10-11 01:21:58 +00:00
s = m : section ( TypedSection , " switch_vlan " ,
switch_title and translatef ( " VLANs on %q (%s) " , switch_name , switch_title )
or translatef ( " VLANs on %q " , switch_name ) )
2010-03-06 19:13:03 +00:00
s.template = " cbi/tblsection "
s.addremove = true
2010-10-12 23:12:30 +00:00
s.anonymous = true
2010-03-06 19:13:03 +00:00
2011-10-09 22:28:14 +00:00
-- Filter by switch
s.filter = function ( self , section )
local device = m : get ( section , " device " )
return ( device and device == switch_name )
end
2010-10-14 23:38:54 +00:00
-- Override cfgsections callback to enforce row ordering by vlan id.
2010-03-06 19:13:03 +00:00
s.cfgsections = function ( self )
local osections = TypedSection.cfgsections ( self )
local sections = { }
2010-10-12 23:12:30 +00:00
local section
2010-03-06 19:13:03 +00:00
for _ , section in luci.util . spairs (
osections ,
function ( a , b )
2011-10-09 22:28:14 +00:00
return ( tonumber ( m : get ( osections [ a ] , has_vlan4k or " vlan " ) ) or 9999 )
< ( tonumber ( m : get ( osections [ b ] , has_vlan4k or " vlan " ) ) or 9999 )
2010-03-06 19:13:03 +00:00
end
) do
sections [ # sections + 1 ] = section
end
return sections
end
2010-10-14 23:38:54 +00:00
-- When creating a new vlan, preset it with the highest found vid + 1.
2011-10-09 22:28:14 +00:00
s.create = function ( self , section , origin )
-- Filter by switch
if m : get ( origin , " device " ) ~= switch_name then
return
end
2010-10-14 00:24:01 +00:00
local sid = TypedSection.create ( self , section )
local max_nr = 0
local max_id = 0
m.uci : foreach ( " network " , " switch_vlan " ,
function ( s )
2011-10-09 22:28:14 +00:00
if s.device == switch_name then
local nr = tonumber ( s.vlan )
local id = has_vlan4k and tonumber ( s [ has_vlan4k ] )
if nr ~= nil and nr > max_nr then max_nr = nr end
if id ~= nil and id > max_id then max_id = id end
end
2010-10-14 00:24:01 +00:00
end )
2011-10-27 00:02:10 +00:00
m : set ( sid , " device " , switch_name )
m : set ( sid , " vlan " , max_nr + 1 )
2010-10-14 00:24:01 +00:00
if has_vlan4k then
2011-10-27 00:02:10 +00:00
m : set ( sid , has_vlan4k , max_id + 1 )
2010-10-14 00:24:01 +00:00
end
return sid
end
2010-10-12 23:12:30 +00:00
local port_opts = { }
local untagged = { }
2010-10-14 23:38:54 +00:00
-- Parse current tagging state from the "ports" option.
2010-10-12 23:12:30 +00:00
local portvalue = function ( self , section )
local pt
2011-10-09 22:28:14 +00:00
for pt in ( m : get ( section , " ports " ) or " " ) : gmatch ( " %w+ " ) do
2010-10-12 23:12:30 +00:00
local pc , tu = pt : match ( " ^(%d+)([tu]*) " )
if pc == self.option then return ( # tu > 0 ) and tu or " u " end
end
return " "
end
2010-10-14 23:38:54 +00:00
-- Validate port tagging. Ensure that a port is only untagged once,
-- bail out if not.
2010-10-12 23:12:30 +00:00
local portvalidate = function ( self , value , section )
2010-10-14 00:24:01 +00:00
-- ensure that the ports appears untagged only once
2010-10-12 23:12:30 +00:00
if value == " u " then
if not untagged [ self.option ] then
untagged [ self.option ] = true
2011-05-20 11:49:09 +00:00
elseif min_vid > 0 or tonumber ( self.option ) ~= cpu_port then -- enable multiple untagged cpu ports due to weird broadcom default setup
2010-10-12 23:12:30 +00:00
return nil ,
translatef ( " Port %d is untagged in multiple VLANs! " , tonumber ( self.option ) + 1 )
end
2010-03-06 19:13:03 +00:00
end
2010-10-12 23:12:30 +00:00
return value
2010-03-06 19:13:03 +00:00
end
2012-11-01 20:03:36 +00:00
local vid = s : option ( Value , has_vlan4k or " vlan " , " VLAN ID " , " <div id='portstatus-%s'></div> " % switch_name )
2013-04-11 17:32:24 +00:00
local mx_vid = has_vlan4k and 4094 or ( num_vlans - 1 )
2010-03-06 19:13:03 +00:00
2010-10-14 00:24:01 +00:00
vid.rmempty = false
2010-10-30 02:30:49 +00:00
vid.forcewrite = true
2011-10-09 23:39:34 +00:00
vid.vlan_used = { }
2012-11-10 20:55:50 +00:00
vid.datatype = " and(uinteger,range( " .. min_vid .. " , " .. mx_vid .. " )) "
2010-03-06 19:13:03 +00:00
2010-10-14 23:38:54 +00:00
-- Validate user provided VLAN ID, make sure its within the bounds
-- allowed by the switch.
2010-10-12 23:12:30 +00:00
vid.validate = function ( self , value , section )
local v = tonumber ( value )
local m = has_vlan4k and 4094 or ( num_vlans - 1 )
2011-05-19 12:22:18 +00:00
if v ~= nil and v >= min_vid and v <= m then
2011-10-09 23:39:34 +00:00
if not self.vlan_used [ v ] then
self.vlan_used [ v ] = true
return value
else
return nil ,
translatef ( " Invalid VLAN ID given! Only unique IDs are allowed " )
end
2010-10-12 23:12:30 +00:00
else
return nil ,
2011-05-19 12:22:18 +00:00
translatef ( " Invalid VLAN ID given! Only IDs between %d and %d are allowed. " , min_vid , m )
2010-10-12 23:12:30 +00:00
end
2010-03-06 19:13:03 +00:00
end
2010-10-14 23:38:54 +00:00
-- When writing the "vid" or "vlan" option, serialize the port states
-- as well and write them as "ports" option to uci.
2010-10-14 00:24:01 +00:00
vid.write = function ( self , section , value )
2010-10-12 23:12:30 +00:00
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
p [ # p + 1 ] = o.option
end
end
2011-10-27 00:02:10 +00:00
if enable_vlan4k then
m : set ( sid , " enable_vlan4k " , " 1 " )
end
m : set ( section , " ports " , table.concat ( p , " " ) )
2010-10-14 00:24:01 +00:00
return Value.write ( self , section , value )
2010-03-06 19:13:03 +00:00
end
2010-10-14 23:38:54 +00:00
-- Fallback to "vlan" option if "vid" option is supported but unset.
vid.cfgvalue = function ( self , section )
2011-10-09 22:28:14 +00:00
return m : get ( section , has_vlan4k or " vlan " )
or m : get ( section , " vlan " )
2010-10-14 23:38:54 +00:00
end
2010-10-12 23:12:30 +00:00
2010-10-14 23:38:54 +00:00
-- Build per-port off/untagged/tagged choice lists.
2010-10-12 23:12:30 +00:00
local pt
for pt = 0 , num_ports - 1 do
2011-10-09 23:39:34 +00:00
local title
if pt == cpu_port then
title = translate ( " CPU " )
else
2012-03-19 21:04:15 +00:00
title = translatef ( " Port %d " , pt )
2011-10-09 23:39:34 +00:00
end
local po = s : option ( ListValue , tostring ( pt ) , title )
2010-10-12 23:12:30 +00:00
2011-10-09 23:39:34 +00:00
po : value ( " " , translate ( " off " ) )
po : value ( " u " , translate ( " untagged " ) )
po : value ( " t " , translate ( " tagged " ) )
2010-10-12 23:12:30 +00:00
po.cfgvalue = portvalue
po.validate = portvalidate
2010-10-30 02:30:49 +00:00
po.write = function ( ) end
2010-10-12 23:12:30 +00:00
port_opts [ # port_opts + 1 ] = po
end
2012-09-11 14:18:28 +00:00
2012-11-01 20:03:36 +00:00
switches [ # switches + 1 ] = switch_name
2010-03-06 19:13:03 +00:00
end
)
2012-11-01 20:03:36 +00:00
-- Switch status template
s = m : section ( SimpleSection )
s.template = " admin_network/switch_status "
s.switches = switches
2010-03-06 19:13:03 +00:00
return m