--- /dev/null
+#
+# Copyright (C) 2006-2014 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-app-mwan3
+PKG_VERSION:=1.2
+PKG_RELEASE:=19
+PKG_MAINTAINER:=Aedan Renner <chipdankly@gmail.com>
+PKG_LICENSE:=GPLv2
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/luci-app-mwan3
+ SECTION:=LuCI
+ CATEGORY:=LuCI
+ SUBMENU:=3. Applications
+ TITLE:=LuCI support for MWAN3 Multiwan hotplug script with connection tracking support
+ DEPENDS:=+mwan3
+ PKGARCH:=all
+ MAINTAINER:=Aedan Renner <chipdankly@gmail.com>
+endef
+
+define Package/luci-app-mwan3/description
+ Hotplug script which makes configuration of multiple WAN interfaces simple and manageable. With loadbalancing/failover support for up to 250 wan interfaces, connection tracking and an easy to manage traffic ruleset.
+endef
+
+define Build/Compile
+endef
+
+define Package/luci-app-mwan3/install
+ $(CP) ./files/* $(1)
+endef
+
+$(eval $(call BuildPackage,luci-app-mwan3))
--- /dev/null
+#!/bin/sh
+
+# to enable this script uncomment the case loop at the bottom
+# to report MWAN3 status on interface up/down events modify the lines in the send_reportdata function
+
+send_alert()
+{
+ # $1 stores the mwan3 status information
+ # insert your code here to send the contents of $1
+ echo "$1"
+}
+
+gather_event_info()
+{
+ # create event information message
+ local EVENT_INFO="Interface [ "$INTERFACE" ($DEVICE) ] on router [ "$(uci get -p /var/state system.@system[0].hostname)" ] has triggered an [ "$ACTION" ] hotplug event on "$(date)""
+
+ # get current interface, policy and rule status
+ local CURRENT_STATUS="$(mwan3 status)"
+
+ # get last 50 mwan3 systemlog messages
+ local MWAN3_LOG="$(echo -e "Last 50 MWAN3 systemlog entries. Newest entries sorted at the top:\n$(logread | grep mwan3 | tail -n 50 | sed 'x;1!H;$!d;x')")"
+
+ # pass event info to send_alert function
+ send_alert "$(echo -e "$EVENT_INFO\n\n$CURRENT_STATUS\n\n$MWAN3_LOG")"
+}
+
+#case "$ACTION" in
+# ifup)
+# gather_event_info
+# ;;
+#
+# ifdown)
+# gather_event_info
+# ;;
+#esac
+
+exit 0
--- /dev/null
+#!/bin/sh
+
+# delete existing mwan3 ucitrack entry and add new entry
+uci -q batch <<-EOF >/dev/null
+ del ucitrack.@mwan3[-1]
+ add ucitrack mwan3
+ set ucitrack.@mwan3[-1].exec="/etc/init.d/mwan3 restart"
+ commit ucitrack
+EOF
+
+# make controller file addition take effect without system restart
+rm -rf /tmp/luci-indexcache /tmp/luci-modulecache
+
+exit 0
--- /dev/null
+module("luci.controller.mwan3", package.seeall)
+
+sys = require "luci.sys"
+ut = require "luci.util"
+
+function index()
+ if not nixio.fs.access("/etc/config/mwan3") then
+ return
+ end
+
+ entry({"admin", "network", "mwan3"},
+ alias("admin", "network", "mwan3", "overview"),
+ _("Load Balancing"), 600)
+
+ entry({"admin", "network", "mwan3", "overview"},
+ alias("admin", "network", "mwan3", "overview", "over_iface"),
+ _("Overview"), 10)
+ entry({"admin", "network", "mwan3", "overview", "over_iface"},
+ template("mwan3/mwan3_over_interface"))
+ entry({"admin", "network", "mwan3", "overview", "iface_status"},
+ call("mwan3_iface_status"))
+ entry({"admin", "network", "mwan3", "overview", "over_detail"},
+ template("mwan3/mwan3_over_detail"))
+ entry({"admin", "network", "mwan3", "overview", "detail_status"},
+ call("mwan3_detail_status"))
+
+ entry({"admin", "network", "mwan3", "configuration"},
+ alias("admin", "network", "mwan3", "configuration", "interface"),
+ _("Configuration"), 20)
+ entry({"admin", "network", "mwan3", "configuration", "interface"},
+ arcombine(cbi("mwan3/mwan3_interface"), cbi("mwan3/mwan3_interfaceconfig")),
+ _("Interfaces"), 10).leaf = true
+ entry({"admin", "network", "mwan3", "configuration", "member"},
+ arcombine(cbi("mwan3/mwan3_member"), cbi("mwan3/mwan3_memberconfig")),
+ _("Members"), 20).leaf = true
+ entry({"admin", "network", "mwan3", "configuration", "policy"},
+ arcombine(cbi("mwan3/mwan3_policy"), cbi("mwan3/mwan3_policyconfig")),
+ _("Policies"), 30).leaf = true
+ entry({"admin", "network", "mwan3", "configuration", "rule"},
+ arcombine(cbi("mwan3/mwan3_rule"), cbi("mwan3/mwan3_ruleconfig")),
+ _("Rules"), 40).leaf = true
+
+ entry({"admin", "network", "mwan3", "advanced"},
+ alias("admin", "network", "mwan3", "advanced", "hotplug"),
+ _("Advanced"), 100)
+ entry({"admin", "network", "mwan3", "advanced", "hotplug"},
+ form("mwan3/mwan3_adv_hotplug"))
+ entry({"admin", "network", "mwan3", "advanced", "mwan3"},
+ form("mwan3/mwan3_adv_mwan3"))
+ entry({"admin", "network", "mwan3", "advanced", "network"},
+ form("mwan3/mwan3_adv_network"))
+ entry({"admin", "network", "mwan3", "advanced", "diag"},
+ template("mwan3/mwan3_adv_diagnostics"))
+ entry({"admin", "network", "mwan3", "advanced", "diag_display"},
+ call("mwan3_diag_data"), nil).leaf = true
+ entry({"admin", "network", "mwan3", "advanced", "tshoot"},
+ template("mwan3/mwan3_adv_troubleshoot"))
+ entry({"admin", "network", "mwan3", "advanced", "tshoot_display"},
+ call("mwan3_tshoot_data"))
+end
+
+function mwan3_get_iface_status(rulenum, ifname)
+ if ut.trim(sys.exec("uci get -p /var/state mwan3." .. ifname .. ".enabled")) == "1" then
+ if ut.trim(sys.exec("ip route list table " .. rulenum)) ~= "" then
+ if ut.trim(sys.exec("uci get -p /var/state mwan3." .. ifname .. ".track_ip")) ~= "" then
+ return "on"
+ else
+ return "nm"
+ end
+ else
+ return "off"
+ end
+ else
+ return "ne"
+ end
+end
+
+function mwan3_get_iface()
+ local rulenum, str = 0, ""
+ uci.cursor():foreach("mwan3", "interface",
+ function (section)
+ rulenum = rulenum+1
+ str = str .. section[".name"] .. "[" .. mwan3_get_iface_status(rulenum, section[".name"]) .. "]"
+ end
+ )
+ return str
+end
+
+function mwan3_iface_status()
+ local ntm = require "luci.model.network".init()
+
+ local rv = { }
+
+ -- overview status
+ local statstr = mwan3_get_iface()
+ if statstr ~= "" then
+ rv.wans = { }
+ wansid = {}
+
+ for wanname, ifstat in string.gfind(statstr, "([^%[]+)%[([^%]]+)%]") do
+ local wanifname = ut.trim(sys.exec("uci get -p /var/state network." .. wanname .. ".ifname"))
+ if wanifname == "" then
+ wanifname = "X"
+ end
+ local wanlink = ntm:get_interface(wanifname)
+ wanlink = wanlink and wanlink:get_network()
+ wanlink = wanlink and wanlink:adminlink() or "#"
+ wansid[wanname] = #rv.wans + 1
+ rv.wans[wansid[wanname]] = { name = wanname, link = wanlink, ifname = wanifname, status = ifstat }
+ end
+ end
+
+ -- overview status log
+ local mwlg = ut.trim(sys.exec("logread | grep mwan3 | tail -n 50 | sed 'x;1!H;$!d;x'"))
+ if mwlg ~= "" then
+ rv.mwan3log = { }
+ mwlog = {}
+ mwlog[mwlg] = #rv.mwan3log + 1
+ rv.mwan3log[mwlog[mwlg]] = { mwanlog = mwlg }
+ end
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(rv)
+end
+
+function mwan3_detail_status()
+ local rv = { }
+
+ -- detailed mwan3 status
+ local dst = ut.trim(sys.exec("mwan3 status"))
+ if dst ~= "" then
+ rv.mwan3dst = { }
+ dstat = {}
+ dstat[dst] = #rv.mwan3dst + 1
+ rv.mwan3dst[dstat[dst]] = { detailstat = dst }
+ end
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(rv)
+end
+
+function mwan3_diag_data(iface, tool, alt)
+ function get_ifnum()
+ local num = 0
+ uci.cursor():foreach("mwan3", "interface",
+ function (section)
+ num = num+1
+ if section[".name"] == iface then
+ ifnum = num
+ end
+ end
+ )
+ end
+
+ local rv = { }
+
+ local res = ""
+ if tool == "service" then
+ os.execute("mwan3 " .. alt)
+ if alt == "restart" then
+ res = "MWAN3 restarted"
+ elseif alt == "stop" then
+ res = "MWAN3 stopped"
+ else
+ res = "MWAN3 started"
+ end
+ else
+ local ifdev = ut.trim(sys.exec("uci get -p /var/state network." .. iface .. ".ifname"))
+ if ifdev ~= "" then
+ if tool == "ping" then
+ local gateway = ut.trim(sys.exec("route -n | awk -F' ' '{ if ($8 == \"" .. ifdev .. "\" && $1 == \"0.0.0.0\") print $2 }'"))
+ if gateway ~= "" then
+ if alt == "gateway" then
+ local cmd = "ping -c 3 -W 2 -I " .. ifdev .. " " .. gateway
+ res = cmd .. "\n\n" .. sys.exec(cmd)
+ else
+ local str = ut.trim(sys.exec("uci get -p /var/state mwan3." .. iface .. ".track_ip"))
+ if str ~= "" then
+ for z in str:gmatch("[^ ]+") do
+ local cmd = "ping -c 3 -W 2 -I " .. ifdev .. " " .. z
+ res = res .. cmd .. "\n\n" .. sys.exec(cmd) .. "\n\n"
+ end
+ else
+ res = "No tracking IP addresses configured on " .. iface
+ end
+ end
+ else
+ res = "No default gateway for " .. iface .. " found. Default route does not exist or is configured incorrectly"
+ end
+ elseif tool == "rulechk" then
+ get_ifnum()
+ local rule1 = sys.exec("ip rule | grep $(echo $((" .. ifnum .. " + 1000)))")
+ local rule2 = sys.exec("ip rule | grep $(echo $((" .. ifnum .. " + 2000)))")
+ if rule1 ~= "" and rule2 ~= "" then
+ res = "All required interface IP rules found:\n\n" .. rule1 .. rule2
+ elseif rule1 ~= "" or rule2 ~= "" then
+ res = "Missing 1 of the 2 required interface IP rules\n\n\nRules found:\n\n" .. rule1 .. rule2
+ else
+ res = "Missing both of the required interface IP rules"
+ end
+ elseif tool == "routechk" then
+ get_ifnum()
+ local table = sys.exec("ip route list table " .. ifnum)
+ if table ~= "" then
+ res = "Interface routing table " .. ifnum .. " was found:\n\n" .. table
+ else
+ res = "Missing required interface routing table " .. ifnum
+ end
+ elseif tool == "hotplug" then
+ if alt == "ifup" then
+ os.execute("mwan3 ifup " .. iface)
+ res = "Hotplug ifup sent to interface " .. iface .. "..."
+ else
+ os.execute("mwan3 ifdown " .. iface)
+ res = "Hotplug ifdown sent to interface " .. iface .. "..."
+ end
+ end
+ else
+ res = "Unable to perform diagnostic tests on " .. iface .. ". There is no physical or virtual device associated with this interface"
+ end
+ end
+ if res ~= "" then
+ res = ut.trim(res)
+ rv.diagres = { }
+ dres = {}
+ dres[res] = #rv.diagres + 1
+ rv.diagres[dres[res]] = { diagresult = res }
+ end
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(rv)
+end
+
+function mwan3_tshoot_data()
+ local rv = { }
+
+ -- software versions
+ local wrtrelease = ut.trim(luci.version.distversion)
+ if wrtrelease ~= "" then
+ wrtrelease = "OpenWrt - " .. wrtrelease
+ else
+ wrtrelease = "OpenWrt - unknown"
+ end
+ local lucirelease = ut.trim(luci.version.luciversion)
+ if lucirelease ~= "" then
+ lucirelease = "\nLuCI - " .. lucirelease
+ else
+ lucirelease = "\nLuCI - unknown"
+ end
+ local mwan3version = ut.trim(sys.exec("opkg info mwan3 | grep Version | awk -F' ' '{ print $2 }'"))
+ if mwan3version ~= "" then
+ mwan3version = "\n\nmwan3 - " .. mwan3version
+ else
+ mwan3version = "\nmwan3 - unknown"
+ end
+ local mwan3lversion = ut.trim(sys.exec("opkg info luci-app-mwan3 | grep Version | awk -F' ' '{ print $2 }'"))
+ if mwan3lversion ~= "" then
+ mwan3lversion = "\nluci-app-mwan3 - " .. mwan3lversion
+ else
+ mwan3lversion = "\nluci-app-mwan3 - unknown"
+ end
+ local softrev = wrtrelease .. lucirelease .. mwan3version .. mwan3lversion
+ rv.mw3ver = { }
+ mwv = {}
+ mwv[softrev] = #rv.mw3ver + 1
+ rv.mw3ver[mwv[softrev]] = { mwan3v = softrev }
+
+ -- mwan3 config
+ local mwcg = ut.trim(sys.exec("cat /etc/config/mwan3"))
+ if mwcg == "" then
+ mwcg = "No data found"
+ end
+ rv.mwan3config = { }
+ mwan3cfg = {}
+ mwan3cfg[mwcg] = #rv.mwan3config + 1
+ rv.mwan3config[mwan3cfg[mwcg]] = { mwn3cfg = mwcg }
+
+ -- network config
+ local netcg = ut.trim(sys.exec("cat /etc/config/network | sed -e 's/.*username.*/ USERNAME HIDDEN/' -e 's/.*password.*/ PASSWORD HIDDEN/'"))
+ if netcg == "" then
+ netcg = "No data found"
+ end
+ rv.netconfig = { }
+ ncfg = {}
+ ncfg[netcg] = #rv.netconfig + 1
+ rv.netconfig[ncfg[netcg]] = { netcfg = netcg }
+
+ -- ifconfig
+ local ifcg = ut.trim(sys.exec("ifconfig"))
+ if ifcg == "" then
+ ifcg = "No data found"
+ end
+ rv.ifconfig = { }
+ icfg = {}
+ icfg[ifcg] = #rv.ifconfig + 1
+ rv.ifconfig[icfg[ifcg]] = { ifcfg = ifcg }
+
+ -- route -n
+ local routeshow = ut.trim(sys.exec("route -n"))
+ if routeshow == "" then
+ routeshow = "No data found"
+ end
+ rv.rtshow = { }
+ rshw = {}
+ rshw[routeshow] = #rv.rtshow + 1
+ rv.rtshow[rshw[routeshow]] = { iprtshow = routeshow }
+
+ -- ip rule show
+ local ipr = ut.trim(sys.exec("ip rule show"))
+ if ipr == "" then
+ ipr = "No data found"
+ end
+ rv.iprule = { }
+ ipruleid = {}
+ ipruleid[ipr] = #rv.iprule + 1
+ rv.iprule[ipruleid[ipr]] = { rule = ipr }
+
+ -- ip route list table 1-250
+ local routelisting, rlstr = ut.trim(sys.exec("ip rule | sed 's/://g' | awk -F' ' '$1>=2001 && $1<=2250' | awk -F' ' '{ print $NF }'")), ""
+ if routelisting ~= "" then
+ for line in routelisting:gmatch("[^\r\n]+") do
+ rlstr = rlstr .. line .. "\n" .. sys.exec("ip route list table " .. line)
+ end
+ rlstr = ut.trim(rlstr)
+ else
+ rlstr = "No data found"
+ end
+ rv.routelist = { }
+ rtlist = {}
+ rtlist[rlstr] = #rv.routelist + 1
+ rv.routelist[rtlist[rlstr]] = { iprtlist = rlstr }
+
+ -- default firewall output policy
+ local defout = ut.trim(sys.exec("uci get -p /var/state firewall.@defaults[0].output"))
+ if defout == "" then
+ defout = "No data found"
+ end
+ rv.fidef = { }
+ fwdf = {}
+ fwdf[defout] = #rv.fidef + 1
+ rv.fidef[fwdf[defout]] = { firedef = defout }
+
+ -- iptables
+ local iptbl = ut.trim(sys.exec("iptables -L -t mangle -v -n"))
+ if iptbl == "" then
+ iptbl = "No data found"
+ end
+ rv.iptables = { }
+ tables = {}
+ tables[iptbl] = #rv.iptables + 1
+ rv.iptables[tables[iptbl]] = { iptbls = iptbl }
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(rv)
+end
--- /dev/null
+-- ------ hotplug script configuration ------ --
+
+fs = require "nixio.fs"
+sys = require "luci.sys"
+ut = require "luci.util"
+
+script = "/etc/hotplug.d/iface/16-mwan3custom"
+scriptbak = "/etc/hotplug.d/iface/16-mwan3custombak"
+
+if luci.http.formvalue("cbid.luci.1._restorebak") then -- restore button has been clicked
+ luci.http.redirect(luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug") .. "?restore=yes")
+elseif luci.http.formvalue("restore") == "yes" then -- restore script from backup
+ os.execute("cp -f " .. scriptbak .. " " .. script)
+end
+
+
+m5 = SimpleForm("luci", nil)
+ m5:append(Template("mwan3/mwan3_adv_hotplug")) -- highlight current tab
+
+f = m5:section(SimpleSection, nil,
+ translate("This section allows you to modify the contents of /etc/hotplug.d/iface/16-mwan3custom<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 " .. scriptbak .. " " .. 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
--- /dev/null
+-- ------ mwan3 configuration ------ --
+
+ut = require "luci.util"
+
+mwan3file = "/etc/config/mwan3"
+
+
+m5 = SimpleForm("luci", nil)
+ m5:append(Template("mwan3/mwan3_adv_mwan3")) -- 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(mwan3file) or ""
+ end
+
+ function t.write(self, section, data) -- format and write new data to script
+ return nixio.fs.writefile(mwan3file, "\n" .. ut.trim(data:gsub("\r\n", "\n")) .. "\n")
+ end
+
+ function f.handle(self, state, data)
+ return true
+ end
+
+
+return m5
--- /dev/null
+-- ------ network configuration ------ --
+
+ut = require "luci.util"
+
+netfile = "/etc/config/network"
+
+
+m5 = SimpleForm("networkconf", nil)
+ m5:append(Template("mwan3/mwan3_adv_network")) -- 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(netfile) or ""
+ end
+
+ function t.write(self, section, data) -- format and write new data to script
+ return nixio.fs.writefile(netfile, "\n" .. ut.trim(data:gsub("\r\n", "\n")) .. "\n")
+ end
+
+ function f.handle(self, state, data)
+ return true
+ end
+
+
+return m5
--- /dev/null
+-- ------ extra functions ------ --
+
+function iface_check() -- find issues with too many interfaces, reliability and metric
+ uci.cursor():foreach("mwan3", "interface",
+ function (section)
+ local ifname = section[".name"]
+ ifnum = ifnum+1 -- count number of mwan3 interfaces configured
+ -- create list of metrics for none and duplicate checking
+ local metlkp = ut.trim(sys.exec("uci get -p /var/state network." .. ifname .. ".metric"))
+ if metlkp == "" then
+ err_found = 1
+ err_nomet_list = err_nomet_list .. ifname .. " "
+ else
+ metric_list = metric_list .. ifname .. " " .. metlkp .. "\n"
+ end
+ -- check if any interfaces have a higher reliability requirement than tracking IPs configured
+ local tipnum = tonumber(ut.trim(sys.exec("echo $(uci get -p /var/state mwan3." .. ifname .. ".track_ip) | wc -w")))
+ if tipnum > 0 then
+ local relnum = tonumber(ut.trim(sys.exec("uci get -p /var/state mwan3." .. ifname .. ".reliability")))
+ if relnum and relnum > tipnum then
+ err_found = 1
+ err_rel_list = err_rel_list .. ifname .. " "
+ 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 get -p /var/state network." .. ifname)) == "interface" then
+ local ifdev = ut.trim(sys.exec("uci get -p /var/state network." .. ifname .. ".ifname"))
+ if ifdev == "uci: Entry not found" or ifdev == "" then
+ err_found = 1
+ err_netcfg_list = err_netcfg_list .. ifname .. " "
+ err_route_list = err_route_list .. ifname .. " "
+ else
+ local rtcheck = ut.trim(sys.exec("route -n | awk -F' ' '{ if ($8 == \"" .. ifdev .. "\" && $1 == \"0.0.0.0\") print $1 }'"))
+ if rtcheck == "" then
+ err_found = 1
+ err_route_list = err_route_list .. ifname .. " "
+ end
+ end
+ else
+ err_found = 1
+ err_netcfg_list = err_netcfg_list .. ifname .. " "
+ err_route_list = err_route_list .. ifname .. " "
+ end
+ end
+ )
+ -- check if any interfaces have duplicate metrics
+ local metric_dupnums = sys.exec("echo '" .. metric_list .. "' | awk -F' ' '{ print $2 }' | uniq -d")
+ if metric_dupnums ~= "" then
+ err_found = 1
+ local metric_dupes = ""
+ for line in metric_dupnums:gmatch("[^\r\n]+") do
+ metric_dupes = sys.exec("echo '" .. metric_list .. "' | grep '" .. line .. "' | awk -F' ' '{ print $1 }'")
+ err_dupmet_list = err_dupmet_list .. metric_dupes
+ end
+ err_dupmet_list = sys.exec("echo '" .. err_dupmet_list .. "' | tr '\n' ' '")
+ end
+end
+
+function iface_warn() -- display status and warning messages at the top of the page
+ local warns = ""
+ if ifnum <= 250 then
+ warns = "<strong>There are currently " .. ifnum .. " of 250 supported interfaces configured</strong>"
+ else
+ warns = "<font color=\"ff0000\"><strong>WARNING: " .. ifnum .. " interfaces are configured exceeding the maximum of 250!</strong></font>"
+ end
+ if err_rel_list ~= " " then
+ warns = warns .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have a higher reliability requirement than there are tracking IP addresses!</strong></font>"
+ end
+ if err_route_list ~= " " then
+ warns = warns .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have no default route in the main routing table!</strong></font>"
+ end
+ if err_netcfg_list ~= " " then
+ warns = warns .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces are configured incorrectly or not at all in /etc/config/network!</strong></font>"
+ end
+ if err_nomet_list ~= " " then
+ warns = warns .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have no metric configured in /etc/config/network!</strong></font>"
+ end
+ if err_dupmet_list ~= " " then
+ warns = warns .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some 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"
+
+ifnum = 0
+metric_list = ""
+err_found = 0
+err_dupmet_list = " "
+err_netcfg_list = " "
+err_nomet_list = " "
+err_rel_list = " "
+err_route_list = " "
+iface_check()
+
+
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Interface Configuration"),
+ translate(iface_warn()))
+ m5:append(Template("mwan3/mwan3_config_css"))
+
+
+mwan_interface = m5:section(TypedSection, "interface", translate("Interfaces"),
+ translate("MWAN3 supports up to 250 physical and/or logical interfaces<br />" ..
+ "MWAN3 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", "mwan3", "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", "mwan3", "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)
+ local str = ""
+ tracked = self.map:get(s, "track_ip")
+ if tracked then
+ for k,v in pairs(tracked) do
+ str = str .. v .. "<br />"
+ end
+ return str
+ 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 tcheck = self.map:get(s, "timeout")
+ if tcheck then
+ return tcheck .. "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 icheck = self.map:get(s, "interval")
+ if icheck then
+ return icheck .. "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 metcheck = sys.exec("uci get -p /var/state network." .. s .. ".metric")
+ if metcheck ~= "" then
+ return metcheck
+ else
+ return "—"
+ end
+ end
+
+errors = mwan_interface:option(DummyValue, "errors", translate("Errors"))
+ errors.rawhtml = true
+ function errors.cfgvalue(self, s)
+ if err_found == 1 then
+ local mouseover, linebrk = "", ""
+ if string.find(err_rel_list, " " .. s .. " ") then
+ mouseover = "Higher reliability requirement than there are tracking IP addresses"
+ linebrk = " "
+ end
+ if string.find(err_route_list, " " .. s .. " ") then
+ mouseover = mouseover .. linebrk .. "No default route in the main routing table"
+ linebrk = " "
+ end
+ if string.find(err_netcfg_list, " " .. s .. " ") then
+ mouseover = mouseover .. linebrk .. "Configured incorrectly or not at all in /etc/config/network"
+ linebrk = " "
+ end
+ if string.find(err_nomet_list, " " .. s .. " ") then
+ mouseover = mouseover .. linebrk .. "No metric configured in /etc/config/network"
+ linebrk = " "
+ end
+ if string.find(err_dupmet_list, " " .. s .. " ") then
+ mouseover = mouseover .. linebrk .. "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
--- /dev/null
+-- ------ extra functions ------ --
+
+function iface_check()
+ metcheck = ut.trim(sys.exec("uci get -p /var/state network." .. arg[1] .. ".metric"))
+ if metcheck == "" then -- no metric
+ err_nomet = 1
+ else -- if metric exists create list of interface metrics to compare against for duplicates
+ uci.cursor():foreach("mwan3", "interface",
+ function (section)
+ local metlkp = ut.trim(sys.exec("uci get -p /var/state network." .. section[".name"] .. ".metric"))
+ metric_list = metric_list .. section[".name"] .. " " .. metlkp .. "\n"
+ end
+ )
+ -- compare metric against list
+ local metric_dupnums, metric_dupes = sys.exec("echo '" .. metric_list .. "' | awk -F' ' '{ print $2 }' | uniq -d"), ""
+ for line in metric_dupnums:gmatch("[^\r\n]+") do
+ metric_dupes = sys.exec("echo '" .. metric_list .. "' | grep '" .. line .. "' | awk -F' ' '{ print $1 }'")
+ err_dupmet_list = err_dupmet_list .. metric_dupes
+ end
+ if sys.exec("echo '" .. err_dupmet_list .. "' | grep -w " .. arg[1]) ~= "" then
+ err_dupmet = 1
+ end
+ end
+ -- check if this interface has a higher reliability requirement than track IPs configured
+ local tipnum = tonumber(ut.trim(sys.exec("echo $(uci get -p /var/state mwan3." .. arg[1] .. ".track_ip) | wc -w")))
+ if tipnum > 0 then
+ local relnum = tonumber(ut.trim(sys.exec("uci get -p /var/state mwan3." .. arg[1] .. ".reliability")))
+ if relnum and relnum > tipnum then
+ err_reliability = 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 get -p /var/state network." .. arg[1])) == "interface" then
+ local ifdev = ut.trim(sys.exec("uci get -p /var/state network." .. arg[1] .. ".ifname"))
+ if ifdev == "uci: Entry not found" or ifdev == "" then
+ err_netcfg = 1
+ err_route = 1
+ else
+ local rtcheck = ut.trim(sys.exec("route -n | awk -F' ' '{ if ($8 == \"" .. ifdev .. "\" && $1 == \"0.0.0.0\") print $1 }'"))
+ if rtcheck == "" then
+ err_route = 1
+ end
+ end
+ else
+ err_netcfg = 1
+ err_route = 1
+ end
+end
+
+function iface_warn() -- display warning messages at the top of the page
+ local warns, linebrk = "", ""
+ if err_reliability == 1 then
+ warns = "<font color=\"ff0000\"><strong>WARNING: this interface has a higher reliability requirement than there are tracking IP addresses!</strong></font>"
+ linebrk = "<br /><br />"
+ end
+ if err_route == 1 then
+ warns = warns .. linebrk .. "<font color=\"ff0000\"><strong>WARNING: this interface has no default route in the main routing table!</strong></font>"
+ linebrk = "<br /><br />"
+ end
+ if err_netcfg == 1 then
+ warns = warns .. linebrk .. "<font color=\"ff0000\"><strong>WARNING: this interface is configured incorrectly or not at all in /etc/config/network!</strong></font>"
+ linebrk = "<br /><br />"
+ end
+ if err_nomet == 1 then
+ warns = warns .. linebrk .. "<font color=\"ff0000\"><strong>WARNING: this interface has no metric configured in /etc/config/network!</strong></font>"
+ elseif err_dupmet == 1 then
+ warns = warns .. linebrk .. "<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 ""
+
+metcheck = ""
+metric_list = ""
+err_dupmet_list = ""
+err_rel_list = ""
+err_nomet = 0
+err_dupmet = 0
+err_route = 0
+err_netcfg = 0
+err_reliability = 0
+iface_check()
+
+
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Interface Configuration - " .. arg[1]),
+ translate(iface_warn()))
+ m5.redirect = dsp.build_url("admin", "network", "mwan3", "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 err_nomet == 0 then
+ return metcheck
+ else
+ return "—"
+ end
+ end
+
+
+return m5
--- /dev/null
+-- ------ member configuration ------ --
+
+ds = require "luci.dispatcher"
+
+
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Member Configuration"))
+ m5:append(Template("mwan3/mwan3_config_css"))
+
+
+mwan_member = m5:section(TypedSection, "member", translate("Members"),
+ translate("Members are profiles attaching a metric and weight to an MWAN3 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", "mwan3", "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", "mwan3", "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
--- /dev/null
+-- ------ 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("MWAN3 Multi-WAN Member Configuration - ") .. arg[1])
+ m5.redirect = dsp.build_url("admin", "network", "mwan3", "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
--- /dev/null
+-- ------ extra functions ------ --
+
+function policy_check() -- 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
+ toolong = 1
+ err_name_list = err_name_list .. section[".name"] .. " "
+ end
+ end
+ )
+end
+
+function policy_warn() -- display status and warning messages at the top of the page
+ if toolong == 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"
+
+toolong = 0
+err_name_list = " "
+policy_check()
+
+
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Policy Configuration"),
+ translate(policy_warn()))
+ m5:append(Template("mwan3/mwan3_config_css"))
+
+
+mwan_policy = m5:section(TypedSection, "policy", translate("Policies"),
+ translate("Policies are profiles grouping one or more members controlling how MWAN3 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", "mwan3", "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", "mwan3", "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 tab, str = self.map:get(s, "use_member"), ""
+ if tab then
+ for k,v in pairs(tab) do
+ str = str .. v .. "<br />"
+ end
+ return str
+ else
+ return "—"
+ 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
--- /dev/null
+-- ------ extra functions ------ --
+
+function policy_check() -- check to see if this policy's name exceed the maximum of 15 characters
+ polchar = string.len(arg[1])
+ if polchar > 15 then
+ toolong = 1
+ end
+end
+
+function policy_warn() -- display status and warning messages at the top of the page
+ if toolong == 1 then
+ return "<font color=\"ff0000\"><strong>WARNING: this policy's name is " .. polchar .. " characters exceeding the maximum of 15!</strong></font>"
+ else
+ return ""
+ end
+end
+
+function cbi_add_member(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 ""
+
+toolong = 0
+policy_check()
+
+
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Policy Configuration - " .. arg[1]),
+ translate(policy_warn()))
+ m5.redirect = dsp.build_url("admin", "network", "mwan3", "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"))
+ cbi_add_member(use_member)
+
+
+-- ------ 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
--- /dev/null
+-- ------ extra functions ------ --
+
+function rule_check() -- determine if rules needs a proper protocol configured
+ uci.cursor():foreach("mwan3", "rule",
+ function (section)
+ local sport = ut.trim(sys.exec("uci get -p /var/state mwan3." .. section[".name"] .. ".src_port"))
+ local dport = ut.trim(sys.exec("uci get -p /var/state mwan3." .. section[".name"] .. ".dest_port"))
+ if sport ~= "" or dport ~= "" then -- ports configured
+ local proto = ut.trim(sys.exec("uci get -p /var/state mwan3." .. section[".name"] .. ".proto"))
+ if proto == "" or proto == "all" then -- no or improper protocol
+ err_proto_list = err_proto_list .. section[".name"] .. " "
+ end
+ end
+ end
+ )
+end
+
+function rule_warn() -- display warning messages at the top of the page
+ if err_proto_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"
+
+err_proto = 0
+err_proto_list = " "
+rule_check()
+
+
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Traffic Rule Configuration"),
+ translate(rule_warn()))
+ m5:append(Template("mwan3/mwan3_config_css"))
+
+
+mwan_rule = m5:section(TypedSection, "rule", translate("Traffic Rules"),
+ translate("Rules specify which traffic will use a particular MWAN3 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", "mwan3", "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", "mwan3", "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
+
+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(err_proto_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
--- /dev/null
+-- ------ extra functions ------ --
+
+function rule_check() -- determine if rule needs a protocol specified
+ local sport = ut.trim(sys.exec("uci get -p /var/state mwan3." .. arg[1] .. ".src_port"))
+ local dport = ut.trim(sys.exec("uci get -p /var/state mwan3." .. arg[1] .. ".dest_port"))
+ if sport ~= "" or dport ~= "" then -- ports configured
+ local proto = ut.trim(sys.exec("uci get -p /var/state mwan3." .. arg[1] .. ".proto"))
+ if proto == "" or proto == "all" then -- no or improper protocol
+ err_proto = 1
+ end
+ end
+end
+
+function rule_warn() -- display warning message at the top of the page
+ if err_proto == 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 cbi_add_policy(field)
+ uci.cursor():foreach("mwan3", "policy",
+ function (section)
+ field:value(section[".name"])
+ end
+ )
+end
+
+function cbi_add_protocol(field)
+ local protos = ut.trim(sys.exec("cat /etc/protocols | grep ' # ' | awk -F' ' '{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(protos, "%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 ""
+
+err_proto = 0
+rule_check()
+
+
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Rule Configuration - ") .. arg[1],
+ translate(rule_warn()))
+ m5.redirect = dsp.build_url("admin", "network", "mwan3", "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")
+ cbi_add_protocol(proto)
+
+use_policy = mwan_rule:option(Value, "use_policy", translate("Policy assigned"))
+ cbi_add_policy(use_policy)
+ use_policy:value("unreachable")
+ use_policy:value("default")
+
+
+-- ------ 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
--- /dev/null
+<%+mwan3/mwan3_status%>
--- /dev/null
+<%+header%>
+
+<ul class="cbi-tabmenu">
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug")%>"><%:Hotplug Script%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/mwan3")%>"><%:MWAN3 Config%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/network")%>"><%:Network Config%></a></li>
+ <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/diag")%>"><%:Diagnostics%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/tshoot")%>"><%:Troubleshooting%></a></li>
+</ul>
+
+<%
+ local uci = require "luci.model.uci"
+
+ str = ""
+ uci.cursor():foreach("mwan3", "interface",
+ function (section)
+ str = str .. section[".name"] .. " "
+ end
+ )
+%>
+
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+<script type="text/javascript">//<![CDATA[
+ var stxhr = new XHR();
+
+ function update_status(tool, alt)
+ {
+ var iface = document.getElementById('mwan3iface').value;
+ var output = document.getElementById('diag_output');
+
+ if (tool == "service")
+ {
+ output.innerHTML =
+ '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="padding: 20px; vertical-align: middle;" /> ' +
+ "Waiting for MWAN3 to " + alt + "..."
+ ;
+ }
+ else
+ {
+ output.innerHTML =
+ '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="padding: 20px; vertical-align: middle;" /> ' +
+ "Waiting for diagnostic results..."
+ ;
+ }
+
+ output.parentNode.style.display = 'block';
+ output.style.display = 'inline';
+
+ stxhr.get('<%=luci.dispatcher.build_url("admin", "network", "mwan3", "advanced")%>/diag_display' + '/' + iface + '/' + tool + '/' + alt, null,
+ function(x, st)
+ {
+ if (st.diagres)
+ {
+ output.innerHTML = String.format('<pre id="diag_output_css">%h</pre>', st.diagres[0].diagresult);
+ }
+ else
+ {
+ var temp = '';
+ var ncint = 'No diagnostic results returned';
+ temp = String.format(
+ '<pre id="diag_output_css"><strong>%s</strong></pre>',
+ ncint
+ );
+ output.innerHTML = temp;
+ }
+ }
+ );
+ }
+//]]></script>
+
+<div id="mwan3_diagnostics" class="cbi-map">
+ <fieldset id="diag_select" class="cbi-section">
+ <legend><%:MWAN3 Interface Diagnostics%></legend>
+ <select id="mwan3iface">
+ <% for z in str:gmatch("[^ ]+") do -%><option value="<%=z%>"><%=z%></option><%- end %>
+ </select>
+ <div id="buttoncss">
+ <input type="button" value="<%:Ping default gateway%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'gateway')" />
+ <input type="button" value="<%:Ping tracking IP%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'track_ip')" />
+ <input type="button" value="<%:Check IP rules%>" class="cbi-button cbi-button-apply" onclick="update_status('rulechk', null)" />
+ <input type="button" value="<%:Check routing table%>" class="cbi-button cbi-button-apply" onclick="update_status('routechk', null)" />
+ <input type="button" value="<%:Hotplug ifup%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifup')" />
+ <input type="button" value="<%:Hotplug ifdown%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifdown')" />
+ </div>
+ </fieldset>
+ <fieldset id="diag_select" class="cbi-section">
+ <legend><%:MWAN3 Service Control%></legend>
+ <div id="buttoncss">
+ <input type="button" value="<%:Restart MWAN3%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'restart')" />
+ <input type="button" value="<%:Stop MWAN3%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'stop')" />
+ <input type="button" value="<%:Start MWAN3%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'start')" />
+ </div>
+ </fieldset>
+ <fieldset class="cbi-section" style="display:none">
+ <legend><%:Diagnostic Results%></legend>
+ <div id="diag_output"></div>
+ </fieldset>
+</div>
+
+<style type="text/css">
+ .container { /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
+ max-width: none;
+ margin-left: 30px;
+ padding-right: 30px;
+ width: auto;
+ }
+ #mwan3_diagnostics {
+ background-color: #FFFFFF;
+ border: 1px dotted #555555;
+ padding: 20px;
+ }
+ #diag_select {
+ padding: 12px 20px 20px 20px;
+ }
+ #mwan3iface {
+ float: left;
+ margin: 8px 20px 0px 0px;
+ }
+ #buttoncss {
+ display: table;
+ float: left;
+ text-align: left;
+ }
+ .cbi-button {
+ margin: 8px 20px 0px 0px;
+ min-width: 153px;
+ }
+ #diag_output_css {
+ padding: 20px;
+ text-align: left;
+ }
+</style>
+
+<%+footer%>
--- /dev/null
+<ul class="cbi-tabmenu">
+ <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug")%>"><%:Hotplug Script%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/mwan3")%>"><%:MWAN3 Config%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/network")%>"><%:Network Config%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/diag")%>"><%:Diagnostics%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/tshoot")%>"><%:Troubleshooting%></a></li>
+</ul>
+
+<style type="text/css">
+ .container { /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
+ max-width: none;
+ margin: 0px 0px 0px 30px;
+ padding-right: 30px;
+ width: auto;
+ }
+ .cbi-section-node {
+ margin-top: 20px;
+ }
+ .cbi-section {
+ border: 1px dotted #555555;
+ padding: 20px;
+ }
+</style>
--- /dev/null
+<ul class="cbi-tabmenu">
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug")%>"><%:Hotplug Script%></a></li>
+ <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/mwan3")%>"><%:MWAN3 Config%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/network")%>"><%:Network Config%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/diag")%>"><%:Diagnostics%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/tshoot")%>"><%:Troubleshooting%></a></li>
+</ul>
+
+<style type="text/css">
+ .container { /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
+ max-width: none;
+ margin: 0px 0px 0px 30px;
+ padding-right: 30px;
+ width: auto;
+ }
+ .cbi-section-node {
+ margin-top: 20px;
+ }
+ .cbi-section {
+ border: 1px dotted #555555;
+ padding: 20px;
+ }
+</style>
--- /dev/null
+<ul class="cbi-tabmenu">
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug")%>"><%:Hotplug Script%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/mwan3")%>"><%:MWAN3 Config%></a></li>
+ <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/network")%>"><%:Network Config%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/diag")%>"><%:Diagnostics%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/tshoot")%>"><%:Troubleshooting%></a></li>
+</ul>
+
+<style type="text/css">
+ .container { /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
+ max-width: none;
+ margin: 0px 0px 0px 30px;
+ padding-right: 30px;
+ width: auto;
+ }
+ .cbi-section-node {
+ margin-top: 20px;
+ }
+ .cbi-section {
+ border: 1px dotted #555555;
+ padding: 20px;
+ }
+</style>
--- /dev/null
+<%+header%>
+
+<ul class="cbi-tabmenu">
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug")%>"><%:Hotplug Script%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/mwan3")%>"><%:MWAN3 Config%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/network")%>"><%:Network Config%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/diag")%>"><%:Diagnostics%></a></li>
+ <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/tshoot")%>"><%:Troubleshooting%></a></li>
+</ul>
+
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+<script type="text/javascript">//<![CDATA[
+ XHR.poll(15, '<%=luci.dispatcher.build_url("admin", "network", "mwan3", "advanced", "tshoot_display")%>', null,
+ function(x, st)
+ {
+ var tx = document.getElementById('mwan3_tshoot_text');
+ if (st.mw3ver)
+ {
+ var temp = '';
+ var mwanvers = 'Software versions : <br /><br />';
+ var mwan3cnfg = '<br /><br />Output of "cat /etc/config/mwan3" : <br /><br />';
+ var netcnfg = '<br /><br />Output of "cat /etc/config/network" : <br /><br />';
+ var ifcnfg = '<br /><br />Output of "ifconfig" : <br /><br />';
+ var iproute = '<br /><br />Output of "route -n" : <br /><br />';
+ var iprulesh = '<br /><br />Output of "ip rule show" : <br /><br />';
+ var routelisttbl = '<br /><br />Output of "ip route list table 1-250" : <br /><br />';
+ var firewalldef = '<br /><br />Firewall default output policy (must be ACCEPT) : <br /><br />';
+ var iptable = '<br /><br />Output of "iptables -L -t mangle -v -n" : <br /><br />';
+
+ temp = String.format(
+ '<pre><span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s</pre>',
+ mwanvers, st.mw3ver[0].mwan3v, mwan3cnfg, st.mwan3config[0].mwn3cfg, netcnfg, st.netconfig[0].netcfg, ifcnfg, st.ifconfig[0].ifcfg, iproute, st.rtshow[0].iprtshow, iprulesh, st.iprule[0].rule, routelisttbl, st.routelist[0].iprtlist, firewalldef, st.fidef[0].firedef, iptable, st.iptables[0].iptbls
+ );
+ tx.innerHTML = temp;
+ }
+ else
+ {
+ var temp = '';
+ var terror = 'Error collecting troubleshooting information';
+ temp = String.format(
+ '<strong>%s</strong>',
+ terror
+ );
+ tx.innerHTML = temp;
+ }
+ }
+ );
+//]]></script>
+
+<div id="tshoot_div">
+ <fieldset class="cbi-section">
+ <legend><%:Troubleshooting Data%></legend>
+ <div id="mwan3_tshoot_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
+ </fieldset>
+</div>
+<style type="text/css">
+ .container { /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
+ max-width: none;
+ margin-left: 30px;
+ padding-right: 30px;
+ width: auto;
+ }
+ #tshoot_div {
+ background-color: #FFFFFF;
+ border: 1px dotted #555555;
+ padding: 20px;
+ }
+ #mwan3_tshoot_text {
+ padding: 20px;
+ text-align: left;
+ }
+ .tsht {
+ background-color: rgb(78, 186, 241);
+ }
+</style>
+
+<%+footer%>
--- /dev/null
+<style type="text/css">
+ .container { /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
+ max-width: none;
+ margin-left: 30px;
+ padding-right: 30px;
+ width: auto;
+ }
+ table td { /*cells showing the configuration values*/
+ padding: 0px;
+ text-align: center;
+ vertical-align: middle;
+ }
+ table th { /*column for configuration section name*/
+ padding: 0px;
+ text-align: center;
+ vertical-align: middle;
+ }
+ table tbody th { /*column for configuration section name*/
+ padding: 0px;
+ vertical-align: middle;
+ }
+ .cbi-section-node table div { /*rows*/
+ padding-top: 5px;
+ }
+ table.cbi-section-table td.cbi-section-table-cell { /*sort buttons column*/
+ text-align: center;
+ }
+ .cbi-section h3 {
+ color: rgb(85, 85, 85);
+ font-family: Trebuchet MS,Verdana,sans-serif;
+ font-style: italic;
+ font-weight: normal;
+ }
+</style>
--- /dev/null
+<%+header%>
+
+<ul class="cbi-tabmenu">
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/overview")%>"><%:Interface Status%></a></li>
+ <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/overview/over_detail")%>"><%:Detailed Status%></a></li>
+</ul>
+
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+<script type="text/javascript">//<![CDATA[
+ XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "network", "mwan3", "overview", "detail_status")%>', null,
+ function(x, st)
+ {
+ var tx = document.getElementById('mwan3_detail_text');
+ if (st.mwan3dst)
+ {
+ var temp = '';
+ temp = String.format(
+ '<pre>%s</pre>',
+ st.mwan3dst[0].detailstat
+ );
+ tx.innerHTML = temp;
+ }
+ else
+ {
+ var temp = '';
+ var nslg = 'No detailed status information available';
+ temp = String.format(
+ '<strong>%s</strong>',
+ nslg
+ );
+ tx.innerHTML = temp;
+ }
+ }
+ );
+//]]></script>
+
+<div id="mwan3_detail_status">
+ <fieldset class="cbi-section">
+ <legend><%:MWAN3 Multi-WAN Detailed Status%></legend>
+ <div id="mwan3_detail_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
+ </fieldset>
+</div>
+
+<style type="text/css">
+ .container { /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
+ max-width: none;
+ margin-left: 30px;
+ padding-right: 30px;
+ width: auto;
+ }
+ #mwan3_detail_status {
+ border: 1px dotted #555555;
+ background-color: #FFFFFF;
+ padding: 20px;
+ }
+ #mwan3_detail_text {
+ padding: 20px;
+ text-align: left;
+ }
+</style>
+
+<%+footer%>
--- /dev/null
+<%+header%>
+
+<ul class="cbi-tabmenu">
+ <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/overview")%>"><%:Interface Status%></a></li>
+ <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/overview/over_detail")%>"><%:Detailed Status%></a></li>
+</ul>
+
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+<script type="text/javascript">//<![CDATA[
+ XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "network", "mwan3", "overview", "iface_status")%>', null,
+ function(x, st)
+ {
+ var tx = document.getElementById('mwan3_status_text');
+ if (st.wans)
+ {
+ var temp = '';
+
+ for( var i = 0; i < st.wans.length; i++ )
+ {
+ var stat = '';
+ var cssc = '';
+
+ switch (st.wans[i].status)
+ {
+ case 'on':
+ stat = 'Online (tracking active)';
+ cssc = 'wanon';
+ break;
+
+ case 'nm':
+ stat = 'Online (tracking off)';
+ cssc = 'wanon';
+ break;
+
+ case 'off':
+ stat = 'Offline';
+ cssc = 'wanoff';
+ break;
+
+ case 'ne':
+ stat = 'Disabled';
+ cssc = 'wanoff';
+ break;
+ }
+
+ temp += String.format(
+ '<span class="%s"><strong>%s (<a href="%q">%s</a>)</strong><br />%s</span>',
+ cssc, st.wans[i].name, st.wans[i].link, st.wans[i].ifname, stat
+ );
+ }
+ tx.innerHTML = temp;
+ }
+ else
+ {
+ var temp = '';
+ var ncint = 'No MWAN3 interfaces found';
+ temp = String.format(
+ '<strong>%s</strong>',
+ ncint
+ );
+ tx.innerHTML = temp;
+ }
+
+ var tx = document.getElementById('mwan3_statuslog_text');
+ if (st.mwan3log)
+ {
+ var temp = '';
+ var mwan3lg = 'Last 50 MWAN3 systemlog entries. Newest entries sorted at the top :';
+
+ temp = String.format(
+ '<pre>%s<br /><br />%s</pre>',
+ mwan3lg, st.mwan3log[0].mwanlog
+ );
+ tx.innerHTML = temp;
+ }
+ else
+ {
+ var temp = '';
+ var nslg = 'No MWAN3 systemlog history found';
+ temp = String.format(
+ '<strong>%s</strong>',
+ nslg
+ );
+ tx.innerHTML = temp;
+ }
+ }
+ );
+//]]></script>
+
+<div id="mwan3_interface_status">
+ <fieldset id="interface_field" class="cbi-section">
+ <legend><%:MWAN3 Multi-WAN Interface Live Status%></legend>
+ <div id="mwan3_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
+ </fieldset>
+ <fieldset class="cbi-section">
+ <legend><%:MWAN3 Multi-WAN Interface Systemlog%></legend>
+ <div id="mwan3_statuslog_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
+ </fieldset>
+</div>
+
+<style type="text/css">
+ .container { /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
+ max-width: none;
+ margin-left: 30px;
+ padding-right: 30px;
+ width: auto;
+ }
+ #mwan3_interface_status {
+ background-color: #FFFFFF;
+ border: 1px dotted #555555;
+ padding: 20px;
+ }
+ #interface_field {
+ padding: 12px 20px 20px 20px;
+ }
+ #mwan3_status_text {
+ display: table;
+ font-size: 14px;
+ margin: auto;
+ max-width: 1044px;
+ min-width: 246px;
+ width: 100%;
+ }
+ .wanon {
+ background-color: rgb(144, 240, 144);
+ }
+ .wanoff {
+ background-color: rgb(240, 144, 144);
+ }
+ .wanon, .wanoff {
+ border-radius: 60px;
+ box-shadow: 0px 2px 5px -3px;
+ float: left;
+ margin: 8px 3px 0px 3px;
+ min-height: 30px;
+ min-width: 235px;
+ padding: 5px 10px 8px 10px;
+ text-align: center;
+ }
+ #mwan3_statuslog_text {
+ padding: 20px;
+ text-align: left;
+ }
+</style>
+
+<%+footer%>
--- /dev/null
+<script type="text/javascript">//<![CDATA[
+ XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "network", "mwan3", "overview", "iface_status")%>', null,
+ function(x, st)
+ {
+ var tx = document.getElementById('mwan3_status_text');
+ if (st.wans)
+ {
+ var temp = '';
+
+ for( var i = 0; i < st.wans.length; i++ )
+ {
+ var stat = '';
+ var cssc = '';
+
+ switch (st.wans[i].status)
+ {
+ case 'on':
+ stat = 'Online (tracking active)';
+ cssc = 'wanon';
+ break;
+
+ case 'nm':
+ stat = 'Online (tracking off)';
+ cssc = 'wanon';
+ break;
+
+ case 'off':
+ stat = 'Offline';
+ cssc = 'wanoff';
+ break;
+
+ case 'ne':
+ stat = 'Disabled';
+ cssc = 'wanoff';
+ break;
+ }
+
+ temp += String.format(
+ '<span class="%s"><strong>%s (<a href="%q">%s</a>)</strong><br />%s</span>',
+ cssc, st.wans[i].name, st.wans[i].link, st.wans[i].ifname, stat
+ );
+ }
+ tx.innerHTML = temp;
+ }
+ else
+ {
+ var temp = '';
+ var ncint = 'No MWAN3 interfaces found';
+ temp += String.format(
+ '<strong>%s</strong>',
+ ncint
+ );
+ tx.innerHTML = temp;
+ }
+ }
+ );
+//]]></script>
+
+<fieldset id="interface_field" class="cbi-section">
+ <legend><%:MWAN3 Multi-WAN Interface Live Status%></legend>
+ <div id="mwan3_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
+</fieldset>
+
+<style type="text/css">
+ .container { /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
+ max-width: 1044px;
+ }
+ #interface_field {
+ padding: 12px 20px 20px 20px;
+ }
+ #mwan3_status_text {
+ display: table;
+ font-size: 14px;
+ margin: auto;
+ max-width: 1044px;
+ min-width: 246px;
+ width: 100%;
+ }
+ .wanon {
+ background-color: rgb(144, 240, 144);
+ }
+ .wanoff {
+ background-color: rgb(240, 144, 144);
+ }
+ .wanon, .wanoff {
+ border-radius: 60px;
+ box-shadow: 0px 2px 5px -3px;
+ float: left;
+ margin: 8px 3px 0px 3px;
+ min-height: 30px;
+ min-width: 235px;
+ padding: 5px 10px 8px 10px;
+ text-align: center;
+ }
+</style>