diff --git a/net/mwan3-luci/Makefile b/net/mwan3-luci/Makefile new file mode 100644 index 0000000000..e221705c78 --- /dev/null +++ b/net/mwan3-luci/Makefile @@ -0,0 +1,39 @@ +# +# 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 +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 +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)) diff --git a/net/mwan3-luci/files/etc/hotplug.d/iface/16-mwan3custombak b/net/mwan3-luci/files/etc/hotplug.d/iface/16-mwan3custombak new file mode 100644 index 0000000000..9bd0beeff6 --- /dev/null +++ b/net/mwan3-luci/files/etc/hotplug.d/iface/16-mwan3custombak @@ -0,0 +1,38 @@ +#!/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 diff --git a/net/mwan3-luci/files/etc/uci-defaults/mwan3 b/net/mwan3-luci/files/etc/uci-defaults/mwan3 new file mode 100755 index 0000000000..a19d4cb25f --- /dev/null +++ b/net/mwan3-luci/files/etc/uci-defaults/mwan3 @@ -0,0 +1,14 @@ +#!/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 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/controller/mwan3.lua b/net/mwan3-luci/files/usr/lib/lua/luci/controller/mwan3.lua new file mode 100644 index 0000000000..13ff0902e1 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/controller/mwan3.lua @@ -0,0 +1,355 @@ +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 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_hotplug.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_hotplug.lua new file mode 100644 index 0000000000..93e125cf36 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_hotplug.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-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
" .. + "This is useful for running system commands and/or scripts based on interface ifup or ifdown hotplug events

" .. + "Notes:
" .. + "The first line of the script must be "#!/bin/sh" without quotes
" .. + "Lines beginning with # are comments and are not executed

" .. + "Available variables:
" .. + "$ACTION is the hotplug event (ifup, ifdown)
" .. + "$INTERFACE is the interface name (wan1, wan2, etc.)
" .. + "$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 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_mwan3.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_mwan3.lua new file mode 100644 index 0000000000..9eb0df52cc --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_mwan3.lua @@ -0,0 +1,32 @@ +-- ------ 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 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_network.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_network.lua new file mode 100644 index 0000000000..fee6a07d2a --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_network.lua @@ -0,0 +1,32 @@ +-- ------ 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 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_interface.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_interface.lua new file mode 100644 index 0000000000..3d504a24f8 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_interface.lua @@ -0,0 +1,266 @@ +-- ------ 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 = "There are currently " .. ifnum .. " of 250 supported interfaces configured" + else + warns = "WARNING: " .. ifnum .. " interfaces are configured exceeding the maximum of 250!" + end + if err_rel_list ~= " " then + warns = warns .. "

WARNING: some interfaces have a higher reliability requirement than there are tracking IP addresses!" + end + if err_route_list ~= " " then + warns = warns .. "

WARNING: some interfaces have no default route in the main routing table!" + end + if err_netcfg_list ~= " " then + warns = warns .. "

WARNING: some interfaces are configured incorrectly or not at all in /etc/config/network!" + end + if err_nomet_list ~= " " then + warns = warns .. "

WARNING: some interfaces have no metric configured in /etc/config/network!" + end + if err_dupmet_list ~= " " then + warns = warns .. "

WARNING: some interfaces have duplicate metrics configured in /etc/config/network!" + 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
" .. + "MWAN3 requires that all interfaces have a unique metric configured in /etc/config/network
" .. + "Names must match the interface name found in /etc/config/network (see advanced tab)
" .. + "Names may contain characters A-Z, a-z, 0-9, _ and no spaces
" .. + "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 .. "
" + 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 "\"error\"" + end + else + return "" + end + end + + +return m5 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_interfaceconfig.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_interfaceconfig.lua new file mode 100644 index 0000000000..2119c048ba --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_interfaceconfig.lua @@ -0,0 +1,191 @@ +-- ------ 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 = "WARNING: this interface has a higher reliability requirement than there are tracking IP addresses!" + linebrk = "

" + end + if err_route == 1 then + warns = warns .. linebrk .. "WARNING: this interface has no default route in the main routing table!" + linebrk = "

" + end + if err_netcfg == 1 then + warns = warns .. linebrk .. "WARNING: this interface is configured incorrectly or not at all in /etc/config/network!" + linebrk = "

