diff options
author | Florian Eckert <fe@dev.tdt.de> | 2021-07-02 15:23:39 +0200 |
---|---|---|
committer | Florian Eckert <fe@dev.tdt.de> | 2021-07-09 08:38:11 +0200 |
commit | f5f6b3e4f8e3faed4e9c9926e283c9cf226d4bd8 (patch) | |
tree | e2718f194ede046a90274443f6b61198f35a0961 | |
parent | 12067c8308936abb567001650105b33bec9a56a9 (diff) |
luci-app-mwan3: convert to JS
Signed-off-by: Florian Eckert <fe@dev.tdt.de>
33 files changed, 1283 insertions, 1628 deletions
diff --git a/applications/luci-app-mwan3/Makefile b/applications/luci-app-mwan3/Makefile index f53fa13f0c..8edba3ccd5 100644 --- a/applications/luci-app-mwan3/Makefile +++ b/applications/luci-app-mwan3/Makefile @@ -6,17 +6,12 @@ include $(TOPDIR)/rules.mk -LUCI_TITLE:=LuCI support for the MWAN3 multiwan hotplug script -LUCI_DEPENDS:=+luci-compat \ - +mwan3 \ - +libuci-lua \ - +luci-mod-admin-full \ - +luci-lib-nixio +LUCI_TITLE:=LuCI support for the MWAN3 MultiWAN Manager +LUCI_DEPENDS:=+mwan3 LUCI_PKGARCH:=all PKG_LICENSE:=GPLv2 -PKG_MAINTAINER:=Aedan Renner <chipdankly@gmail.com> \ - Florian Eckert <fe@dev.tdt.de> +PKG_MAINTAINER:=Florian Eckert <fe@dev.tdt.de> include ../../luci.mk diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/mwan3.css b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/mwan3.css new file mode 100644 index 0000000000..ebe7764c83 --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/mwan3.css @@ -0,0 +1,8 @@ +#mwan3-service-status > .alert-message { + display: inline-block; + margin: 1rem; + padding: 1rem; + width: 15rem; + height: 6rem; + vertical-align: middle; +} diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/globals.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/globals.js new file mode 100644 index 0000000000..b8c12c0e9b --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/globals.js @@ -0,0 +1,43 @@ +'use strict'; +'require form'; +'require view'; + +return view.extend({ + + render: function () { + var m, s, o; + + m = new form.Map('mwan3', _('MultiWAN Manager - Globals')); + + s = m.section(form.NamedSection, 'globals', 'globals'); + + o = s.option(form.Value, 'mmx_mask', _('Firewall mask'), + _('Enter value in hex, starting with <code>0x</code>')); + o.datatype = 'hex(4)'; + o.default = '0x3F00'; + + o = s.option(form.Flag, 'logging', _('Logging'), + _('Enables global firewall logging')); + + o = s.option(form.ListValue, 'loglevel', _('Loglevel'), + _('Firewall loglevel')); + o.default = 'notice'; + o.value('emerg', _('Emergency')); + o.value('alert', _('Alert')); + o.value('crit', _('Critical')); + o.value('error', _('Error')); + o.value('warning', _('Warning')); + o.value('notice', _('Notice')); + o.value('info', _('Info')); + o.value('debug', _('Debug')); + o.depends('logging', '1'); + + o = s.option(form.DynamicList, 'rt_table_lookup', + _('Routing table lookup'), + _('Also scan this Routing table for connected networks')); + o.datatype = 'uinteger'; + o.value('220', _('Routing table %d').format('220')); + + return m.render(); + } +}) diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/interface.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/interface.js new file mode 100644 index 0000000000..3615d223ec --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/interface.js @@ -0,0 +1,276 @@ +'use strict'; +'require form'; +'require fs'; +'require view'; +'require uci'; + +return view.extend({ + load: function() { + return Promise.all([ + L.resolveDefault(fs.stat('/usr/bin/httping'), {}), + L.resolveDefault(fs.stat('/usr/bin/nping'), {}), + L.resolveDefault(fs.stat('/usr/bin/arping'), {}), + uci.load('network') + ]); + }, + + render: function (stats) { + var m, s, o; + + m = new form.Map('mwan3', _('MultiWAN Manager - Interfaces'), + _('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.') + '<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.')); + + s = m.section(form.GridSection, 'interface'); + s.addremove = true; + s.anonymous = false; + s.nodescriptions = true; + + o = s.option(form.Flag, 'enabled', _('Enabled')); + o.default = false; + + o = s.option(form.ListValue, 'initial_state', _('Initial state'), + _('Expect interface state on up event')); + o.default = 'online'; + o.value('online', _('Online')); + o.value('offline', _('Offline')); + o.modalonly = true; + + o = s.option(form.ListValue, 'family', _('Internet Protocol')); + o.default = 'ipv4'; + o.value('ipv4', _('IPv4')); + o.value('ipv6', _('IPv6')); + o.modalonly = true; + + o = s.option(form.DynamicList, 'track_ip', _('Tracking hostname or IP address'), + _('This hostname or IP address will be pinged to determine if the link is up or down. Leave blank to assume interface is always online')); + o.datatype = 'host'; + o.modalonly = true; + + o = s.option(form.ListValue, 'track_method', _('Tracking method')); + o.default = 'ping'; + o.value('ping'); + if (stats[0].type === 'file') { + o.value('httping'); + } + if (stats[1].type === 'file') { + o.value('nping-tcp'); + o.value('nping-udp'); + o.value('nping-icmp'); + o.value('nping-arp'); + } + if (stats[2].type === 'file') { + o.value('arping'); + } + + o = s.option(form.Flag, 'httping_ssl', _('Enable ssl tracking'), + _('Enables https tracking on ssl port 443')); + o.depends('track_method', 'httping'); + o.rmempty = false; + o.modalonly = true; + + o = s.option(form.Value, 'reliability', _('Tracking reliability'), + _('Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up')); + o.datatype = 'range(1, 100)'; + o.default = '1'; + + o = s.option(form.ListValue, 'count', _('Ping count')); + o.default = '1'; + o.value('1'); + o.value('2'); + o.value('3'); + o.value('4'); + o.value('5'); + o.modalonly = true; + + o = s.option(form.Value, 'size', _('Ping size')); + o.default = '56'; + o.depends('track_method', 'ping'); + o.value('8'); + o.value('24'); + o.value('56'); + o.value('120'); + o.value('248'); + o.value('504'); + o.value('1016'); + o.value('1472'); + o.value('2040'); + o.datatype = 'range(1, 65507)'; + o.modalonly = true; + + o =s.option(form.Value, 'max_ttl', _('Max TTL')); + o.default = '60'; + o.depends('track_method', 'ping'); + o.value('10'); + o.value('20'); + o.value('30'); + o.value('40'); + o.value('50'); + o.value('60'); + o.value('70'); + o.datatype = 'range(1, 255)'; + o.modalonly = true; + + o = s.option(form.Flag, 'check_quality', _('Check link quality')); + o.depends('track_method', 'ping'); + o.default = false; + o.modalonly = true; + + o = s.option(form.Value, 'failure_latency', _('Failure latency [ms]')); + o.depends('check_quality', '1'); + o.default = '1000'; + o.value('25'); + o.value('50'); + o.value('75'); + o.value('100'); + o.value('150'); + o.value('200'); + o.value('250'); + o.value('300'); + o.modalonly = true; + + o = s.option(form.Value, 'failure_loss', _('Failure packet loss [%]')); + o.depends('check_quality', '1'); + o.default = '40'; + o.value('2'); + o.value('5'); + o.value('10'); + o.value('20'); + o.value('25'); + o.modalonly = true; + + o = s.option(form.Value, 'recovery_latency', _('Recovery latency [ms]')); + o.depends('check_quality', '1'); + o.default = '500'; + o.value('25'); + o.value('50'); + o.value('75'); + o.value('100'); + o.value('150'); + o.value('200'); + o.value('250'); + o.value('300'); + o.modalonly = true; + + o = s.option(form.Value, 'recovery_loss', _('Recovery packet loss [%]')); + o.depends('check_quality', '1'); + o.default = '10'; + o.value('2'); + o.value('5'); + o.value('10'); + o.value('20'); + o.value('25'); + o.modalonly = true; + + o = s.option(form.ListValue, "timeout", _("Ping timeout")); + o.default = '4'; + for (var i = 1; i <= 10; i++) + o.value(String(i), N_(i, '%d second', '%d seconds').format(i)); + o.modalonly = true; + + o = s.option(form.ListValue, 'interval', _('Ping interval')); + o.default = '10'; + o.value('1', _('%d second').format('1')); + o.value('3', _('%d seconds').format('3')); + o.value('5', _('%d seconds').format('5')); + o.value('10', _('%d seconds').format('10')); + o.value('20', _('%d seconds').format('20')); + o.value('30', _('%d seconds').format('30')); + o.value('60', _('%d minute').format('1')); + o.value('300', _('%d minutes').format('5')); + o.value('600', _('%d minutes').format('10')); + o.value('900', _('%d minutes').format('15')); + o.value('1800', _('%d minutes').format('30')); + o.value('3600', _('%d hour').format('1')); + + o = s.option(form.Value, 'failure_interval', _('Failure interval'), + _('Ping interval during failure detection')); + o.default = '5'; + o.value('1', _('%d second').format('1')); + o.value('3', _('%d seconds').format('3')); + o.value('5', _('%d seconds').format('5')); + o.value('10', _('%d seconds').format('10')); + o.value('20', _('%d seconds').format('20')); + o.value('30', _('%d seconds').format('30')); + o.value('60', _('%d minute').format('1')); + o.value('300', _('%d minutes').format('5')); + o.value('600', _('%d minutes').format('10')); + o.value('900', _('%d minutes').format('15')); + o.value('1800', _('%d minutes').format('30')); + o.value('3600', _('%d hour').format('1')); + o.modalonly = true; + + o = s.option(form.Flag, 'keep_failure_interval', _('Keep failure interval'), + _('Keep ping failure interval during failure state')); + o.default = false; + o.modalonly = true; + + o = s.option(form.Value, 'recovery_interval', _('Recovery interval'), + _('Ping interval during failure recovering')); + o.default = '5'; + o.value('1', _('%d second').format('1')); + o.value('3', _('%d seconds').format('3')); + o.value('5', _('%d seconds').format('5')); + o.value('10', _('%d seconds').format('10')); + o.value('20', _('%d seconds').format('20')); + o.value('30', _('%d seconds').format('30')); + o.value('60', _('%d minute').format('1')); + o.value('300', _('%d minutes').format('5')); + o.value('600', _('%d minutes').format('10')); + o.value('900', _('%d minutes').format('15')); + o.value('1800', _('%d minutes').format('30')); + o.value('3600', _('%d hour').format('1')); + o.modalonly = true; + + o = s.option(form.ListValue, 'down', _('Interface down'), + _('Interface will be deemed down after this many failed ping tests')); + o.default = '5'; + o.value('1'); + o.value('2'); + o.value('3'); + o.value('4'); + o.value('5'); + o.value('6'); + o.value('7'); + o.value('8'); + o.value('9'); + o.value('10'); + + o = s.option(form.ListValue, 'up', _('Interface up'), + _('Downed interface will be deemed up after this many successful ping tests')); + o.default = "5"; + o.value('1'); + o.value('2'); + o.value('3'); + o.value('4'); + o.value('5'); + o.value('6'); + o.value('7'); + o.value('8'); + o.value('9'); + o.value('10'); + + o = s.option(form.ListValue, 'flush_conntrack', _('Flush conntrack table'), + _('Flush global firewall conntrack table on interface events')); + o.value('ifup', _('ifup (netifd)')); + o.value('ifdown', _('ifdown (netifd)')); + o.value('connected', _('connected (mwan3)')); + o.value('disconnected', _('disconnected (mwan3)')); + o.modalonly = true; + + o = s.option(form.DummyValue, 'metric', _('Metric'), + _('This displays the metric assigned to this interface in /etc/config/network')); + o.rawhtml = true; + o.cfgvalue = function(s) { + var metric = uci.get('network', s, 'metric') + if (metric) + return metric; + else + return _('No interface metric set!'); + } + + return m.render(); + } +}) diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/member.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/member.js new file mode 100644 index 0000000000..c49cc6e1ef --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/member.js @@ -0,0 +1,43 @@ +'use strict'; +'require form'; +'require view'; +'require uci'; + +return view.extend({ + load: function() { + return Promise.all([ + uci.load('mwan3') + ]); + }, + + render: function () { + var m, s, o; + + m = new form.Map('mwan3', _('MultiWAN Manager - Members'), + _('Members are profiles attaching a metric and weight to an MWAN interface.') + '<br />' + + _('Names may contain characters A-Z, a-z, 0-9, _ and no spaces.') + '<br />' + + _('Members may not share the same name as configured interfaces, policies or rules.')); + + s = m.section(form.GridSection, 'member'); + s.addremove = true; + s.anonymous = false; + s.nodescriptions = true; + + o = s.option(form.ListValue, 'interface', _('Interface')); + var options = uci.sections('mwan3', 'interface') + for (var i = 0; i < options.length; i++) { + var value = options[i]['.name']; + o.value(value); + } + + o = s.option(form.Value, 'metric', _('Metric'), + _('Acceptable values: 1-256. Defaults to 1 if not set')); + o.datatype = 'range(1, 256)'; + + o = s.option(form.Value, 'weight', ('Weight'), + _('Acceptable values: 1-1000. Defaults to 1 if not set')); + o.datatype = 'range(1, 1000)'; + + return m.render(); + } +}) diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/notify.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/notify.js new file mode 100644 index 0000000000..ed27535c2a --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/notify.js @@ -0,0 +1,52 @@ +'use strict'; +'require view'; +'require fs'; +'require ui'; + +var isReadonlyView = !L.hasViewPermission() || null; + +return view.extend({ + load: function() { + return L.resolveDefault(fs.read('/etc/mwan3.user'), ''); + }, + + handleSave: function(ev) { + var value = (document.querySelector('textarea').value || '').trim().replace(/\r\n/g, '\n') + '\n'; + + return fs.write('/etc/mwan3.user', value).then(function(rc) { + document.querySelector('textarea').value = value; + ui.addNotification(null, E('p', _('Contents have been saved.')), 'info'); + }).catch(function(e) { + ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message))); + }); + }, + + render: function(mwan3user) { + return E([ + E('h2', _('MultiWAN Manager - Notify')), + E('p', { 'class': 'cbi-section-descr' }, + _('This section allows you to modify the content of \"/etc/mwan3.user\".') + '<br/>' + + _('The file is also preserved during sysupgrade.') + '<br/>' + + '<br />' + + _('Notes:') + '<br />' + + _('This file is interpreted as a shell script.') + '<br />' + + _('The first line of the script must be "#!/bin/sh" without quotes.') + '<br />' + + _('Lines beginning with # are comments and are not executed.') + '<br />' + + _('Put your custom mwan3 action here, they will be executed with each netifd hotplug interface event on interfaces for which mwan3 is enabled.') + '<br />' + + '<br />' + + _('There are three main environment variables that are passed to this script.') + '<br />' + + '<br />' + + _('%s: Name of the action that triggered this event').format('$ACTION') + '<br />' + + _('* %s: Is called by netifd and mwan3track').format('ifup') + '<br />' + + _('* %s: Is called by netifd and mwan3track').format('ifdown') + '<br />' + + _('* %s: Is only called by mwan3track if tracking was successful').format('connected') + '<br />' + + _('* %s: Is only called by mwan3track if tracking has failed').format('disonnected') + '<br />' + + _('%s: Name of the interface which went up or down (e.g. \"wan\" or \"wwan\")').format('$INTERFACE') + '<br />' + + _('%s: Name of Physical device which interface went up or down (e.g. \"eth0\" or \"wwan0\")').format('$DEVICE') + '<br />'), + E('p', {}, E('textarea', { 'style': 'width:100%', 'rows': 10, 'disabled': isReadonlyView }, [ mwan3user != null ? mwan3user : '' ])) + ]); + }, + + handleSaveApply: null, + handleReset: null +}); diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/policy.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/policy.js new file mode 100644 index 0000000000..d39eb3bfba --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/policy.js @@ -0,0 +1,46 @@ +'use strict'; +'require form'; +'require view'; +'require uci'; + +return view.extend({ + load: function() { + return Promise.all([ + uci.load('mwan3') + ]); + }, + + render: function () { + var m, s, o; + + m = new form.Map('mwan3', _('MultiWAN Manager - Policies'), + _('Policies are profiles grouping one or more members controlling how Mwan3 distributes traffic.') + + _('Member interfaces with lower metrics are used first.') + + _('Member interfaces with the same metric will be load-balanced.') + + _('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')); + + s = m.section(form.GridSection, 'policy'); + s.addremove = true; + s.anonymous = false; + s.nodescriptions = true; + + o = s.option(form.DynamicList, 'use_member', _('Member used')); + var options = uci.sections('mwan3', 'member') + for (var i = 0; i < options.length; i++) { + var value = options[i]['.name']; + o.value(value); + } + + o = s.option(form.ListValue, 'last_resort', _('Last resort'), + _('When all policy members are offline use this behavior for matched traffic')); + o.default = 'unreachable'; + o.value('unreachable', _('unreachable (reject)')); + o.value('blackhole', _('blackhole (drop)')); + o.value('default', _('default (use main routing table)')); + + return m.render(); + } +}) diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/rule.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/rule.js new file mode 100644 index 0000000000..76020173e9 --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/rule.js @@ -0,0 +1,107 @@ +'use strict'; +'require form'; +'require fs'; +'require view'; +'require uci'; + +return view.extend({ + load: function() { + return Promise.all([ + fs.exec_direct('/usr/libexec/luci-mwan3', ['ipset', 'dump']), + uci.load('mwan3') + ]); + }, + + render: function (data) { + var m, s, o; + + m = new form.Map('mwan3', _('MultiWAN Manager - Rules'), + _('Rules specify which traffic will use a particular MWAN policy.') + '<br />' + + _('Rules are based on IP address, port or protocol.') + '<br />' + + _('Rules are matched from top to bottom.') + '<br />' + + _('Rules below a matching rule are ignored.') + '<br />' + + _('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.') + '<br />' + + _('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.')); + + s = m.section(form.GridSection, 'rule'); + s.addremove = true; + s.anonymous = false; + s.nodescriptions = true; + + o = s.option(form.ListValue, 'family', _('Internet Protocol')); + o.default = ''; + o.value('', _('IPv4 and IPv6')); + o.value('ipv4', _('IPv4 only')); + o.value('ipv6', _('IPv6 only')); + o.modalonly = true; + + o = s.option(form.Value, 'src_ip', _('Source address'), + _('Supports CIDR notation (eg \"192.168.100.0/24\") without quotes')); + o.datatype = 'ipaddr'; + + o = s.option(form.Value, 'src_port', _('Source port'), + _('May be entered as a single or multiple port(s) (eg \"22\" or \"80,443\") or as a portrange (eg \"1024:2048\") without quotes')); + o.depends('proto', 'tcp'); + o.depends('proto', 'udp'); + + o = s.option(form.Value, 'dest_ip', _('Destination address'), + _('Supports CIDR notation (eg \"192.168.100.0/24\") without quotes')); + o.datatype = 'ipaddr'; + + o = s.option(form.Value, 'dest_port', _('Destination port'), + _('May be entered as a single or multiple port(s) (eg \"22\" or \"80,443\") or as a portrange (eg \"1024:2048\") without quotes')); + o.depends('proto', 'tcp'); + o.depends('proto', 'udp'); + + o = s.option(form.Value, 'proto', _('Protocol'), + _('View the content of /etc/protocols for protocol description')); + o.default = 'all'; + o.rmempty = false; + o.value('all'); + o.value('tcp'); + o.value('udp'); + o.value('icmp'); + o.value('esp'); + + o = s.option(form.ListValue, 'sticky', _('Sticky'), + _('Traffic from the same source IP address that previously matched this rule within the sticky timeout period will use the same WAN interface')); + o.default = '0'; + o.value('1', _('Yes')); + o.value('0', _('No')); + o.modalonly = true; + + o = s.option(form.Value, 'timeout', _('Sticky timeout'), + _('Seconds. Acceptable values: 1-1000000. Defaults to 600 if not set')); + o.datatype = 'range(1, 1000000)'; + o.modalonly = true; + + o = s.option(form.Value, 'ipset', _('IPset'), + _('Name of IPset rule. Requires IPset rule in /etc/dnsmasq.conf (eg \"ipset=/youtube.com/youtube\")')); + o.value('', _('-- Please choose --')); + var ipsets = data[0].split(/\n/); + for (var i = 0; i < ipsets.length; i++) { + if (ipsets[i].length > 0) + o.value(ipsets[i]); + } + o.modalonly = true; + + o = s.option(form.Flag, 'logging', _('Logging'), + _('Enables firewall rule logging (global mwan3 logging must also be enabled)')); + o.modalonly = true; + + o = s.option(form.ListValue, 'use_policy', _('Policy assigned')); + var options = uci.sections('mwan3', 'policy') + for (var i = 0; i < options.length; i++) { + var value = options[i]['.name']; + o.value(value); + } + o.value('unreachable', _('unreachable (reject)')); + o.value('blackhole', _('blackhole (drop)')); + o.value('default', _('default (use main routing table)')); + + return m.render(); + } +}) diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/detail.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/detail.js new file mode 100644 index 0000000000..552b1321fb --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/detail.js @@ -0,0 +1,22 @@ +'use strict'; +'require fs'; +'require view'; + +return view.extend({ + load: function() { + return L.resolveDefault(fs.exec_direct('/usr/sbin/mwan3', [ 'status' ]),''); + }, + + render: function (report) { + return E('div', { 'class': 'cbi-map', 'id': 'map' }, [ + E('h2', _('MultiWAN Manager - Status')), + E('div', { 'class': 'cbi-section' }, [ + E('pre', [ report ]) + ]), + ]) + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}) diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/diagnostics.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/diagnostics.js new file mode 100644 index 0000000000..71bee68f88 --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/diagnostics.js @@ -0,0 +1,116 @@ +'use strict'; +'require fs'; +'require uci'; +'require dom'; +'require ui'; +'require view'; + +return view.extend({ + handleCommand: function(exec, args) { + var buttons = document.querySelectorAll('.cbi-button'); + + for (var i = 0; i < buttons.length; i++) + buttons[i].setAttribute('disabled', 'true'); + + return fs.exec(exec, args).then(function(res) { + var out = document.querySelector('.command-output'); + out.style.display = ''; + + dom.content(out, [ res.stdout || '', res.stderr || '' ]); + }).catch(function(err) { + ui.addNotification(null, E('p', [ err ])) + }).finally(function() { + for (var i = 0; i < buttons.length; i++) + buttons[i].removeAttribute('disabled'); + }); + }, + + handleAction: function(ev) { + var iface = document.getElementById('iface'); + var task = document.getElementById('task'); + + switch (task.value) { + case 'gateway': + return this.handleCommand('/usr/libexec/luci-mwan3', + [ 'diag', 'gateway', iface.value ]); + case 'tracking': + return this.handleCommand('/usr/libexec/luci-mwan3', + [ 'diag', 'tracking', iface.value ]); + case 'rules': + return this.handleCommand('/usr/libexec/luci-mwan3', + [ 'diag', 'rules', iface.value ]); + case 'routes': + return this.handleCommand('/usr/libexec/luci-mwan3', + [ 'diag', 'routes', iface.value ]); + case 'ifup': + return this.handleCommand('/usr/sbin/mwan3', + [ 'ifup', iface.value]); + case 'ifdown': + return this.handleCommand('/usr/sbin/mwan3', + [ 'ifdown', iface.value]); + } + }, + + load: function() { + return Promise.all([ + uci.load('mwan3') + ]); + }, + + render: function () { + + var taskSel = [ + E('option', { 'value': 'gateway' }, [ _('Ping default gateway') ]), + E('option', { 'value': 'tracking' }, [ _('Ping tracking IP') ]), + E('option', { 'value': 'rules' }, [ _('Check IP rules') ]), + E('option', { 'value': 'routes' }, [ _('Check routing table') ]), + E('option', { 'value': 'ifup' }, [ _('Hotplug ifup') ]), + E('option', { 'value': 'ifdown' }, [ _('Hotplug ifdown') ]) + ]; + + var ifaceSel = [E('option', { value: '' }, [_('-- Interface Selection --')])]; + + var options = uci.sections('mwan3', 'interface') + for (var i = 0; i < options.length; i++) { + ifaceSel.push(E('option', { 'value': options[i]['.name'] }, options[i]['.name'])); + } + + return E('div', { 'class': 'cbi-map', 'id': 'map' }, [ + E('h2', {}, [ _('MultiWAN Manager - Diagnostics') ]), + E('div', { 'class': 'cbi-section' }, [ + E('div', { 'class': 'cbi-section-node' }, [ + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, [ _('Interface') ]), + E('div', { 'class': 'cbi-value-field' }, [ + E('select', {'class': 'cbi-input-select', 'id': 'iface'}, + ifaceSel + ) + ]) + ]), + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, [ _('Task') ]), + E('div', { 'class': 'cbi-value-field' }, [ + E('select', { 'class': 'cbi-input-select', 'id': 'task' }, + taskSel + ) + ]) + ]) + ]) + ]), + '\xa0', + E('pre', { 'class': 'command-output', 'style': 'display:none' }), + '\xa0', + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'cbi-button cbi-button-apply', + 'id': 'execute', + 'click': ui.createHandlerFn(this, 'handleAction') + }, [ _('Execute') ]), + ]), + ]); + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}) diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/overview.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/overview.js new file mode 100644 index 0000000000..14e0b0252b --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/overview.js @@ -0,0 +1,104 @@ +'use strict'; +'require poll'; +'require view'; +'require rpc'; + +var callMwan3Status = rpc.declare({ + object: 'mwan3', + method: 'status', + expect: { }, +}); + +document.querySelector('head').appendChild(E('link', { + 'rel': 'stylesheet', + 'type': 'text/css', + 'href': L.resource('view/mwan3/mwan3.css') +})); + +function renderMwan3Status(status) { + if (!status.interfaces) + return '<strong>%h</strong>'.format(_('No MWAN interfaces found')); + + var statusview = ''; + for ( var iface in status.interfaces) { + var state = ''; + var css = ''; + var time = ''; + var tname = ''; + switch (status.interfaces[iface].status) { + case 'online': + state = _('Online'); + css = 'success'; + time = '%t'.format(status.interfaces[iface].online); + tname = _('Uptime'); + css = 'success'; + break; + case 'offline': + state = _('Offline'); + css = 'danger'; + time = '%t'.format(status.interfaces[iface].offline); + tname = _('Downtime'); + break; + case 'notracking': + state = _('No Tracking'); + if ((status.interfaces[iface].uptime) > 0) { + css = 'success'; + time = '%t'.format(status.interfaces[iface].uptime); + tname = _('Uptime'); + } + else { + css = 'warning'; + time = ''; + tname = ''; + } + break; + default: + state = _('Disabled'); + css = 'warning'; + time = ''; + tname = ''; + break; + } + + statusview += '<div class="alert-message %h">'.format(css); + statusview += '<div><strong>%h: </strong>%h</div>'.format(_('Interface'), iface); + statusview += '<div><strong>%h: </strong>%h</div>'.format(_('Status'), state); + + if (time) + statusview += '<div><strong>%h: </strong>%h</div>'.format(tname, time); + + statusview += '</div>'; + } + + return statusview; +} + +return view.extend({ + load: function() { + return Promise.all([ + callMwan3Status(), + ]); + }, + + render: function (data) { + poll.add(function() { + return callMwan3Status().then(function(result) { + var view = document.getElementById('mwan3-service-status'); + view.innerHTML = renderMwan3Status(result); + }); + }); + + return E('div', { class: 'cbi-map' }, [ + E('h2', [ _('MultiWAN Manager - Overview') ]), + E('div', { class: 'cbi-section' }, [ + E('div', { 'id': 'mwan3-service-status' }, [ + E('em', { 'class': 'spinning' }, [ _('Collecting data ...') ]) + ]) + ]) + ]); + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}) diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/troubleshooting.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/troubleshooting.js new file mode 100644 index 0000000000..6446125175 --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/troubleshooting.js @@ -0,0 +1,22 @@ +'use strict'; +'require fs'; +'require view'; + +return view.extend({ + load: function() { + return L.resolveDefault(fs.exec_direct('/usr/sbin/mwan3', [ 'internal', 'ipv4' ]),''); + }, + + render: function (report) { + return E('div', { 'class': 'cbi-map', 'id': 'map' }, [ + E('h2', _('MultiWAN Manager - Troubleshooting')), + E('div', { 'class': 'cbi-section' }, [ + E('pre', [ report ]) + ]), + ]) + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}) diff --git a/applications/luci-app-mwan3/htdocs/luci-static/resources/view/status/include/90_mwan3.js b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/status/include/90_mwan3.js new file mode 100644 index 0000000000..0fc11550e9 --- /dev/null +++ b/applications/luci-app-mwan3/htdocs/luci-static/resources/view/status/include/90_mwan3.js @@ -0,0 +1,117 @@ +'use strict'; +'require baseclass'; +'require rpc'; + +var callMwan3Status = rpc.declare({ + object: 'mwan3', + method: 'status', + expect: { }, +}); + +document.querySelector('head').appendChild(E('link', { + 'rel': 'stylesheet', + 'type': 'text/css', + 'href': L.resource('view/mwan3/mwan3.css') +})); + +return baseclass.extend({ + title: _('MultiWAN Manager'), + + load: function() { + return Promise.all([ + callMwan3Status(), + ]); + }, + + render: function (result) { + if (!result[0].interfaces) + return null; + + var container = E('div', { 'id': 'mwan3-service-status' }); + var iface; + for ( iface in result[0].interfaces) { + var state = ''; + var css = ''; + var time = ''; + var tname = ''; + switch (result[0].interfaces[iface].status) { + case 'online': + state = _('Online'); + css = 'alert-message success'; + time = '%t'.format(result[0].interfaces[iface].online); + tname = _('Uptime'); + break; + case 'offline': + state = _('Offline'); + css = 'alert-message danger'; + time = '%t'.format(result[0].interfaces[iface].offline); + tname = _('Downtime'); + break; + case 'notracking': + state = _('No Tracking'); + if ((result[0].interfaces[iface].uptime) > 0) { + css = 'alert-message success'; + time = '%t'.format(result[0].interfaces[iface].uptime); + tname = _('Uptime'); + } + else { + css = 'alert-message warning'; + time = ''; + tname = ''; + } + break; + default: + css = 'alert-message warning'; + state = _('Disabled'); + time = ''; + tname = ''; + break; + } + + if (time !== '' ) { + container.appendChild( + E('div', { 'class': css }, [ + E('div', {}, [ + E('strong', {}, [ + _('Interface'), ':', ' ' + ]), + iface + ]), + E('div', {}, [ + E('strong', {}, [ + _('Status'), ':', ' ' + ]), + state + ]), + E('div', {}, [ + E('strong', {}, [ + tname, ':', ' ' + ]), + time + ]) + ]) + ); + } + else { + container.appendChild( + E('div', { 'class': css }, [ + E('div', {}, [ + E('strong', {}, [ + _('Interface'), ':', ' ' + ]), + iface + ]), + E('div', {}, [ + E('strong', {}, [ + _('Status'), ':', ' ' + ]), + state + ]) + ]) + ); + } + } + + return container; + } +}); diff --git a/applications/luci-app-mwan3/luasrc/controller/mwan3.lua b/applications/luci-app-mwan3/luasrc/controller/mwan3.lua deleted file mode 100644 index 1fb9083a53..0000000000 --- a/applications/luci-app-mwan3/luasrc/controller/mwan3.lua +++ /dev/null @@ -1,320 +0,0 @@ --- Copyright 2014 Aedan Renner <chipdankly@gmail.com> --- Copyright 2018 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -module("luci.controller.mwan3", package.seeall) - -sys = require "luci.sys" -ut = require "luci.util" - -ip = "ip -4 " - -function index() - if not nixio.fs.access("/etc/config/mwan3") then - return - end - - entry({"admin", "status", "mwan"}, - alias("admin", "status", "mwan", "overview"), - _("Load Balancing"), 600).acl_depends = { "luci-app-mwan3" } - - entry({"admin", "status", "mwan", "overview"}, - template("mwan/status_interface")) - entry({"admin", "status", "mwan", "detail"}, - template("mwan/status_detail")) - entry({"admin", "status", "mwan", "diagnostics"}, - template("mwan/status_diagnostics")) - entry({"admin", "status", "mwan", "troubleshooting"}, - template("mwan/status_troubleshooting")) - entry({"admin", "status", "mwan", "interface_status"}, - call("mwan_Status")) - entry({"admin", "status", "mwan", "detailed_status"}, - call("detailedStatus")) - entry({"admin", "status", "mwan", "diagnostics_display"}, - call("diagnosticsData"), nil).leaf = true - entry({"admin", "status", "mwan", "troubleshooting_display"}, - call("troubleshootingData")) - - - entry({"admin", "network", "mwan"}, - alias("admin", "network", "mwan", "interface"), - _("Load Balancing"), 600).acl_depends = { "luci-app-mwan3" } - - entry({"admin", "network", "mwan", "globals"}, - cbi("mwan/globalsconfig"), - _("Globals"), 5).leaf = true - entry({"admin", "network", "mwan", "interface"}, - arcombine(cbi("mwan/interface"), cbi("mwan/interfaceconfig")), - _("Interfaces"), 10).leaf = true - entry({"admin", "network", "mwan", "member"}, - arcombine(cbi("mwan/member"), cbi("mwan/memberconfig")), - _("Members"), 20).leaf = true - entry({"admin", "network", "mwan", "policy"}, - arcombine(cbi("mwan/policy"), cbi("mwan/policyconfig")), - _("Policies"), 30).leaf = true - entry({"admin", "network", "mwan", "rule"}, - arcombine(cbi("mwan/rule"), cbi("mwan/ruleconfig")), - _("Rules"), 40).leaf = true - entry({"admin", "network", "mwan", "notify"}, - form("mwan/notify"), - _("Notification"), 50).leaf = true -end - -function mwan_Status() - local status = ut.ubus("mwan3", "status", {}) - - luci.http.prepare_content("application/json") - if status ~= nil then - luci.http.write_json(status) - else - luci.http.write_json({}) - end -end - -function detailedStatus() - local statusInfo = ut.trim(sys.exec("/usr/sbin/mwan3 status")) - luci.http.prepare_content("text/plain") - if statusInfo ~= "" then - luci.http.write(statusInfo) - else - luci.http.write("Unable to get status information") - end -end - -function diagnosticsData(interface, task) - function getInterfaceNumber(interface) - local number = 0 - local interfaceNumber - local uci = require "luci.model.uci".cursor() - uci:foreach("mwan3", "interface", - function (section) - number = number+1 - if section[".name"] == interface then - interfaceNumber = number - end - end - ) - return interfaceNumber - end - - function diag_command(cmd, device, addr) - if addr and addr:match("^[a-zA-Z0-9%-%.:_]+$") then - local util = io.popen(cmd %{ut.shellquote(device), ut.shellquote(addr)}) - if util then - luci.http.write("Command:\n") - luci.http.write(cmd %{ut.shellquote(device), - ut.shellquote(addr)} .. "\n\n") - luci.http.write("Result:\n") - while true do - local ln = util:read("*l") - if not ln then break end - luci.http.write(ln) - luci.http.write("\n") - end - util:close() - end - return - end - end - - function get_gateway(interface) - local gateway = nil - local dump = nil - - dump = require("luci.util").ubus("network.interface.%s_4" % interface, "status", {}) - if not dump then - dump = require("luci.util").ubus("network.interface.%s" % interface, "status", {}) - end - - if dump and dump.route then - local _, route - for _, route in ipairs(dump.route) do - if dump.route[_].target == "0.0.0.0" then - gateway = dump.route[_].nexthop - end - end - end - return gateway - end - - local mArray = {} - local results = "" - local number = getInterfaceNumber(interface) - - local uci = require "luci.model.uci".cursor(nil, "/var/state") - local nw = require "luci.model.network".init() - local i18n = require "luci.i18n" - local network = nw:get_network(interface) - local device = network and network:get_interface() - device = device:name() - - luci.http.prepare_content("text/plain") - if device then - if task == "ping_gateway" then - local gateway = get_gateway(interface) - if gateway ~= nil then - diag_command("ping -I %s -c 5 -W 1 %s 2>&1", device, gateway) - else - luci.http.prepare_content("text/plain") - luci.http.write(i18n.translatef("No gateway for interface %s found.", interface)) - end - elseif task == "ping_trackips" then - local trackips = uci:get("mwan3", interface, "track_ip") - if #trackips > 0 then - for i in pairs(trackips) do - diag_command("ping -I %s -c 5 -W 1 %s 2>&1", device, trackips[i]) - end - else - luci.http.write(i18n.translatef("No tracking Hosts for interface %s defined.", interface)) - end - elseif task == "check_rules" then - local number = getInterfaceNumber(interface) - local iif = 1000 + number - local fwmark = 2000 + number - local iif_rule = sys.exec(string.format("ip rule | grep %d", iif)) - local fwmark_rule = sys.exec(string.format("ip rule | grep %d", fwmark)) - if iif_rule ~= "" and fwmark_rule ~= "" then - luci.http.write(i18n.translatef("All required IP rules for interface %s found", interface)) - luci.http.write("\n") - luci.http.write(fwmark_rule) - luci.http.write(iif_rule) - elseif iif_rule == "" and fwmark_rule ~= "" then - luci.http.write(i18n.translatef("Only one IP rules for interface %s found", interface)) - luci.http.write("\n") - luci.http.write(fwmark_rule) - elseif iif_rule ~= "" and fwmark_rule == "" then - luci.http.write(i18n.translatef("Only one IP rules for interface %s found", interface)) - luci.http.write("\n") - luci.http.write(iif_rule) - else - luci.http.write(i18n.translatef("Missing both IP rules for interface %s", interface)) - end - elseif task == "check_routes" then - local number = getInterfaceNumber(interface) - local routeTable = sys.exec(string.format("ip route list table %s", number)) - if routeTable ~= "" then - luci.http.write(i18n.translatef("Routing table %s for interface %s found", number, interface)) - luci.http.write("\n") - luci.http.write(routeTable) - else - luci.http.write(i18n.translatef("Routing table %s for interface %s not found", number, interface)) - end - elseif task == "hotplug_ifup" then - os.execute(string.format("/usr/sbin/mwan3 ifup %s", ut.shellquote(interface))) - luci.http.write(string.format("Hotplug ifup sent to interface %s", interface)) - elseif task == "hotplug_ifdown" then - os.execute(string.format("/usr/sbin/mwan3 ifdown %s", ut.shellquote(interface))) - luci.http.write(string.format("Hotplug ifdown sent to interface %s", interface)) - else - luci.http.write("Unknown task") - end - else - luci.http.write(string.format("Unable to perform diagnostic tests on %s.", interface)) - luci.http.write("\n") - luci.http.write("There is no physical or virtual device associated with this interface.") - end -end - -function troubleshootingData() - local ver = require "luci.version" - local dash = "-------------------------------------------------" - - luci.http.prepare_content("text/plain") - - luci.http.write("\n") - luci.http.write("\n") - luci.http.write("Software-Version") - luci.http.write("\n") - luci.http.write(dash) - luci.http.write("\n") - if ver.distversion then - luci.http.write(string.format("OpenWrt - %s", ver.distversion)) - luci.http.write("\n") - else - luci.http.write("OpenWrt - unknown") - luci.http.write("\n") - end - - if ver.luciversion then - luci.http.write(string.format("LuCI - %s", ver.luciversion)) - luci.http.write("\n") - else - luci.http.write("LuCI - unknown") - luci.http.write("\n") - end - - luci.http.write("\n") - luci.http.write("\n") - local output = ut.trim(sys.exec("ip a show")) - luci.http.write("Output of \"ip a show\"") - luci.http.write("\n") - luci.http.write(dash) - luci.http.write("\n") - if output ~= "" then - luci.http.write(output) - luci.http.write("\n") - else - luci.http.write("No data found") - luci.http.write("\n") - end - - luci.http.write("\n") - luci.http.write("\n") - local output = ut.trim(sys.exec("ip route show")) - luci.http.write("Output of \"ip route show\"") - luci.http.write("\n") - luci.http.write(dash) - luci.http.write("\n") - if output ~= "" then - luci.http.write(output) - luci.http.write("\n") - else - luci.http.write("No data found") - luci.http.write("\n") - end - - luci.http.write("\n") - luci.http.write("\n") - local output = ut.trim(sys.exec("ip rule show")) - luci.http.write("Output of \"ip rule show\"") - luci.http.write("\n") - luci.http.write(dash) - luci.http.write("\n") - if output ~= "" then - luci.http.write(output) - luci.http.write("\n") - else - luci.http.write("No data found") - luci.http.write("\n") - end - - luci.http.write("\n") - luci.http.write("\n") - luci.http.write("Output of \"ip route list table 1-250\"") - luci.http.write("\n") - luci.http.write(dash) - luci.http.write("\n") - for i=1,250 do - local output = ut.trim(sys.exec(string.format("ip route list table %d", i))) - if output ~= "" then - luci.http.write(string.format("Table %s: ", i)) - luci.http.write(output) - luci.http.write("\n") - end - end - - luci.http.write("\n") - luci.http.write("\n") - local output = ut.trim(sys.exec("iptables -L -t mangle -v -n")) - luci.http.write("Output of \"iptables -L -t mangle -v -n\"") - luci.http.write("\n") - luci.http.write(dash) - luci.http.write("\n") - if output ~= "" then - luci.http.write(output) - luci.http.write("\n") - else - luci.http.write("No data found") - luci.http.write("\n") - end -end diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/globalsconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/globalsconfig.lua deleted file mode 100644 index ec4085eb4b..0000000000 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/globalsconfig.lua +++ /dev/null @@ -1,42 +0,0 @@ --- Copyright 2017 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -local net = require "luci.model.network".init() - -local s, m, o - -m = Map("mwan3", translate("MWAN - Globals")) - -s = m:section(NamedSection, "globals", "globals", nil) - -o = s:option(Value, "mmx_mask", - translate("Firewall mask"), - translate("Enter value in hex, starting with <code>0x</code>")) -o.datatype = "hex(4)" -o.default = "0x3F00" - -o = s:option(Flag, "logging", - translate("Logging"), - translate("Enables global firewall logging")) - -o = s:option(ListValue, "loglevel", - translate("Loglevel"), - translate("Firewall loglevel")) -o.default = "notice" -o:value("emerg", translate("Emergency")) -o:value("alert", translate("Alert")) -o:value("crit", translate("Critical")) -o:value("error", translate("Error")) -o:value("warning", translate("Warning")) -o:value("notice", translate("Notice")) -o:value("info", translate("Info")) -o:value("debug", translate("Debug")) -o:depends("logging", "1") - -o = s:option(DynamicList, "rt_table_lookup", - translate("Routing table lookup"), - translate("Also scan this Routing table for connected networks")) -o.datatype = "integer" -o:value("220", translatef("Routing table %d", 220)) - -return m diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua deleted file mode 100644 index 6e34311d06..0000000000 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua +++ /dev/null @@ -1,241 +0,0 @@ --- Copyright 2014 Aedan Renner <chipdankly@gmail.com --- Copyright 2018 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -local dsp = require "luci.dispatcher" -local uci = require "uci" - -local m, mwan_interface, enabled, track_method, reliability, interval -local down, up, metric - -function interfaceWarnings(overview, count, iface_max) - local warnings = "" - if count <= iface_max then - warnings = string.format("<strong>%s</strong><br />", - translatef("There are currently %d of %d supported interfaces configured", count, iface_max) - ) - else - warnings = string.format("<strong>%s</strong><br />", - translatef("WARNING: %d interfaces are configured exceeding the maximum of %d!", count, iface_max) - ) - end - - for i, k in pairs(overview) do - if overview[i]["network"] == false then - warnings = warnings .. string.format("<strong>%s</strong><br />", - translatef("WARNING: Interface %s are not found in /etc/config/network", i) - ) - end - - if overview[i]["default_route"] == false then - warnings = warnings .. string.format("<strong>%s</strong><br />", - translatef("WARNING: Interface %s has no default route in the main routing table", i) - ) - end - - if overview[i]["reliability"] == false then - warnings = warnings .. string.format("<strong>%s</strong><br />", - translatef("WARNING: Interface %s has a higher reliability " .. - "requirement than tracking hosts (%d)", i, overview[i]["tracking"]) - ) - end - - if overview[i]["duplicate_metric"] == true then - warnings = warnings .. string.format("<strong>%s</strong><br />", - translatef("WARNING: Interface %s has a duplicate metric %s configured", i, overview[i]["metric"]) - ) - end - end - - return warnings -end - -function configCheck() - local overview = {} - local count = 0 - local duplicate_metric = {} - uci.cursor():foreach("mwan3", "interface", - function (section) - local uci = uci.cursor(nil, "/var/state") - local iface = section[".name"] - overview[iface] = {} - count = count + 1 - local network = uci:get("network", iface) - overview[iface]["network"] = false - if network ~= nil then - overview[iface]["network"] = true - - local device = uci:get("network", iface, "ifname") - if device ~= nil then - overview[iface]["device"] = device - end - - local metric = uci:get("network", iface, "metric") - if metric ~= nil then - overview[iface]["metric"] = metric - overview[iface]["duplicate_metric"] = false - for _, m in ipairs(duplicate_metric) do - if m == metric then - overview[iface]["duplicate_metric"] = true - end - end - table.insert(duplicate_metric, metric) - end - - local dump = require("luci.util").ubus("network.interface.%s" % iface, "status", {}) - overview[iface]["default_route"] = false - if dump and dump.route then - local _, route - for _, route in ipairs(dump.route) do - if dump.route[_].target == "0.0.0.0" then - overview[iface]["default_route"] = true - end - end - end - end - - local trackingNumber = uci:get("mwan3", iface, "track_ip") - overview[iface]["tracking"] = 0 - if trackingNumber and #trackingNumber > 0 then - overview[iface]["tracking"] = #trackingNumber - overview[iface]["reliability"] = false - local reliabilityNumber = tonumber(uci:get("mwan3", iface, "reliability") or "1") - if reliabilityNumber and reliabilityNumber <= #trackingNumber then - overview[iface]["reliability"] = true - end - end - end - ) - - -- calculate iface_max usage from firewall mmx_mask - function bit(p) - return 2 ^ (p - 1) - end - function hasbit(x, p) - return x % (p + p) >= p - end - function setbit(x, p) - return hasbit(x, p) and x or x + p - end - - local uci = require("uci").cursor(nil, "/var/state") - local mmx_mask = uci:get("mwan3", "globals", "mmx_mask") or "0x3F00" - local number = tonumber(mmx_mask, 16) - local bits = 0 - local iface_max = 0 - for i=1,16 do - if hasbit(number, bit(i)) then - bits = bits + 1 - iface_max = setbit( iface_max, bit(bits)) - end - end - - -- subtract blackhole, unreachable and default table from iface_max - iface_max = iface_max - 3 - - return overview, count, iface_max -end - -m = Map("mwan3", translate("MWAN - Interfaces"), - interfaceWarnings(configCheck())) - -mwan_interface = m:section(TypedSection, "interface", nil, - translate("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<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 = translate("Interface") -mwan_interface.sortable = false -mwan_interface.template = "cbi/tblsection" -mwan_interface.extedit = dsp.build_url("admin", "network", "mwan", "interface", "%s") -function mwan_interface.create(self, section) - TypedSection.create(self, section) - m.uci:save("mwan3") - luci.http.redirect(dsp.build_url("admin", "network", "mwan", "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 translate("Yes") - else - return translate("No") - end -end - -track_method = mwan_interface:option(DummyValue, "track_method", translate("Tracking method")) -track_method.rawhtml = true -function track_method.cfgvalue(self, s) - local tracked = self.map:get(s, "track_ip") - if tracked then - return self.map:get(s, "track_method") or "ping" - else - return "—" - end -end - -reliability = mwan_interface:option(DummyValue, "reliability", translate("Tracking reliability")) -reliability.rawhtml = true -function reliability.cfgvalue(self, s) - local tracked = self.map:get(s, "track_ip") - if tracked then - return self.map:get(s, "reliability") or "1" - else - return "—" - end -end - -interval = mwan_interface:option(DummyValue, "interval", translate("Ping interval")) -interval.rawhtml = true -function interval.cfgvalue(self, s) - local tracked = self.map:get(s, "track_ip") - if tracked then - local intervalValue = self.map:get(s, "interval") - if intervalValue then - return intervalValue .. "s" - else - return "5s" - end - else - return "—" - end -end - -down = mwan_interface:option(DummyValue, "down", translate("Interface down")) -down.rawhtml = true -function down.cfgvalue(self, s) - local tracked = self.map:get(s, "track_ip") - if tracked then - return self.map:get(s, "down") or "3" - else - return "—" - end -end - -up = mwan_interface:option(DummyValue, "up", translate("Interface up")) -up.rawhtml = true -function up.cfgvalue(self, s) - local tracked = self.map:get(s, "track_ip") - if tracked then - return self.map:get(s, "up") or "3" - else - return "—" - end -end - -metric = mwan_interface:option(DummyValue, "metric", translate("Metric")) -metric.rawhtml = true -function metric.cfgvalue(self, s) - local uci = uci.cursor(nil, "/var/state") - local metric = uci:get("network", s, "metric") - if metric then - return metric - else - return "—" - end -end - -return m diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interfaceconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interfaceconfig.lua deleted file mode 100644 index ea07bd4f12..0000000000 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/interfaceconfig.lua +++ /dev/null @@ -1,262 +0,0 @@ --- Copyright 2014 Aedan Renner <chipdankly@gmail.com> --- Copyright 2018 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -local dsp = require "luci.dispatcher" - -local m, mwan_interface, enabled, initial_state, family, track_ip -local track_method, reliability, count, size, max_ttl -local check_quality, failure_latency, failure_loss, recovery_latency -local recovery_loss, timeout, interval, failure -local keep_failure, recovery, down, up, flush, metric -local httping_ssl - -arg[1] = arg[1] or "" - -m = Map("mwan3", translatef("MWAN Interface Configuration - %s", arg[1])) -m.redirect = dsp.build_url("admin", "network", "mwan", "interface") - -mwan_interface = m:section(NamedSection, arg[1], "interface", "") -mwan_interface.addremove = false -mwan_interface.dynamic = false - -enabled = mwan_interface:option(Flag, "enabled", translate("Enabled")) -enabled.default = false - -initial_state = mwan_interface:option(ListValue, "initial_state", translate("Initial state"), - translate("Expect interface state on up event")) -initial_state.default = "online" -initial_state:value("online", translate("Online")) -initial_state:value("offline", translate("Offline")) - -family = mwan_interface:option(ListValue, "family", translate("Internet Protocol")) -family.default = "ipv4" -family:value("ipv4", translate("IPv4")) -family:value("ipv6", translate("IPv6")) - -track_ip = mwan_interface:option(DynamicList, "track_ip", translate("Tracking hostname or IP address"), - translate("This hostname or IP address will be pinged to determine if the link is up or down. Leave blank to assume interface is always online")) -track_ip.datatype = "host" - -track_method = mwan_interface:option(ListValue, "track_method", translate("Tracking method")) -track_method.default = "ping" -track_method:value("ping") -if os.execute("command -v nping 1>/dev/null") == 0 then - track_method:value("nping-tcp") - track_method:value("nping-udp") - track_method:value("nping-icmp") - track_method:value("nping-arp") -end - -if os.execute("command -v arping 1>/dev/null") == 0 then - track_method:value("arping") -end - -if os.execute("command -v httping 1>/dev/null") == 0 then - track_method:value("httping") -end - -httping_ssl = mwan_interface:option(Flag, "httping_ssl", translate("Enable ssl tracking"), - translate("Enables https tracking on ssl port 443")) -httping_ssl:depends("track_method", "httping") -httping_ssl.rmempty = false -httping_ssl.default = httping_ssl.enabled - -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") - -size = mwan_interface:option(Value, "size", translate("Ping size")) -size.default = "56" -size:depends("track_method", "ping") -size:value("8") -size:value("24") -size:value("56") -size:value("120") -size:value("248") -size:value("504") -size:value("1016") -size:value("1472") -size:value("2040") -size.datatype = "range(1, 65507)" - -max_ttl = mwan_interface:option(Value, "max_ttl", translate("Max TTL")) -max_ttl.default = "60" -max_ttl:depends("track_method", "ping") -max_ttl:value("10") -max_ttl:value("20") -max_ttl:value("30") -max_ttl:value("40") -max_ttl:value("50") -max_ttl:value("60") -max_ttl:value("70") -max_ttl.datatype = "range(1, 255)" - -check_quality = mwan_interface:option(Flag, "check_quality", translate("Check link quality")) -check_quality:depends("track_method", "ping") -check_quality.default = false - -failure_latency = mwan_interface:option(Value, "failure_latency", translate("Failure latency [ms]")) -failure_latency:depends("check_quality", 1) -failure_latency.default = "1000" -failure_latency:value("25") -failure_latency:value("50") -failure_latency:value("75") -failure_latency:value("100") -failure_latency:value("150") -failure_latency:value("200") -failure_latency:value("250") -failure_latency:value("300") - -failure_loss = mwan_interface:option(Value, "failure_loss", translate("Failure packet loss [%]")) -failure_loss:depends("check_quality", 1) -failure_loss.default = "40" -failure_loss:value("2") -failure_loss:value("5") -failure_loss:value("10") -failure_loss:value("20") -failure_loss:value("25") - -recovery_latency = mwan_interface:option(Value, "recovery_latency", translate("Recovery latency [ms]")) -recovery_latency:depends("check_quality", 1) -recovery_latency.default = "500" -recovery_latency:value("25") -recovery_latency:value("50") -recovery_latency:value("75") -recovery_latency:value("100") -recovery_latency:value("150") -recovery_latency:value("200") -recovery_latency:value("250") -recovery_latency:value("300") - -recovery_loss = mwan_interface:option(Value, "recovery_loss", translate("Recovery packet loss [%]")) -recovery_loss:depends("check_quality", 1) -recovery_loss.default = "10" -recovery_loss:value("2") -recovery_loss:value("5") -recovery_loss:value("10") -recovery_loss:value("20") -recovery_loss:value("25") - -timeout = mwan_interface:option(ListValue, "timeout", translate("Ping timeout")) -timeout.default = "4" -timeout:value("1", translatef("%d second", 1)) -timeout:value("2", translatef("%d seconds", 2)) -timeout:value("3", translatef("%d seconds", 3)) -timeout:value("4", translatef("%d seconds", 4)) -timeout:value("5", translatef("%d seconds", 5)) -timeout:value("6", translatef("%d seconds", 6)) -timeout:value("7", translatef("%d seconds", 7)) -timeout:value("8", translatef("%d seconds", 8)) -timeout:value("9", translatef("%d seconds", 9)) -timeout:value("10", translatef("%d seconds", 10)) - -interval = mwan_interface:option(ListValue, "interval", translate("Ping interval")) -interval.default = "10" -interval:value("1", translatef("%d second", 1)) -interval:value("3", translatef("%d seconds", 3)) -interval:value("5", translatef("%d seconds", 5)) -interval:value("10", translatef("%d seconds", 10)) -interval:value("20", translatef("%d seconds", 20)) -interval:value("30", translatef("%d seconds", 30)) -interval:value("60", translatef("%d minute", 1)) -interval:value("300", translatef("%d minutes", 5)) -interval:value("600", translatef("%d minutes", 10)) -interval:value("900", translatef("%d minutes", 15)) -interval:value("1800", translatef("%d minutes", 30)) -interval:value("3600", translatef("%d hour", 1)) - -failure = mwan_interface:option(Value, "failure_interval", translate("Failure interval"), - translate("Ping interval during failure detection")) -failure.default = "5" -failure:value("1", translatef("%d second", 1)) -failure:value("3", translatef("%d seconds", 3)) -failure:value("5", translatef("%d seconds", 5)) -failure:value("10", translatef("%d seconds", 10)) -failure:value("20", translatef("%d seconds", 20)) -failure:value("30", translatef("%d seconds", 30)) -failure:value("60", translatef("%d minute", 1)) -failure:value("300", translatef("%d minutes", 5)) -failure:value("600", translatef("%d minutes", 10)) -failure:value("900", translatef("%d minutes", 15)) -failure:value("1800", translatef("%d minutes", 30)) -failure:value("3600", translatef("%d hour", 1)) - -keep_failure = mwan_interface:option(Flag, "keep_failure_interval", translate("Keep failure interval"), - translate("Keep ping failure interval during failure state")) -keep_failure.default = keep_failure.disabled - -recovery = mwan_interface:option(Value, "recovery_interval", translate("Recovery interval"), - translate("Ping interval during failure recovering")) -recovery.default = "5" -recovery:value("1", translatef("%d second", 1)) -recovery:value("3", translatef("%d seconds", 3)) -recovery:value("5", translatef("%d seconds", 5)) -recovery:value("10", translatef("%d seconds", 10)) -recovery:value("20", translatef("%d seconds", 20)) -recovery:value("30", translatef("%d seconds", 30)) -recovery:value("60", translatef("%d minute", 1)) -recovery:value("300", translatef("%d minutes", 5)) -recovery:value("600", translatef("%d minutes", 10)) -recovery:value("900", translatef("%d minutes", 15)) -recovery:value("1800", translatef("%d minutes", 30)) -recovery:value("3600", translatef("%d hour", 1)) - -down = mwan_interface:option(ListValue, "down", translate("Interface down"), - translate("Interface will be deemed down after this many failed ping tests")) -down.default = "5" -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 = "5" -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") - -flush = mwan_interface:option(StaticList, "flush_conntrack", translate("Flush conntrack table"), - translate("Flush global firewall conntrack table on interface events")) -flush:value("ifup", translate("ifup (netifd)")) -flush:value("ifdown", translate("ifdown (netifd)")) -flush:value("connected", translate("connected (mwan3)")) -flush:value("disconnected", translate("disconnected (mwan3)")) - -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) - local uci = require "luci.model.uci".cursor(nil, "/var/state") - local metric = uci:get("network", arg[1], "metric") - if metric then - return metric - else - return "—" - end -end - -return m diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua deleted file mode 100644 index 5c3d0c1524..0000000000 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua +++ /dev/null @@ -1,45 +0,0 @@ --- Copyright 2014 Aedan Renner <chipdankly@gmail.com> --- Copyright 2018 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -local dsp = require "luci.dispatcher" - -local m, s, o - -m = Map("mwan3", translate("MWAN - Members")) - -s = m:section(TypedSection, "member", nil, - translate("Members are profiles attaching a metric and weight to an MWAN interface<br />" .. - "Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />" .. - "Members may not share the same name as configured interfaces, policies or rules")) -s.addremove = true -s.dynamic = false -s.sectionhead = translate("Member") -s.sortable = true -s.template = "cbi/tblsection" -s.extedit = dsp.build_url("admin", "network", "mwan", "member", "%s") -function s.create(self, section) - TypedSection.create(self, section) - m.uci:save("mwan3") - luci.http.redirect(dsp.build_url("admin", "network", "mwan", "member", section)) -end - -o = s:option(DummyValue, "interface", translate("Interface")) -o.rawhtml = true -function o.cfgvalue(self, s) - return self.map:get(s, "interface") or "—" -end - -o = s:option(DummyValue, "metric", translate("Metric")) -o.rawhtml = true -function o.cfgvalue(self, s) - return self.map:get(s, "metric") or "1" -end - -o = s:option(DummyValue, "weight", translate("Weight")) -o.rawhtml = true -function o.cfgvalue(self, s) - return self.map:get(s, "weight") or "1" -end - -return m diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/memberconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/memberconfig.lua deleted file mode 100644 index 3464ebfc92..0000000000 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/memberconfig.lua +++ /dev/null @@ -1,33 +0,0 @@ --- Copyright 2014 Aedan Renner <chipdankly@gmail.com> --- Copyright 2018 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -local dsp = require "luci.dispatcher" - -local m, mwan_member, interface, metric, weight - -arg[1] = arg[1] or "" - -m = Map("mwan3", translatef("MWAN Member Configuration - %s", arg[1])) -m.redirect = dsp.build_url("admin", "network", "mwan", "member") - -mwan_member = m:section(NamedSection, arg[1], "member", "") -mwan_member.addremove = false -mwan_member.dynamic = false - -interface = mwan_member:option(Value, "interface", translate("Interface")) -m.uci:foreach("mwan3", "interface", - function(s) - interface:value(s['.name'], s['.name']) - end -) - -metric = mwan_member:option(Value, "metric", translate("Metric"), - translate("Acceptable values: 1-256. Defaults to 1 if not set")) -metric.datatype = "range(1, 256)" - -weight = mwan_member:option(Value, "weight", translate("Weight"), - translate("Acceptable values: 1-1000. Defaults to 1 if not set")) -weight.datatype = "range(1, 1000)" - -return m diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/notify.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/notify.lua deleted file mode 100644 index ff1d338eee..0000000000 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/notify.lua +++ /dev/null @@ -1,46 +0,0 @@ --- Copyright 2014 Aedan Renner <chipdankly@gmail.com> --- Copyright 2018 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -local fs = require "nixio.fs" -local ut = require "luci.util" -local script = "/etc/mwan3.user" - -local m, f, t - -m = SimpleForm("luci", translate("MWAN - Notification")) - -f = m:section(SimpleSection, nil, - translate("This section allows you to modify the content of \"/etc/mwan3.user\".<br />" .. - "The file is also preserved during sysupgrade.<br />" .. - "<br />" .. - "Notes:<br />" .. - "This file is interpreted as a shell script.<br />" .. - "The first line of the script must be "#!/bin/sh" without quotes.<br />" .. - "Lines beginning with # are comments and are not executed.<br />" .. - "Put your custom mwan3 action here, they will<br />" .. - "be executed with each netifd hotplug interface event<br />" .. - "on interfaces for which mwan3 is enabled.<br />" .. - "<br />" .. - "There are three main environment variables that are passed to this script.<br />" .. - "<br />" .. - "$ACTION <br />" .. - "* \"ifup\" Is called by netifd and mwan3track <br />" .. - "* \"ifdown\" Is called by netifd and mwan3track <br />" .. - "* \"connected\" Is only called by mwan3track if tracking was successful <br />" .. - "* \"disconnected\" Is only called by mwan3track if tracking has failed <br />" .. - "$INTERFACE Name of the interface which went up or down (e.g. \"wan\" or \"wwan\")<br />" .. - "$DEVICE Physical device name which interface went up or down (e.g. \"eth0\" or \"wwan0\")<br />" .. - "<br />")) - -t = f:option(TextValue, "lines") -t.rmempty = true -t.rows = 20 -function t.cfgvalue() - return fs.readfile(script) -end -function t.write(self, section, data) - return fs.writefile(script, ut.trim(data:gsub("\r\n", "\n")) .. "\n") -end - -return m diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua deleted file mode 100644 index 48a4dcce38..0000000000 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua +++ /dev/null @@ -1,92 +0,0 @@ --- Copyright 2014 Aedan Renner <chipdankly@gmail.com> --- Copyright 2018 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -local dsp = require "luci.dispatcher" -local uci = require "uci" - -local m, s, o - -function policyCheck() - local policy_error = {} - - uci.cursor():foreach("mwan3", "policy", - function (section) - policy_error[section[".name"]] = false - if string.len(section[".name"]) > 15 then - policy_error[section[".name"]] = true - end - end - ) - - return policy_error -end - -function policyError(policy_error) - local warnings = "" - for i, k in pairs(policy_error) do - if policy_error[i] == true then - warnings = warnings .. string.format("<strong>%s</strong><br />", - translatef("WARNING: Policy %s has exceeding the maximum name of 15 characters", i) - ) - end - end - - return warnings -end - -m = Map("mwan3", translate("MWAN - Policies"), - policyError(policyCheck())) - -s = m:section(TypedSection, "policy", nil, - translate("Policies are profiles grouping one or more members controlling how MWAN distributes traffic<br />" .. - "Member interfaces with lower metrics are used first<br />" .. - "Member interfaces with the same metric will be load-balanced<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<br />" .. - "Names must be 15 characters or less<br />" .. - "Policies may not share the same name as configured interfaces, members or rules")) -s.addremove = true -s.dynamic = false -s.sectionhead = translate("Policy") -s.sortable = true -s.template = "cbi/tblsection" -s.extedit = dsp.build_url("admin", "network", "mwan", "policy", "%s") -function s.create(self, section) - if #section > 15 then - self.invalid_cts = true - else - TypedSection.create(self, section) - m.uci:save("mwan3") - luci.http.redirect(dsp.build_url("admin", "network", "mwan", "policy", section)) - end -end - -o = s:option(DummyValue, "use_member", translate("Members assigned")) -o.rawhtml = true -function o.cfgvalue(self, s) - local memberConfig, memberList = self.map:get(s, "use_member"), "" - if memberConfig then - for k,v in pairs(memberConfig) do - memberList = memberList .. v .. "<br />" - end - return memberList - else - return "—" - end -end - -o = s:option(DummyValue, "last_resort", translate("Last resort")) -o.rawhtml = true -function o.cfgvalue(self, s) - local action = self.map:get(s, "last_resort") - if action == "blackhole" then - return translate("blackhole (drop)") - elseif action == "default" then - return translate("default (use main routing table)") - else - return translate("unreachable (reject)") - end -end - -return m diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policyconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policyconfig.lua deleted file mode 100644 index 8e5a3fa950..0000000000 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/policyconfig.lua +++ /dev/null @@ -1,32 +0,0 @@ --- Copyright 2014 Aedan Renner <chipdankly@gmail.com> --- Copyright 2018 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -local dsp = require "luci.dispatcher" - -local m, mwan_policy, member, last_resort - -arg[1] = arg[1] or "" - -m = Map("mwan3", translatef("MWAN Policy Configuration - %s", arg[1])) -m.redirect = dsp.build_url("admin", "network", "mwan", "policy") - -mwan_policy = m:section(NamedSection, arg[1], "policy", "") -mwan_policy.addremove = false -mwan_policy.dynamic = false - -member = mwan_policy:option(DynamicList, "use_member", translate("Member used")) -m.uci:foreach("mwan3", "member", - function(s) - member:value(s['.name'], s['.name']) - end -) - -last_resort = mwan_policy:option(ListValue, "last_resort", translate("Last resort"), - translate("When all policy members are offline use this behavior for matched traffic")) -last_resort.default = "unreachable" -last_resort:value("unreachable", translate("unreachable (reject)")) -last_resort:value("blackhole", translate("blackhole (drop)")) -last_resort:value("default", translate("default (use main routing table)")) - -return m diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua deleted file mode 100644 index 1a97d40c1a..0000000000 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua +++ /dev/null @@ -1,109 +0,0 @@ --- Copyright 2014 Aedan Renner <chipdankly@gmail.com> --- Copyright 2018 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -local dsp = require "luci.dispatcher" -local uci = require "uci" - -local m, mwan_rule, src_ip, src_port, dest_ip, dest_port, proto, use_policy - -function ruleCheck() - local rule_error = {} - uci.cursor():foreach("mwan3", "rule", - function (section) - rule_error[section[".name"]] = false - local uci = uci.cursor(nil, "/var/state") - local sourcePort = uci:get("mwan3", section[".name"], "src_port") - local destPort = uci:get("mwan3", section[".name"], "dest_port") - if sourcePort ~= nil or destPort ~= nil then - local protocol = uci:get("mwan3", section[".name"], "proto") - if protocol == nil or protocol == "all" then - rule_error[section[".name"]] = true - end - end - end - ) - return rule_error -end - -function ruleWarn(rule_error) - local warnings = "" - for i, k in pairs(rule_error) do - if rule_error[i] == true then - warnings = warnings .. string.format("<strong>%s</strong><br />", - translatef("WARNING: Rule %s have a port configured with no or improper protocol specified!", i) - ) - end - end - - return warnings -end - -m = Map("mwan3", translate("MWAN - Rules"), - ruleWarn(ruleCheck()) - ) - -mwan_rule = m:section(TypedSection, "rule", nil, - translate("Rules specify which traffic will use a particular MWAN policy<br />" .. - "Rules are based on IP address, port or protocol<br />" .. - "Rules are matched from top to bottom<br />" .. - "Rules below a matching rule are ignored<br />" .. - "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<br />" .. - "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 = translate("Rule") -mwan_rule.sortable = true -mwan_rule.template = "cbi/tblsection" -mwan_rule.extedit = dsp.build_url("admin", "network", "mwan", "rule", "%s") -function mwan_rule.create(self, section) - if #section > 15 then - self.invalid_cts = true - else - TypedSection.create(self, section) - m.uci:save("mwan3") - luci.http.redirect(dsp.build_url("admin", "network", "mwan", "rule", section)) - end -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 - -return m diff --git a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/ruleconfig.lua b/applications/luci-app-mwan3/luasrc/model/cbi/mwan/ruleconfig.lua deleted file mode 100644 index eca53959a4..0000000000 --- a/applications/luci-app-mwan3/luasrc/model/cbi/mwan/ruleconfig.lua +++ /dev/null @@ -1,85 +0,0 @@ --- Copyright 2014 Aedan Renner <chipdankly@gmail.com> --- Copyright 2018 Florian Eckert <fe@dev.tdt.de> --- Licensed to the public under the GNU General Public License v2. - -local dsp = require "luci.dispatcher" -local util = require("luci.util") - -local m, s, o - -arg[1] = arg[1] or "" - -local ipsets = util.split(util.trim(util.exec("ipset -n -L 2>/dev/null | grep -v mwan3_ | sort")), "\n", nil, true) or {} - -m = Map("mwan3", translatef("MWAN Rule Configuration - %s", arg[1])) -m.redirect = dsp.build_url("admin", "network", "mwan", "rule") - -s = m:section(NamedSection, arg[1], "rule", "") -s.addremove = false -s.dynamic = false - -o = s:option(ListValue, "family", translate("Internet Protocol")) -o.default = "" -o:value("", translate("IPv4 and IPv6")) -o:value("ipv4", translate("IPv4 only")) -o:value("ipv6", translate("IPv6 only")) - -o = s:option(Value, "src_ip", translate("Source address"), - translate("Supports CIDR notation (eg \"192.168.100.0/24\") without quotes")) -o.datatype = ipaddr - -o = s: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")) -o:depends("proto", "tcp") -o:depends("proto", "udp") - -o = s:option(Value, "dest_ip", translate("Destination address"), - translate("Supports CIDR notation (eg \"192.168.100.0/24\") without quotes")) -o.datatype = ipaddr - -o = s: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")) -o:depends("proto", "tcp") -o:depends("proto", "udp") - -o = s:option(Value, "proto", translate("Protocol"), - translate("View the content of /etc/protocols for protocol description")) -o.default = "all" -o.rmempty = false -o:value("all") -o:value("tcp") -o:value("udp") -o:value("icmp") -o:value("esp") - -o = s:option(ListValue, "sticky", translate("Sticky"), - translate("Traffic from the same source IP address that previously matched this rule within the sticky timeout period will use the same WAN interface")) -o.default = "0" -o:value("1", translate("Yes")) -o:value("0", translate("No")) - -o = s:option(Value, "timeout", translate("Sticky timeout"), - translate("Seconds. Acceptable values: 1-1000000. Defaults to 600 if not set")) -o.datatype = "range(1, 1000000)" - -o = s:option(Value, "ipset", translate("IPset"), - translate("Name of IPset rule. Requires IPset rule in /etc/dnsmasq.conf (eg \"ipset=/youtube.com/youtube\")")) -o:value("", translate("-- Please choose --")) -for _, z in ipairs(ipsets) do - o:value(z) -end - -o = s:option(Flag, "logging", translate("Logging"), - translate("Enables firewall rule logging (global mwan3 logging must also be enabled)")) - -o = s:option(Value, "use_policy", translate("Policy assigned")) -m.uci:foreach("mwan3", "policy", - function(s) - o:value(s['.name'], s['.name']) - end -) -o:value("unreachable", translate("unreachable (reject)")) -o:value("blackhole", translate("blackhole (drop)")) -o:value("default", translate("default (use main routing table)")) - -return m diff --git a/applications/luci-app-mwan3/luasrc/view/admin_status/index/mwan.htm b/applications/luci-app-mwan3/luasrc/view/admin_status/index/mwan.htm deleted file mode 100644 index e4b3c06999..0000000000 --- a/applications/luci-app-mwan3/luasrc/view/admin_status/index/mwan.htm +++ /dev/null @@ -1,3 +0,0 @@ -<%if require("luci.sys").init.enabled("mwan3") then%> -<%+mwan/overview_status_interface%> -<%end%> diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/overview_status_interface.htm b/applications/luci-app-mwan3/luasrc/view/mwan/overview_status_interface.htm deleted file mode 100644 index b3210ee137..0000000000 --- a/applications/luci-app-mwan3/luasrc/view/mwan/overview_status_interface.htm +++ /dev/null @@ -1,114 +0,0 @@ -<%# - Copyright 2014 Aedan Renner <chipdankly@gmail.com> - Copyright 2018 Florian Eckert <fe@dev.tdt.de> - Licensed to the public under the GNU General Public License v2. --%> - -<script type="text/javascript">//<![CDATA[ - -function secondsToString(time) { - var seconds = parseInt(time, 10); - - var hrs = Math.floor(seconds / 3600); - seconds -= hrs*3600; - var mnts = Math.floor(seconds / 60); - seconds -= mnts*60; - return String.format("%sh:%sm:%ss", hrs, mnts, seconds); -} - -XHR.poll(-1, '<%=luci.dispatcher.build_url("admin", "status", "mwan", "interface_status")%>', null, - function(x, status) - { - var statusDiv = document.getElementById('mwan_status_text'); - if (status.interfaces) - { - var statusview = ''; - for ( var iface in status.interfaces) - { - var state = ''; - var css = ''; - var time = ''; - switch (status.interfaces[iface].status) - { - case 'online': - state = '<%:Online%>'; - time = String.format( - '<div><strong><%:Uptime%>: </strong>%s</div>', - secondsToString(status.interfaces[iface].online) - ); - css = 'success'; - break; - case 'offline': - state = '<%:Offline%>'; - time = String.format( - '<div><strong><%:Downtime%>: </strong>%s</div>', - secondsToString(status.interfaces[iface].offline) - ); - css = 'danger'; - break; - case 'notracking': - state = '<%:No Tracking%>'; - if ((status.interfaces[iface].uptime) > 0) { - time = String.format( - '<div><strong><%:Uptime%>: </strong>%s</div>', - secondsToString(status.interfaces[iface].uptime) - ); - css = 'success'; - } - else { - time = '<div> </div>' - css = 'warning'; - } - break; - default: - state = '<%:Disabled%>'; - time = '<div> </div>' - css = 'warning'; - break; - } - statusview += String.format( - '<div class="alert-message %s">', - css - ); - statusview += String.format( - '<div><strong><%:Interface%>: </strong>%s</div>', - iface - ); - statusview += String.format( - '<div><strong><%:Status%>: </strong>%s</div>', - state - ); - if (time) - { - statusview += time; - } - statusview += '</div>' - } - statusDiv.innerHTML = statusview; - } - else - { - statusDiv.innerHTML = '<strong><%:No MWAN interfaces found%></strong>'; - } - } - ); -//]]></script> - -<style type="text/css"> - #mwan_status_text > div { - display: inline-block; - margin: 1rem; - padding: 1rem; - width: 15rem; - float: left; - line-height: 125%; - } -</style> - -<fieldset id="interface_field" class="cbi-section"> - <legend><%:MWAN Interfaces%></legend> - <div id="mwan_status_text"> - <img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> - <%:Collecting data...%> - </div> -</fieldset> diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/status_detail.htm b/applications/luci-app-mwan3/luasrc/view/mwan/status_detail.htm deleted file mode 100644 index 77fce3f913..0000000000 --- a/applications/luci-app-mwan3/luasrc/view/mwan/status_detail.htm +++ /dev/null @@ -1,39 +0,0 @@ -<%# - Copyright 2014 Aedan Renner <chipdankly@gmail.com> - Copyright 2018 Florian Eckert <fe@dev.tdt.de> - Licensed to the public under the GNU General Public License v2. --%> - -<%+header%> - -<ul class="cbi-tabmenu"> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/overview")%>"><%:Interface%></a></li> - <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/detail")%>"><%:Detail%></a></li> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/diagnostics")%>"><%:Diagnostics%></a></li> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/troubleshooting")%>"><%:Troubleshooting%></a></li> -</ul> - -<script type="text/javascript">//<![CDATA[ - XHR.poll(-1, '<%=luci.dispatcher.build_url("admin", "status", "mwan", "detailed_status")%>', null, - function(x) - { - var output = document.getElementById('diag-rc-output'); - output.innerHTML = String.format('<pre>%h</pre>', x.responseText); - } - ); -//]]></script> - -<div class="cbi-map"> - <h2 name="content"><%:MWAN Status - Detail%></h2> - <%if not require("luci.sys").init.enabled("mwan3") then%> - <div><strong><%:INFO: MWAN not running%></strong></div> - <%end%> - <fieldset class="cbi-section"> - <span id="diag-rc-output"> - <img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align: middle;" /> - <%:Collecting data...%> - </span> - </fieldset> -</div> - -<%+footer%> diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/status_diagnostics.htm b/applications/luci-app-mwan3/luasrc/view/mwan/status_diagnostics.htm deleted file mode 100644 index b08f1d138a..0000000000 --- a/applications/luci-app-mwan3/luasrc/view/mwan/status_diagnostics.htm +++ /dev/null @@ -1,97 +0,0 @@ -<%# - Copyright 2014 Aedan Renner <chipdankly@gmail.com> - Copyright 2018 Florian Eckert <fe@dev.tdt.de> - Licensed to the public under the GNU General Public License v2. --%> - -<%+header%> - -<ul class="cbi-tabmenu"> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/overview")%>"><%:Interface%></a></li> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/detail")%>"><%:Detail%></a></li> - <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/diagnostics")%>"><%:Diagnostics%></a></li> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/troubleshooting")%>"><%:Troubleshooting%></a></li> -</ul> - -<% - local uci = require "luci.model.uci" - - local iface = {} - - uci.cursor():foreach("mwan3", "interface", - function (section) - table.insert(iface, section[".name"]) - end - ) -%> - -<script type="text/javascript">//<![CDATA[ - var stxhr = new XHR(); - - function update_status(iface, task) - { - var output = document.getElementById('diag-rc-output'); - - output.innerHTML = - '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align: middle;" />' + - "<%:Waiting for command to complete...%>" - ; - - output.parentNode.style.display = 'block'; - output.style.display = 'inline'; - - stxhr.post('<%=url('admin/status/mwan')%>/diagnostics_display' + '/' + iface + '/' + task, { token: '<%=token%>' }, - function(x) - { - output.innerHTML = String.format('<pre>%h</pre>', x.responseText); - } - ); - } -//]]></script> - -<form method="post" action="<%=url('admin/network/diagnostics')%>"> - <div class="cbi-map"> - <h2 name="content"><%:MWAN Status - Diagnostics%></h2> - <%if not require("luci.sys").init.enabled("mwan3") then%> - <div><strong><%:INFO: MWAN not running%></strong></div> - <%end%> - <div class="cbi-section"> - <div class="cbi-section-node"> - <div class="cbi-value"> - <label class="cbi-value-title"><%:Interface%></label> - <div class="cbi-value-field"> - <select class="cbi-input-select" name="iface"> - <% for _, z in ipairs(iface) do -%><option value="<%=z%>"><%=z%></option><%- end %> - </select> - </div> - </div> - </div> - - <div class="cbi-section-node"> - <div class="cbi-value"> - <label class="cbi-value-title"><%:Task%></label> - <div class="cbi-value-field"> - <select class="cbi-input-select" name="task"> - <option value="ping_gateway"><%:Ping default gateway%></option> - <option value="ping_trackips"><%:Ping tracking IP%></option> - <option value="check_rules"><%:Check IP rules%></option> - <option value="check_routes"><%:Check routing table%></option> - <option value="hotplug_ifup"><%:Hotplug ifup%></option> - <option value="hotplug_ifdown"><%:Hotplug ifdown%></option> - </select> - </div> - </div> - </div> - </div> - - <div class="cbi-section-create"> - <input type="button" value="<%:Execute%>" class="btn cbi-button cbi-button-apply" onclick="update_status(this.form.iface.value, this.form.task.value)"/> - </div> - - <div class="cbi-section" style="display:none"> - <span id="diag-rc-output"></span> - </div> - </div> -</form> - -<%+footer%> diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/status_interface.htm b/applications/luci-app-mwan3/luasrc/view/mwan/status_interface.htm deleted file mode 100644 index 962cde521d..0000000000 --- a/applications/luci-app-mwan3/luasrc/view/mwan/status_interface.htm +++ /dev/null @@ -1,20 +0,0 @@ -<%# - Copyright 2014 Aedan Renner <chipdankly@gmail.com> - Copyright 2018 Florian Eckert <fe@dev.tdt.de> - Licensed to the public under the GNU General Public License v2. --%> - -<%+header%> - -<ul class="cbi-tabmenu"> - <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/overview")%>"><%:Interface%></a></li> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/detail")%>"><%:Detail%></a></li> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/diagnostics")%>"><%:Diagnostics%></a></li> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/troubleshooting")%>"><%:Troubleshooting%></a></li> -</ul> - - -<div class="cbi-map"> - <%+mwan/overview_status_interface%> -</div> -<%+footer%> diff --git a/applications/luci-app-mwan3/luasrc/view/mwan/status_troubleshooting.htm b/applications/luci-app-mwan3/luasrc/view/mwan/status_troubleshooting.htm deleted file mode 100644 index a20516bd2a..0000000000 --- a/applications/luci-app-mwan3/luasrc/view/mwan/status_troubleshooting.htm +++ /dev/null @@ -1,39 +0,0 @@ -<%# - Copyright 2014 Aedan Renner <chipdankly@gmail.com> - Copyright 2018 Florian Eckert <fe@dev.tdt.de> - Licensed to the public under the GNU General Public License v2. --%> - -<%+header%> - -<ul class="cbi-tabmenu"> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/overview")%>"><%:Interface%></a></li> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/detail")%>"><%:Detail%></a></li> - <li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/diagnostics")%>"><%:Diagnostics%></a></li> - <li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/status/mwan/troubleshooting")%>"><%:Troubleshooting%></a></li> -</ul> - -<script type="text/javascript">//<![CDATA[ - XHR.poll(15, '<%=luci.dispatcher.build_url("admin", "status", "mwan", "troubleshooting_display")%>', null, - function(x) - { - var output = document.getElementById('diag-rc-output'); - output.innerHTML = String.format('<pre>%h</pre>', x.responseText); - } - ); -//]]></script> - -<div class="cbi-map"> - <h2 name="content"><%:MWAN Status - Troubleshooting%></h2> - <%if not require("luci.sys").init.enabled("mwan3") then%> - <div><strong><%:INFO: MWAN not running%></strong></div> - <%end%> - <fieldset class="cbi-section"> - <span id="diag-rc-output"> - <img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align: middle;" /> - <%:Collecting data...%> - </span> - </fieldset> -</div> - -<%+footer%> diff --git a/applications/luci-app-mwan3/root/usr/libexec/luci-mwan3 b/applications/luci-app-mwan3/root/usr/libexec/luci-mwan3 new file mode 100755 index 0000000000..8db3e4723f --- /dev/null +++ b/applications/luci-app-mwan3/root/usr/libexec/luci-mwan3 @@ -0,0 +1,199 @@ +#!/bin/sh +# +# Copyright (C) 2021 TDT AG <development@tdt.de> +# +# This is free software, licensed under the GNU General Public License v2. +# See https://www.gnu.org/licenses/gpl-2.0.txt for more information. +# + +. /lib/functions.sh +. /lib/functions/network.sh +. /usr/share/libubox/jshn.sh + +IIF=1000 +FWMARK=2000 +ID=0 + +usage() { + local status="$1" + local msg="$2" + if [ -n "$msg" ]; then + echo "$msg" + echo "" + fi + echo "Usage: $(basename "$0") <command>" + echo "command:" + echo " diag: diagnostic commands" + echo " ipset: ipset commands" + echo "" + echo "diag <command> <iface>" + echo "command:" + echo " gateway <iface>: ping interface gateway" + echo " tracking <iface>: ping interface tracking targets" + echo " rules <iface>: check interface routing rules" + echo " routes <iface>: check interface routing tables" + echo "" + echo "ipset <command>" + echo "command:" + echo " dump: show all configured ipset names" + + exit "$status" +} + +diag_gateway() { + local iface="$1" + + local gw + + network_get_gateway gw "${iface}" + [ -z "$gw" ] && network_get_gateway gw "${iface}_4" + + [ -z "$gw" ] && { + echo "No gateway for interface ${iface} found." + exit 2 + } + + mwan3 use "$iface" "ping" "-c" "5" "-W" "1" "$gw" +} + +diag_tracking() { + local iface="$1" + + checkips() { + local ip="$1" + local iface="$2" + + mwan3 use "$iface" "ping" "-c" "5" "-W" "1" "$ip" + } + + config_load mwan3 + config_list_foreach "$iface" "track_ip" checkips "$iface" +} + +iface_number() { + local cfg="$1" + local iface="$2" + + let number++ + + [ "$cfg" = "$iface" ] && { + ID="$number" + } +} + +diag_rules() { + local iface="$1" + + local number=0 + local iif=0 + local fwmark=0 + + local iif_rule iif_result + local fwmark_rule fwmark_result + + config_load mwan3 + config_foreach iface_number 'interface' "$iface" + + [ "$ID" = "0" ] && { + echo "Unable to get mwan3 interface number for \"$iface\"." + exit 2 + } + + let "iif=$IIF+$ID" + let "fwmark=$FWMARK+$ID" + + iif_rule="$(ip rule | grep ${iif})" + iif_result="$?" + + fwmark_rule="$(ip rule | grep ${fwmark})" + fwmark_result="$?" + + if [ "$fwmark_result" = 0 ] && [ "$iif_result" = 0 ]; then + echo "All required IP rules for interface \"$iface\" found" + echo "$fwmark_rule" + echo "$iif_rule" + elif [ "$fwmark_result" = 1 ] && [ "$iif_result" = 0 ]; then + echo "Only iif IP rule for interface \"$iface\" found" + echo "$iif_rule" + elif [ "$fwmark_result" = 0 ] && [ "$iif_result" = 1 ]; then + echo "Only fwmark IP rule for interface \"$iface\" found" + echo "$fwmark_rule" + else + echo "Missing fwmark and iif IP rule for interface \"$iface\"" + fi +} + +diag_routes() { + local iface="$1" + + local table table_result + + config_load mwan3 + config_foreach iface_number 'interface' "$iface" + + [ "$ID" = "0" ] && { + echo "Unable to get mwan3 interface number for \"$iface\"." + exit 2 + } + + table="$(ip route list table $ID)" + table_result="$?" + + if [ "$table_result" = 0 ]; then + echo "Routing table \"$ID\" for interface \"$iface\" found" + echo "$table" + else + echo "Routing table \"$ID\" for interface \"$iface\" not found" + fi +} + +diag_cmd() { + case "$1" in + gateway) + diag_gateway "$2" + ;; + tracking) + diag_tracking "$2" + ;; + rules) + diag_rules "$2" + ;; + routes) + diag_routes "$2" + ;; + *) + usage "1" "Command not supported" + ;; + esac +} + +ipset_dump() { + ipset -n -L 2>/dev/null | grep -v mwan3_ | sort -u +} + +ipset_cmd() { + case "$1" in + dump) + ipset_dump + ;; + *) + usage "1" "Command not supported" + ;; + esac +} + +main () { + case "$1" in + diag) + diag_cmd "$2" "$3" + ;; + ipset) + ipset_cmd "$2" + ;; + *) + usage "1" "Command not supported" + ;; + esac +} + +main "$@" diff --git a/applications/luci-app-mwan3/root/usr/share/luci/menu.d/luci-app-mwan3.json b/applications/luci-app-mwan3/root/usr/share/luci/menu.d/luci-app-mwan3.json new file mode 100644 index 0000000000..e646155743 --- /dev/null +++ b/applications/luci-app-mwan3/root/usr/share/luci/menu.d/luci-app-mwan3.json @@ -0,0 +1,103 @@ +{ + "admin/status/mwan3": { + "title": "MultiWAN Manager", + "order": "600", + "action": { + "type": "firstchild" + }, + "depends": { + "acl": [ "luci-app-mwan3" ] + } + }, + "admin/status/mwan3/overview": { + "title": "Overview", + "order": 10, + "action": { + "type": "view", + "path": "mwan3/status/overview" + } + }, + "admin/status/mwan3/detail": { + "title": "Status", + "order": 20, + "action": { + "type": "view", + "path": "mwan3/status/detail" + } + }, + "admin/status/mwan3/diagnostics": { + "title": "Diagnostics", + "order": 30, + "action": { + "type": "view", + "path": "mwan3/status/diagnostics" + } + }, + "admin/status/mwan3/troubleshooting": { + "title": "Troubleshooting", + "order": 40, + "action": { + "type": "view", + "path": "mwan3/status/troubleshooting" + } + }, + + "admin/network/mwan3": { + "title": "MultiWAN Manager", + "order": "600", + "action": { + "type": "firstchild" + }, + "depends": { + "acl": [ "luci-app-mwan3" ] + } + }, + "admin/network/mwan3/globals": { + "title": "Globals", + "order": 10, + "action": { + "type": "view", + "path": "mwan3/network/globals" + } + }, + "admin/network/mwan3/interface": { + "title": "Interface", + "order": 20, + "action": { + "type": "view", + "path": "mwan3/network/interface" + } + }, + "admin/network/mwan3/member": { + "title": "Member", + "order": 30, + "action": { + "type": "view", + "path": "mwan3/network/member" + } + }, + "admin/network/mwan3/policy": { + "title": "Policy", + "order": 40, + "action": { + "type": "view", + "path": "mwan3/network/policy" + } + }, + "admin/network/mwan3/rule": { + "title": "Rule", + "order": 50, + "action": { + "type": "view", + "path": "mwan3/network/rule" + } + }, + "admin/network/mwan3/notify": { + "title": "Notify", + "order": 60, + "action": { + "type": "view", + "path": "mwan3/network/notify" + } + } +} diff --git a/applications/luci-app-mwan3/root/usr/share/rpcd/acl.d/luci-app-mwan3.json b/applications/luci-app-mwan3/root/usr/share/rpcd/acl.d/luci-app-mwan3.json index 539ed0fb90..91dd225358 100644 --- a/applications/luci-app-mwan3/root/usr/share/rpcd/acl.d/luci-app-mwan3.json +++ b/applications/luci-app-mwan3/root/usr/share/rpcd/acl.d/luci-app-mwan3.json @@ -2,9 +2,30 @@ "luci-app-mwan3": { "description": "Grant UCI access for luci-app-mwan3", "read": { - "uci": [ "mwan3" ] + "file": { + "/etc/mwan3.user": [ "read" ], + "/usr/bin/httping": [ "list" ], + "/usr/bin/nping": [ "list" ], + "/usr/bin/arping": [ "list" ], + "/usr/sbin/mwan3 status": [ "exec" ], + "/usr/sbin/mwan3 ifup *": [ "exec" ], + "/usr/sbin/mwan3 ifdown *": [ "exec" ], + "/usr/sbin/mwan3 internal ipv4": [ "exec" ], + "/usr/sbin/mwan3 internal ipv6": [ "exec" ], + "/usr/libexec/luci-mwan3 diag * *": [ "exec" ], + "/usr/libexec/luci-mwan3 ipset *": [ "exec" ] + }, + "ubus": { + "mwan3": [ "status" ] + }, + "uci": [ "mwan3", "network" ] }, "write": { + "file": { + "/etc/mwan3.user": ["write"], + "/usr/sbin/mwan3 ifup *": [ "exec" ], + "/usr/sbin/mwan3 ifdown *": [ "exec" ] + }, "uci": [ "mwan3" ] } } |