diff options
Diffstat (limited to 'applications/luci-app-mwan3/luasrc/model')
12 files changed, 1114 insertions, 0 deletions
diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_hotplugscript.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_hotplugscript.lua new file mode 100644 index 000000000..0e7b8b11d --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_hotplugscript.lua @@ -0,0 +1,55 @@ +-- ------ hotplug script configuration ------ -- + +fs = require "nixio.fs" +sys = require "luci.sys" +ut = require "luci.util" + +script = "/etc/hotplug.d/iface/16-mwancustom" +scriptBackup = "/etc/hotplug.d/iface/16-mwancustombak" + +if luci.http.formvalue("cbid.luci.1._restorebak") then -- restore button has been clicked + luci.http.redirect(luci.dispatcher.build_url("admin/network/mwan/advanced/hotplugscript") .. "?restore=yes") +elseif luci.http.formvalue("restore") == "yes" then -- restore script from backup + os.execute("cp -f " .. scriptBackup .. " " .. script) +end + + +m5 = SimpleForm("luci", nil) + m5:append(Template("mwan/advanced_hotplugscript")) -- highlight current tab + +f = m5:section(SimpleSection, nil, + translate("This section allows you to modify the contents of /etc/hotplug.d/iface/16-mwancustom<br />" .. + "This is useful for running system commands and/or scripts based on interface ifup or ifdown hotplug events<br /><br />" .. + "Notes:<br />" .. + "The first line of the script must be "#!/bin/sh" without quotes<br />" .. + "Lines beginning with # are comments and are not executed<br /><br />" .. + "Available variables:<br />" .. + "$ACTION is the hotplug event (ifup, ifdown)<br />" .. + "$INTERFACE is the interface name (wan1, wan2, etc.)<br />" .. + "$DEVICE is the device name attached to the interface (eth0.1, eth1, etc.)")) + + +restore = f:option(Button, "_restorebak", translate("Restore default hotplug script")) + restore.inputtitle = translate("Restore...") + restore.inputstyle = "apply" + +t = f:option(TextValue, "lines") + t.rmempty = true + t.rows = 20 + + function t.cfgvalue() + local hps = fs.readfile(script) + if not hps or hps == "" then -- if script does not exist or is blank restore from backup + sys.call("cp -f " .. scriptBackup .. " " .. script) + return fs.readfile(script) + else + return hps + end + end + + function t.write(self, section, data) -- format and write new data to script + return fs.writefile(script, ut.trim(data:gsub("\r\n", "\n")) .. "\n") + end + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_mwanconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_mwanconfig.lua new file mode 100644 index 000000000..e0a99e836 --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_mwanconfig.lua @@ -0,0 +1,32 @@ +-- ------ mwan configuration ------ -- + +ut = require "luci.util" + +mwanConfig = "/etc/config/mwan3" + + +m5 = SimpleForm("luci", nil) + m5:append(Template("mwan/advanced_mwanconfig")) -- highlight current tab + + +f = m5:section(SimpleSection, nil, + translate("This section allows you to modify the contents of /etc/config/mwan3")) + +t = f:option(TextValue, "lines") + t.rmempty = true + t.rows = 20 + + function t.cfgvalue() + return nixio.fs.readfile(mwanConfig) or "" + end + + function t.write(self, section, data) -- format and write new data to script + return nixio.fs.writefile(mwanConfig, "\n" .. ut.trim(data:gsub("\r\n", "\n")) .. "\n") + end + + function f.handle(self, state, data) + return true + end + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_networkconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_networkconfig.lua new file mode 100644 index 000000000..b93d89751 --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_networkconfig.lua @@ -0,0 +1,32 @@ +-- ------ network configuration ------ -- + +ut = require "luci.util" + +networkConfig = "/etc/config/network" + + +m5 = SimpleForm("networkconf", nil) + m5:append(Template("mwan/advanced_networkconfig")) -- highlight current tab + + +f = m5:section(SimpleSection, nil, + translate("This section allows you to modify the contents of /etc/config/network")) + +t = f:option(TextValue, "lines") + t.rmempty = true + t.rows = 20 + + function t.cfgvalue() + return nixio.fs.readfile(networkConfig) or "" + end + + function t.write(self, section, data) -- format and write new data to script + return nixio.fs.writefile(networkConfig, "\n" .. ut.trim(data:gsub("\r\n", "\n")) .. "\n") + end + + function f.handle(self, state, data) + return true + end + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_wirelessconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_wirelessconfig.lua new file mode 100644 index 000000000..95e9f7c7e --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/advanced_wirelessconfig.lua @@ -0,0 +1,32 @@ +-- ------ wireless configuration ------ -- + +ut = require "luci.util" + +wirelessConfig = "/etc/config/wireless" + + +m5 = SimpleForm("wirelessconf", nil) + m5:append(Template("mwan/advanced_wirelessconfig")) -- highlight current tab + + +f = m5:section(SimpleSection, nil, + translate("This section allows you to modify the contents of /etc/config/wireless")) + +t = f:option(TextValue, "lines") + t.rmempty = true + t.rows = 20 + + function t.cfgvalue() + return nixio.fs.readfile(wirelessConfig) or "" + end + + function t.write(self, section, data) -- format and write new data to script + return nixio.fs.writefile(wirelessConfig, "\n" .. ut.trim(data:gsub("\r\n", "\n")) .. "\n") + end + + function f.handle(self, state, data) + return true + end + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua new file mode 100644 index 000000000..a8e68a01b --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua @@ -0,0 +1,266 @@ +-- ------ extra functions ------ -- + +function interfaceCheck() -- find issues with too many interfaces, reliability and metric + uci.cursor():foreach("mwan3", "interface", + function (section) + local interfaceName = section[".name"] + interfaceNumber = interfaceNumber+1 -- count number of mwan interfaces configured + -- create list of metrics for none and duplicate checking + local metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. interfaceName .. ".metric")) + if metricValue == "" then + errorFound = 1 + errorNoMetricList = errorNoMetricList .. interfaceName .. " " + else + metricList = metricList .. interfaceName .. " " .. metricValue .. "\n" + end + -- check if any interfaces have a higher reliability requirement than tracking IPs configured + local trackingNumber = tonumber(ut.trim(sys.exec("echo $(uci -p /var/state get mwan3." .. interfaceName .. ".track_ip) | wc -w"))) + if trackingNumber > 0 then + local reliabilityNumber = tonumber(ut.trim(sys.exec("uci -p /var/state get mwan3." .. interfaceName .. ".reliability"))) + if reliabilityNumber and reliabilityNumber > trackingNumber then + errorFound = 1 + errorReliabilityList = errorReliabilityList .. interfaceName .. " " + end + end + -- check if any interfaces are not properly configured in /etc/config/network or have no default route in main routing table + if ut.trim(sys.exec("uci -p /var/state get network." .. interfaceName)) == "interface" then + local interfaceDevice = ut.trim(sys.exec("uci -p /var/state get network." .. interfaceName .. ".ifname")) + if interfaceDevice == "uci: Entry not found" or interfaceDevice == "" then + errorFound = 1 + errorNetConfigList = errorNetConfigList .. interfaceName .. " " + errorRouteList = errorRouteList .. interfaceName .. " " + else + local routeCheck = ut.trim(sys.exec("route -n | awk '{if ($8 == \"" .. interfaceDevice .. "\" && $1 == \"0.0.0.0\" && $3 == \"0.0.0.0\") print $1}'")) + if routeCheck == "" then + errorFound = 1 + errorRouteList = errorRouteList .. interfaceName .. " " + end + end + else + errorFound = 1 + errorNetConfigList = errorNetConfigList .. interfaceName .. " " + errorRouteList = errorRouteList .. interfaceName .. " " + end + end + ) + -- check if any interfaces have duplicate metrics + local metricDuplicateNumbers = sys.exec("echo '" .. metricList .. "' | awk '{print $2}' | uniq -d") + if metricDuplicateNumbers ~= "" then + errorFound = 1 + local metricDuplicates = "" + for line in metricDuplicateNumbers:gmatch("[^\r\n]+") do + metricDuplicates = sys.exec("echo '" .. metricList .. "' | grep '" .. line .. "' | awk '{print $1}'") + errorDuplicateMetricList = errorDuplicateMetricList .. metricDuplicates + end + errorDuplicateMetricList = sys.exec("echo '" .. errorDuplicateMetricList .. "' | tr '\n' ' '") + end +end + +function interfaceWarnings() -- display status and warning messages at the top of the page + local warnings = "" + if interfaceNumber <= 250 then + warnings = "<strong>There are currently " .. interfaceNumber .. " of 250 supported interfaces configured</strong>" + else + warnings = "<font color=\"ff0000\"><strong>WARNING: " .. interfaceNumber .. " interfaces are configured exceeding the maximum of 250!</strong></font>" + end + if errorReliabilityList ~= " " then + warnings = warnings .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have a higher reliability requirement than there are tracking IP addresses!</strong></font>" + end + if errorRouteList ~= " " then + warnings = warnings .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have no default route in the main routing table!</strong></font>" + end + if errorNetConfigList ~= " " then + warnings = warnings .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces are configured incorrectly or not at all in /etc/config/network!</strong></font>" + end + if errorNoMetricList ~= " " then + warnings = warnings .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have no metric configured in /etc/config/network!</strong></font>" + end + if errorDuplicateMetricList ~= " " then + warnings = warnings .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have duplicate metrics configured in /etc/config/network!</strong></font>" + end + return warnings +end + +-- ------ interface configuration ------ -- + +dsp = require "luci.dispatcher" +sys = require "luci.sys" +ut = require "luci.util" + +interfaceNumber = 0 +metricList = "" +errorFound = 0 +errorDuplicateMetricList = " " +errorNetConfigList = " " +errorNoMetricList = " " +errorReliabilityList = " " +errorRouteList = " " +interfaceCheck() + + +m5 = Map("mwan3", translate("MWAN Interface Configuration"), + translate(interfaceWarnings())) + m5:append(Template("mwan/config_css")) + + +mwan_interface = m5:section(TypedSection, "interface", translate("Interfaces"), + translate("MWAN supports up to 250 physical and/or logical interfaces<br />" .. + "MWAN requires that all interfaces have a unique metric configured in /etc/config/network<br />" .. + "Names must match the interface name found in /etc/config/network (see advanced tab)<br />" .. + "Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />" .. + "Interfaces may not share the same name as configured members, policies or rules")) + mwan_interface.addremove = true + mwan_interface.dynamic = false + mwan_interface.sectionhead = "Interface" + mwan_interface.sortable = true + mwan_interface.template = "cbi/tblsection" + mwan_interface.extedit = dsp.build_url("admin", "network", "mwan", "configuration", "interface", "%s") + function mwan_interface.create(self, section) + TypedSection.create(self, section) + m5.uci:save("mwan3") + luci.http.redirect(dsp.build_url("admin", "network", "mwan", "configuration", "interface", section)) + end + + +enabled = mwan_interface:option(DummyValue, "enabled", translate("Enabled")) + enabled.rawhtml = true + function enabled.cfgvalue(self, s) + if self.map:get(s, "enabled") == "1" then + return "Yes" + else + return "No" + end + end + +track_ip = mwan_interface:option(DummyValue, "track_ip", translate("Tracking IP")) + track_ip.rawhtml = true + function track_ip.cfgvalue(self, s) + tracked = self.map:get(s, "track_ip") + if tracked then + local ipList = "" + for k,v in pairs(tracked) do + ipList = ipList .. v .. "<br />" + end + return ipList + else + return "—" + end + end + +reliability = mwan_interface:option(DummyValue, "reliability", translate("Tracking reliability")) + reliability.rawhtml = true + function reliability.cfgvalue(self, s) + if tracked then + return self.map:get(s, "reliability") or "—" + else + return "—" + end + end + +count = mwan_interface:option(DummyValue, "count", translate("Ping count")) + count.rawhtml = true + function count.cfgvalue(self, s) + if tracked then + return self.map:get(s, "count") or "—" + else + return "—" + end + end + +timeout = mwan_interface:option(DummyValue, "timeout", translate("Ping timeout")) + timeout.rawhtml = true + function timeout.cfgvalue(self, s) + if tracked then + local timeoutValue = self.map:get(s, "timeout") + if timeoutValue then + return timeoutValue .. "s" + else + return "—" + end + else + return "—" + end + end + +interval = mwan_interface:option(DummyValue, "interval", translate("Ping interval")) + interval.rawhtml = true + function interval.cfgvalue(self, s) + if tracked then + local intervalValue = self.map:get(s, "interval") + if intervalValue then + return intervalValue .. "s" + else + return "—" + end + else + return "—" + end + end + +down = mwan_interface:option(DummyValue, "down", translate("Interface down")) + down.rawhtml = true + function down.cfgvalue(self, s) + if tracked then + return self.map:get(s, "down") or "—" + else + return "—" + end + end + +up = mwan_interface:option(DummyValue, "up", translate("Interface up")) + up.rawhtml = true + function up.cfgvalue(self, s) + if tracked then + return self.map:get(s, "up") or "—" + else + return "—" + end + end + +metric = mwan_interface:option(DummyValue, "metric", translate("Metric")) + metric.rawhtml = true + function metric.cfgvalue(self, s) + local metricValue = sys.exec("uci -p /var/state get network." .. s .. ".metric") + if metricValue ~= "" then + return metricValue + else + return "—" + end + end + +errors = mwan_interface:option(DummyValue, "errors", translate("Errors")) + errors.rawhtml = true + function errors.cfgvalue(self, s) + if errorFound == 1 then + local mouseOver, lineBreak = "", "" + if string.find(errorReliabilityList, " " .. s .. " ") then + mouseOver = "Higher reliability requirement than there are tracking IP addresses" + lineBreak = " " + end + if string.find(errorRouteList, " " .. s .. " ") then + mouseOver = mouseOver .. lineBreak .. "No default route in the main routing table" + lineBreak = " " + end + if string.find(errorNetConfigList, " " .. s .. " ") then + mouseOver = mouseOver .. lineBreak .. "Configured incorrectly or not at all in /etc/config/network" + lineBreak = " " + end + if string.find(errorNoMetricList, " " .. s .. " ") then + mouseOver = mouseOver .. lineBreak .. "No metric configured in /etc/config/network" + lineBreak = " " + end + if string.find(errorDuplicateMetricList, " " .. s .. " ") then + mouseOver = mouseOver .. lineBreak .. "Duplicate metric configured in /etc/config/network" + end + if mouseOver == "" then + return "" + else + return "<span title=\"" .. mouseOver .. "\"><img src=\"/luci-static/resources/cbi/reset.gif\" alt=\"error\"></img></span>" + end + else + return "" + end + end + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interfaceconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interfaceconfig.lua new file mode 100644 index 000000000..387ff01a8 --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interfaceconfig.lua @@ -0,0 +1,190 @@ +-- ------ extra functions ------ -- + +function interfaceCheck() + metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. arg[1] .. ".metric")) + if metricValue == "" then -- no metric + errorNoMetric = 1 + else -- if metric exists create list of interface metrics to compare against for duplicates + uci.cursor():foreach("mwan3", "interface", + function (section) + local metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. section[".name"] .. ".metric")) + metricList = metricList .. section[".name"] .. " " .. metricValue .. "\n" + end + ) + -- compare metric against list + local metricDuplicateNumbers, metricDuplicates = sys.exec("echo '" .. metricList .. "' | awk '{print $2}' | uniq -d"), "" + for line in metricDuplicateNumbers:gmatch("[^\r\n]+") do + metricDuplicates = sys.exec("echo '" .. metricList .. "' | grep '" .. line .. "' | awk '{print $1}'") + errorDuplicateMetricList = errorDuplicateMetricList .. metricDuplicates + end + if sys.exec("echo '" .. errorDuplicateMetricList .. "' | grep -w " .. arg[1]) ~= "" then + errorDuplicateMetric = 1 + end + end + -- check if this interface has a higher reliability requirement than track IPs configured + local trackingNumber = tonumber(ut.trim(sys.exec("echo $(uci -p /var/state get mwan3." .. arg[1] .. ".track_ip) | wc -w"))) + if trackingNumber > 0 then + local reliabilityNumber = tonumber(ut.trim(sys.exec("uci -p /var/state get mwan3." .. arg[1] .. ".reliability"))) + if reliabilityNumber and reliabilityNumber > trackingNumber then + errorReliability = 1 + end + end + -- check if any interfaces are not properly configured in /etc/config/network or have no default route in main routing table + if ut.trim(sys.exec("uci -p /var/state get network." .. arg[1])) == "interface" then + local interfaceDevice = ut.trim(sys.exec("uci -p /var/state get network." .. arg[1] .. ".ifname")) + if interfaceDevice == "uci: Entry not found" or interfaceDevice == "" then + errorNetConfig = 1 + errorRoute = 1 + else + local routeCheck = ut.trim(sys.exec("route -n | awk '{if ($8 == \"" .. interfaceDevice .. "\" && $1 == \"0.0.0.0\" && $3 == \"0.0.0.0\") print $1}'")) + if routeCheck == "" then + errorRoute = 1 + end + end + else + errorNetConfig = 1 + errorRoute = 1 + end +end + +function interfaceWarnings() -- display warning messages at the top of the page + local warns, lineBreak = "", "" + if errorReliability == 1 then + warns = "<font color=\"ff0000\"><strong>WARNING: this interface has a higher reliability requirement than there are tracking IP addresses!</strong></font>" + lineBreak = "<br /><br />" + end + if errorRoute == 1 then + warns = warns .. lineBreak .. "<font color=\"ff0000\"><strong>WARNING: this interface has no default route in the main routing table!</strong></font>" + lineBreak = "<br /><br />" + end + if errorNetConfig == 1 then + warns = warns .. lineBreak .. "<font color=\"ff0000\"><strong>WARNING: this interface is configured incorrectly or not at all in /etc/config/network!</strong></font>" + lineBreak = "<br /><br />" + end + if errorNoMetric == 1 then + warns = warns .. lineBreak .. "<font color=\"ff0000\"><strong>WARNING: this interface has no metric configured in /etc/config/network!</strong></font>" + elseif errorDuplicateMetric == 1 then + warns = warns .. lineBreak .. "<font color=\"ff0000\"><strong>WARNING: this and other interfaces have duplicate metrics configured in /etc/config/network!</strong></font>" + end + return warns +end + +-- ------ interface configuration ------ -- + +dsp = require "luci.dispatcher" +sys = require "luci.sys" +ut = require "luci.util" +arg[1] = arg[1] or "" + +metricValue = "" +metricList = "" +errorDuplicateMetricList = "" +errorNoMetric = 0 +errorDuplicateMetric = 0 +errorRoute = 0 +errorNetConfig = 0 +errorReliability = 0 +interfaceCheck() + + +m5 = Map("mwan3", translate("MWAN Interface Configuration - " .. arg[1]), + translate(interfaceWarnings())) + m5.redirect = dsp.build_url("admin", "network", "mwan", "configuration", "interface") + + +mwan_interface = m5:section(NamedSection, arg[1], "interface", "") + mwan_interface.addremove = false + mwan_interface.dynamic = false + + +enabled = mwan_interface:option(ListValue, "enabled", translate("Enabled")) + enabled.default = "1" + enabled:value("1", translate("Yes")) + enabled:value("0", translate("No")) + +track_ip = mwan_interface:option(DynamicList, "track_ip", translate("Tracking IP"), + translate("This IP address will be pinged to dermine if the link is up or down. Leave blank to assume interface is always online")) + track_ip.datatype = "ipaddr" + +reliability = mwan_interface:option(Value, "reliability", translate("Tracking reliability"), + translate("Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up")) + reliability.datatype = "range(1, 100)" + reliability.default = "1" + +count = mwan_interface:option(ListValue, "count", translate("Ping count")) + count.default = "1" + count:value("1") + count:value("2") + count:value("3") + count:value("4") + count:value("5") + +timeout = mwan_interface:option(ListValue, "timeout", translate("Ping timeout")) + timeout.default = "2" + timeout:value("1", translate("1 second")) + timeout:value("2", translate("2 seconds")) + timeout:value("3", translate("3 seconds")) + timeout:value("4", translate("4 seconds")) + timeout:value("5", translate("5 seconds")) + timeout:value("6", translate("6 seconds")) + timeout:value("7", translate("7 seconds")) + timeout:value("8", translate("8 seconds")) + timeout:value("9", translate("9 seconds")) + timeout:value("10", translate("10 seconds")) + +interval = mwan_interface:option(ListValue, "interval", translate("Ping interval")) + interval.default = "5" + interval:value("1", translate("1 second")) + interval:value("3", translate("3 seconds")) + interval:value("5", translate("5 seconds")) + interval:value("10", translate("10 seconds")) + interval:value("20", translate("20 seconds")) + interval:value("30", translate("30 seconds")) + interval:value("60", translate("1 minute")) + interval:value("300", translate("5 minutes")) + interval:value("600", translate("10 minutes")) + interval:value("900", translate("15 minutes")) + interval:value("1800", translate("30 minutes")) + interval:value("3600", translate("1 hour")) + +down = mwan_interface:option(ListValue, "down", translate("Interface down"), + translate("Interface will be deemed down after this many failed ping tests")) + down.default = "3" + down:value("1") + down:value("2") + down:value("3") + down:value("4") + down:value("5") + down:value("6") + down:value("7") + down:value("8") + down:value("9") + down:value("10") + +up = mwan_interface:option(ListValue, "up", translate("Interface up"), + translate("Downed interface will be deemed up after this many successful ping tests")) + up.default = "3" + up:value("1") + up:value("2") + up:value("3") + up:value("4") + up:value("5") + up:value("6") + up:value("7") + up:value("8") + up:value("9") + up:value("10") + +metric = mwan_interface:option(DummyValue, "metric", translate("Metric"), + translate("This displays the metric assigned to this interface in /etc/config/network")) + metric.rawhtml = true + function metric.cfgvalue(self, s) + if errorNoMetric == 0 then + return metricValue + else + return "—" + end + end + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua new file mode 100644 index 000000000..3bccbd942 --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua @@ -0,0 +1,46 @@ +-- ------ member configuration ------ -- + +ds = require "luci.dispatcher" + + +m5 = Map("mwan3", translate("MWAN Member Configuration")) + m5:append(Template("mwan/config_css")) + + +mwan_member = m5:section(TypedSection, "member", translate("Members"), + translate("Members are profiles attaching a metric and weight to an MWAN interface<br />" .. + "Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />" .. + "Members may not share the same name as configured interfaces, policies or rules")) + mwan_member.addremove = true + mwan_member.dynamic = false + mwan_member.sectionhead = "Member" + mwan_member.sortable = true + mwan_member.template = "cbi/tblsection" + mwan_member.extedit = ds.build_url("admin", "network", "mwan", "configuration", "member", "%s") + function mwan_member.create(self, section) + TypedSection.create(self, section) + m5.uci:save("mwan3") + luci.http.redirect(ds.build_url("admin", "network", "mwan", "configuration", "member", section)) + end + + +interface = mwan_member:option(DummyValue, "interface", translate("Interface")) + interface.rawhtml = true + function interface.cfgvalue(self, s) + return self.map:get(s, "interface") or "—" + end + +metric = mwan_member:option(DummyValue, "metric", translate("Metric")) + metric.rawhtml = true + function metric.cfgvalue(self, s) + return self.map:get(s, "metric") or "1" + end + +weight = mwan_member:option(DummyValue, "weight", translate("Weight")) + weight.rawhtml = true + function weight.cfgvalue(self, s) + return self.map:get(s, "weight") or "1" + end + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/memberconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/memberconfig.lua new file mode 100644 index 000000000..181d22e06 --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/memberconfig.lua @@ -0,0 +1,47 @@ +-- ------ extra functions ------ -- + +function cbi_add_interface(field) + uci.cursor():foreach("mwan3", "interface", + function (section) + field:value(section[".name"]) + end + ) +end + +-- ------ member configuration ------ -- + +dsp = require "luci.dispatcher" +arg[1] = arg[1] or "" + + +m5 = Map("mwan3", translate("MWAN Member Configuration - ") .. arg[1]) + m5.redirect = dsp.build_url("admin", "network", "mwan", "configuration", "member") + + +mwan_member = m5:section(NamedSection, arg[1], "member", "") + mwan_member.addremove = false + mwan_member.dynamic = false + + +interface = mwan_member:option(Value, "interface", translate("Interface")) + cbi_add_interface(interface) + +metric = mwan_member:option(Value, "metric", translate("Metric"), + translate("Acceptable values: 1-1000. Defaults to 1 if not set")) + metric.datatype = "range(1, 1000)" + +weight = mwan_member:option(Value, "weight", translate("Weight"), + translate("Acceptable values: 1-1000. Defaults to 1 if not set")) + weight.datatype = "range(1, 1000)" + + +-- ------ currently configured interfaces ------ -- + +mwan_interface = m5:section(TypedSection, "interface", translate("Currently Configured Interfaces")) + mwan_interface.addremove = false + mwan_interface.dynamic = false + mwan_interface.sortable = false + mwan_interface.template = "cbi/tblsection" + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua new file mode 100644 index 000000000..e141d696a --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua @@ -0,0 +1,95 @@ +-- ------ extra functions ------ -- + +function policyCheck() -- check to see if any policy names exceed the maximum of 15 characters + uci.cursor():foreach("mwan3", "policy", + function (section) + if string.len(section[".name"]) > 15 then + nameTooLong = 1 + err_name_list = err_name_list .. section[".name"] .. " " + end + end + ) +end + +function policyWarn() -- display status and warning messages at the top of the page + if nameTooLong == 1 then + return "<font color=\"ff0000\"><strong>WARNING: Some policies have names exceeding the maximum of 15 characters!</strong></font>" + else + return "" + end +end + +-- ------ policy configuration ------ -- + +ds = require "luci.dispatcher" +sys = require "luci.sys" + +nameTooLong = 0 +err_name_list = " " +policyCheck() + + +m5 = Map("mwan3", translate("MWAN Policy Configuration"), + translate(policyWarn())) + m5:append(Template("mwan/config_css")) + + +mwan_policy = m5:section(TypedSection, "policy", translate("Policies"), + translate("Policies are profiles grouping one or more members controlling how MWAN distributes traffic<br />" .. + "Member interfaces with lower metrics are used first. Interfaces with the same metric load-balance<br />" .. + "Load-balanced member interfaces distribute more traffic out those with higher weights<br />" .. + "Names may contain characters A-Z, a-z, 0-9, _ and no spaces. Names must be 15 characters or less<br />" .. + "Policies may not share the same name as configured interfaces, members or rules")) + mwan_policy.addremove = true + mwan_policy.dynamic = false + mwan_policy.sectionhead = "Policy" + mwan_policy.sortable = true + mwan_policy.template = "cbi/tblsection" + mwan_policy.extedit = ds.build_url("admin", "network", "mwan", "configuration", "policy", "%s") + function mwan_policy.create(self, section) + TypedSection.create(self, section) + m5.uci:save("mwan3") + luci.http.redirect(ds.build_url("admin", "network", "mwan", "configuration", "policy", section)) + end + + +use_member = mwan_policy:option(DummyValue, "use_member", translate("Members assigned")) + use_member.rawhtml = true + function use_member.cfgvalue(self, s) + local memberConfig, memberList = self.map:get(s, "use_member"), "" + if memberConfig then + for k,v in pairs(memberConfig) do + memberList = memberList .. v .. "<br />" + end + return memberList + else + return "—" + end + + end + +last_resort = mwan_policy:option(DummyValue, "last_resort", translate("Last resort")) + last_resort.rawhtml = true + function last_resort.cfgvalue(self, s) + local action = self.map:get(s, "last_resort") + if action == "blackhole" then + return "blackhole (drop)" + elseif action == "default" then + return "default (use main routing table)" + else + return "unreachable (reject)" + end + end + +errors = mwan_policy:option(DummyValue, "errors", translate("Errors")) + errors.rawhtml = true + function errors.cfgvalue(self, s) + if not string.find(err_name_list, " " .. s .. " ") then + return "" + else + return "<span title=\"Name exceeds 15 characters\"><img src=\"/luci-static/resources/cbi/reset.gif\" alt=\"error\"></img></span>" + end + end + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policyconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policyconfig.lua new file mode 100644 index 000000000..f48a104c6 --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policyconfig.lua @@ -0,0 +1,65 @@ +-- ------ extra functions ------ -- + +function policyCheck() -- check to see if this policy's name exceed the maximum of 15 characters + policyNameLength = string.len(arg[1]) + if policyNameLength > 15 then + nameTooLong = 1 + end +end + +function policyWarn() -- display status and warning messages at the top of the page + if nameTooLong == 1 then + return "<font color=\"ff0000\"><strong>WARNING: this policy's name is " .. policyNameLength .. " characters exceeding the maximum of 15!</strong></font>" + else + return "" + end +end + +function cbiAddMember(field) + uci.cursor():foreach("mwan3", "member", + function (section) + field:value(section[".name"]) + end + ) +end + +-- ------ policy configuration ------ -- + +dsp = require "luci.dispatcher" +arg[1] = arg[1] or "" + +nameTooLong = 0 +policyCheck() + + +m5 = Map("mwan3", translate("MWAN Policy Configuration - " .. arg[1]), + translate(policyWarn())) + m5.redirect = dsp.build_url("admin", "network", "mwan", "configuration", "policy") + + +mwan_policy = m5:section(NamedSection, arg[1], "policy", "") + mwan_policy.addremove = false + mwan_policy.dynamic = false + + +use_member = mwan_policy:option(DynamicList, "use_member", translate("Member used")) + cbiAddMember(use_member) + +last_resort = mwan_policy:option(ListValue, "last_resort", translate("Last resort"), + translate("When all policy members are offline use this behavior for matched traffic")) + last_resort.default = "unreachable" + last_resort:value("unreachable", translate("unreachable (reject)")) + last_resort:value("blackhole", translate("blackhole (drop)")) + last_resort:value("default", translate("default (use main routing table)")) + + +-- ------ currently configured members ------ -- + +mwan_member = m5:section(TypedSection, "member", translate("Currently Configured Members")) + mwan_member.addremove = false + mwan_member.dynamic = false + mwan_member.sortable = false + mwan_member.template = "cbi/tblsection" + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua new file mode 100644 index 000000000..a22e01054 --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua @@ -0,0 +1,141 @@ +-- ------ extra functions ------ -- + +function ruleCheck() -- determine if rules needs a proper protocol configured + uci.cursor():foreach("mwan3", "rule", + function (section) + local sourcePort = ut.trim(sys.exec("uci -p /var/state get mwan3." .. section[".name"] .. ".src_port")) + local destPort = ut.trim(sys.exec("uci -p /var/state get mwan3." .. section[".name"] .. ".dest_port")) + if sourcePort ~= "" or destPort ~= "" then -- ports configured + local protocol = ut.trim(sys.exec("uci -p /var/state get mwan3." .. section[".name"] .. ".proto")) + if protocol == "" or protocol == "all" then -- no or improper protocol + error_protocol_list = error_protocol_list .. section[".name"] .. " " + end + end + end + ) +end + +function ruleWarn() -- display warning messages at the top of the page + if error_protocol_list ~= " " then + return "<font color=\"ff0000\"><strong>WARNING: some rules have a port configured with no or improper protocol specified! Please configure a specific protocol!</strong></font>" + else + return "" + end +end + +-- ------ rule configuration ------ -- + +dsp = require "luci.dispatcher" +sys = require "luci.sys" +ut = require "luci.util" + +error_protocol_list = " " +ruleCheck() + + +m5 = Map("mwan3", translate("MWAN Rule Configuration"), + translate(ruleWarn())) + m5:append(Template("mwan/config_css")) + + +mwan_rule = m5:section(TypedSection, "rule", translate("Traffic Rules"), + translate("Rules specify which traffic will use a particular MWAN policy based on IP address, port or protocol<br />" .. + "Rules are matched from top to bottom. Rules below a matching rule are ignored. Traffic not matching any rule is routed using the main routing table<br />" .. + "Traffic destined for known (other than default) networks is handled by the main routing table. Traffic matching a rule, but all WAN interfaces for that policy are down will be blackholed<br />" .. + "Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />" .. + "Rules may not share the same name as configured interfaces, members or policies")) + mwan_rule.addremove = true + mwan_rule.anonymous = false + mwan_rule.dynamic = false + mwan_rule.sectionhead = "Rule" + mwan_rule.sortable = true + mwan_rule.template = "cbi/tblsection" + mwan_rule.extedit = dsp.build_url("admin", "network", "mwan", "configuration", "rule", "%s") + function mwan_rule.create(self, section) + TypedSection.create(self, section) + m5.uci:save("mwan3") + luci.http.redirect(dsp.build_url("admin", "network", "mwan", "configuration", "rule", section)) + end + + +src_ip = mwan_rule:option(DummyValue, "src_ip", translate("Source address")) + src_ip.rawhtml = true + function src_ip.cfgvalue(self, s) + return self.map:get(s, "src_ip") or "—" + end + +src_port = mwan_rule:option(DummyValue, "src_port", translate("Source port")) + src_port.rawhtml = true + function src_port.cfgvalue(self, s) + return self.map:get(s, "src_port") or "—" + end + +dest_ip = mwan_rule:option(DummyValue, "dest_ip", translate("Destination address")) + dest_ip.rawhtml = true + function dest_ip.cfgvalue(self, s) + return self.map:get(s, "dest_ip") or "—" + end + +dest_port = mwan_rule:option(DummyValue, "dest_port", translate("Destination port")) + dest_port.rawhtml = true + function dest_port.cfgvalue(self, s) + return self.map:get(s, "dest_port") or "—" + end + +proto = mwan_rule:option(DummyValue, "proto", translate("Protocol")) + proto.rawhtml = true + function proto.cfgvalue(self, s) + return self.map:get(s, "proto") or "all" + end + +sticky = mwan_rule:option(DummyValue, "sticky", translate("Sticky")) + sticky.rawhtml = true + function sticky.cfgvalue(self, s) + if self.map:get(s, "sticky") == "1" then + stickied = 1 + return "Yes" + else + stickied = nil + return "No" + end + end + +timeout = mwan_rule:option(DummyValue, "timeout", translate("Sticky timeout")) + timeout.rawhtml = true + function timeout.cfgvalue(self, s) + if stickied then + local timeoutValue = self.map:get(s, "timeout") + if timeoutValue then + return timeoutValue .. "s" + else + return "600s" + end + else + return "—" + end + end + +ipset = mwan_rule:option(DummyValue, "ipset", translate("IPset")) + ipset.rawhtml = true + function ipset.cfgvalue(self, s) + return self.map:get(s, "ipset") or "—" + end + +use_policy = mwan_rule:option(DummyValue, "use_policy", translate("Policy assigned")) + use_policy.rawhtml = true + function use_policy.cfgvalue(self, s) + return self.map:get(s, "use_policy") or "—" + end + +errors = mwan_rule:option(DummyValue, "errors", translate("Errors")) + errors.rawhtml = true + function errors.cfgvalue(self, s) + if not string.find(error_protocol_list, " " .. s .. " ") then + return "" + else + return "<span title=\"No protocol specified\"><img src=\"/luci-static/resources/cbi/reset.gif\" alt=\"error\"></img></span>" + end + end + + +return m5 diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/ruleconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/ruleconfig.lua new file mode 100644 index 000000000..f7fb341e1 --- /dev/null +++ b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/ruleconfig.lua @@ -0,0 +1,113 @@ +-- ------ extra functions ------ -- + +function ruleCheck() -- determine if rule needs a protocol specified + local sourcePort = ut.trim(sys.exec("uci -p /var/state get mwan3." .. arg[1] .. ".src_port")) + local destPort = ut.trim(sys.exec("uci -p /var/state get mwan3." .. arg[1] .. ".dest_port")) + if sourcePort ~= "" or destPort ~= "" then -- ports configured + local protocol = ut.trim(sys.exec("uci -p /var/state get mwan3." .. arg[1] .. ".proto")) + if protocol == "" or protocol == "all" then -- no or improper protocol + error_protocol = 1 + end + end +end + +function ruleWarn() -- display warning message at the top of the page + if error_protocol == 1 then + return "<font color=\"ff0000\"><strong>WARNING: this rule is incorrectly configured with no or improper protocol specified! Please configure a specific protocol!</strong></font>" + else + return "" + end +end + +function cbiAddPolicy(field) + uci.cursor():foreach("mwan3", "policy", + function (section) + field:value(section[".name"]) + end + ) +end + +function cbiAddProtocol(field) + local protocols = ut.trim(sys.exec("cat /etc/protocols | grep ' # ' | awk '{print $1}' | grep -vw -e 'ip' -e 'tcp' -e 'udp' -e 'icmp' -e 'esp' | grep -v 'ipv6' | sort | tr '\n' ' '")) + for p in string.gmatch(protocols, "%S+") do + field:value(p) + end +end + +-- ------ rule configuration ------ -- + +dsp = require "luci.dispatcher" +sys = require "luci.sys" +ut = require "luci.util" +arg[1] = arg[1] or "" + +error_protocol = 0 +ruleCheck() + + +m5 = Map("mwan3", translate("MWAN Rule Configuration - ") .. arg[1], + translate(ruleWarn())) + m5.redirect = dsp.build_url("admin", "network", "mwan", "configuration", "rule") + + +mwan_rule = m5:section(NamedSection, arg[1], "rule", "") + mwan_rule.addremove = false + mwan_rule.dynamic = false + + +src_ip = mwan_rule:option(Value, "src_ip", translate("Source address"), + translate("Supports CIDR notation (eg \"192.168.100.0/24\") without quotes")) + src_ip.datatype = ipaddr + +src_port = mwan_rule:option(Value, "src_port", translate("Source port"), + translate("May be entered as a single or multiple port(s) (eg \"22\" or \"80,443\") or as a portrange (eg \"1024:2048\") without quotes")) + +dest_ip = mwan_rule:option(Value, "dest_ip", translate("Destination address"), + translate("Supports CIDR notation (eg \"192.168.100.0/24\") without quotes")) + dest_ip.datatype = ipaddr + +dest_port = mwan_rule:option(Value, "dest_port", translate("Destination port"), + translate("May be entered as a single or multiple port(s) (eg \"22\" or \"80,443\") or as a portrange (eg \"1024:2048\") without quotes")) + +proto = mwan_rule:option(Value, "proto", translate("Protocol"), + translate("View the contents of /etc/protocols for protocol descriptions")) + proto.default = "all" + proto.rmempty = false + proto:value("all") + proto:value("ip") + proto:value("tcp") + proto:value("udp") + proto:value("icmp") + proto:value("esp") + cbiAddProtocol(proto) + +sticky = mwan_rule:option(ListValue, "sticky", translate("Sticky"), + translate("Traffic from the same source IP address that previously matched this rule within the sticky timeout period will use the same WAN interface")) + sticky.default = "0" + sticky:value("1", translate("Yes")) + sticky:value("0", translate("No")) + +timeout = mwan_rule:option(Value, "timeout", translate("Sticky timeout"), + translate("Seconds. Acceptable values: 1-1000000. Defaults to 600 if not set")) + timeout.datatype = "range(1, 1000000)" + +ipset = mwan_rule:option(Value, "ipset", translate("IPset"), + translate("Name of IPset rule. Requires IPset rule in /etc/dnsmasq.conf (eg \"ipset=/youtube.com/youtube\")")) + +use_policy = mwan_rule:option(Value, "use_policy", translate("Policy assigned")) + cbiAddPolicy(use_policy) + use_policy:value("unreachable", translate("unreachable (reject)")) + use_policy:value("blackhole", translate("blackhole (drop)")) + use_policy:value("default", translate("default (use main routing table)")) + + +-- ------ currently configured policies ------ -- + +mwan_policy = m5:section(TypedSection, "policy", translate("Currently Configured Policies")) + mwan_policy.addremove = false + mwan_policy.dynamic = false + mwan_policy.sortable = false + mwan_policy.template = "cbi/tblsection" + + +return m5 |