" + end + if err_nomet == 1 then + warns = warns .. linebrk .. "WARNING: this interface has no metric configured in /etc/config/network!" + elseif err_dupmet == 1 then + warns = warns .. linebrk .. "WARNING: this and other interfaces have duplicate metrics configured in /etc/config/network!" + 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 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_member.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_member.lua new file mode 100644 index 0000000000..68b9549e10 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_member.lua @@ -0,0 +1,46 @@ +-- ------ 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
" .. + "Names may contain characters A-Z, a-z, 0-9, _ and no spaces
" .. + "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 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_memberconfig.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_memberconfig.lua new file mode 100644 index 0000000000..8ea7d9824d --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_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("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 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_policy.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_policy.lua new file mode 100644 index 0000000000..02471b9927 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_policy.lua @@ -0,0 +1,82 @@ +-- ------ 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 "WARNING: Some policies have names exceeding the maximum of 15 characters!" + 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
" .. + "Member interfaces with lower metrics are used first. Interfaces with the same metric load-balance
" .. + "Load-balanced member interfaces distribute more traffic out those with higher weights
" .. + "Names may contain characters A-Z, a-z, 0-9, _ and no spaces. Names must be 15 characters or less
" .. + "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 .. "
" + 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 "\"error\"" + end + end + + +return m5 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_policyconfig.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_policyconfig.lua new file mode 100644 index 0000000000..a6523ad8e1 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_policyconfig.lua @@ -0,0 +1,58 @@ +-- ------ 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 "WARNING: this policy's name is " .. polchar .. " characters exceeding the maximum of 15!" + 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 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_rule.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_rule.lua new file mode 100644 index 0000000000..efae81b2a4 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_rule.lua @@ -0,0 +1,109 @@ +-- ------ 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 "WARNING: some rules have a port configured with no or improper protocol specified! Please configure a specific protocol!" + 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
" .. + "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
" .. + "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
" .. + "Names may contain characters A-Z, a-z, 0-9, _ and no spaces
" .. + "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 "\"error\"" + end + end + + +return m5 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_ruleconfig.lua b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_ruleconfig.lua new file mode 100644 index 0000000000..e83fa629fc --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_ruleconfig.lua @@ -0,0 +1,99 @@ +-- ------ 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 "WARNING: this rule is incorrectly configured with no or improper protocol specified! Please configure a specific protocol!" + 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 diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/view/admin_status/index/mwan3.htm b/net/mwan3-luci/files/usr/lib/lua/luci/view/admin_status/index/mwan3.htm new file mode 100644 index 0000000000..f510f158c5 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/view/admin_status/index/mwan3.htm @@ -0,0 +1 @@ +<%+mwan3/mwan3_status%> diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_diagnostics.htm b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_diagnostics.htm new file mode 100644 index 0000000000..6d9abcbed0 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_diagnostics.htm @@ -0,0 +1,134 @@ +<%+header%> + + + +<% + local uci = require "luci.model.uci" + + str = "" + uci.cursor():foreach("mwan3", "interface", + function (section) + str = str .. section[".name"] .. " " + end + ) +%> + + + + +
+
+ <%:MWAN3 Interface Diagnostics%> + +
+ + + + + + +
+
+
+ <%:MWAN3 Service Control%> +
+ + + +
+
+ +
+ + + +<%+footer%> diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_hotplug.htm b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_hotplug.htm new file mode 100644 index 0000000000..e90942b917 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_hotplug.htm @@ -0,0 +1,23 @@ + + + diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_mwan3.htm b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_mwan3.htm new file mode 100644 index 0000000000..d11bdd7d84 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_mwan3.htm @@ -0,0 +1,23 @@ + + + diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_network.htm b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_network.htm new file mode 100644 index 0000000000..59dd4fa0e7 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_network.htm @@ -0,0 +1,23 @@ + + + diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_troubleshoot.htm b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_troubleshoot.htm new file mode 100644 index 0000000000..1a92ebd02f --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_troubleshoot.htm @@ -0,0 +1,77 @@ +<%+header%> + + + + + + +
+
+ <%:Troubleshooting Data%> +
<%:Loading%> Collecting data...
+
+
+ + +<%+footer%> diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_config_css.htm b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_config_css.htm new file mode 100644 index 0000000000..303fa20941 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_config_css.htm @@ -0,0 +1,34 @@ + diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_over_detail.htm b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_over_detail.htm new file mode 100644 index 0000000000..920a20bda8 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_over_detail.htm @@ -0,0 +1,62 @@ +<%+header%> + + + + + + +
+
+ <%:MWAN3 Multi-WAN Detailed Status%> +
<%:Loading%> Collecting data...
+
+
+ + + +<%+footer%> diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_over_interface.htm b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_over_interface.htm new file mode 100644 index 0000000000..2e846a71e3 --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_over_interface.htm @@ -0,0 +1,146 @@ +<%+header%> + + + + + + +
+
+ <%:MWAN3 Multi-WAN Interface Live Status%> +
<%:Loading%> Collecting data...
+
+
+ <%:MWAN3 Multi-WAN Interface Systemlog%> +
<%:Loading%> Collecting data...
+
+
+ + + +<%+footer%> diff --git a/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_status.htm b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_status.htm new file mode 100644 index 0000000000..9315a850ca --- /dev/null +++ b/net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_status.htm @@ -0,0 +1,95 @@ + + +
+ <%:MWAN3 Multi-WAN Interface Live Status%> +
<%:Loading%> Collecting data...
+
+ +