summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFlorian Eckert <fe@dev.tdt.de>2021-07-02 15:23:39 +0200
committerFlorian Eckert <fe@dev.tdt.de>2021-07-09 08:38:11 +0200
commitf5f6b3e4f8e3faed4e9c9926e283c9cf226d4bd8 (patch)
treee2718f194ede046a90274443f6b61198f35a0961
parent12067c8308936abb567001650105b33bec9a56a9 (diff)
luci-app-mwan3: convert to JS
Signed-off-by: Florian Eckert <fe@dev.tdt.de>
-rw-r--r--applications/luci-app-mwan3/Makefile11
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/mwan3.css8
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/globals.js43
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/interface.js276
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/member.js43
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/notify.js52
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/policy.js46
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/rule.js107
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/detail.js22
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/diagnostics.js116
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/overview.js104
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/troubleshooting.js22
-rw-r--r--applications/luci-app-mwan3/htdocs/luci-static/resources/view/status/include/90_mwan3.js117
-rw-r--r--applications/luci-app-mwan3/luasrc/controller/mwan3.lua320
-rw-r--r--applications/luci-app-mwan3/luasrc/model/cbi/mwan/globalsconfig.lua42
-rw-r--r--applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua241
-rw-r--r--applications/luci-app-mwan3/luasrc/model/cbi/mwan/interfaceconfig.lua262
-rw-r--r--applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua45
-rw-r--r--applications/luci-app-mwan3/luasrc/model/cbi/mwan/memberconfig.lua33
-rw-r--r--applications/luci-app-mwan3/luasrc/model/cbi/mwan/notify.lua46
-rw-r--r--applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua92
-rw-r--r--applications/luci-app-mwan3/luasrc/model/cbi/mwan/policyconfig.lua32
-rw-r--r--applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua109
-rw-r--r--applications/luci-app-mwan3/luasrc/model/cbi/mwan/ruleconfig.lua85
-rw-r--r--applications/luci-app-mwan3/luasrc/view/admin_status/index/mwan.htm3
-rw-r--r--applications/luci-app-mwan3/luasrc/view/mwan/overview_status_interface.htm114
-rw-r--r--applications/luci-app-mwan3/luasrc/view/mwan/status_detail.htm39
-rw-r--r--applications/luci-app-mwan3/luasrc/view/mwan/status_diagnostics.htm97
-rw-r--r--applications/luci-app-mwan3/luasrc/view/mwan/status_interface.htm20
-rw-r--r--applications/luci-app-mwan3/luasrc/view/mwan/status_troubleshooting.htm39
-rwxr-xr-xapplications/luci-app-mwan3/root/usr/libexec/luci-mwan3199
-rw-r--r--applications/luci-app-mwan3/root/usr/share/luci/menu.d/luci-app-mwan3.json103
-rw-r--r--applications/luci-app-mwan3/root/usr/share/rpcd/acl.d/luci-app-mwan3.json23
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 &#34;#!/bin/sh&#34; 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:&nbsp;</strong>%h</div>'.format(_('Interface'), iface);
+ statusview += '<div><strong>%h:&nbsp;</strong>%h</div>'.format(_('Status'), state);
+
+ if (time)
+ statusview += '<div><strong>%h:&nbsp;</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 "&#8212;"
- 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 "&#8212;"
- 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 "&#8212;"
- 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 "&#8212;"
- 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 "&#8212;"
- 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 "&#8212;"
- 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 "&#8212;"
- 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 "&#8212;"
-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 &#34;#!/bin/sh&#34; 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 "&#8212;"
- 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 "&#8212;"
-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 "&#8212;"
-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 "&#8212;"
-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 "&#8212;"
-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 "&#8212;"
-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%>:&nbsp;</strong>%s</div>',
- secondsToString(status.interfaces[iface].online)
- );
- css = 'success';
- break;
- case 'offline':
- state = '<%:Offline%>';
- time = String.format(
- '<div><strong><%:Downtime%>:&nbsp;</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%>:&nbsp;</strong>%s</div>',
- secondsToString(status.interfaces[iface].uptime)
- );
- css = 'success';
- }
- else {
- time = '<div>&nbsp;</div>'
- css = 'warning';
- }
- break;
- default:
- state = '<%:Disabled%>';
- time = '<div>&nbsp;</div>'
- css = 'warning';
- break;
- }
- statusview += String.format(
- '<div class="alert-message %s">',
- css
- );
- statusview += String.format(
- '<div><strong><%:Interface%>:&nbsp;</strong>%s</div>',
- iface
- );
- statusview += String.format(
- '<div><strong><%:Status%>:&nbsp;</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" ]
}
}